diff --git a/src/main/java/leets/weeth/domain/account/application/dto/ReceiptDTO.java b/src/main/java/leets/weeth/domain/account/application/dto/ReceiptDTO.java index d5fe0679..3a9af7ad 100644 --- a/src/main/java/leets/weeth/domain/account/application/dto/ReceiptDTO.java +++ b/src/main/java/leets/weeth/domain/account/application/dto/ReceiptDTO.java @@ -1,6 +1,9 @@ package leets.weeth.domain.account.application.dto; +import jakarta.validation.Valid; import jakarta.validation.constraints.NotNull; +import leets.weeth.domain.file.application.dto.request.FileSaveRequest; +import leets.weeth.domain.file.application.dto.response.FileResponse; import java.time.LocalDate; import java.util.List; @@ -12,13 +15,14 @@ public record Response( String description, Integer amount, LocalDate date, - List images + List fileUrls ) {} public record Save( String description, @NotNull Integer amount, @NotNull LocalDate date, - @NotNull Integer cardinal + @NotNull Integer cardinal, + @Valid List<@NotNull FileSaveRequest> files ) {} } diff --git a/src/main/java/leets/weeth/domain/account/application/mapper/ReceiptMapper.java b/src/main/java/leets/weeth/domain/account/application/mapper/ReceiptMapper.java index ce76075b..bfcbb5d7 100644 --- a/src/main/java/leets/weeth/domain/account/application/mapper/ReceiptMapper.java +++ b/src/main/java/leets/weeth/domain/account/application/mapper/ReceiptMapper.java @@ -3,6 +3,7 @@ import leets.weeth.domain.account.application.dto.ReceiptDTO; import leets.weeth.domain.account.domain.entity.Account; import leets.weeth.domain.account.domain.entity.Receipt; +import leets.weeth.domain.file.application.dto.response.FileResponse; import org.mapstruct.Mapper; import org.mapstruct.Mapping; import org.mapstruct.MappingConstants; @@ -15,8 +16,10 @@ public interface ReceiptMapper { List to(List account); + ReceiptDTO.Response to(Receipt receipt, List fileUrls); + @Mapping(target = "id", ignore = true) @Mapping(target = "description", source = "dto.description") @Mapping(target = "account", source = "account") - Receipt from(ReceiptDTO.Save dto, List images, Account account); + Receipt from(ReceiptDTO.Save dto, Account account); } diff --git a/src/main/java/leets/weeth/domain/account/application/usecase/AccountUseCaseImpl.java b/src/main/java/leets/weeth/domain/account/application/usecase/AccountUseCaseImpl.java index 591b1f34..d2aae767 100644 --- a/src/main/java/leets/weeth/domain/account/application/usecase/AccountUseCaseImpl.java +++ b/src/main/java/leets/weeth/domain/account/application/usecase/AccountUseCaseImpl.java @@ -2,12 +2,16 @@ import leets.weeth.domain.account.application.dto.AccountDTO; import leets.weeth.domain.account.application.dto.ReceiptDTO; +import leets.weeth.domain.account.application.exception.AccountExistsException; import leets.weeth.domain.account.application.mapper.AccountMapper; import leets.weeth.domain.account.application.mapper.ReceiptMapper; import leets.weeth.domain.account.domain.entity.Account; +import leets.weeth.domain.account.domain.entity.Receipt; import leets.weeth.domain.account.domain.service.AccountGetService; import leets.weeth.domain.account.domain.service.AccountSaveService; -import leets.weeth.domain.account.application.exception.AccountExistsException; +import leets.weeth.domain.file.application.dto.response.FileResponse; +import leets.weeth.domain.file.application.mapper.FileMapper; +import leets.weeth.domain.file.domain.service.FileGetService; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -19,14 +23,20 @@ public class AccountUseCaseImpl implements AccountUseCase { private final AccountGetService accountGetService; private final AccountSaveService accountSaveService; + private final FileGetService fileGetService; private final AccountMapper accountMapper; private final ReceiptMapper receiptMapper; + private final FileMapper fileMapper; @Override public AccountDTO.Response find(Integer cardinal) { Account account = accountGetService.find(cardinal); - List receipts = receiptMapper.to(account.getReceipts()); - return accountMapper.to(account, receipts); + List receipts = account.getReceipts(); + List response = receipts.stream() + .map(receipt -> receiptMapper.to(receipt, getFiles(receipt.getId()))) + .toList(); + + return accountMapper.to(account, response); } @Override @@ -36,7 +46,13 @@ public void save(AccountDTO.Save dto) { } private void validate(AccountDTO.Save dto) { - if(accountGetService.validate(dto.cardinal())) + if (accountGetService.validate(dto.cardinal())) throw new AccountExistsException(); } + + private List getFiles(Long receiptId) { + return fileGetService.findAllByReceipt(receiptId).stream() + .map(fileMapper::toFileResponse) + .toList(); + } } diff --git a/src/main/java/leets/weeth/domain/account/application/usecase/ReceiptUseCase.java b/src/main/java/leets/weeth/domain/account/application/usecase/ReceiptUseCase.java index 430edf31..6207b941 100644 --- a/src/main/java/leets/weeth/domain/account/application/usecase/ReceiptUseCase.java +++ b/src/main/java/leets/weeth/domain/account/application/usecase/ReceiptUseCase.java @@ -6,7 +6,7 @@ import java.util.List; public interface ReceiptUseCase { - void save(ReceiptDTO.Save dto, List images); + void save(ReceiptDTO.Save dto); void delete(Long id); } diff --git a/src/main/java/leets/weeth/domain/account/application/usecase/ReceiptUseCaseImpl.java b/src/main/java/leets/weeth/domain/account/application/usecase/ReceiptUseCaseImpl.java index adc1d1d5..76e0dec9 100644 --- a/src/main/java/leets/weeth/domain/account/application/usecase/ReceiptUseCaseImpl.java +++ b/src/main/java/leets/weeth/domain/account/application/usecase/ReceiptUseCaseImpl.java @@ -9,6 +9,11 @@ import leets.weeth.domain.account.domain.service.ReceiptDeleteService; import leets.weeth.domain.account.domain.service.ReceiptGetService; import leets.weeth.domain.account.domain.service.ReceiptSaveService; +import leets.weeth.domain.file.application.mapper.FileMapper; +import leets.weeth.domain.file.domain.entity.File; +import leets.weeth.domain.file.domain.service.FileDeleteService; +import leets.weeth.domain.file.domain.service.FileGetService; +import leets.weeth.domain.file.domain.service.FileSaveService; import leets.weeth.domain.file.service.S3Service; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -20,25 +25,39 @@ @RequiredArgsConstructor public class ReceiptUseCaseImpl implements ReceiptUseCase { + private final ReceiptGetService receiptGetService; private final ReceiptDeleteService receiptDeleteService; private final ReceiptSaveService receiptSaveService; - private final S3Service s3Service; - private final ReceiptMapper mapper; private final AccountGetService accountGetService; - private final ReceiptGetService receiptGetService; - @Override @Transactional - public void save(ReceiptDTO.Save dto, List files) { - List images = s3Service.uploadFiles(files); + private final FileGetService fileGetService; + private final FileSaveService fileSaveService; + private final FileDeleteService fileDeleteService; + + private final ReceiptMapper mapper; + private final FileMapper fileMapper; + + + @Override + @Transactional + public void save(ReceiptDTO.Save dto) { Account account = accountGetService.find(dto.cardinal()); - Receipt receipt = receiptSaveService.save(mapper.from(dto, images, account)); + Receipt receipt = receiptSaveService.save(mapper.from(dto, account)); account.spend(receipt); + + List files = fileMapper.toFileList(dto.files(), receipt); + fileSaveService.save(files); } - @Override @Transactional + @Override + @Transactional public void delete(Long id) { Receipt receipt = receiptGetService.find(id); + List fileList = fileGetService.findAllByReceipt(id); + receipt.getAccount().cancel(receipt); + + fileDeleteService.delete(fileList); receiptDeleteService.delete(receipt); } } diff --git a/src/main/java/leets/weeth/domain/account/presentation/ReceiptAdminController.java b/src/main/java/leets/weeth/domain/account/presentation/ReceiptAdminController.java index adaf8b65..df226e7a 100644 --- a/src/main/java/leets/weeth/domain/account/presentation/ReceiptAdminController.java +++ b/src/main/java/leets/weeth/domain/account/presentation/ReceiptAdminController.java @@ -24,10 +24,10 @@ public class ReceiptAdminController { private final ReceiptUseCase receiptUseCase; - @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + @PostMapping @Operation(summary="회비 사용 내역 기입") - public CommonResponse save(@RequestPart @Valid ReceiptDTO.Save dto, @RequestPart(required = false) List images) { - receiptUseCase.save(dto, images); + public CommonResponse save(@RequestBody @Valid ReceiptDTO.Save dto) { + receiptUseCase.save(dto); return CommonResponse.createSuccess(RECEIPT_SAVE_SUCCESS.getMessage()); } diff --git a/src/main/java/leets/weeth/domain/board/application/dto/NoticeDTO.java b/src/main/java/leets/weeth/domain/board/application/dto/NoticeDTO.java index 8ed9b3ca..44540214 100644 --- a/src/main/java/leets/weeth/domain/board/application/dto/NoticeDTO.java +++ b/src/main/java/leets/weeth/domain/board/application/dto/NoticeDTO.java @@ -1,7 +1,11 @@ package leets.weeth.domain.board.application.dto; +import jakarta.validation.Valid; import jakarta.validation.constraints.NotNull; import leets.weeth.domain.comment.application.dto.CommentDTO; +import leets.weeth.domain.file.application.dto.request.FileSaveRequest; +import leets.weeth.domain.file.application.dto.request.FileUpdateRequest; +import leets.weeth.domain.file.application.dto.response.FileResponse; import lombok.Builder; import java.time.LocalDateTime; @@ -12,14 +16,18 @@ public class NoticeDTO { @Builder public record Save( @NotNull String title, - @NotNull String content - ){} + @NotNull String content, + @Valid List<@NotNull FileSaveRequest> files + ) { + } @Builder public record Update( @NotNull String title, - @NotNull String content - ){} + @NotNull String content, + @Valid List<@NotNull FileSaveRequest> files + ) { + } @Builder public record Response( @@ -29,9 +37,10 @@ public record Response( String content, LocalDateTime time,//modifiedAt Integer commentCount, - List fileUrls, - List comments - ){} + List comments, + List fileUrls + ) { + } @Builder public record ResponseAll( @@ -41,6 +50,7 @@ public record ResponseAll( String content, LocalDateTime time,//modifiedAt Integer commentCount - ){} + ) { + } } diff --git a/src/main/java/leets/weeth/domain/board/application/dto/PostDTO.java b/src/main/java/leets/weeth/domain/board/application/dto/PostDTO.java index 450271f4..918c21a3 100644 --- a/src/main/java/leets/weeth/domain/board/application/dto/PostDTO.java +++ b/src/main/java/leets/weeth/domain/board/application/dto/PostDTO.java @@ -1,7 +1,11 @@ package leets.weeth.domain.board.application.dto; +import jakarta.validation.Valid; import jakarta.validation.constraints.NotNull; import leets.weeth.domain.comment.application.dto.CommentDTO; +import leets.weeth.domain.file.application.dto.request.FileSaveRequest; +import leets.weeth.domain.file.application.dto.request.FileUpdateRequest; +import leets.weeth.domain.file.application.dto.response.FileResponse; import lombok.Builder; import java.time.LocalDateTime; @@ -12,13 +16,15 @@ public class PostDTO { @Builder public record Save( @NotNull String title, - @NotNull String content + @NotNull String content, + @Valid List<@NotNull FileSaveRequest> files ){} @Builder public record Update( @NotNull String title, - @NotNull String content + @NotNull String content, + @Valid List<@NotNull FileSaveRequest> files ){} @Builder @@ -29,8 +35,8 @@ public record Response( String content, LocalDateTime time,//modifiedAt Integer commentCount, - List fileUrls, - List comments + List comments, + List fileUrls ){} @Builder diff --git a/src/main/java/leets/weeth/domain/board/application/mapper/NoticeMapper.java b/src/main/java/leets/weeth/domain/board/application/mapper/NoticeMapper.java index 8dff57f7..db9e2098 100644 --- a/src/main/java/leets/weeth/domain/board/application/mapper/NoticeMapper.java +++ b/src/main/java/leets/weeth/domain/board/application/mapper/NoticeMapper.java @@ -5,6 +5,8 @@ import leets.weeth.domain.comment.application.dto.CommentDTO; import leets.weeth.domain.comment.application.mapper.CommentMapper; import leets.weeth.domain.comment.domain.entity.Comment; +import leets.weeth.domain.file.application.dto.response.FileResponse; +import leets.weeth.domain.file.domain.entity.File; import leets.weeth.domain.user.domain.entity.User; import org.mapstruct.*; @@ -19,7 +21,7 @@ public interface NoticeMapper { @Mapping(target = "id", ignore = true), @Mapping(target = "user", source = "user") }) - Notice fromNoticeDto(NoticeDTO.Save dto, List fileUrls, User user); + Notice fromNoticeDto(NoticeDTO.Save dto, User user); @Mappings({ @Mapping(target = "name", source = "user.name"), @@ -28,11 +30,11 @@ public interface NoticeMapper { NoticeDTO.ResponseAll toAll(Notice notice); @Mappings({ - @Mapping(target = "name", source = "user.name"), + @Mapping(target = "name", source = "notice.user.name"), @Mapping(target = "comments", expression = "java(filterParentComments(notice.getComments()))"), - @Mapping(target = "time", source = "modifiedAt") + @Mapping(target = "time", source = "notice.modifiedAt") }) - NoticeDTO.Response toNoticeDto(Notice notice); + NoticeDTO.Response toNoticeDto(Notice notice, List fileUrls); default List filterParentComments(List comments) { if (comments == null || comments.isEmpty()) { diff --git a/src/main/java/leets/weeth/domain/board/application/mapper/PostMapper.java b/src/main/java/leets/weeth/domain/board/application/mapper/PostMapper.java index ecb55f77..27474fff 100644 --- a/src/main/java/leets/weeth/domain/board/application/mapper/PostMapper.java +++ b/src/main/java/leets/weeth/domain/board/application/mapper/PostMapper.java @@ -5,6 +5,7 @@ import leets.weeth.domain.comment.application.dto.CommentDTO; import leets.weeth.domain.comment.application.mapper.CommentMapper; import leets.weeth.domain.comment.domain.entity.Comment; +import leets.weeth.domain.file.application.dto.response.FileResponse; import leets.weeth.domain.user.domain.entity.User; import org.mapstruct.*; @@ -19,7 +20,7 @@ public interface PostMapper { @Mapping(target = "id", ignore = true), @Mapping(target = "user", source = "user") }) - Post fromPostDto(PostDTO.Save dto, List fileUrls, User user); + Post fromPostDto(PostDTO.Save dto, User user); @Mappings({ @Mapping(target = "name", source = "user.name"), @@ -28,11 +29,11 @@ public interface PostMapper { PostDTO.ResponseAll toAll(Post post); @Mappings({ - @Mapping(target = "name", source = "user.name"), + @Mapping(target = "name", source = "post.user.name"), @Mapping(target = "comments", expression = "java(filterParentComments(post.getComments()))"), - @Mapping(target = "time", source = "modifiedAt") + @Mapping(target = "time", source = "post.modifiedAt") }) - PostDTO.Response toPostDto(Post post); + PostDTO.Response toPostDto(Post post, List fileUrls); default List filterParentComments(List comments) { if (comments == null || comments.isEmpty()) { diff --git a/src/main/java/leets/weeth/domain/board/application/usecase/NoticeUsecase.java b/src/main/java/leets/weeth/domain/board/application/usecase/NoticeUsecase.java index 4c98526e..b0db4d6f 100644 --- a/src/main/java/leets/weeth/domain/board/application/usecase/NoticeUsecase.java +++ b/src/main/java/leets/weeth/domain/board/application/usecase/NoticeUsecase.java @@ -8,13 +8,13 @@ public interface NoticeUsecase { - void save(NoticeDTO.Save dto, List files, Long userId); + void save(NoticeDTO.Save dto, Long userId); NoticeDTO.Response findNotice(Long noticeId); List findNotices(Long noticeId, Integer count); - void update(Long noticeId, NoticeDTO.Update dto, List files, Long userId) throws UserNotMatchException; + void update(Long noticeId, NoticeDTO.Update dto, Long userId) throws UserNotMatchException; void delete(Long noticeId, Long userId) throws UserNotMatchException; diff --git a/src/main/java/leets/weeth/domain/board/application/usecase/NoticeUsecaseImpl.java b/src/main/java/leets/weeth/domain/board/application/usecase/NoticeUsecaseImpl.java index 6c5ed166..9024b9a7 100644 --- a/src/main/java/leets/weeth/domain/board/application/usecase/NoticeUsecaseImpl.java +++ b/src/main/java/leets/weeth/domain/board/application/usecase/NoticeUsecaseImpl.java @@ -1,22 +1,28 @@ package leets.weeth.domain.board.application.usecase; import leets.weeth.domain.board.application.dto.NoticeDTO; +import leets.weeth.domain.board.application.exception.NoticeNotFoundException; import leets.weeth.domain.board.application.mapper.NoticeMapper; import leets.weeth.domain.board.domain.entity.Notice; import leets.weeth.domain.board.domain.service.NoticeDeleteService; import leets.weeth.domain.board.domain.service.NoticeFindService; import leets.weeth.domain.board.domain.service.NoticeSaveService; import leets.weeth.domain.board.domain.service.NoticeUpdateService; -import leets.weeth.domain.file.service.S3Service; +import leets.weeth.domain.file.application.dto.response.FileResponse; +import leets.weeth.domain.file.application.mapper.FileMapper; +import leets.weeth.domain.file.domain.entity.File; +import leets.weeth.domain.file.domain.service.FileDeleteService; +import leets.weeth.domain.file.domain.service.FileGetService; +import leets.weeth.domain.file.domain.service.FileSaveService; +import leets.weeth.domain.file.domain.service.FileUpdateService; +import leets.weeth.domain.user.application.exception.UserNotMatchException; import leets.weeth.domain.user.domain.entity.User; import leets.weeth.domain.user.domain.service.UserGetService; -import leets.weeth.domain.board.application.exception.NoticeNotFoundException; -import leets.weeth.domain.user.application.exception.UserNotMatchException; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; -import org.springframework.web.multipart.MultipartFile; +import org.springframework.transaction.annotation.Transactional; import java.util.List; @@ -30,23 +36,36 @@ public class NoticeUsecaseImpl implements NoticeUsecase { private final NoticeDeleteService noticeDeleteService; private final UserGetService userGetService; - private final S3Service s3Service; + + private final FileSaveService fileSaveService; + private final FileGetService fileGetService; + private final FileUpdateService fileUpdateService; + private final FileDeleteService fileDeleteService; private final NoticeMapper mapper; + private final FileMapper fileMapper; @Override - public void save(NoticeDTO.Save request, List files, Long userId) { + @Transactional + public void save(NoticeDTO.Save request, Long userId) { User user = userGetService.find(userId); - List fileUrls; - fileUrls = s3Service.uploadFiles(files); - noticeSaveService.save(mapper.fromNoticeDto(request, fileUrls, user)); + Notice notice = mapper.fromNoticeDto(request, user); + noticeSaveService.save(notice); + + List files = fileMapper.toFileList(request.files(), notice); + fileSaveService.save(files); } @Override public NoticeDTO.Response findNotice(Long noticeId) { Notice notice = noticeFindService.find(noticeId); - return mapper.toNoticeDto(notice); + + List response = getFiles(noticeId).stream() + .map(fileMapper::toFileResponse) + .toList(); + + return mapper.toNoticeDto(notice, response); } @Override @@ -55,12 +74,12 @@ public List findNotices(Long noticeId, Integer count) { Long finalNoticeId = noticeFindService.findFinalNoticeId(); // 첫번째 요청인 경우 - if(noticeId==null){ + if (noticeId == null) { noticeId = finalNoticeId + 1; } // postId가 1 이하이거나 최대값보다 클경우 - if(noticeId < 1 || noticeId > finalNoticeId + 1){ + if (noticeId < 1 || noticeId > finalNoticeId + 1) { throw new NoticeNotFoundException(); } @@ -74,24 +93,35 @@ public List findNotices(Long noticeId, Integer count) { } @Override - public void update(Long noticeId, NoticeDTO.Update dto, List files, Long userId) throws UserNotMatchException { + @Transactional + public void update(Long noticeId, NoticeDTO.Update dto, Long userId) { Notice notice = validateOwner(noticeId, userId); - List fileUrls = notice.getFileUrls(); - List uploadedFileUrls = s3Service.uploadFiles(files); + List fileList = getFiles(noticeId); + fileDeleteService.delete(fileList); - fileUrls.addAll(uploadedFileUrls); + List files = fileMapper.toFileList(dto.files(), notice); + fileSaveService.save(files); - noticeUpdateService.update(notice, dto, fileUrls); + noticeUpdateService.update(notice, dto); } @Override - public void delete(Long noticeId, Long userId) throws UserNotMatchException { + @Transactional + public void delete(Long noticeId, Long userId) { validateOwner(noticeId, userId); + + List fileList = getFiles(noticeId); + fileDeleteService.delete(fileList); + noticeDeleteService.delete(noticeId); } - private Notice validateOwner(Long noticeId, Long userId) throws UserNotMatchException { + private List getFiles(Long noticeId) { + return fileGetService.findAllByNotice(noticeId); + } + + private Notice validateOwner(Long noticeId, Long userId) { Notice notice = noticeFindService.find(noticeId); if (!notice.getUser().getId().equals(userId)) { throw new UserNotMatchException(); diff --git a/src/main/java/leets/weeth/domain/board/application/usecase/PostUseCaseImpl.java b/src/main/java/leets/weeth/domain/board/application/usecase/PostUseCaseImpl.java index dd7da129..552d69f7 100644 --- a/src/main/java/leets/weeth/domain/board/application/usecase/PostUseCaseImpl.java +++ b/src/main/java/leets/weeth/domain/board/application/usecase/PostUseCaseImpl.java @@ -1,22 +1,28 @@ package leets.weeth.domain.board.application.usecase; import leets.weeth.domain.board.application.dto.PostDTO; +import leets.weeth.domain.board.application.exception.PostNotFoundException; import leets.weeth.domain.board.application.mapper.PostMapper; import leets.weeth.domain.board.domain.entity.Post; import leets.weeth.domain.board.domain.service.PostDeleteService; import leets.weeth.domain.board.domain.service.PostFindService; import leets.weeth.domain.board.domain.service.PostSaveService; import leets.weeth.domain.board.domain.service.PostUpdateService; -import leets.weeth.domain.file.service.S3Service; +import leets.weeth.domain.file.application.dto.response.FileResponse; +import leets.weeth.domain.file.application.mapper.FileMapper; +import leets.weeth.domain.file.domain.entity.File; +import leets.weeth.domain.file.domain.service.FileDeleteService; +import leets.weeth.domain.file.domain.service.FileGetService; +import leets.weeth.domain.file.domain.service.FileSaveService; +import leets.weeth.domain.file.domain.service.FileUpdateService; +import leets.weeth.domain.user.application.exception.UserNotMatchException; import leets.weeth.domain.user.domain.entity.User; import leets.weeth.domain.user.domain.service.UserGetService; -import leets.weeth.domain.board.application.exception.PostNotFoundException; -import leets.weeth.domain.user.application.exception.UserNotMatchException; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; -import org.springframework.web.multipart.MultipartFile; +import org.springframework.transaction.annotation.Transactional; import java.util.List; @@ -30,23 +36,36 @@ public class PostUseCaseImpl implements PostUsecase { private final PostDeleteService postDeleteService; private final UserGetService userGetService; - private final S3Service s3Service; + + private final FileSaveService fileSaveService; + private final FileGetService fileGetService; + private final FileUpdateService fileUpdateService; + private final FileDeleteService fileDeleteService; private final PostMapper mapper; + private final FileMapper fileMapper; @Override - public void save(PostDTO.Save request, List files, Long userId) { + @Transactional + public void save(PostDTO.Save request, Long userId) { User user = userGetService.find(userId); - List fileUrls; - fileUrls = s3Service.uploadFiles(files); - postSaveService.save(mapper.fromPostDto(request, fileUrls, user)); + Post post = mapper.fromPostDto(request, user); + postSaveService.save(post); + + List files = fileMapper.toFileList(request.files(), post); + fileSaveService.save(files); } @Override public PostDTO.Response findPost(Long postId) { Post post = postFindService.find(postId); - return mapper.toPostDto(post); + + List response = getFiles(postId).stream() + .map(fileMapper::toFileResponse) + .toList(); + + return mapper.toPostDto(post, response); } @Override @@ -55,12 +74,12 @@ public List findPosts(Long postId, Integer count) { Long finalPostId = postFindService.findFinalPostId(); // 첫번째 요청인 경우 - if(postId==null){ + if (postId == null) { postId = finalPostId + 1; } // postId가 1 이하이거나 최대값보다 클경우 - if(postId < 1 || postId > finalPostId + 1){ + if (postId < 1 || postId > finalPostId + 1) { throw new PostNotFoundException(); } @@ -74,24 +93,35 @@ public List findPosts(Long postId, Integer count) { } @Override - public void update(Long postId, PostDTO.Update dto, List files, Long userId) throws UserNotMatchException { + @Transactional + public void update(Long postId, PostDTO.Update dto, Long userId) { Post post = validateOwner(postId, userId); - List fileUrls = post.getFileUrls(); - List uploadedFileUrls = s3Service.uploadFiles(files); + List fileList = getFiles(postId); + fileDeleteService.delete(fileList); - fileUrls.addAll(uploadedFileUrls); + List files = fileMapper.toFileList(dto.files(), post); + fileSaveService.save(files); - postUpdateService.update(post, dto, fileUrls); + postUpdateService.update(post, dto); } @Override - public void delete(Long postId, Long userId) throws UserNotMatchException { + @Transactional + public void delete(Long postId, Long userId) { validateOwner(postId, userId); + + List fileList = getFiles(postId); + fileDeleteService.delete(fileList); + postDeleteService.delete(postId); } - private Post validateOwner(Long postId, Long userId) throws UserNotMatchException { + private List getFiles(Long postId) { + return fileGetService.findAllByPost(postId); + } + + private Post validateOwner(Long postId, Long userId) { Post post = postFindService.find(postId); if (!post.getUser().getId().equals(userId)) { diff --git a/src/main/java/leets/weeth/domain/board/application/usecase/PostUsecase.java b/src/main/java/leets/weeth/domain/board/application/usecase/PostUsecase.java index 30bbf762..4cd55926 100644 --- a/src/main/java/leets/weeth/domain/board/application/usecase/PostUsecase.java +++ b/src/main/java/leets/weeth/domain/board/application/usecase/PostUsecase.java @@ -8,13 +8,13 @@ public interface PostUsecase { - void save(PostDTO.Save request, List files, Long userId); + void save(PostDTO.Save request, Long userId); PostDTO.Response findPost(Long postId); List findPosts(Long postId, Integer count); - void update(Long postId, PostDTO.Update dto, List files, Long userId) throws UserNotMatchException; + void update(Long postId, PostDTO.Update dto, Long userId) throws UserNotMatchException; void delete(Long postId, Long userId) throws UserNotMatchException; diff --git a/src/main/java/leets/weeth/domain/board/domain/entity/Board.java b/src/main/java/leets/weeth/domain/board/domain/entity/Board.java index 8c98ee93..9591ef24 100644 --- a/src/main/java/leets/weeth/domain/board/domain/entity/Board.java +++ b/src/main/java/leets/weeth/domain/board/domain/entity/Board.java @@ -38,9 +38,6 @@ public class Board extends BaseEntity { @JoinColumn(name = "user_id") private User user; - @Convert(converter = FileListConverter.class) - private List fileUrls = new ArrayList<>(); - private Integer commentCount; @PrePersist @@ -64,16 +61,14 @@ public void updateCommentCount(List comments) { .count(); } - public void updateUpperClass(NoticeDTO.Update dto, List fileUrls) { + public void updateUpperClass(NoticeDTO.Update dto) { this.title = dto.title(); this.content = dto.content(); - this.fileUrls = fileUrls; } - public void updateUpperClass(PostDTO.Update dto, List fileUrls) { + public void updateUpperClass(PostDTO.Update dto) { this.title = dto.title(); this.content = dto.content(); - this.fileUrls = fileUrls; } } diff --git a/src/main/java/leets/weeth/domain/board/domain/entity/Notice.java b/src/main/java/leets/weeth/domain/board/domain/entity/Notice.java index 010c0809..f7f6e327 100644 --- a/src/main/java/leets/weeth/domain/board/domain/entity/Notice.java +++ b/src/main/java/leets/weeth/domain/board/domain/entity/Notice.java @@ -30,8 +30,8 @@ public void addComment(Comment comment) { comments.add(comment); } - public void update(NoticeDTO.Update dto, List fileUrls){ - this.updateUpperClass(dto, fileUrls); + public void update(NoticeDTO.Update dto){ + this.updateUpperClass(dto); } } diff --git a/src/main/java/leets/weeth/domain/board/domain/entity/Post.java b/src/main/java/leets/weeth/domain/board/domain/entity/Post.java index 5a713594..82e14e41 100644 --- a/src/main/java/leets/weeth/domain/board/domain/entity/Post.java +++ b/src/main/java/leets/weeth/domain/board/domain/entity/Post.java @@ -30,8 +30,8 @@ public void addComment(Comment comment) { comments.add(comment); } - public void update(PostDTO.Update dto, List fileUrls) { - this.updateUpperClass(dto, fileUrls); + public void update(PostDTO.Update dto) { + this.updateUpperClass(dto); } } diff --git a/src/main/java/leets/weeth/domain/board/domain/service/NoticeSaveService.java b/src/main/java/leets/weeth/domain/board/domain/service/NoticeSaveService.java index e5454869..b26e1061 100644 --- a/src/main/java/leets/weeth/domain/board/domain/service/NoticeSaveService.java +++ b/src/main/java/leets/weeth/domain/board/domain/service/NoticeSaveService.java @@ -12,7 +12,6 @@ public class NoticeSaveService { private final NoticeRepository noticeRepository; - @Transactional public void save(Notice notice){ noticeRepository.save(notice); } diff --git a/src/main/java/leets/weeth/domain/board/domain/service/NoticeUpdateService.java b/src/main/java/leets/weeth/domain/board/domain/service/NoticeUpdateService.java index 69878d2a..c5197d11 100644 --- a/src/main/java/leets/weeth/domain/board/domain/service/NoticeUpdateService.java +++ b/src/main/java/leets/weeth/domain/board/domain/service/NoticeUpdateService.java @@ -12,9 +12,8 @@ @RequiredArgsConstructor public class NoticeUpdateService { - @Transactional - public void update(Notice notice, NoticeDTO.Update dto, List fileUrls){ - notice.update(dto, fileUrls); + public void update(Notice notice, NoticeDTO.Update dto){ + notice.update(dto); } } diff --git a/src/main/java/leets/weeth/domain/board/domain/service/PostDeleteService.java b/src/main/java/leets/weeth/domain/board/domain/service/PostDeleteService.java index d30f74e3..5af7aa2c 100644 --- a/src/main/java/leets/weeth/domain/board/domain/service/PostDeleteService.java +++ b/src/main/java/leets/weeth/domain/board/domain/service/PostDeleteService.java @@ -11,7 +11,6 @@ public class PostDeleteService { private final PostRepository postRepository; - @Transactional public void delete(Long postId) { postRepository.deleteById(postId); } diff --git a/src/main/java/leets/weeth/domain/board/domain/service/PostSaveService.java b/src/main/java/leets/weeth/domain/board/domain/service/PostSaveService.java index b5d6f6bc..80cd5b55 100644 --- a/src/main/java/leets/weeth/domain/board/domain/service/PostSaveService.java +++ b/src/main/java/leets/weeth/domain/board/domain/service/PostSaveService.java @@ -12,7 +12,6 @@ public class PostSaveService { private final PostRepository postRepository; - @Transactional public void save(Post post) { postRepository.save(post); } diff --git a/src/main/java/leets/weeth/domain/board/domain/service/PostUpdateService.java b/src/main/java/leets/weeth/domain/board/domain/service/PostUpdateService.java index a7406814..159d7722 100644 --- a/src/main/java/leets/weeth/domain/board/domain/service/PostUpdateService.java +++ b/src/main/java/leets/weeth/domain/board/domain/service/PostUpdateService.java @@ -15,9 +15,8 @@ @RequiredArgsConstructor public class PostUpdateService { - @Transactional - public void update(Post post, PostDTO.Update dto, List fileUrls){ - post.update(dto, fileUrls); + public void update(Post post, PostDTO.Update dto){ + post.update(dto); } } diff --git a/src/main/java/leets/weeth/domain/board/presentation/NoticeAdminController.java b/src/main/java/leets/weeth/domain/board/presentation/NoticeAdminController.java index f1cebc3e..de5b3c70 100644 --- a/src/main/java/leets/weeth/domain/board/presentation/NoticeAdminController.java +++ b/src/main/java/leets/weeth/domain/board/presentation/NoticeAdminController.java @@ -26,22 +26,20 @@ public class NoticeAdminController { private final NoticeUsecase noticeUsecase; - @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + @PostMapping @Operation(summary="공지사항 생성") - public CommonResponse save(@RequestPart @Valid NoticeDTO.Save dto, - @RequestPart(value = "files", required = false) List files, + public CommonResponse save(@RequestBody @Valid NoticeDTO.Save dto, @Parameter(hidden = true) @CurrentUser Long userId) { - noticeUsecase.save(dto, files, userId); + noticeUsecase.save(dto, userId); return CommonResponse.createSuccess(NOTICE_CREATED_SUCCESS.getMessage()); } - @PatchMapping(value = "/{noticeId}", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + @PatchMapping(value = "/{noticeId}") @Operation(summary="특정 공지사항 수정") public CommonResponse update(@PathVariable Long noticeId, - @RequestPart @Valid NoticeDTO.Update dto, - @RequestPart(value = "files", required = false) List files, + @RequestBody @Valid NoticeDTO.Update dto, @Parameter(hidden = true) @CurrentUser Long userId) throws UserNotMatchException { - noticeUsecase.update(noticeId, dto, files, userId); + noticeUsecase.update(noticeId, dto, userId); return CommonResponse.createSuccess(NOTICE_UPDATED_SUCCESS.getMessage()); } diff --git a/src/main/java/leets/weeth/domain/board/presentation/PostController.java b/src/main/java/leets/weeth/domain/board/presentation/PostController.java index fd32dcb2..1c512910 100644 --- a/src/main/java/leets/weeth/domain/board/presentation/PostController.java +++ b/src/main/java/leets/weeth/domain/board/presentation/PostController.java @@ -26,12 +26,11 @@ public class PostController { private final PostUsecase postUsecase; - @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + @PostMapping @Operation(summary="게시글 생성") - public CommonResponse save(@RequestPart @Valid PostDTO.Save dto, - @RequestPart(value = "files", required = false) List files, + public CommonResponse save(@RequestBody @Valid PostDTO.Save dto, @Parameter(hidden = true) @CurrentUser Long userId) { - postUsecase.save(dto, files, userId); + postUsecase.save(dto, userId); return CommonResponse.createSuccess(POST_CREATED_SUCCESS.getMessage()); } @@ -47,13 +46,12 @@ public CommonResponse findPost(@PathVariable Long postId) { return CommonResponse.createSuccess(POST_FIND_BY_ID_SUCCESS.getMessage(),postUsecase.findPost(postId)); } - @PatchMapping(value = "/{postId}", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + @PatchMapping(value = "/{postId}") @Operation(summary="특정 게시글 수정") public CommonResponse update(@PathVariable Long postId, - @RequestPart @Valid PostDTO.Update dto, - @RequestPart(value = "files", required = false) List files, + @RequestBody @Valid PostDTO.Update dto, @Parameter(hidden = true) @CurrentUser Long userId) throws UserNotMatchException { - postUsecase.update(postId, dto, files, userId); + postUsecase.update(postId, dto, userId); return CommonResponse.createSuccess(POST_UPDATED_SUCCESS.getMessage()); } diff --git a/src/main/java/leets/weeth/domain/file/application/dto/request/FileSaveRequest.java b/src/main/java/leets/weeth/domain/file/application/dto/request/FileSaveRequest.java index 8965f7a4..73921fab 100644 --- a/src/main/java/leets/weeth/domain/file/application/dto/request/FileSaveRequest.java +++ b/src/main/java/leets/weeth/domain/file/application/dto/request/FileSaveRequest.java @@ -1,7 +1,10 @@ package leets.weeth.domain.file.application.dto.request; +import jakarta.validation.constraints.NotBlank; +import org.hibernate.validator.constraints.URL; + public record FileSaveRequest( - String fileName, - String fileUrl + @NotBlank String fileName, + @NotBlank @URL String fileUrl ) { } diff --git a/src/main/java/leets/weeth/domain/file/application/dto/request/FileUpdateRequest.java b/src/main/java/leets/weeth/domain/file/application/dto/request/FileUpdateRequest.java index 8285a646..51544cf6 100644 --- a/src/main/java/leets/weeth/domain/file/application/dto/request/FileUpdateRequest.java +++ b/src/main/java/leets/weeth/domain/file/application/dto/request/FileUpdateRequest.java @@ -1,10 +1,12 @@ package leets.weeth.domain.file.application.dto.request; import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import org.hibernate.validator.constraints.URL; public record FileUpdateRequest( - @NotBlank Long fileId, - String fileName, - String fileUrl + @NotNull Long fileId, + @NotBlank String fileName, + @NotBlank @URL String fileUrl ) { } diff --git a/src/main/java/leets/weeth/domain/file/application/mapper/FileMapper.java b/src/main/java/leets/weeth/domain/file/application/mapper/FileMapper.java index 1d856377..13fd228a 100644 --- a/src/main/java/leets/weeth/domain/file/application/mapper/FileMapper.java +++ b/src/main/java/leets/weeth/domain/file/application/mapper/FileMapper.java @@ -1,8 +1,10 @@ package leets.weeth.domain.file.application.mapper; +import leets.weeth.domain.account.domain.entity.Receipt; import leets.weeth.domain.board.domain.entity.Notice; import leets.weeth.domain.board.domain.entity.Post; import leets.weeth.domain.comment.application.mapper.CommentMapper; +import leets.weeth.domain.file.application.dto.request.FileSaveRequest; import leets.weeth.domain.file.application.dto.response.FileResponse; import leets.weeth.domain.file.application.dto.response.UrlResponse; import leets.weeth.domain.file.domain.entity.File; @@ -11,16 +13,58 @@ import org.mapstruct.MappingConstants; import org.mapstruct.ReportingPolicy; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + @Mapper(componentModel = MappingConstants.ComponentModel.SPRING, uses = CommentMapper.class, unmappedTargetPolicy = ReportingPolicy.IGNORE) public interface FileMapper { + @Mapping(target = "id", ignore = true) @Mapping(target = "post", source = "post") - File toFile(String fileName, String fileUrl, Post post); + File toFileWithPost(String fileName, String fileUrl, Post post); + @Mapping(target = "id", ignore = true) @Mapping(target = "notice", source = "notice") - File toFile(String fileName, String fileUrl, Notice notice); + File toFileWithNotice(String fileName, String fileUrl, Notice notice); + + @Mapping(target = "id", ignore = true) + @Mapping(target = "receipt", source = "receipt") + File toFileWithReceipt(String fileName, String fileUrl, Receipt receipt); + @Mapping(target = "fileId", source = "file.id") FileResponse toFileResponse(File file); UrlResponse toUrlResponse(String fileName, String putUrl); + + default List toFileList(List requests, Post post) { + List dto = requests; + if (dto == null || dto.isEmpty()) { + return Collections.emptyList(); + } + + return dto.stream() + .map(request -> toFileWithPost(request.fileName(), request.fileUrl(), post)) + .collect(Collectors.toList()); + } + + default List toFileList(List requests, Notice notice) { + if (requests == null || requests.isEmpty()) { + return Collections.emptyList(); + } + + return requests.stream() + .map(request -> toFileWithNotice(request.fileName(), request.fileUrl(), notice)) + .collect(Collectors.toList()); + } + + default List toFileList(List requests, Receipt receipt) { + if (requests == null || requests.isEmpty()) { + return Collections.emptyList(); + } + + return requests.stream() + .map(request -> toFileWithReceipt(request.fileName(), request.fileUrl(), receipt)) + .collect(Collectors.toList()); + } } diff --git a/src/main/java/leets/weeth/domain/file/domain/entity/File.java b/src/main/java/leets/weeth/domain/file/domain/entity/File.java index 60ebc533..21faf750 100644 --- a/src/main/java/leets/weeth/domain/file/domain/entity/File.java +++ b/src/main/java/leets/weeth/domain/file/domain/entity/File.java @@ -1,6 +1,7 @@ package leets.weeth.domain.file.domain.entity; import jakarta.persistence.*; +import leets.weeth.domain.account.domain.entity.Receipt; import leets.weeth.domain.board.domain.entity.Notice; import leets.weeth.domain.board.domain.entity.Post; import lombok.*; @@ -14,7 +15,7 @@ public class File { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) - public Long id; + private Long id; private String fileName; @@ -28,6 +29,10 @@ public class File { @JoinColumn(name = "notice_id") private Notice notice; + @ManyToOne + @JoinColumn(name = "receipt_id") + private Receipt receipt; + public void update(String fileName, String fileUrl) { this.fileName = fileName; this.fileUrl = fileUrl; diff --git a/src/main/java/leets/weeth/domain/file/domain/repository/FileRepository.java b/src/main/java/leets/weeth/domain/file/domain/repository/FileRepository.java index de102c43..2daee101 100644 --- a/src/main/java/leets/weeth/domain/file/domain/repository/FileRepository.java +++ b/src/main/java/leets/weeth/domain/file/domain/repository/FileRepository.java @@ -10,4 +10,6 @@ public interface FileRepository extends JpaRepository { List findAllByPostId(Long postId); List findAllByNoticeId(Long noticeId); + + List findAllByReceiptId(Long receiptId); } diff --git a/src/main/java/leets/weeth/domain/file/domain/service/FileDeleteService.java b/src/main/java/leets/weeth/domain/file/domain/service/FileDeleteService.java new file mode 100644 index 00000000..eefde008 --- /dev/null +++ b/src/main/java/leets/weeth/domain/file/domain/service/FileDeleteService.java @@ -0,0 +1,23 @@ +package leets.weeth.domain.file.domain.service; + +import leets.weeth.domain.file.domain.entity.File; +import leets.weeth.domain.file.domain.repository.FileRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +@RequiredArgsConstructor +public class FileDeleteService { + + private final FileRepository fileRepository; + + public void delete(File file) { + fileRepository.delete(file); + } + + public void delete(List files) { + fileRepository.deleteAll(files); + } +} diff --git a/src/main/java/leets/weeth/domain/file/domain/service/FileGetService.java b/src/main/java/leets/weeth/domain/file/domain/service/FileGetService.java index 68200c67..cfeb82f0 100644 --- a/src/main/java/leets/weeth/domain/file/domain/service/FileGetService.java +++ b/src/main/java/leets/weeth/domain/file/domain/service/FileGetService.java @@ -18,6 +18,10 @@ public List findAllByPost(Long postId) { } public List findAllByNotice(Long noticeId) { - return fileRepository.findAllByPostId(noticeId); + return fileRepository.findAllByNoticeId(noticeId); + } + + public List findAllByReceipt(Long receiptId) { + return fileRepository.findAllByReceiptId(receiptId); } } diff --git a/src/main/java/leets/weeth/domain/file/domain/service/FileUpdateService.java b/src/main/java/leets/weeth/domain/file/domain/service/FileUpdateService.java index ea795e2e..d84bea4c 100644 --- a/src/main/java/leets/weeth/domain/file/domain/service/FileUpdateService.java +++ b/src/main/java/leets/weeth/domain/file/domain/service/FileUpdateService.java @@ -5,6 +5,8 @@ import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; +import java.util.List; + @Service @RequiredArgsConstructor public class FileUpdateService { @@ -12,4 +14,13 @@ public class FileUpdateService { public void update(File file, FileUpdateRequest dto) { file.update(dto.fileName(), dto.fileUrl()); } + + public void updateFiles(List fileList, List dto) { + for (File file : fileList) { + dto.stream() + .filter(updateRequest -> updateRequest.fileId().equals(file.getId())) + .findFirst() + .ifPresent(updateRequest -> file.update(updateRequest.fileName(), updateRequest.fileUrl())); + } + } } diff --git a/src/main/java/leets/weeth/global/auth/jwt/exception/AnonymousAuthenticationException.java b/src/main/java/leets/weeth/global/auth/jwt/exception/AnonymousAuthenticationException.java new file mode 100644 index 00000000..7cc77451 --- /dev/null +++ b/src/main/java/leets/weeth/global/auth/jwt/exception/AnonymousAuthenticationException.java @@ -0,0 +1,9 @@ +package leets.weeth.global.auth.jwt.exception; + +import leets.weeth.global.common.exception.BusinessLogicException; + +public class AnonymousAuthenticationException extends BusinessLogicException { + public AnonymousAuthenticationException() { + super(401, "인증정보가 존재하지 않습니다."); + } +} diff --git a/src/main/java/leets/weeth/global/auth/resolver/CurrentUserArgumentResolver.java b/src/main/java/leets/weeth/global/auth/resolver/CurrentUserArgumentResolver.java index 056ec5e8..f775e374 100644 --- a/src/main/java/leets/weeth/global/auth/resolver/CurrentUserArgumentResolver.java +++ b/src/main/java/leets/weeth/global/auth/resolver/CurrentUserArgumentResolver.java @@ -1,6 +1,7 @@ package leets.weeth.global.auth.resolver; import leets.weeth.global.auth.annotation.CurrentUser; +import leets.weeth.global.auth.jwt.exception.AnonymousAuthenticationException; import leets.weeth.global.auth.jwt.service.JwtService; import lombok.RequiredArgsConstructor; import org.springframework.core.MethodParameter; @@ -31,7 +32,7 @@ public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer m Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); // 인증 객체 가져오기 if (authentication instanceof AnonymousAuthenticationToken) { // 익명 인증 토큰의 인스턴스라면 0 반환 - return 0; + throw new AnonymousAuthenticationException(); } String token = Optional.ofNullable(webRequest.getHeader("Authorization")) diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 992865b4..c5f39637 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -15,6 +15,7 @@ springdoc: swagger-ui: operations-sorter: method tags-sorter: alpha + display-request-duration: true auth: kakao: