From ae7e7f0c71b563059339321f38071902bcf5bac2 Mon Sep 17 00:00:00 2001 From: eeddiinn Date: Sun, 14 Jul 2024 02:43:34 +0900 Subject: [PATCH] =?UTF-8?q?[FIX]=20=EB=8C=80=EC=83=81,=20=EC=9D=BC?= =?UTF-8?q?=EC=8B=9C,=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20null=20=ED=97=88?= =?UTF-8?q?=EC=9A=A9=ED=95=98=EB=8F=84=EB=A1=9D=20=EB=B3=80=EA=B2=BD=20?= =?UTF-8?q?=EB=B0=8F=20=EC=A2=8B=EC=95=84=EC=9A=94=EC=88=98,=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=EC=88=98,=20=EC=A0=80=EC=9E=A5=20=EC=88=98=200?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EC=B4=88=EA=B8=B0=ED=99=94=20=20#25?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 2 + .../notice/dto/NoticeCreateRequest.java | 3 +- .../univoice/domain/notice/entity/Notice.java | 5 ++ .../domain/notice/service/NoticeService.java | 61 +++++++++++-------- .../exception/ValidationExceptionHandler.java | 24 ++++++++ .../univoice/infra/external/S3Service.java | 34 +++++------ 6 files changed, 84 insertions(+), 45 deletions(-) create mode 100644 src/main/java/sopt/univoice/infra/common/exception/ValidationExceptionHandler.java diff --git a/build.gradle b/build.gradle index 04b66f4..52848d1 100644 --- a/build.gradle +++ b/build.gradle @@ -59,6 +59,8 @@ dependencies { annotationProcessor 'org.projectlombok:lombok' testImplementation 'org.springframework.boot:spring-boot-starter-test' testRuntimeOnly 'org.junit.platform:junit-platform-launcher' + + implementation 'org.springframework.boot:spring-boot-starter-validation' // 유효성 검사 위해 } tasks.named('test') { diff --git a/src/main/java/sopt/univoice/domain/notice/dto/NoticeCreateRequest.java b/src/main/java/sopt/univoice/domain/notice/dto/NoticeCreateRequest.java index 64023d9..172b649 100644 --- a/src/main/java/sopt/univoice/domain/notice/dto/NoticeCreateRequest.java +++ b/src/main/java/sopt/univoice/domain/notice/dto/NoticeCreateRequest.java @@ -27,6 +27,5 @@ public class NoticeCreateRequest { @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) private LocalDateTime endTime; - private List studentCardImages; - + private List imageList; } diff --git a/src/main/java/sopt/univoice/domain/notice/entity/Notice.java b/src/main/java/sopt/univoice/domain/notice/entity/Notice.java index 52d250c..0246c25 100644 --- a/src/main/java/sopt/univoice/domain/notice/entity/Notice.java +++ b/src/main/java/sopt/univoice/domain/notice/entity/Notice.java @@ -30,8 +30,12 @@ public class Notice extends BaseTimeEntity { private Long noticeLike = 0L; private Long viewCount = 0L; + + private Long noticeSave = 0L; + private String target; + private LocalDateTime startTime; private LocalDateTime endTime; @@ -71,6 +75,7 @@ public Notice(String title, String content, String target, LocalDateTime startTi this.member = member; this.noticeLike = 0L; this.viewCount = 0L; + this.noticeSave = 0L; this.contentSummary = contentSummary; this.category = category; } diff --git a/src/main/java/sopt/univoice/domain/notice/service/NoticeService.java b/src/main/java/sopt/univoice/domain/notice/service/NoticeService.java index a5af6e2..930ff47 100644 --- a/src/main/java/sopt/univoice/domain/notice/service/NoticeService.java +++ b/src/main/java/sopt/univoice/domain/notice/service/NoticeService.java @@ -31,16 +31,16 @@ public class NoticeService { private final PrincipalHandler principalHandler; private final S3Service s3Service; private final OpenAiService openAiService; + private final NoticeViewRepository noticeViewRepository; private final NoticeLikeRepository noticeLikeRepository; private final SaveNoticeRepository saveNoticeRepository; - private final NoticeViewRepository noticeViewRepository; @Transactional public void createPost(NoticeCreateRequest noticeCreateRequest) { Long memberId = principalHandler.getUserIdFromPrincipal(); System.out.println("Authenticated Member ID: " + memberId); Member member = authRepository.findById(memberId) - .orElseThrow(() -> new RuntimeException("회원이 존재하지 않습니다.")); + .orElseThrow(() -> new RuntimeException("회원이 존재하지 않습니다.")); System.out.println("Member Role: " + member.getAffiliation().getRole()); String summarizedContent = null; @@ -60,27 +60,36 @@ public void createPost(NoticeCreateRequest noticeCreateRequest) { // Notice 엔티티 생성 및 저장 Notice notice = Notice.builder() - .title(noticeCreateRequest.getTitle()) - .content(noticeCreateRequest.getContent()) - .target(noticeCreateRequest.getTarget()) - .startTime(noticeCreateRequest.getStartTime()) - .endTime(noticeCreateRequest.getEndTime()) - .member(member) - .contentSummary(summarizedContent) - .category("공지사항") // category 값을 '공지사항'으로 설정 - .build(); + .title(noticeCreateRequest.getTitle()) + .content(noticeCreateRequest.getContent()) + .target(noticeCreateRequest.getTarget() != null ? noticeCreateRequest.getTarget() : "") + .startTime(noticeCreateRequest.getStartTime() != null ? noticeCreateRequest.getStartTime() : null) + .endTime(noticeCreateRequest.getEndTime() != null ? noticeCreateRequest.getEndTime() : null) + .member(member) + .contentSummary(summarizedContent) + .category("공지사항") // category 값을 '공지사항'으로 설정 + .build(); noticeRepository.save(notice); System.out.println("Notice saved successfully with ID: " + notice.getId()); // NoticeImage 엔티티 생성 및 저장 - for (MultipartFile file : noticeCreateRequest.getStudentCardImages()) { - String fileName = storeFile(file); // 파일 저장 로직 필요 - NoticeImage noticeImage = NoticeImage.builder() - .notice(notice) - .noticeImage(fileName) - .build(); - noticeImageRepository.save(noticeImage); - System.out.println("NoticeImage saved successfully with file name: " + fileName); + List files = noticeCreateRequest.getImageList(); + + if (files != null && !files.isEmpty()) { + for (MultipartFile file : files) { + try { + String fileUrl = storeFile(file); // 파일 저장 로직 필요 + NoticeImage noticeImage = NoticeImage.builder() + .notice(notice) + .noticeImage(fileUrl) + .build(); + noticeImageRepository.save(noticeImage); + System.out.println("NoticeImage saved successfully with file URL: " + fileUrl); + } catch (Exception e) { + System.err.println("Error saving NoticeImage: " + e.getMessage()); + e.printStackTrace(); + } + } } // NoticeView 엔티티 생성 및 저장 @@ -89,24 +98,24 @@ public void createPost(NoticeCreateRequest noticeCreateRequest) { for (Member universityMember : universityMembers) { NoticeView noticeView = NoticeView.builder() - .notice(notice) - .member(universityMember) - .readAt(false) - .build(); + .notice(notice) + .member(universityMember) + .readAt(false) + .build(); noticeViewRepository.save(noticeView); } - } private String storeFile(MultipartFile file) { try { - return s3Service.uploadImage("notice-images/", file); + String fileUrl = s3Service.uploadImage("notice-images/", file); + return fileUrl; } catch (IOException e) { + System.err.println("File upload failed: " + e.getMessage()); throw new RuntimeException("파일 업로드에 실패했습니다.", e); } } - @Transactional public void likeNotice(Long noticeId) { Long memberId = principalHandler.getUserIdFromPrincipal(); diff --git a/src/main/java/sopt/univoice/infra/common/exception/ValidationExceptionHandler.java b/src/main/java/sopt/univoice/infra/common/exception/ValidationExceptionHandler.java new file mode 100644 index 0000000..f8c1c2a --- /dev/null +++ b/src/main/java/sopt/univoice/infra/common/exception/ValidationExceptionHandler.java @@ -0,0 +1,24 @@ +package sopt.univoice.infra.common.exception; + +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +import java.util.HashMap; +import java.util.Map; + +@RestControllerAdvice +public class ValidationExceptionHandler { + + @ExceptionHandler(MethodArgumentNotValidException.class) + public ResponseEntity> handleValidationExceptions(MethodArgumentNotValidException ex) { + Map errors = new HashMap<>(); + ex.getBindingResult().getFieldErrors().forEach(error -> + errors.put(error.getField(), error.getDefaultMessage()) + ); + return new ResponseEntity<>(errors, HttpStatus.BAD_REQUEST); + } +} + diff --git a/src/main/java/sopt/univoice/infra/external/S3Service.java b/src/main/java/sopt/univoice/infra/external/S3Service.java index 6852aea..4741d45 100644 --- a/src/main/java/sopt/univoice/infra/external/S3Service.java +++ b/src/main/java/sopt/univoice/infra/external/S3Service.java @@ -20,15 +20,17 @@ public class S3Service { private final String bucketName; private final AwsConfig awsConfig; + private final String region; private static final List IMAGE_EXTENSIONS = Arrays.asList("image/jpeg", "image/png", "image/jpg", "image/webp"); - - public S3Service(@Value("${aws-property.s3-bucket-name}") final String bucketName, AwsConfig awsConfig) { + public S3Service(@Value("${aws-property.s3-bucket-name}") final String bucketName, + AwsConfig awsConfig, + @Value("${aws-property.aws-region}") final String region) { this.bucketName = bucketName; this.awsConfig = awsConfig; + this.region = region; } - public String uploadImage(String directoryPath, MultipartFile image) throws IOException { final String key = directoryPath + generateImageFileName(); final S3Client s3Client = awsConfig.getS3Client(); @@ -37,33 +39,33 @@ public String uploadImage(String directoryPath, MultipartFile image) throws IOEx validateFileSize(image); PutObjectRequest request = PutObjectRequest.builder() - .bucket(bucketName) - .key(key) - .contentType(image.getContentType()) - .contentDisposition("inline") - .build(); + .bucket(bucketName) + .key(key) + .contentType(image.getContentType()) + .contentDisposition("inline") + .build(); RequestBody requestBody = RequestBody.fromBytes(image.getBytes()); s3Client.putObject(request, requestBody); - return key; + + // Return the URL of the uploaded image + return String.format("https://%s.s3.%s.amazonaws.com/%s", bucketName, region, key); } public void deleteImage(String key) throws IOException { final S3Client s3Client = awsConfig.getS3Client(); s3Client.deleteObject((DeleteObjectRequest.Builder builder) -> - builder.bucket(bucketName) - .key(key) - .build() + builder.bucket(bucketName) + .key(key) + .build() ); } - private String generateImageFileName() { return UUID.randomUUID() + ".jpg"; } - private void validateExtension(MultipartFile image) { String contentType = image.getContentType(); if (!IMAGE_EXTENSIONS.contains(contentType)) { @@ -78,6 +80,4 @@ private void validateFileSize(MultipartFile image) { throw new RuntimeException("이미지 사이즈는 5MB를 넘을 수 없습니다."); } } - -} - +} \ No newline at end of file