diff --git a/src/main/java/ussum/homepage/application/post/controller/PostManageController.java b/src/main/java/ussum/homepage/application/post/controller/PostManageController.java index 70c99dda..48af2dec 100644 --- a/src/main/java/ussum/homepage/application/post/controller/PostManageController.java +++ b/src/main/java/ussum/homepage/application/post/controller/PostManageController.java @@ -3,9 +3,13 @@ import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; import ussum.homepage.application.post.service.PostManageService; +import ussum.homepage.application.post.service.dto.request.PostCreateRequest; +import ussum.homepage.application.post.service.dto.request.PostUpdateRequest; import ussum.homepage.application.post.service.dto.request.PostUserRequest; import ussum.homepage.global.ApiResponse; +import ussum.homepage.global.config.auth.UserId; @RestController @RequiredArgsConstructor @@ -30,4 +34,32 @@ public ResponseEntity> getBoardPost(@PathVariable(name = "boardCo return ApiResponse.success(postManageService.getPost(postUserRequest, boardCode, postId)); } + @PostMapping("/{boardCode}/posts") + public ResponseEntity> createBoardPost(@UserId Long userId, + @PathVariable(name = "boardCode") String boardCode, + @RequestBody PostCreateRequest postCreateRequest){ + return ApiResponse.success(postManageService.createBoardPost(userId, boardCode, postCreateRequest)); + } + + @PostMapping("/{boardCode}/posts/files") + public ResponseEntity> createBoardPostFile(@UserId Long userId, + @PathVariable(name = "boardCode") String boardCode, + @RequestPart(value = "files") MultipartFile[] files, + @RequestParam(value = "type") String typeName){ + return ApiResponse.success(postManageService.createBoardPostFile(userId, boardCode, files, typeName)); + } + + @PatchMapping("/{boardCode}/posts/{postId}") + public ResponseEntity> editBoardPost(@PathVariable(name = "boardCode") String boardCode, + @PathVariable(name = "postId") Long postId, + @RequestBody PostUpdateRequest postUpdateRequest) { + return ApiResponse.success(postManageService.editBoardPost(boardCode, postId, postUpdateRequest)); + } + + @PostMapping("/userIdTest") + public ResponseEntity> apiTest(@UserId Long userId) { + System.out.println("userId = " + userId);; + return null; + } + } diff --git a/src/main/java/ussum/homepage/application/post/service/PostManageService.java b/src/main/java/ussum/homepage/application/post/service/PostManageService.java index 86014553..358bfc77 100644 --- a/src/main/java/ussum/homepage/application/post/service/PostManageService.java +++ b/src/main/java/ussum/homepage/application/post/service/PostManageService.java @@ -5,9 +5,14 @@ import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.multipart.MultipartFile; +import ussum.homepage.application.post.service.dto.request.PostCreateRequest; +import ussum.homepage.application.post.service.dto.request.PostUpdateRequest; import ussum.homepage.application.post.service.dto.request.PostUserRequest; import ussum.homepage.application.post.service.dto.response.postDetail.*; import ussum.homepage.application.post.service.dto.response.postList.*; +import ussum.homepage.application.post.service.dto.response.postSave.PostCreateResponse; +import ussum.homepage.application.post.service.dto.response.postSave.PostFileResponse; import ussum.homepage.domain.post.Board; import ussum.homepage.domain.post.Category; import ussum.homepage.domain.post.Post; @@ -20,10 +25,14 @@ import ussum.homepage.global.common.PageInfo; import ussum.homepage.global.error.exception.GeneralException; import ussum.homepage.global.error.status.ErrorStatus; +import ussum.homepage.infra.jpa.post.entity.OngoingStatus; +import ussum.homepage.infra.utils.S3utils; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.function.BiFunction; +import java.util.stream.Collectors; @Service @RequiredArgsConstructor @@ -35,7 +44,11 @@ public class PostManageService { private final CategoryReader categoryReader; private final UserReader userReader; private final PostFileReader postFileReader; + private final PostAppender postAppender; + private final PostFileAppender postFileAppender; + private final PostModifier postModifier; private final PostStatusProcessor postStatusProcessor; + private final S3utils s3utils; private final Map> postResponseMap = Map.of( "공지사항게시판", (post, ignored) -> NoticePostResponse.of(post), @@ -115,6 +128,41 @@ public PostDetailRes getPost(PostUserRequest postUserRequest, String boardCod return PostDetailRes.of(response); } + + @Transactional + public PostCreateResponse createBoardPost(Long userId, String boardCode, PostCreateRequest postCreateRequest){ + Board board = boardReader.getBoardWithBoardCode(boardCode); + Category category = categoryReader.getCategoryWithCode(postCreateRequest.categoryCode()); + User user = userReader.getUserWithId(userId); + String onGoingStatus = Objects.equals(boardCode, "PETITION") ? "IN_PROGRESS" : null; + + Post post = postAppender.createPost(postCreateRequest.toDomain(board, user, category, onGoingStatus)); + postFileAppender.updatePostIdForIds(postCreateRequest.postFileList(), post.getId()); + return PostCreateResponse.of(post.getId(), boardCode); + } + + @Transactional + public List createBoardPostFile(Long userId, String boardCode, MultipartFile[] files, String typeName){ + List urlList = s3utils.uploadFileWithPath(userId, boardCode, files, typeName); + List postFiles = convertUrlsToPostFiles(urlList, typeName); + List afterSaveList = postFileAppender.saveAllPostFile(postFiles); + + return afterSaveList.stream() + .map(postFile -> PostFileResponse.of(postFile.getId(), postFile.getUrl())) + .collect(Collectors.toList()); + } + + private List convertUrlsToPostFiles(List urlList, String typeName) { + return urlList.stream() + .map(url -> PostFile.of(null, typeName, url, null, null)) + .collect(Collectors.toList()); + } + + @Transactional + public Long editBoardPost(String boardCode, Long postId, PostUpdateRequest postUpdateRequest){ + Post post = postModifier.updatePost(boardCode, postId, postUpdateRequest); + return post.getId(); + } } //스위치 사용 로직 /* diff --git a/src/main/java/ussum/homepage/application/post/service/PostService.java b/src/main/java/ussum/homepage/application/post/service/PostService.java index 431707ea..8dd68f86 100644 --- a/src/main/java/ussum/homepage/application/post/service/PostService.java +++ b/src/main/java/ussum/homepage/application/post/service/PostService.java @@ -62,7 +62,7 @@ public void createPost(Long userId, String boardCode, PostCreateRequest postCrea //user도 찾아 와야 하지 않을까 User user = userReader.getUserWithId(userId); - postAppender.createPost(postCreateRequest.toDomain(board, user, category)); + postAppender.createPost(postCreateRequest.toDomain(board, user, category, null)); } public PostResponse editPost(String boardCode,Long postId, PostUpdateRequest postUpdateRequest) { diff --git a/src/main/java/ussum/homepage/application/post/service/dto/request/PostCreateRequest.java b/src/main/java/ussum/homepage/application/post/service/dto/request/PostCreateRequest.java index 57a1a8ec..6b3fae0f 100644 --- a/src/main/java/ussum/homepage/application/post/service/dto/request/PostCreateRequest.java +++ b/src/main/java/ussum/homepage/application/post/service/dto/request/PostCreateRequest.java @@ -5,13 +5,16 @@ import ussum.homepage.domain.post.Post; import ussum.homepage.domain.user.User; +import java.util.List; + public record PostCreateRequest( String title, String content, String categoryCode, - String thumbNailImage + String thumbNailImage, + List postFileList ) { - public Post toDomain(Board board, User user, Category category) { + public Post toDomain(Board board, User user, Category category, String OnGoingStatus) { return Post.of( null, title, @@ -19,7 +22,7 @@ public Post toDomain(Board board, User user, Category category) { 1, thumbNailImage, "새로운", - null, + OnGoingStatus, null, null, null, diff --git a/src/main/java/ussum/homepage/application/post/service/dto/response/postSave/PostCreateResponse.java b/src/main/java/ussum/homepage/application/post/service/dto/response/postSave/PostCreateResponse.java new file mode 100644 index 00000000..cc838e83 --- /dev/null +++ b/src/main/java/ussum/homepage/application/post/service/dto/response/postSave/PostCreateResponse.java @@ -0,0 +1,19 @@ +package ussum.homepage.application.post.service.dto.response.postSave; + +import lombok.Builder; +import lombok.Getter; +import ussum.homepage.domain.post.Post; + +@Getter +@Builder +public class PostCreateResponse { + private Long post_id; + private String boardCode; + + public static PostCreateResponse of(Long id, String boardCode){ + return PostCreateResponse.builder() + .post_id(id) + .boardCode(boardCode) + .build(); + } +} diff --git a/src/main/java/ussum/homepage/application/post/service/dto/response/postSave/PostFileResponse.java b/src/main/java/ussum/homepage/application/post/service/dto/response/postSave/PostFileResponse.java new file mode 100644 index 00000000..e237ef49 --- /dev/null +++ b/src/main/java/ussum/homepage/application/post/service/dto/response/postSave/PostFileResponse.java @@ -0,0 +1,18 @@ +package ussum.homepage.application.post.service.dto.response.postSave; + +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +public class PostFileResponse { + private Long id; + private String url; + + public static PostFileResponse of(Long id, String url){ + return PostFileResponse.builder() + .id(id) + .url(url) + .build(); + } +} diff --git a/src/main/java/ussum/homepage/application/user/service/OnBoardingService.java b/src/main/java/ussum/homepage/application/user/service/OnBoardingService.java index 22e8d628..e63e4137 100644 --- a/src/main/java/ussum/homepage/application/user/service/OnBoardingService.java +++ b/src/main/java/ussum/homepage/application/user/service/OnBoardingService.java @@ -3,20 +3,13 @@ import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import org.springframework.web.multipart.MultipartFile; import ussum.homepage.application.user.service.dto.request.OnBoardingRequest; -import ussum.homepage.application.user.service.dto.response.OnBoardingResponse; import ussum.homepage.domain.csv_user.StudentCsv; -import ussum.homepage.domain.csv_user.StudentCsvRepository; -import ussum.homepage.domain.csv_user.service.StudentCsvModifier; import ussum.homepage.domain.csv_user.service.StudentCsvReader; import ussum.homepage.domain.user.User; import ussum.homepage.domain.user.service.UserModifier; import ussum.homepage.domain.user.service.UserReader; import ussum.homepage.global.error.exception.GeneralException; -import ussum.homepage.infra.jpa.csv_user.StudentCsvMapper; - -import java.util.Optional; import static ussum.homepage.global.error.status.ErrorStatus.USER_NOT_FOUND; @@ -27,7 +20,6 @@ public class OnBoardingService { private final UserModifier userModifier; private final UserReader userReader; private final StudentCsvReader studentCsvReader; - private final StudentCsvModifier studentCsvModifier; public void saveUserOnBoarding(Long userId, OnBoardingRequest request){ User user = userReader.getUserWithId(userId); @@ -38,7 +30,7 @@ public void saveUserOnBoarding(Long userId, OnBoardingRequest request){ /* TO DO // MemberEntity, GroupEntity 연결 */ - user.updateOnBoardingUser(request); + user.updateOnBoardingUser(request); // 이 메소드 수정 필요 userModifier.save(user); } } \ No newline at end of file diff --git a/src/main/java/ussum/homepage/domain/post/PostFileRepository.java b/src/main/java/ussum/homepage/domain/post/PostFileRepository.java index 20d597bd..e3113a36 100644 --- a/src/main/java/ussum/homepage/domain/post/PostFileRepository.java +++ b/src/main/java/ussum/homepage/domain/post/PostFileRepository.java @@ -1,7 +1,11 @@ package ussum.homepage.domain.post; import java.util.List; +import java.util.Optional; public interface PostFileRepository { List findAllByPostId(Long postId); + Optional findById(Long id); + List saveAll(List postFiles); + void updatePostIdForIds(List postFileIds, Long postId); } diff --git a/src/main/java/ussum/homepage/domain/post/service/PostAppender.java b/src/main/java/ussum/homepage/domain/post/service/PostAppender.java index c99d6caf..c817e438 100644 --- a/src/main/java/ussum/homepage/domain/post/service/PostAppender.java +++ b/src/main/java/ussum/homepage/domain/post/service/PostAppender.java @@ -20,7 +20,7 @@ public class PostAppender { private final PostAclRepository postAclRepository; @Transactional - public void createPost(Post post) { - postRepository.save(post); + public Post createPost(Post post) { + return postRepository.save(post); } } diff --git a/src/main/java/ussum/homepage/domain/post/service/PostFileAppender.java b/src/main/java/ussum/homepage/domain/post/service/PostFileAppender.java new file mode 100644 index 00000000..1a99f1f8 --- /dev/null +++ b/src/main/java/ussum/homepage/domain/post/service/PostFileAppender.java @@ -0,0 +1,27 @@ +package ussum.homepage.domain.post.service; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import ussum.homepage.domain.post.Post; +import ussum.homepage.domain.post.PostFile; +import ussum.homepage.domain.post.PostFileRepository; + +import java.util.List; +import java.util.Optional; + +@Service +@RequiredArgsConstructor +public class PostFileAppender { + private final PostFileRepository postFileRepository; + + @Transactional + public List saveAllPostFile(List fileList) { + return postFileRepository.saveAll(fileList); + } + + @Transactional + public void updatePostIdForIds(List postFileIds, Long postId) { + postFileRepository.updatePostIdForIds(postFileIds, postId); + } +} diff --git a/src/main/java/ussum/homepage/global/config/SecurityConfig.java b/src/main/java/ussum/homepage/global/config/SecurityConfig.java index 79929466..df183d10 100644 --- a/src/main/java/ussum/homepage/global/config/SecurityConfig.java +++ b/src/main/java/ussum/homepage/global/config/SecurityConfig.java @@ -33,8 +33,9 @@ public class SecurityConfig { "/swagger-ui/**", "/swagger-resources/**", "/v3/api-docs/**", - "/board/{boardCode}/posts/{postId}", - "/board/posts/{postId}/comments" +// "/board/{boardCode}/posts/{postId}", +// "/board/posts/{postId}/comments" + }; diff --git a/src/main/java/ussum/homepage/global/error/status/ErrorStatus.java b/src/main/java/ussum/homepage/global/error/status/ErrorStatus.java index fbd8b5cf..8b3049db 100644 --- a/src/main/java/ussum/homepage/global/error/status/ErrorStatus.java +++ b/src/main/java/ussum/homepage/global/error/status/ErrorStatus.java @@ -89,6 +89,9 @@ public enum ErrorStatus implements BaseErrorCode { //온보딩에러 INVALID_ONBOARDING_REQUEST(HttpStatus.BAD_REQUEST,"ONBOARDING_001","온보딩 정보가 올바르지 않습니다."), + //S3에러 + S3_ERROR(HttpStatus.INTERNAL_SERVER_ERROR,"S3_001","S3에 파일 저장이 실패했습니다."), + //Body 에러 INVALID_BODY(HttpStatus.BAD_REQUEST, "BODY_ERROR", "Body가 올바르지 않습니다."); diff --git a/src/main/java/ussum/homepage/infra/jpa/post/PostFileMapper.java b/src/main/java/ussum/homepage/infra/jpa/post/PostFileMapper.java index 8508804d..195c6a0b 100644 --- a/src/main/java/ussum/homepage/infra/jpa/post/PostFileMapper.java +++ b/src/main/java/ussum/homepage/infra/jpa/post/PostFileMapper.java @@ -4,6 +4,8 @@ import ussum.homepage.domain.post.PostFile; import ussum.homepage.infra.jpa.post.entity.PostFileEntity; +import java.util.List; + @Component public class PostFileMapper { public PostFile toDomain(PostFileEntity postFileEntity) { @@ -12,7 +14,23 @@ public PostFile toDomain(PostFileEntity postFileEntity) { postFileEntity.getTypeName(), postFileEntity.getUrl(), postFileEntity.getSize(), - postFileEntity.getPostEntity().getId() + null + ); + } + + public List toDomain(List postFileEntities) { + return postFileEntities.stream() + .map(this::toDomain) + .toList(); + } + + public PostFileEntity toEntity(PostFile postFile) { + return PostFileEntity.of( + postFile.getId(), + postFile.getTypeName(), + postFile.getUrl(), + null, + null ); } diff --git a/src/main/java/ussum/homepage/infra/jpa/post/PostFileRepositoryImpl.java b/src/main/java/ussum/homepage/infra/jpa/post/PostFileRepositoryImpl.java index 249fc720..aef971f3 100644 --- a/src/main/java/ussum/homepage/infra/jpa/post/PostFileRepositoryImpl.java +++ b/src/main/java/ussum/homepage/infra/jpa/post/PostFileRepositoryImpl.java @@ -8,6 +8,7 @@ import ussum.homepage.infra.jpa.post.repository.PostFileJpaRepository; import java.util.List; +import java.util.Optional; import static ussum.homepage.infra.jpa.post.entity.QPostFileEntity.postFileEntity; @@ -28,4 +29,27 @@ public List findAllByPostId(Long postId) { .map(postFileMapper::toDomain).toList(); } + @Override + public Optional findById(Long id) { + return postFileJpaRepository.findById(id).map(postFileMapper::toDomain); + } + + @Override + public List saveAll(List fileList) { + return postFileMapper.toDomain( + postFileJpaRepository.saveAll( + fileList.stream() + .map(file -> postFileMapper.toEntity(file)) + .toList()) + ); + } + + @Override + public void updatePostIdForIds(List postFileIds, Long postId) { + queryFactory + .update(postFileEntity) + .set(postFileEntity.postEntity.id, postId) + .where(postFileEntity.id.in(postFileIds)) + .execute(); + } } diff --git a/src/main/java/ussum/homepage/infra/jpa/post/entity/PostFileEntity.java b/src/main/java/ussum/homepage/infra/jpa/post/entity/PostFileEntity.java index e73909e8..63daf6a3 100644 --- a/src/main/java/ussum/homepage/infra/jpa/post/entity/PostFileEntity.java +++ b/src/main/java/ussum/homepage/infra/jpa/post/entity/PostFileEntity.java @@ -6,6 +6,7 @@ @Table(name = "post_file") @Getter @NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor public class PostFileEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @@ -18,5 +19,9 @@ public class PostFileEntity { @JoinColumn(name = "post_id") private PostEntity postEntity; + public static PostFileEntity of(Long id, String typeName, String url, String size, PostEntity postEntity) { + return new PostFileEntity(id, typeName, url, size, postEntity); + } + } diff --git a/src/main/java/ussum/homepage/infra/utils/S3utils.java b/src/main/java/ussum/homepage/infra/utils/S3utils.java index 384a4f12..a83a9959 100644 --- a/src/main/java/ussum/homepage/infra/utils/S3utils.java +++ b/src/main/java/ussum/homepage/infra/utils/S3utils.java @@ -2,22 +2,25 @@ import com.amazonaws.services.s3.AmazonS3; import com.amazonaws.services.s3.model.ObjectMetadata; +import com.amazonaws.services.s3.model.PutObjectRequest; import com.amazonaws.services.s3.model.S3Object; import com.amazonaws.services.s3.model.S3ObjectInputStream; -import com.amazonaws.util.IOUtils; import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import org.springframework.web.multipart.MultipartFile; +import ussum.homepage.global.error.exception.GeneralException; +import ussum.homepage.global.error.status.ErrorStatus; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; @RequiredArgsConstructor -@Slf4j @Component public class S3utils { private final AmazonS3 amazonS3; @@ -48,6 +51,40 @@ public String uploadFile(MultipartFile file) { return amazonS3.getUrl(bucket, originalFilename).toString(); } + + public List uploadFileWithPath(Long userId, String boardCode, MultipartFile[] files, String typeName) { + List uploadedFileUrls = new ArrayList<>(); + for (MultipartFile file : files) { + String fileName = file.getOriginalFilename(); + String fileExtension = fileName.substring(fileName.lastIndexOf(".")); + String uniqueFileName = UUID.randomUUID().toString() + fileExtension; + + String folderPath = boardCode + "/" + userId + "/" + typeName + "/"; + String fileKey = folderPath + uniqueFileName; + + try { + File convertedFile = convertMultiPartToFile(file); + amazonS3.putObject(new PutObjectRequest(bucket, fileKey, convertedFile)); + convertedFile.delete(); // 임시 파일 삭제 + + String fileUrl = amazonS3.getUrl(bucket, fileKey).toString(); + uploadedFileUrls.add(fileUrl); + } catch (IOException e) { + throw new GeneralException(ErrorStatus.S3_ERROR); + } + } + + return uploadedFileUrls; + } + + private File convertMultiPartToFile(MultipartFile file) throws IOException { + File convertedFile = new File(file.getOriginalFilename()); + FileOutputStream fos = new FileOutputStream(convertedFile); + fos.write(file.getBytes()); + fos.close(); + return convertedFile; + } + public void getFileToProject(String fileName) { S3Object s3Object = amazonS3.getObject(bucket, fileName); S3ObjectInputStream inputStream = s3Object.getObjectContent(); @@ -70,3 +107,5 @@ private void saveFile(InputStream inputStream, String filePath) throws IOExcepti } } } + +