imgupload, comment 모듈 헥사고날 아키텍쳐로 리아키텍쳐링
This commit is contained in:
@@ -5,8 +5,8 @@ import lombok.RequiredArgsConstructor;
|
|||||||
import myblog.blog.category.appliacation.port.incomming.CategoryUseCase;
|
import myblog.blog.category.appliacation.port.incomming.CategoryUseCase;
|
||||||
import myblog.blog.category.appliacation.port.response.CategoryViewForLayout;
|
import myblog.blog.category.appliacation.port.response.CategoryViewForLayout;
|
||||||
import myblog.blog.category.appliacation.port.response.CategorySimpleDto;
|
import myblog.blog.category.appliacation.port.response.CategorySimpleDto;
|
||||||
import myblog.blog.comment.dto.CommentDtoForLayout;
|
import myblog.blog.comment.application.port.incomming.CommentDtoForLayout;
|
||||||
import myblog.blog.comment.service.CommentService;
|
import myblog.blog.comment.application.CommentService;
|
||||||
|
|
||||||
import org.springframework.stereotype.Controller;
|
import org.springframework.stereotype.Controller;
|
||||||
import org.springframework.ui.Model;
|
import org.springframework.ui.Model;
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package myblog.blog.category.adapter.imcomming;
|
package myblog.blog.category.adapter.imcomming;
|
||||||
|
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import myblog.blog.shared.exception.CustomFormException;
|
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import org.springframework.validation.Errors;
|
import org.springframework.validation.Errors;
|
||||||
import org.springframework.validation.Validator;
|
import org.springframework.validation.Validator;
|
||||||
@@ -31,7 +30,7 @@ public class CategoryListValidator implements Validator {
|
|||||||
springValidatorAdapter.validate(object,errors);
|
springValidatorAdapter.validate(object,errors);
|
||||||
}
|
}
|
||||||
if (errors.hasErrors()) {
|
if (errors.hasErrors()) {
|
||||||
throw new CustomFormException(Objects.requireNonNull(errors.getFieldError()).getDefaultMessage());
|
throw new InvalidCategoryRequestException(Objects.requireNonNull(errors.getFieldError()).getDefaultMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package myblog.blog.category.adapter.imcomming;
|
||||||
|
/*
|
||||||
|
- REST 컨트롤러 상태 메세지 전송용 커스텀 에러
|
||||||
|
*/
|
||||||
|
public class InvalidCategoryRequestException extends RuntimeException {
|
||||||
|
public InvalidCategoryRequestException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,35 +1,31 @@
|
|||||||
package myblog.blog.comment.controller;
|
package myblog.blog.comment.adapter.incomming;
|
||||||
|
|
||||||
|
import myblog.blog.comment.application.port.incomming.CommentUseCase;
|
||||||
|
import myblog.blog.comment.application.port.incomming.CommentDto;
|
||||||
|
|
||||||
|
import myblog.blog.member.auth.PrincipalDetails;
|
||||||
|
|
||||||
|
import myblog.blog.member.doamin.Member;
|
||||||
|
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import myblog.blog.article.application.port.incomming.ArticleQueriesUseCase;
|
|
||||||
import myblog.blog.article.domain.Article;
|
|
||||||
import myblog.blog.article.application.ArticleService;
|
|
||||||
import myblog.blog.comment.dto.CommentDto;
|
|
||||||
import myblog.blog.comment.dto.CommentForm;
|
|
||||||
import myblog.blog.comment.service.CommentService;
|
|
||||||
import myblog.blog.shared.exception.CustomFormException;
|
|
||||||
import myblog.blog.member.auth.PrincipalDetails;
|
|
||||||
import myblog.blog.member.doamin.Member;
|
|
||||||
import org.springframework.security.core.annotation.AuthenticationPrincipal;
|
import org.springframework.security.core.annotation.AuthenticationPrincipal;
|
||||||
import org.springframework.validation.Errors;
|
import org.springframework.validation.Errors;
|
||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.Validated;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
import java.util.*;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class CommentController {
|
public class CommentController {
|
||||||
|
|
||||||
private final CommentService commentService;
|
private final CommentUseCase commentUseCase;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
- 아티클 조회시 아티클에 달린 댓글들 전체 조회
|
- 아티클 조회시 아티클에 달린 댓글들 전체 조회
|
||||||
*/
|
*/
|
||||||
@GetMapping("/comment/list/{articleId}")
|
@GetMapping("/comment/list/{articleId}")
|
||||||
public List<CommentDto> getCommentList(@PathVariable Long articleId){
|
public List<CommentDto> getCommentList(@PathVariable Long articleId){
|
||||||
return CommentDto.listCreateFrom(commentService.getCommentList(articleId),0);
|
return commentUseCase.getCommentList(articleId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -41,19 +37,19 @@ public class CommentController {
|
|||||||
@Validated @RequestBody CommentForm commentForm, Errors errors,
|
@Validated @RequestBody CommentForm commentForm, Errors errors,
|
||||||
@AuthenticationPrincipal PrincipalDetails principal){
|
@AuthenticationPrincipal PrincipalDetails principal){
|
||||||
if (errors.hasErrors()) {
|
if (errors.hasErrors()) {
|
||||||
throw new CustomFormException(Objects.requireNonNull(errors.getFieldError()).getDefaultMessage());
|
throw new InvalidCommentRequestException(Objects.requireNonNull(errors.getFieldError()).getDefaultMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
Member member = principal.getMember();
|
Member member = principal.getMember();
|
||||||
// 부모 댓글인지 자식댓글인지 분기로 저장
|
// 부모 댓글인지 자식댓글인지 분기로 저장
|
||||||
if(parentId != null){
|
if(parentId != null){
|
||||||
commentService.saveCComment(commentForm, member, articleId, parentId);
|
commentUseCase.saveCComment(commentForm.getContent(), commentForm.isSecret(), member, articleId, parentId);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
commentService.savePComment(commentForm, member, articleId);
|
commentUseCase.savePComment(commentForm.getContent(), commentForm.isSecret(), member, articleId);
|
||||||
}
|
}
|
||||||
|
|
||||||
return CommentDto.listCreateFrom(commentService.getCommentList(articleId),0);
|
return commentUseCase.getCommentList(articleId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -62,7 +58,7 @@ public class CommentController {
|
|||||||
@PostMapping("/comment/delete")
|
@PostMapping("/comment/delete")
|
||||||
public List<CommentDto> deleteComment(@RequestParam Long articleId,
|
public List<CommentDto> deleteComment(@RequestParam Long articleId,
|
||||||
@RequestParam Long commentId) {
|
@RequestParam Long commentId) {
|
||||||
commentService.deleteComment(commentId);
|
commentUseCase.deleteComment(commentId);
|
||||||
return CommentDto.listCreateFrom(commentService.getCommentList(articleId),0);
|
return commentUseCase.getCommentList(articleId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package myblog.blog.comment.dto;
|
package myblog.blog.comment.adapter.incomming;
|
||||||
|
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package myblog.blog.comment.adapter.incomming;
|
||||||
|
/*
|
||||||
|
- REST 컨트롤러 상태 메세지 전송용 커스텀 에러
|
||||||
|
*/
|
||||||
|
public class InvalidCommentRequestException extends RuntimeException {
|
||||||
|
public InvalidCommentRequestException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
package myblog.blog.comment.adapter.outgoing.persistence;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import myblog.blog.comment.application.port.outgoing.CommentRepositoryPort;
|
||||||
|
import myblog.blog.article.domain.Article;
|
||||||
|
import myblog.blog.comment.domain.Comment;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class CommentRepositoryAdapter implements CommentRepositoryPort {
|
||||||
|
private final JpaCommentRepository jpaCommentRepository;
|
||||||
|
private final MybatisCommentRepository mybatisCommentRepository;
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int countCommentsByArticleAndTier(Article article, int tier) {
|
||||||
|
return jpaCommentRepository.countCommentsByArticleAndTier(article, tier);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void save(Comment comment) {
|
||||||
|
jpaCommentRepository.save(comment);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Comment> findCommentsByArticleId(Long articleId) {
|
||||||
|
return jpaCommentRepository.findCommentsByArticleId(articleId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void deleteComment(Long commentId) {
|
||||||
|
mybatisCommentRepository.deleteComment(commentId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Comment> findTop5ByOrderByIdDesc() {
|
||||||
|
return jpaCommentRepository.findTop5ByOrderByIdDesc();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<Comment> findById(Long parentId) {
|
||||||
|
return jpaCommentRepository.findById(parentId);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package myblog.blog.comment.repository;
|
package myblog.blog.comment.adapter.outgoing.persistence;
|
||||||
|
|
||||||
import myblog.blog.article.domain.Article;
|
import myblog.blog.article.domain.Article;
|
||||||
import myblog.blog.comment.domain.Comment;
|
import myblog.blog.comment.domain.Comment;
|
||||||
@@ -8,7 +8,7 @@ import org.springframework.data.repository.query.Param;
|
|||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public interface CommentRepository extends JpaRepository<Comment, Long> {
|
public interface JpaCommentRepository extends JpaRepository<Comment, Long> {
|
||||||
|
|
||||||
/*
|
/*
|
||||||
- 특정 아티클에 해당하는 댓글 리스트 가져오기
|
- 특정 아티클에 해당하는 댓글 리스트 가져오기
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
package myblog.blog.comment.repository;
|
package myblog.blog.comment.adapter.outgoing.persistence;
|
||||||
|
|
||||||
import org.apache.ibatis.annotations.Delete;
|
import org.apache.ibatis.annotations.Delete;
|
||||||
import org.apache.ibatis.annotations.Mapper;
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
|
||||||
@Mapper
|
@Mapper
|
||||||
public interface NaCommentRepository {
|
public interface MybatisCommentRepository {
|
||||||
|
|
||||||
/*
|
/*
|
||||||
- cascade 삭제처리
|
- cascade 삭제처리
|
||||||
@@ -1,57 +1,57 @@
|
|||||||
package myblog.blog.comment.service;
|
package myblog.blog.comment.application;
|
||||||
|
|
||||||
|
import myblog.blog.article.application.port.incomming.ArticleUseCase;
|
||||||
|
import myblog.blog.comment.application.port.incomming.CommentUseCase;
|
||||||
|
import myblog.blog.comment.application.port.incomming.CommentDto;
|
||||||
|
import myblog.blog.comment.application.port.incomming.CommentDtoForLayout;
|
||||||
|
import myblog.blog.comment.application.port.outgoing.CommentRepositoryPort;
|
||||||
|
|
||||||
|
import myblog.blog.comment.domain.Comment;
|
||||||
|
import myblog.blog.article.domain.Article;
|
||||||
|
import myblog.blog.member.doamin.Member;
|
||||||
|
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import myblog.blog.article.application.port.incomming.ArticleUseCase;
|
|
||||||
import myblog.blog.article.domain.Article;
|
|
||||||
import myblog.blog.comment.domain.Comment;
|
|
||||||
import myblog.blog.comment.dto.CommentDtoForLayout;
|
|
||||||
import myblog.blog.comment.dto.CommentForm;
|
|
||||||
import myblog.blog.comment.repository.CommentRepository;
|
|
||||||
import myblog.blog.comment.repository.NaCommentRepository;
|
|
||||||
import myblog.blog.member.doamin.Member;
|
|
||||||
import org.springframework.cache.annotation.CacheEvict;
|
import org.springframework.cache.annotation.CacheEvict;
|
||||||
import org.springframework.cache.annotation.Cacheable;
|
import org.springframework.cache.annotation.Cacheable;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
@Transactional
|
@Transactional
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class CommentService {
|
public class CommentService implements CommentUseCase {
|
||||||
|
|
||||||
private final ArticleUseCase articleUseCase;
|
private final ArticleUseCase articleUseCase;
|
||||||
|
private final CommentRepositoryPort commentRepositoryPort;
|
||||||
private final CommentRepository commentRepository;
|
|
||||||
private final NaCommentRepository naCommentRepository;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
- 아티클에 달린 댓글 전체 가져오기
|
- 아티클에 달린 댓글 전체 가져오기
|
||||||
*/
|
*/
|
||||||
public List<Comment> getCommentList(Long articleId){
|
@Override
|
||||||
return commentRepository.findCommentsByArticleId(articleId);
|
public List<CommentDto> getCommentList(Long articleId){
|
||||||
|
return CommentDto.listCreateFrom(commentRepositoryPort.findCommentsByArticleId(articleId),0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
- 부모 댓글 저장
|
- 부모 댓글 저장
|
||||||
*/
|
*/
|
||||||
@CacheEvict(value = "layoutRecentCommentCaching", allEntries = true)
|
@CacheEvict(value = "layoutRecentCommentCaching", allEntries = true)
|
||||||
public void savePComment(CommentForm commentForm, Member member, Long articleId){
|
@Override
|
||||||
|
public void savePComment(String content, boolean secret, Member member, Long articleId){
|
||||||
|
|
||||||
Article article = articleUseCase.getArticle(articleId);
|
Article article = articleUseCase.getArticle(articleId);
|
||||||
|
|
||||||
Comment comment = Comment.builder()
|
Comment comment = Comment.builder()
|
||||||
.article(article)
|
.article(article)
|
||||||
.content(removeDuplicatedEnter(commentForm))
|
.content(removeDuplicatedEnter(content))
|
||||||
.tier(0)
|
.tier(0)
|
||||||
.pOrder(commentRepository.countCommentsByArticleAndTier(article,0)+1)
|
.pOrder(commentRepositoryPort.countCommentsByArticleAndTier(article,0)+1)
|
||||||
.member(member)
|
.member(member)
|
||||||
.secret(commentForm.isSecret())
|
.secret(secret)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
commentRepository.save(comment);
|
commentRepositoryPort.save(comment);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,22 +59,24 @@ public class CommentService {
|
|||||||
- 자식 댓글 저장
|
- 자식 댓글 저장
|
||||||
*/
|
*/
|
||||||
@CacheEvict(value = "layoutRecentCommentCaching", allEntries = true)
|
@CacheEvict(value = "layoutRecentCommentCaching", allEntries = true)
|
||||||
public void saveCComment(CommentForm commentForm, Member member, Long articleId, Long parentId) {
|
@Override
|
||||||
|
public void saveCComment(String content, boolean secret, Member member, Long articleId, Long parentId) {
|
||||||
|
|
||||||
Article article = articleUseCase.getArticle(articleId);
|
Article article = articleUseCase.getArticle(articleId);
|
||||||
Comment pComment = commentRepository.findById(parentId).get();
|
Comment pComment = commentRepositoryPort.findById(parentId)
|
||||||
|
.orElseThrow(() -> new IllegalArgumentException("NotfoundParentCommentException"));
|
||||||
|
|
||||||
Comment comment = Comment.builder()
|
Comment comment = Comment.builder()
|
||||||
.article(article)
|
.article(article)
|
||||||
.content(removeDuplicatedEnter(commentForm))
|
.content(removeDuplicatedEnter(content))
|
||||||
.tier(1)
|
.tier(1)
|
||||||
.pOrder(pComment.getPOrder())
|
.pOrder(pComment.getPOrder())
|
||||||
.member(member)
|
.member(member)
|
||||||
.parents(pComment)
|
.parents(pComment)
|
||||||
.secret(commentForm.isSecret())
|
.secret(secret)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
commentRepository.save(comment);
|
commentRepositoryPort.save(comment);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,8 +84,9 @@ public class CommentService {
|
|||||||
- 댓글 삭제
|
- 댓글 삭제
|
||||||
*/
|
*/
|
||||||
@CacheEvict(value = "layoutRecentCommentCaching", allEntries = true)
|
@CacheEvict(value = "layoutRecentCommentCaching", allEntries = true)
|
||||||
|
@Override
|
||||||
public void deleteComment(Long commentId){
|
public void deleteComment(Long commentId){
|
||||||
naCommentRepository.deleteComment(commentId);
|
commentRepositoryPort.deleteComment(commentId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -93,8 +96,9 @@ public class CommentService {
|
|||||||
DTO 매핑 로직 서비스단에서 처리
|
DTO 매핑 로직 서비스단에서 처리
|
||||||
*/
|
*/
|
||||||
@Cacheable(value = "layoutRecentCommentCaching", key = "0")
|
@Cacheable(value = "layoutRecentCommentCaching", key = "0")
|
||||||
|
@Override
|
||||||
public List<CommentDtoForLayout> recentCommentList(){
|
public List<CommentDtoForLayout> recentCommentList(){
|
||||||
return commentRepository.findTop5ByOrderByIdDesc()
|
return commentRepositoryPort.findTop5ByOrderByIdDesc()
|
||||||
.stream()
|
.stream()
|
||||||
.map(comment ->
|
.map(comment ->
|
||||||
new CommentDtoForLayout(comment.getId(), comment.getArticle().getId(), comment.getContent(), comment.isSecret()))
|
new CommentDtoForLayout(comment.getId(), comment.getArticle().getId(), comment.getContent(), comment.isSecret()))
|
||||||
@@ -104,15 +108,15 @@ public class CommentService {
|
|||||||
/*
|
/*
|
||||||
- 중복 개행 개행 하나로 압축 알고리즘
|
- 중복 개행 개행 하나로 압축 알고리즘
|
||||||
*/
|
*/
|
||||||
private String removeDuplicatedEnter(CommentForm commentForm) {
|
private String removeDuplicatedEnter(String content) {
|
||||||
|
|
||||||
char[] contentBox = new char[commentForm.getContent().length()];
|
char[] contentBox = new char[content.length()];
|
||||||
int idx = 0;
|
int idx = 0;
|
||||||
String zipWord = "\n\n";
|
String zipWord = "\n\n";
|
||||||
|
|
||||||
for(int i = 0; i< commentForm.getContent().length(); i++){
|
for(int i = 0; i< content.length(); i++){
|
||||||
|
|
||||||
contentBox[idx] = commentForm.getContent().charAt(i);
|
contentBox[idx] = content.charAt(i);
|
||||||
|
|
||||||
if(contentBox[idx] == '\n'&&idx >= 1){
|
if(contentBox[idx] == '\n'&&idx >= 1){
|
||||||
|
|
||||||
@@ -1,13 +1,11 @@
|
|||||||
package myblog.blog.comment.dto;
|
package myblog.blog.comment.application.port.incomming;
|
||||||
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
import myblog.blog.comment.domain.Comment;
|
import myblog.blog.comment.domain.Comment;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package myblog.blog.comment.dto;
|
package myblog.blog.comment.application.port.incomming;
|
||||||
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package myblog.blog.comment.application.port.incomming;
|
||||||
|
|
||||||
|
import myblog.blog.member.doamin.Member;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public interface CommentUseCase {
|
||||||
|
List<CommentDto> getCommentList(Long articleId);
|
||||||
|
void savePComment(String content, boolean secret, Member member, Long articleId);
|
||||||
|
void saveCComment(String content, boolean secret, Member member, Long articleId, Long parentId);
|
||||||
|
void deleteComment(Long commentId);
|
||||||
|
List<CommentDtoForLayout> recentCommentList();
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package myblog.blog.comment.application.port.outgoing;
|
||||||
|
|
||||||
|
import myblog.blog.article.domain.Article;
|
||||||
|
import myblog.blog.comment.domain.Comment;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
public interface CommentRepositoryPort {
|
||||||
|
int countCommentsByArticleAndTier(Article article, int tier);
|
||||||
|
void save(Comment comment);
|
||||||
|
List<Comment> findCommentsByArticleId(Long articleId);
|
||||||
|
void deleteComment(Long commentId);
|
||||||
|
List<Comment> findTop5ByOrderByIdDesc();
|
||||||
|
Optional<Comment> findById(Long parentId);
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package myblog.blog.imgupload.adapter.incomming;
|
||||||
|
|
||||||
|
import myblog.blog.imgupload.service.port.incomming.ImgUploadUseCase;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class UploadImgController {
|
||||||
|
|
||||||
|
private final ImgUploadUseCase imgUploadUseCase;
|
||||||
|
|
||||||
|
@PostMapping("/article/uploadImg")
|
||||||
|
public @ResponseBody
|
||||||
|
String imgUpload(@ModelAttribute UploadImgForm uploadImgForm) throws IOException {
|
||||||
|
return imgUploadUseCase.storeImg(uploadImgForm.getImg());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,14 +1,11 @@
|
|||||||
package myblog.blog.imgupload.dto;
|
package myblog.blog.imgupload.adapter.incomming;
|
||||||
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
/*
|
|
||||||
- 멀티파트 파일 래핑용 DTO
|
|
||||||
*/
|
|
||||||
@Getter
|
@Getter
|
||||||
@Setter
|
@Setter
|
||||||
public class UploadImgDto {
|
public class UploadImgForm {
|
||||||
private MultipartFile img;
|
private MultipartFile img;
|
||||||
}
|
}
|
||||||
@@ -1,8 +1,10 @@
|
|||||||
package myblog.blog.imgupload.service;
|
package myblog.blog.imgupload.adapter.outgoing;
|
||||||
|
|
||||||
import com.amazonaws.services.s3.AmazonS3;
|
import com.amazonaws.services.s3.AmazonS3;
|
||||||
import com.amazonaws.services.s3.model.*;
|
import com.amazonaws.services.s3.model.*;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import myblog.blog.imgupload.domain.ImageFile;
|
||||||
|
import myblog.blog.imgupload.service.port.outgoing.ImgUploadStrategyPort;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
@@ -15,22 +17,22 @@ import java.io.InputStream;
|
|||||||
*/
|
*/
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
@Service
|
@Service
|
||||||
public class AwsS3ImgUploadStrategy implements ImgUploadStrategy {
|
public class AwsS3ImgUploadStrategyAdapter implements ImgUploadStrategyPort {
|
||||||
|
|
||||||
private final AmazonS3 amazonS3;
|
private final AmazonS3 amazonS3;
|
||||||
@Value("${cloud.aws.s3.bucket}")
|
@Value("${cloud.aws.s3.bucket}")
|
||||||
private String bucket;
|
private String bucket;
|
||||||
|
|
||||||
@Override
|
public String uploadFile(ImageFile imageFile) {
|
||||||
public String uploadFile(MultipartFile file, String storeFileName) {
|
MultipartFile file = imageFile.getMultipartFile();
|
||||||
ObjectMetadata metadata = createObjectMetadata(file);
|
ObjectMetadata metadata = createObjectMetadata(file);
|
||||||
try(InputStream inputStream = file.getInputStream()) {
|
try(InputStream inputStream = file.getInputStream()) {
|
||||||
amazonS3.putObject(new PutObjectRequest(bucket, storeFileName, inputStream, metadata)
|
amazonS3.putObject(new PutObjectRequest(bucket, imageFile.getStoredFileName(), inputStream, metadata)
|
||||||
.withCannedAcl(CannedAccessControlList.PublicRead));
|
.withCannedAcl(CannedAccessControlList.PublicRead));
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new IllegalArgumentException("파일 업로드에 실패했습니다.");
|
throw new IllegalArgumentException("파일 업로드에 실패했습니다.");
|
||||||
}
|
}
|
||||||
return amazonS3.getUrl(bucket, storeFileName).toString();
|
return amazonS3.getUrl(bucket, imageFile.getStoredFileName()).toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1,11 +1,9 @@
|
|||||||
package myblog.blog.imgupload.service;
|
package myblog.blog.imgupload.adapter.outgoing;
|
||||||
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import org.kohsuke.github.GHRepository;
|
import org.kohsuke.github.GHRepository;
|
||||||
import org.kohsuke.github.GitHub;
|
import org.kohsuke.github.GitHub;
|
||||||
import org.kohsuke.github.GitHubBuilder;
|
import org.kohsuke.github.GitHubBuilder;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.stereotype.Service;
|
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -15,7 +13,8 @@ import java.io.IOException;
|
|||||||
*/
|
*/
|
||||||
//@RequiredArgsConstructor
|
//@RequiredArgsConstructor
|
||||||
//@Service
|
//@Service
|
||||||
public class GithubRepoImgUploadStrategy implements ImgUploadStrategy {
|
@Deprecated
|
||||||
|
public class GithubRepoImgUploadStrategyAdapter {
|
||||||
|
|
||||||
/*
|
/*
|
||||||
- 설정 파일로 잡아놓은 깃헙 이미지 레포지토리와 토큰
|
- 설정 파일로 잡아놓은 깃헙 이미지 레포지토리와 토큰
|
||||||
@@ -34,7 +33,6 @@ public class GithubRepoImgUploadStrategy implements ImgUploadStrategy {
|
|||||||
1. 깃허브 Repo에 이미지 업로드
|
1. 깃허브 Repo에 이미지 업로드
|
||||||
2. 업로드된 Url 반환
|
2. 업로드된 Url 반환
|
||||||
*/
|
*/
|
||||||
@Override
|
|
||||||
public String uploadFile(MultipartFile multipartFile, String storeFileName) throws IOException {
|
public String uploadFile(MultipartFile multipartFile, String storeFileName) throws IOException {
|
||||||
GitHub gitHub = new GitHubBuilder().withOAuthToken(gitToken).build();
|
GitHub gitHub = new GitHubBuilder().withOAuthToken(gitToken).build();
|
||||||
GHRepository repository = gitHub.getRepository(gitRepo);
|
GHRepository repository = gitHub.getRepository(gitRepo);
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
package myblog.blog.imgupload.controller;
|
|
||||||
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import myblog.blog.imgupload.dto.UploadImgDto;
|
|
||||||
import myblog.blog.imgupload.service.ImgUploadServiceImpl;
|
|
||||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
|
||||||
import org.springframework.web.bind.annotation.ResponseBody;
|
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
@RestController
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
public class UploadImgController {
|
|
||||||
|
|
||||||
private final ImgUploadServiceImpl imgUploadServiceImpl;
|
|
||||||
|
|
||||||
/*
|
|
||||||
- 썸네일 업로드 요청
|
|
||||||
*/
|
|
||||||
@PostMapping("/article/uploadImg")
|
|
||||||
public @ResponseBody
|
|
||||||
String imgUpload(@ModelAttribute UploadImgDto uploadImgDto) throws IOException {
|
|
||||||
return imgUploadServiceImpl.storeImg(uploadImgDto.getImg());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
38
src/main/java/myblog/blog/imgupload/domain/ImageFile.java
Normal file
38
src/main/java/myblog/blog/imgupload/domain/ImageFile.java
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
package myblog.blog.imgupload.domain;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class ImageFile {
|
||||||
|
String originalFileName;
|
||||||
|
String storedFileName;
|
||||||
|
MultipartFile multipartFile;
|
||||||
|
|
||||||
|
static public ImageFile from(MultipartFile multipartFile){
|
||||||
|
return new ImageFile(multipartFile.getOriginalFilename(),
|
||||||
|
createStoreFileName(multipartFile.getOriginalFilename()),
|
||||||
|
multipartFile
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
- 이미지 중복 방지용 무작위 파일 이름 생성기
|
||||||
|
*/
|
||||||
|
private static String createStoreFileName(String originalFilename) {
|
||||||
|
String ext = extractExt(originalFilename);
|
||||||
|
String uuid = UUID.randomUUID().toString();
|
||||||
|
return uuid + "." + ext;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
- 파일 이름 추출
|
||||||
|
*/
|
||||||
|
private static String extractExt(String originalFilename) {
|
||||||
|
int pos = originalFilename.lastIndexOf(".");
|
||||||
|
return originalFilename.substring(pos + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,9 +1,33 @@
|
|||||||
package myblog.blog.imgupload.service;
|
package myblog.blog.imgupload.service;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import myblog.blog.imgupload.domain.ImageFile;
|
||||||
|
import myblog.blog.imgupload.service.port.incomming.ImgUploadUseCase;
|
||||||
|
import myblog.blog.imgupload.service.port.outgoing.ImgUploadStrategyPort;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
import java.util.List;
|
import java.io.IOException;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
public interface ImgUploadService {
|
@Service
|
||||||
List<String> storeFile(List<MultipartFile> imgList);
|
@Transactional
|
||||||
}
|
@RequiredArgsConstructor
|
||||||
|
public class ImgUploadService implements ImgUploadUseCase {
|
||||||
|
|
||||||
|
private final ImgUploadStrategyPort imgUploadStrategyPort;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String storeImg(MultipartFile multipartFile) {
|
||||||
|
validateFile(multipartFile);
|
||||||
|
ImageFile imageFile = ImageFile.from(multipartFile);
|
||||||
|
return imgUploadStrategyPort.uploadFile(imageFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validateFile(MultipartFile multipartFile) {
|
||||||
|
if (multipartFile.isEmpty()) {
|
||||||
|
throw new IllegalArgumentException("이미지가 존재하지 않습니다.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
package myblog.blog.imgupload.service;
|
|
||||||
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import org.kohsuke.github.GHRepository;
|
|
||||||
import org.kohsuke.github.GitHub;
|
|
||||||
import org.kohsuke.github.GitHubBuilder;
|
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
|
||||||
import org.springframework.stereotype.Service;
|
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
@Service
|
|
||||||
@Transactional
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
public class ImgUploadServiceImpl {
|
|
||||||
|
|
||||||
private final ImgUploadStrategy imgUploadStrategy;
|
|
||||||
|
|
||||||
public String storeImg(MultipartFile multipartFile) throws IOException {
|
|
||||||
validateFile(multipartFile);
|
|
||||||
String storeFileName = createStoreFileName(multipartFile.getOriginalFilename());
|
|
||||||
return imgUploadStrategy.uploadFile(multipartFile, storeFileName);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void validateFile(MultipartFile multipartFile) {
|
|
||||||
if (multipartFile.isEmpty()) {
|
|
||||||
throw new IllegalArgumentException("이미지가 존재하지 않습니다.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
- 이미지 중복 방지용 무작위 파일 이름 생성기
|
|
||||||
*/
|
|
||||||
private String createStoreFileName(String originalFilename) {
|
|
||||||
String ext = extractExt(originalFilename);
|
|
||||||
String uuid = UUID.randomUUID().toString();
|
|
||||||
return uuid + "." + ext;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
- 파일 이름 추출
|
|
||||||
*/
|
|
||||||
private String extractExt(String originalFilename) {
|
|
||||||
int pos = originalFilename.lastIndexOf(".");
|
|
||||||
return originalFilename.substring(pos + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
package myblog.blog.imgupload.service;
|
|
||||||
|
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 파일 업로드 전략패턴 추상화 인터페이스
|
|
||||||
*/
|
|
||||||
public interface ImgUploadStrategy {
|
|
||||||
String uploadFile(MultipartFile file, String storeFileName) throws IOException;
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package myblog.blog.imgupload.service.port.incomming;
|
||||||
|
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public interface ImgUploadUseCase {
|
||||||
|
String storeImg(MultipartFile img);
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package myblog.blog.imgupload.service.port.outgoing;
|
||||||
|
|
||||||
|
import myblog.blog.imgupload.domain.ImageFile;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 파일 업로드 전략패턴 추상화 인터페이스
|
||||||
|
*/
|
||||||
|
public interface ImgUploadStrategyPort {
|
||||||
|
String uploadFile(ImageFile imageFile);
|
||||||
|
}
|
||||||
@@ -3,8 +3,8 @@ package myblog.blog.member.controller;
|
|||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import myblog.blog.category.appliacation.port.response.CategoryViewForLayout;
|
import myblog.blog.category.appliacation.port.response.CategoryViewForLayout;
|
||||||
import myblog.blog.category.appliacation.CategoryService;
|
import myblog.blog.category.appliacation.CategoryService;
|
||||||
import myblog.blog.comment.dto.CommentDtoForLayout;
|
import myblog.blog.comment.application.port.incomming.CommentDtoForLayout;
|
||||||
import myblog.blog.comment.service.CommentService;
|
import myblog.blog.comment.application.CommentService;
|
||||||
import org.springframework.stereotype.Controller;
|
import org.springframework.stereotype.Controller;
|
||||||
import org.springframework.ui.Model;
|
import org.springframework.ui.Model;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
|||||||
@@ -1,9 +0,0 @@
|
|||||||
package myblog.blog.shared.exception;
|
|
||||||
/*
|
|
||||||
- REST 컨트롤러 상태 메세지 전송용 커스텀 에러
|
|
||||||
*/
|
|
||||||
public class CustomFormException extends RuntimeException {
|
|
||||||
public CustomFormException(String message) {
|
|
||||||
super(message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
package myblog.blog.shared.exception;
|
package myblog.blog.shared.exception;
|
||||||
|
|
||||||
|
import myblog.blog.comment.adapter.incomming.InvalidCommentRequestException;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||||
@@ -10,7 +11,7 @@ import org.springframework.web.bind.annotation.RestControllerAdvice;
|
|||||||
*/
|
*/
|
||||||
@RestControllerAdvice
|
@RestControllerAdvice
|
||||||
public class ExceptionRestControllerAdvice {
|
public class ExceptionRestControllerAdvice {
|
||||||
@ExceptionHandler(CustomFormException.class)
|
@ExceptionHandler(InvalidCommentRequestException.class)
|
||||||
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
||||||
public String handleCategoryControllerException(RuntimeException e) {
|
public String handleCategoryControllerException(RuntimeException e) {
|
||||||
return e.getMessage();
|
return e.getMessage();
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ package myblog.blog.shared.queries;
|
|||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import myblog.blog.category.appliacation.port.incomming.CategoryUseCase;
|
import myblog.blog.category.appliacation.port.incomming.CategoryUseCase;
|
||||||
import myblog.blog.category.appliacation.port.response.CategoryViewForLayout;
|
import myblog.blog.category.appliacation.port.response.CategoryViewForLayout;
|
||||||
import myblog.blog.comment.dto.CommentDtoForLayout;
|
import myblog.blog.comment.application.port.incomming.CommentDtoForLayout;
|
||||||
import myblog.blog.comment.service.CommentService;
|
import myblog.blog.comment.application.CommentService;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import org.springframework.ui.Model;
|
import org.springframework.ui.Model;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user