From 858490837591e40ffb070f9a0030c43cf601a8da Mon Sep 17 00:00:00 2001 From: kimyechan Date: Mon, 20 May 2024 16:53:52 +0900 Subject: [PATCH 01/32] =?UTF-8?q?Refactor:=20=ED=94=84=EB=A1=A0=ED=8A=B8?= =?UTF-8?q?=20=EB=B0=B0=ED=8F=AC=EC=84=9C=EB=B2=84=20CORS=20URL=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/web/baebaeBE/global/config/SecurityConfig.java | 8 ++++---- .../java/com/web/baebaeBE/global/config/WebConfig.java | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/baebae-BE/src/main/java/com/web/baebaeBE/global/config/SecurityConfig.java b/baebae-BE/src/main/java/com/web/baebaeBE/global/config/SecurityConfig.java index 31ac96fb..2f72d9b4 100644 --- a/baebae-BE/src/main/java/com/web/baebaeBE/global/config/SecurityConfig.java +++ b/baebae-BE/src/main/java/com/web/baebaeBE/global/config/SecurityConfig.java @@ -42,7 +42,7 @@ public class SecurityConfig { public WebSecurityCustomizer configure() { return web -> { web.ignoring() - //.requestMatchers(toH2Console()) + .requestMatchers(toH2Console()) .requestMatchers(NO_AUTH_LIST); //.requestMatchers("/**"); }; @@ -80,7 +80,7 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { @Bean public CorsConfigurationSource corsConfigurationSource() { CorsConfiguration configuration = new CorsConfiguration(); - configuration.setAllowedOrigins(Arrays.asList("http://localhost:5173", "https://api.flipit.co.kr")); // 허용할 오리진 설정 + configuration.setAllowedOrigins(Arrays.asList("http://localhost:5173", "https://api.flipit.co.kr", "https://www.flipit.co.kr", "https://flipit.co.kr")); // 허용할 오리진 설정 configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "PATCH")); // 허용할 HTTP 메소드 설정 configuration.setAllowedHeaders(Collections.singletonList("*")); // 허용할 HTTP 헤더 설정 configuration.setAllowCredentials(true); // 쿠키를 포함한 요청 허용 설정 @@ -97,10 +97,10 @@ public BCryptPasswordEncoder bCryptPasswordEncoder() { } //H2 Security 제외 설정 - /*@Bean + @Bean @ConditionalOnProperty(name = "spring.h2.console.enabled",havingValue = "true") public WebSecurityCustomizer configureH2ConsoleEnable() { return web -> web.ignoring() .requestMatchers(PathRequest.toH2Console()); - }*/ + } } \ No newline at end of file diff --git a/baebae-BE/src/main/java/com/web/baebaeBE/global/config/WebConfig.java b/baebae-BE/src/main/java/com/web/baebaeBE/global/config/WebConfig.java index de52440f..54737547 100644 --- a/baebae-BE/src/main/java/com/web/baebaeBE/global/config/WebConfig.java +++ b/baebae-BE/src/main/java/com/web/baebaeBE/global/config/WebConfig.java @@ -9,7 +9,7 @@ public class WebConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") - .allowedOrigins("http://localhost:5173", "https://api.flipit.co.kr") + .allowedOrigins("http://localhost:5173", "https://api.flipit.co.kr", "https://www.flipit.co.kr", "https://flipit.co.kr") .allowedMethods("GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS") .allowedHeaders("*") // 모든 헤더 허용 .allowCredentials(true) From d01a9914bab0431ea93cd8de7c6685731de2b74f Mon Sep 17 00:00:00 2001 From: kimyechan Date: Mon, 20 May 2024 18:33:05 +0900 Subject: [PATCH 02/32] =?UTF-8?q?fix:=20h2=EC=84=A4=EC=A0=95=20=EB=B2=84?= =?UTF-8?q?=EA=B7=B8=20=EC=98=A4=EB=A5=98=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/web/baebaeBE/global/config/SecurityConfig.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/baebae-BE/src/main/java/com/web/baebaeBE/global/config/SecurityConfig.java b/baebae-BE/src/main/java/com/web/baebaeBE/global/config/SecurityConfig.java index 2f72d9b4..11a5a680 100644 --- a/baebae-BE/src/main/java/com/web/baebaeBE/global/config/SecurityConfig.java +++ b/baebae-BE/src/main/java/com/web/baebaeBE/global/config/SecurityConfig.java @@ -42,7 +42,7 @@ public class SecurityConfig { public WebSecurityCustomizer configure() { return web -> { web.ignoring() - .requestMatchers(toH2Console()) + //.requestMatchers(toH2Console()) .requestMatchers(NO_AUTH_LIST); //.requestMatchers("/**"); }; @@ -97,10 +97,10 @@ public BCryptPasswordEncoder bCryptPasswordEncoder() { } //H2 Security 제외 설정 - @Bean + /*@Bean @ConditionalOnProperty(name = "spring.h2.console.enabled",havingValue = "true") public WebSecurityCustomizer configureH2ConsoleEnable() { return web -> web.ignoring() .requestMatchers(PathRequest.toH2Console()); - } + }*/ } \ No newline at end of file From 1d70d66cff92e756ca745c09692eec54bc0775e8 Mon Sep 17 00:00:00 2001 From: kimyechan Date: Mon, 20 May 2024 18:39:43 +0900 Subject: [PATCH 03/32] =?UTF-8?q?fix:=20=EB=AF=B8=EC=9C=A0=ED=9A=A8=20?= =?UTF-8?q?=ED=86=A0=ED=81=B0=20=EC=82=AD=EC=A0=9C=20=EC=8A=A4=EC=BC=80?= =?UTF-8?q?=EC=A4=84=EB=9F=AC=20=EC=8B=9C=EA=B0=84=EC=A1=B0=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/web/baebaeBE/global/scheduler/FcmTokenSchduler.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/baebae-BE/src/main/java/com/web/baebaeBE/global/scheduler/FcmTokenSchduler.java b/baebae-BE/src/main/java/com/web/baebaeBE/global/scheduler/FcmTokenSchduler.java index 6274a571..0907f6ed 100644 --- a/baebae-BE/src/main/java/com/web/baebaeBE/global/scheduler/FcmTokenSchduler.java +++ b/baebae-BE/src/main/java/com/web/baebaeBE/global/scheduler/FcmTokenSchduler.java @@ -18,13 +18,13 @@ public class FcmTokenSchduler { // 매일 자정에 실행되는 스케줄러 @Scheduled(cron = "0 0 0 * * ?") public void deleteUnusedTokens() { - // 한 달 전 시간 계산 - LocalDateTime oneMonthAgo = LocalDateTime.now().minusMonths(1); + // 두 달 전 시간 계산 + LocalDateTime oneMonthAgo = LocalDateTime.now().minusMonths(2); // 모든 FcmToken 조회 List tokens = fcmTokenRepository.findAll(); for (FcmToken token : tokens) { - // 마지막 사용 시간이 한 달 이상 지난 토큰 삭제 + // 마지막 사용 시간이 두 달 이상 지난 토큰 삭제 if (token.getLastUsedTime().isBefore(oneMonthAgo)) { fcmTokenRepository.delete(token); } From 00edbc77d80c711494ddf7139fd8d6b216bf7c98 Mon Sep 17 00:00:00 2001 From: kimyechan Date: Mon, 20 May 2024 19:20:08 +0900 Subject: [PATCH 04/32] =?UTF-8?q?Refactor:=20=EC=95=8C=EB=A6=BC=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/answer/service/AnswerService.java | 9 ----- .../notification/dto/NotificationRequest.java | 3 +- .../question/service/QuestionService.java | 3 ++ .../service/MemberAnswerReactionService.java | 1 + .../firebase/FirebaseNotificationService.java | 33 +++++++++++++++++++ 5 files changed, 39 insertions(+), 10 deletions(-) diff --git a/baebae-BE/src/main/java/com/web/baebaeBE/domain/answer/service/AnswerService.java b/baebae-BE/src/main/java/com/web/baebaeBE/domain/answer/service/AnswerService.java index 945bb92f..8141c49f 100644 --- a/baebae-BE/src/main/java/com/web/baebaeBE/domain/answer/service/AnswerService.java +++ b/baebae-BE/src/main/java/com/web/baebaeBE/domain/answer/service/AnswerService.java @@ -82,15 +82,6 @@ public AnswerDetailResponse createAnswer(AnswerCreateRequest request, Long membe .build(); reactionCountJpaRepository.save(reactionCount); - // 알림 생성 및 전송 - NotificationRequest.create notificationDto = new NotificationRequest.create( - member.getId(), - "귀하의 질문에 새로운 답변이 등록되었습니다!", - question.getContent(), - NotificationRequest.EventType.NEW_ANSWER, - null - ); - notificationService.createNotification(notificationDto); // 질문의 isAnswered 상태를 true로 업데이트 question.setAnswered(true); questionRepository.save(question); diff --git a/baebae-BE/src/main/java/com/web/baebaeBE/domain/notification/dto/NotificationRequest.java b/baebae-BE/src/main/java/com/web/baebaeBE/domain/notification/dto/NotificationRequest.java index da49aa3d..a87b09b4 100644 --- a/baebae-BE/src/main/java/com/web/baebaeBE/domain/notification/dto/NotificationRequest.java +++ b/baebae-BE/src/main/java/com/web/baebaeBE/domain/notification/dto/NotificationRequest.java @@ -1,5 +1,6 @@ package com.web.baebaeBE.domain.notification.dto; +import com.web.baebaeBE.domain.reaction.entity.ReactionValue; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; @@ -17,7 +18,7 @@ public static class create{ private String notificationContent; private String detailContent; private EventType eventType; - private String reactionType; + private ReactionValue reactionType; } public enum EventType { diff --git a/baebae-BE/src/main/java/com/web/baebaeBE/domain/question/service/QuestionService.java b/baebae-BE/src/main/java/com/web/baebaeBE/domain/question/service/QuestionService.java index 34d03e9c..1921a7b2 100644 --- a/baebae-BE/src/main/java/com/web/baebaeBE/domain/question/service/QuestionService.java +++ b/baebae-BE/src/main/java/com/web/baebaeBE/domain/question/service/QuestionService.java @@ -3,6 +3,8 @@ import com.web.baebaeBE.domain.login.exception.LoginException; import com.web.baebaeBE.domain.member.entity.Member; import com.web.baebaeBE.domain.member.repository.MemberRepository; +import com.web.baebaeBE.domain.notification.dto.NotificationRequest; +import com.web.baebaeBE.domain.notification.service.NotificationService; import com.web.baebaeBE.domain.question.dto.QuestionCreateRequest; import com.web.baebaeBE.domain.question.dto.QuestionDetailResponse; import com.web.baebaeBE.domain.question.exception.QuestionError; @@ -23,6 +25,7 @@ public class QuestionService { private final QuestionRepository questionRepository; private final MemberRepository memberRepository; private final QuestionMapper questionMapper; + private final NotificationService notificationService; private final FirebaseNotificationService firebaseNotificationService; @Transactional diff --git a/baebae-BE/src/main/java/com/web/baebaeBE/domain/reaction/service/MemberAnswerReactionService.java b/baebae-BE/src/main/java/com/web/baebaeBE/domain/reaction/service/MemberAnswerReactionService.java index 52908897..833e0bbb 100644 --- a/baebae-BE/src/main/java/com/web/baebaeBE/domain/reaction/service/MemberAnswerReactionService.java +++ b/baebae-BE/src/main/java/com/web/baebaeBE/domain/reaction/service/MemberAnswerReactionService.java @@ -7,6 +7,7 @@ import com.web.baebaeBE.domain.login.exception.LoginException; import com.web.baebaeBE.domain.member.entity.Member; import com.web.baebaeBE.domain.member.repository.MemberRepository; +import com.web.baebaeBE.domain.notification.dto.NotificationRequest; import com.web.baebaeBE.domain.reactioncount.dto.ReactionResponse; import com.web.baebaeBE.domain.reaction.entity.MemberAnswerReaction; import com.web.baebaeBE.domain.reaction.entity.ReactionValue; diff --git a/baebae-BE/src/main/java/com/web/baebaeBE/global/firebase/FirebaseNotificationService.java b/baebae-BE/src/main/java/com/web/baebaeBE/global/firebase/FirebaseNotificationService.java index 8d13ba4f..a501019b 100644 --- a/baebae-BE/src/main/java/com/web/baebaeBE/global/firebase/FirebaseNotificationService.java +++ b/baebae-BE/src/main/java/com/web/baebaeBE/global/firebase/FirebaseNotificationService.java @@ -7,6 +7,8 @@ import com.web.baebaeBE.domain.fcm.service.FcmService; import com.web.baebaeBE.domain.member.entity.Member; import com.web.baebaeBE.domain.answer.entity.Answer; +import com.web.baebaeBE.domain.notification.dto.NotificationRequest; +import com.web.baebaeBE.domain.notification.service.NotificationService; import com.web.baebaeBE.domain.question.entity.Question; import com.web.baebaeBE.domain.reaction.entity.MemberAnswerReaction; import lombok.RequiredArgsConstructor; @@ -26,12 +28,23 @@ public class FirebaseNotificationService { private final FirebaseMessagingService firebaseMessagingService; private final FcmTokenRepository fcmTokenRepository; private final FcmService fcmService; + private final NotificationService notificationService; public void notifyNewQuestion(Member member, Question question) { String notificationTitle = question.getNickname() + "님이 질문을 남겼어요!\n"; String notificationBody = question.getContent(); + // 알림 생성 및 전송 + NotificationRequest.create notificationDto = new NotificationRequest.create( + member.getId(), + question.getNickname() + "님이 질문을 남겼어요!", + question.getContent(), + NotificationRequest.EventType.NEW_ANSWER, + null + ); + notificationService.createNotification(notificationDto); + // 모든 fcm 토큰 가져오기 List fcmTokens = fcmTokenRepository.findByMemberId(member.getId()); @@ -44,6 +57,16 @@ public void notifyNewAnswer(Member member, Question question, Answer answer) { String notificationTitle = member.getNickname() + "님이 질문에 답변을 남겼어요!\n"; String notificationBody = answer.getContent(); + // 알림 생성 및 전송 + NotificationRequest.create notificationDto = new NotificationRequest.create( + question.getId(), + member.getNickname() + "님이 질문에 답변을 남겼어요!", + question.getContent(), + NotificationRequest.EventType.NEW_ANSWER, + null + ); + notificationService.createNotification(notificationDto); + // 모든 fcm 토큰 가져오기 List fcmTokens = fcmTokenRepository.findByMemberId(question.getSender().getId()); @@ -72,6 +95,16 @@ public void notifyReaction(Member member, Answer answer, MemberAnswerReaction re } String notificationTitle = member.getNickname()+"님이 " + emoticon + "을 남겼어요!\n"; + //알림 생성 및 저장 + NotificationRequest.create notificationDto = new NotificationRequest.create( + member.getId(), + notificationTitle, + answer.getContent(), + NotificationRequest.EventType.NEW_ANSWER, + reaction.getReaction() + ); + notificationService.createNotification(notificationDto); + // 모든 fcm 토큰 가져오기 List fcmTokens = fcmTokenRepository.findByMemberId(answer.getMember().getId()); From 489d59c6e83de3f6d1d7da2a96b2f6f7bc5af27b Mon Sep 17 00:00:00 2001 From: kimyechan Date: Mon, 20 May 2024 20:52:33 +0900 Subject: [PATCH 05/32] =?UTF-8?q?hotfix:=20=EC=95=8C=EB=A6=BC=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20=EC=9D=BD=EC=9D=8C=EC=B2=98=EB=A6=AC=20=EC=98=A4?= =?UTF-8?q?=EB=A5=98=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../notification/service/NotificationService.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/baebae-BE/src/main/java/com/web/baebaeBE/domain/notification/service/NotificationService.java b/baebae-BE/src/main/java/com/web/baebaeBE/domain/notification/service/NotificationService.java index d74e5fdc..9474f839 100644 --- a/baebae-BE/src/main/java/com/web/baebaeBE/domain/notification/service/NotificationService.java +++ b/baebae-BE/src/main/java/com/web/baebaeBE/domain/notification/service/NotificationService.java @@ -13,6 +13,7 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import java.time.LocalDateTime; import java.util.ArrayList; @@ -21,6 +22,7 @@ @Service @Slf4j @RequiredArgsConstructor +@Transactional public class NotificationService { private final NotificationRepository notificationRepository; @@ -45,14 +47,15 @@ public Notification createNotification(NotificationRequest.create createNotifica // 특정 멤버의 모든 알람 조회 + @Transactional public NotificationResponse.NotificationListResponse getNotificationsListByMember(Long memberId) { Member member = memberRepository.findById(memberId) .orElseThrow(() -> new BusinessException(LoginException.NOT_EXIST_MEMBER)); List notificationList = notificationRepository.findByMemberOrderByNotificationTimeDesc(member); - // 사용자에게 반환할 알림 목록을 복사 - List notificationsToReturn = new ArrayList<>(notificationList); + // 복사본을 사용하여 응답을 생성 + NotificationResponse.NotificationListResponse response = NotificationResponse.NotificationContentResponse.ListOf(new ArrayList<>(notificationList)); // 원본 알림 목록의 isChecked 필드를 true로 설정하고, 데이터베이스에 변경 사항을 저장 for (Notification notification : notificationList) { @@ -60,8 +63,7 @@ public NotificationResponse.NotificationListResponse getNotificationsListByMembe } notificationRepository.saveAll(notificationList); - // 복사한 알림 목록을 사용하여 응답을 생성 - return NotificationResponse.NotificationContentResponse.ListOf(notificationsToReturn); + return response; } From a5dfce30038116efd3a150b4bdd5992f5f735bf7 Mon Sep 17 00:00:00 2001 From: kimyechan Date: Mon, 20 May 2024 22:01:45 +0900 Subject: [PATCH 06/32] =?UTF-8?q?Remove:=20FCM=ED=86=A0=ED=81=B0=20?= =?UTF-8?q?=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/web/baebaeBE/domain/fcm/controller/FcmController.java | 4 ++-- .../com/web/baebaeBE/domain/fcm/controller/api/FcmApi.java | 4 ++-- .../java/com/web/baebaeBE/domain/fcm/service/FcmService.java | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/baebae-BE/src/main/java/com/web/baebaeBE/domain/fcm/controller/FcmController.java b/baebae-BE/src/main/java/com/web/baebaeBE/domain/fcm/controller/FcmController.java index 2f587c32..92038bc0 100644 --- a/baebae-BE/src/main/java/com/web/baebaeBE/domain/fcm/controller/FcmController.java +++ b/baebae-BE/src/main/java/com/web/baebaeBE/domain/fcm/controller/FcmController.java @@ -26,12 +26,12 @@ public ResponseEntity addFcmToken( return ResponseEntity.ok().build(); } - @PutMapping("/{memberId}") + /*@PutMapping("/{memberId}") public ResponseEntity updateFcmToken( @PathVariable Long memberId, @RequestBody FcmRequest.UpdateToken request ) { fcmService.updateFcmToken(request.getOldFcmToken(), request.getNewFcmToken(), memberId); return ResponseEntity.ok().build(); - } + }*/ } diff --git a/baebae-BE/src/main/java/com/web/baebaeBE/domain/fcm/controller/api/FcmApi.java b/baebae-BE/src/main/java/com/web/baebaeBE/domain/fcm/controller/api/FcmApi.java index a1a8d034..bac06a00 100644 --- a/baebae-BE/src/main/java/com/web/baebaeBE/domain/fcm/controller/api/FcmApi.java +++ b/baebae-BE/src/main/java/com/web/baebaeBE/domain/fcm/controller/api/FcmApi.java @@ -53,7 +53,7 @@ ResponseEntity addFcmToken(@PathVariable Long memberId, @RequestBody FcmRequest.CreateToken request); - @Operation( + /*@Operation( summary = "FCM 토큰 업데이트", description = "회원의 기존 FCM 토큰을 새로운 FCM 토큰으로 업데이트합니다.", security = @SecurityRequirement(name = "bearerAuth") @@ -82,5 +82,5 @@ ResponseEntity addFcmToken(@PathVariable Long memberId, }) @RequestMapping(method = RequestMethod.PUT, value = "/{memberId}") ResponseEntity updateFcmToken(@PathVariable Long memberId, - @RequestBody FcmRequest.UpdateToken request); + @RequestBody FcmRequest.UpdateToken request);*/ } diff --git a/baebae-BE/src/main/java/com/web/baebaeBE/domain/fcm/service/FcmService.java b/baebae-BE/src/main/java/com/web/baebaeBE/domain/fcm/service/FcmService.java index ac35f5b2..edd3ec30 100644 --- a/baebae-BE/src/main/java/com/web/baebaeBE/domain/fcm/service/FcmService.java +++ b/baebae-BE/src/main/java/com/web/baebaeBE/domain/fcm/service/FcmService.java @@ -38,7 +38,7 @@ public FcmToken addFcmToken(Long memberId, String fcmToken) { return fcmTokenRepository.save(token); } - public void updateFcmToken(String oldFcmToken, String newFcmToken, Long memberId) { + /*public void updateFcmToken(String oldFcmToken, String newFcmToken, Long memberId) { FcmToken token = fcmTokenRepository.findByToken(oldFcmToken) .orElseThrow(() -> new BusinessException(FcmException.NOT_FOUND_FCM)); @@ -47,7 +47,7 @@ public void updateFcmToken(String oldFcmToken, String newFcmToken, Long memberId token.updateToken(newFcmToken); fcmTokenRepository.save(token); - } + }*/ public void verifyFcmToken(Long memberId, String fcmToken) { Member member = memberRepository.findById(memberId) From 6c5ffa109199d3c11644366063999747f6a1e9d1 Mon Sep 17 00:00:00 2001 From: kimyechan Date: Tue, 21 May 2024 01:44:22 +0900 Subject: [PATCH 07/32] =?UTF-8?q?Feat:=20=ED=9A=8C=EC=9B=90=EC=9D=B8?= =?UTF-8?q?=EA=B0=80=20=ED=94=84=EB=A1=9C=EC=84=B8=EC=8A=A4=20PointCut=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../annotation/AuthorizationAnswer.java | 11 +++ .../annotation/AuthorizationCategory.java | 11 +++ .../annotation/AuthorizationMember.java | 11 +++ .../annotation/AuthorizationQuestion.java | 11 +++ .../aspect/AuthPolicyAspect.java | 92 +++++++++++++++++++ 5 files changed, 136 insertions(+) create mode 100644 baebae-BE/src/main/java/com/web/baebaeBE/global/authorization/annotation/AuthorizationAnswer.java create mode 100644 baebae-BE/src/main/java/com/web/baebaeBE/global/authorization/annotation/AuthorizationCategory.java create mode 100644 baebae-BE/src/main/java/com/web/baebaeBE/global/authorization/annotation/AuthorizationMember.java create mode 100644 baebae-BE/src/main/java/com/web/baebaeBE/global/authorization/annotation/AuthorizationQuestion.java create mode 100644 baebae-BE/src/main/java/com/web/baebaeBE/global/authorization/aspect/AuthPolicyAspect.java diff --git a/baebae-BE/src/main/java/com/web/baebaeBE/global/authorization/annotation/AuthorizationAnswer.java b/baebae-BE/src/main/java/com/web/baebaeBE/global/authorization/annotation/AuthorizationAnswer.java new file mode 100644 index 00000000..25c7c608 --- /dev/null +++ b/baebae-BE/src/main/java/com/web/baebaeBE/global/authorization/annotation/AuthorizationAnswer.java @@ -0,0 +1,11 @@ +package com.web.baebaeBE.global.authorization.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface AuthorizationAnswer { +} diff --git a/baebae-BE/src/main/java/com/web/baebaeBE/global/authorization/annotation/AuthorizationCategory.java b/baebae-BE/src/main/java/com/web/baebaeBE/global/authorization/annotation/AuthorizationCategory.java new file mode 100644 index 00000000..d02bb3cd --- /dev/null +++ b/baebae-BE/src/main/java/com/web/baebaeBE/global/authorization/annotation/AuthorizationCategory.java @@ -0,0 +1,11 @@ +package com.web.baebaeBE.global.authorization.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface AuthorizationCategory { +} diff --git a/baebae-BE/src/main/java/com/web/baebaeBE/global/authorization/annotation/AuthorizationMember.java b/baebae-BE/src/main/java/com/web/baebaeBE/global/authorization/annotation/AuthorizationMember.java new file mode 100644 index 00000000..cf106025 --- /dev/null +++ b/baebae-BE/src/main/java/com/web/baebaeBE/global/authorization/annotation/AuthorizationMember.java @@ -0,0 +1,11 @@ +package com.web.baebaeBE.global.authorization.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface AuthorizationMember { +} diff --git a/baebae-BE/src/main/java/com/web/baebaeBE/global/authorization/annotation/AuthorizationQuestion.java b/baebae-BE/src/main/java/com/web/baebaeBE/global/authorization/annotation/AuthorizationQuestion.java new file mode 100644 index 00000000..63cb434b --- /dev/null +++ b/baebae-BE/src/main/java/com/web/baebaeBE/global/authorization/annotation/AuthorizationQuestion.java @@ -0,0 +1,11 @@ +package com.web.baebaeBE.global.authorization.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface AuthorizationQuestion { +} diff --git a/baebae-BE/src/main/java/com/web/baebaeBE/global/authorization/aspect/AuthPolicyAspect.java b/baebae-BE/src/main/java/com/web/baebaeBE/global/authorization/aspect/AuthPolicyAspect.java new file mode 100644 index 00000000..e730bfef --- /dev/null +++ b/baebae-BE/src/main/java/com/web/baebaeBE/global/authorization/aspect/AuthPolicyAspect.java @@ -0,0 +1,92 @@ +package com.web.baebaeBE.global.authorization.aspect; + +import com.web.baebaeBE.domain.answer.entity.Answer; +import com.web.baebaeBE.domain.answer.exception.AnswerError; +import com.web.baebaeBE.domain.answer.repository.AnswerRepository; +import com.web.baebaeBE.domain.category.entity.Category; +import com.web.baebaeBE.domain.category.exception.CategoryException; +import com.web.baebaeBE.domain.category.repository.CategoryRepository; +import com.web.baebaeBE.domain.member.entity.Member; +import com.web.baebaeBE.domain.member.exception.MemberException; +import com.web.baebaeBE.domain.member.repository.MemberRepository; +import com.web.baebaeBE.domain.question.entity.Question; +import com.web.baebaeBE.domain.question.exception.QuestionError; +import com.web.baebaeBE.domain.question.repository.QuestionRepository; +import com.web.baebaeBE.global.error.exception.BusinessException; +import lombok.RequiredArgsConstructor; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Component; + + +@Aspect +@Component +@RequiredArgsConstructor +public class AuthPolicyAspect { + + private final MemberRepository memberRepository; + private final QuestionRepository questionRepository; + private final AnswerRepository answerRepository; + private final CategoryRepository categoryRepository; + + @Before("@annotation(com.web.baebaeBE.global.authorization.annotation.AuthorizationMember) && args(memberId,..)") + public void checkMember(JoinPoint joinPoint, Long memberId) { + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + String currentEmail = authentication.getName(); + System.out.println("checkMember: " + memberId); + + Member member = memberRepository.findById(memberId) + .orElseThrow(() -> new BusinessException(MemberException.NOT_EXIST_MEMBER)); + + if (!member.getEmail().equals(currentEmail)) { + throw new BusinessException(MemberException.NOT_MATCH_MEMBER); + } + } + + @Before("@annotation(com.web.baebaeBE.global.authorization.annotation.AuthorizationQuestion) && args(questionId,..)") + public void checkQuestion(JoinPoint joinPoint, Long questionId) { + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + String currentEmail = authentication.getName(); + System.out.println("checkQuestion: " + questionId); + + Question question = questionRepository.findById(questionId) + .orElseThrow(() -> new BusinessException(QuestionError.NO_EXIST_QUESTION)); + + if (!question.getSender().getEmail().equals(currentEmail)) { + throw new BusinessException(MemberException.NOT_MATCH_MEMBER); + } + } + + @Before("@annotation(com.web.baebaeBE.global.authorization.annotation.AuthorizationAnswer) && args(answerId,..)") + public void checkAnswer(JoinPoint joinPoint, Long answerId) { + Object[] args = joinPoint.getArgs(); + System.out.println(args[0]); + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + String currentEmail = authentication.getName(); + System.out.println("checkAnswer: " + answerId); + + Answer answer = answerRepository.findByAnswerId(answerId) + .orElseThrow(() -> new BusinessException(AnswerError.NO_EXIST_ANSWER)); + + if (!answer.getMember().getEmail().equals(currentEmail)) { + throw new BusinessException(MemberException.NOT_MATCH_MEMBER); + } + } + + @Before("@annotation(com.web.baebaeBE.global.authorization.annotation.AuthorizationCategory) && args(categoryId,..)") + public void checkCategory(JoinPoint joinPoint, Long categoryId) { + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + String currentEmail = authentication.getName(); + System.out.println("checkCategory: " + categoryId); + + Category category = categoryRepository.findById(categoryId) + .orElseThrow(() -> new BusinessException(CategoryException.CATEGORY_NOT_FOUND)); + + if (!category.getMember().getEmail().equals(currentEmail)) { + throw new BusinessException(MemberException.NOT_MATCH_MEMBER); + } + } +} \ No newline at end of file From d55d07f83141728694da12947c3c8bf3a269afd2 Mon Sep 17 00:00:00 2001 From: kimyechan Date: Tue, 21 May 2024 01:45:35 +0900 Subject: [PATCH 08/32] =?UTF-8?q?Feat:=20=EC=9D=B8=EA=B0=80=20=EC=A0=91?= =?UTF-8?q?=EA=B7=BC=EC=97=90=EB=9F=AC=20Exception=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../web/baebaeBE/domain/member/exception/MemberException.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/baebae-BE/src/main/java/com/web/baebaeBE/domain/member/exception/MemberException.java b/baebae-BE/src/main/java/com/web/baebaeBE/domain/member/exception/MemberException.java index 5d8491e5..50d6ae19 100644 --- a/baebae-BE/src/main/java/com/web/baebaeBE/domain/member/exception/MemberException.java +++ b/baebae-BE/src/main/java/com/web/baebaeBE/domain/member/exception/MemberException.java @@ -11,7 +11,8 @@ public enum MemberException implements ErrorCode { NOT_EXIST_MEMBER(HttpStatus.NOT_FOUND, "MM-001", "존재하지 않는 회원입니다."), NOT_VERIFY_MEMBET_WITH_TOKEN(HttpStatus.UNAUTHORIZED,"MM-002", "회원정보와 토큰정보가 일치하지 않습니다."), - INVAILD_IMAGE_FILE(HttpStatus.UNAUTHORIZED,"MM-003", "존재하지 않는 이미지 파일입니다."); + INVAILD_IMAGE_FILE(HttpStatus.UNAUTHORIZED,"MM-003", "존재하지 않는 이미지 파일입니다."), + NOT_MATCH_MEMBER(HttpStatus.UNAUTHORIZED,"MM-004", "잘못된 접근입니다."); private final HttpStatus httpStatus; private final String errorCode; From 3bdb767bfa21b3dbf7636e1ba05a02cf67355d6d Mon Sep 17 00:00:00 2001 From: kimyechan Date: Tue, 21 May 2024 01:46:21 +0900 Subject: [PATCH 09/32] =?UTF-8?q?Refactor:=20=EC=8A=A4=ED=94=84=EB=A7=81?= =?UTF-8?q?=20=EC=8B=9C=ED=81=90=EB=A6=AC=ED=8B=B0=20=EA=B2=BD=EB=A1=9C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/web/baebaeBE/global/config/SecurityConfig.java | 2 ++ .../com/web/baebaeBE/global/security/SecurityConstants.java | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/baebae-BE/src/main/java/com/web/baebaeBE/global/config/SecurityConfig.java b/baebae-BE/src/main/java/com/web/baebaeBE/global/config/SecurityConfig.java index 11a5a680..26cd3a5f 100644 --- a/baebae-BE/src/main/java/com/web/baebaeBE/global/config/SecurityConfig.java +++ b/baebae-BE/src/main/java/com/web/baebaeBE/global/config/SecurityConfig.java @@ -9,6 +9,7 @@ import org.springframework.boot.autoconfigure.security.servlet.PathRequest; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.http.HttpMethod; import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; @@ -42,6 +43,7 @@ public class SecurityConfig { public WebSecurityCustomizer configure() { return web -> { web.ignoring() + .requestMatchers(HttpMethod.GET, "/api/member/profile-image/{memberId}") //.requestMatchers(toH2Console()) .requestMatchers(NO_AUTH_LIST); //.requestMatchers("/**"); diff --git a/baebae-BE/src/main/java/com/web/baebaeBE/global/security/SecurityConstants.java b/baebae-BE/src/main/java/com/web/baebaeBE/global/security/SecurityConstants.java index da3e7bd8..dc847310 100644 --- a/baebae-BE/src/main/java/com/web/baebaeBE/global/security/SecurityConstants.java +++ b/baebae-BE/src/main/java/com/web/baebaeBE/global/security/SecurityConstants.java @@ -6,7 +6,7 @@ public final class SecurityConstants { // oAuth2 인증 제외 "/api/oauth/kakao", "/favicon.ico", "/oauth/kakao/callback", "/api/auth/login", "/api/auth/isExisting", "/api/auth/nickname/isExisting", - "/api/member/profile-image/{memberId}", "/api/member/nickname/{nickname}", + "/api/member/nickname/{nickname}", "/api/category/{memberId}", "/api/answers", "/api/answers/member/{memberId}", "/api/reactionsCount/{answerId}/reactionsCount", // Swagger 제외 과정 From 7aba801d4b726edb285937f8a6dff1c680033f43 Mon Sep 17 00:00:00 2001 From: kimyechan Date: Tue, 21 May 2024 02:52:55 +0900 Subject: [PATCH 10/32] =?UTF-8?q?Feat:=20Member=EA=B4=80=EB=A6=AC=20API=20?= =?UTF-8?q?=EC=9D=B8=EA=B0=80=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../member/controller/MemberController.java | 6 +++++- .../domain/member/service/MemberService.java | 15 +-------------- .../global/security/SecurityConstants.java | 1 - 3 files changed, 6 insertions(+), 16 deletions(-) diff --git a/baebae-BE/src/main/java/com/web/baebaeBE/domain/member/controller/MemberController.java b/baebae-BE/src/main/java/com/web/baebaeBE/domain/member/controller/MemberController.java index 2b6d9bc5..1784929e 100644 --- a/baebae-BE/src/main/java/com/web/baebaeBE/domain/member/controller/MemberController.java +++ b/baebae-BE/src/main/java/com/web/baebaeBE/domain/member/controller/MemberController.java @@ -5,6 +5,7 @@ import com.web.baebaeBE.domain.member.controller.api.MemberApi; import com.web.baebaeBE.domain.member.dto.MemberRequest; import com.web.baebaeBE.domain.member.dto.MemberResponse; +import com.web.baebaeBE.global.authorization.annotation.AuthorizationMember; import jakarta.servlet.http.HttpServletRequest; import lombok.RequiredArgsConstructor; import org.springframework.http.MediaType; @@ -48,6 +49,7 @@ public ResponseEntity getProfileImage( @PatchMapping(value = "/profile-image/{memberId}", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + @AuthorizationMember public ResponseEntity updateProfileImage( @PathVariable Long memberId, @RequestPart(value = "image") MultipartFile image) { @@ -57,6 +59,7 @@ public ResponseEntity updateProfileImage( @PatchMapping("/nickname/{memberId}") + @AuthorizationMember public ResponseEntity updateNickname( @PathVariable Long memberId, @RequestBody MemberRequest.UpdateNicknameDto updateNicknameDto) { @@ -66,11 +69,12 @@ public ResponseEntity updateNickname( } @DeleteMapping("/{memberId}") + @AuthorizationMember public ResponseEntity deleteMember( @PathVariable Long memberId, HttpServletRequest httpServletRequest) { - memberService.deleteMember(memberId, httpServletRequest); + memberService.deleteMember(memberId); return ResponseEntity.ok().build(); } diff --git a/baebae-BE/src/main/java/com/web/baebaeBE/domain/member/service/MemberService.java b/baebae-BE/src/main/java/com/web/baebaeBE/domain/member/service/MemberService.java index a0f87c16..f1de459e 100644 --- a/baebae-BE/src/main/java/com/web/baebaeBE/domain/member/service/MemberService.java +++ b/baebae-BE/src/main/java/com/web/baebaeBE/domain/member/service/MemberService.java @@ -76,23 +76,10 @@ public void updateNickname(Long memberId, String nickname) { memberRepository.save(member); } - public void deleteMember(Long id, HttpServletRequest httpServletRequest) { - String accessToken = jwtTokenProvider.getToken(httpServletRequest); - verifyMemberWithToken(id, accessToken); - + public void deleteMember(Long id) { memberRepository.deleteById(id); } - public void verifyMemberWithToken(Long memberId,String accessToken){ - Member member = memberRepository.findById(memberId) - .orElseThrow(() -> new BusinessException(LoginException.NOT_EXIST_MEMBER)); - - String memberEmail = jwtTokenProvider.getUserEmail(accessToken); - - //회원 정보와 토큰안의 이메일 정보가 일치하지않으면 예외 발생 - if(!member.getEmail().equals(memberEmail)) - throw new BusinessException(MemberException.NOT_VERIFY_MEMBET_WITH_TOKEN); - } // 닉네임 기반으로 memberId를 찾아오는 메서드 public MemberResponse.MemberIdResponse getMemberIdByNickname(String nickname) { diff --git a/baebae-BE/src/main/java/com/web/baebaeBE/global/security/SecurityConstants.java b/baebae-BE/src/main/java/com/web/baebaeBE/global/security/SecurityConstants.java index dc847310..357b6420 100644 --- a/baebae-BE/src/main/java/com/web/baebaeBE/global/security/SecurityConstants.java +++ b/baebae-BE/src/main/java/com/web/baebaeBE/global/security/SecurityConstants.java @@ -6,7 +6,6 @@ public final class SecurityConstants { // oAuth2 인증 제외 "/api/oauth/kakao", "/favicon.ico", "/oauth/kakao/callback", "/api/auth/login", "/api/auth/isExisting", "/api/auth/nickname/isExisting", - "/api/member/nickname/{nickname}", "/api/category/{memberId}", "/api/answers", "/api/answers/member/{memberId}", "/api/reactionsCount/{answerId}/reactionsCount", // Swagger 제외 과정 From 3e25b7dbe2d56b77cd078eb66671ffb161d6b830 Mon Sep 17 00:00:00 2001 From: kimyechan Date: Tue, 21 May 2024 03:19:06 +0900 Subject: [PATCH 11/32] =?UTF-8?q?Feat:=20=EC=82=AC=EC=9A=A9=EC=9E=90=20?= =?UTF-8?q?=EC=95=8C=EB=A6=BC=EC=A1=B0=ED=9A=8C=20API=20=EC=9D=B8=EA=B0=80?= =?UTF-8?q?=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/notification/controller/NotificationController.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/baebae-BE/src/main/java/com/web/baebaeBE/domain/notification/controller/NotificationController.java b/baebae-BE/src/main/java/com/web/baebaeBE/domain/notification/controller/NotificationController.java index ca66f935..f911f416 100644 --- a/baebae-BE/src/main/java/com/web/baebaeBE/domain/notification/controller/NotificationController.java +++ b/baebae-BE/src/main/java/com/web/baebaeBE/domain/notification/controller/NotificationController.java @@ -3,6 +3,7 @@ import com.web.baebaeBE.domain.notification.controller.api.NotificationApi; import com.web.baebaeBE.domain.notification.dto.NotificationResponse; import com.web.baebaeBE.domain.notification.service.NotificationService; +import com.web.baebaeBE.global.authorization.annotation.AuthorizationMember; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; @@ -20,6 +21,7 @@ public class NotificationController implements NotificationApi { // 특정 멤버의 모든 알람 조회 @GetMapping("/member/{memberId}") + @AuthorizationMember public ResponseEntity getNotificationsListByMember(@PathVariable Long memberId) { return ResponseEntity.ok(notificationService.getNotificationsListByMember(memberId)); From 4b19bb6bfcbedd09dc257ac1029d96417dc66a84 Mon Sep 17 00:00:00 2001 From: kimyechan Date: Tue, 21 May 2024 12:48:57 +0900 Subject: [PATCH 12/32] =?UTF-8?q?Feat:=20=EC=A7=88=EB=AC=B8=EA=B4=80?= =?UTF-8?q?=EB=A0=A8=20API=20=EC=9D=B8=EA=B0=80=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/question/controller/QuestionController.java | 9 ++++++++- .../domain/question/controller/api/QuestionApi.java | 5 +++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/baebae-BE/src/main/java/com/web/baebaeBE/domain/question/controller/QuestionController.java b/baebae-BE/src/main/java/com/web/baebaeBE/domain/question/controller/QuestionController.java index 8737c5f9..55877b5e 100644 --- a/baebae-BE/src/main/java/com/web/baebaeBE/domain/question/controller/QuestionController.java +++ b/baebae-BE/src/main/java/com/web/baebaeBE/domain/question/controller/QuestionController.java @@ -4,6 +4,8 @@ import com.web.baebaeBE.domain.question.dto.QuestionCreateRequest; import com.web.baebaeBE.domain.question.dto.QuestionDetailResponse; import com.web.baebaeBE.domain.question.service.QuestionService; +import com.web.baebaeBE.global.authorization.annotation.AuthorizationMember; +import com.web.baebaeBE.global.authorization.annotation.AuthorizationQuestion; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.http.HttpStatus; @@ -22,8 +24,11 @@ public QuestionController(QuestionService questionService) { } @PostMapping("/sender/{senderId}/receiver/{receiverId}") + @AuthorizationMember public ResponseEntity createQuestion( - @RequestBody QuestionCreateRequest request, @PathVariable Long senderId, @PathVariable Long receiverId) { + @PathVariable Long senderId, + @PathVariable Long receiverId, + @RequestBody QuestionCreateRequest request) { QuestionDetailResponse createdQuestion = questionService.createQuestion(request, senderId, receiverId); return ResponseEntity.status(HttpStatus.CREATED).body(createdQuestion); } @@ -50,6 +55,7 @@ public ResponseEntity> getUnansweredQuestions( } @PutMapping("/{questionId}") + @AuthorizationQuestion public ResponseEntity updateQuestion( @PathVariable Long questionId, @RequestParam String content) { questionService.updateQuestion(questionId, content); @@ -57,6 +63,7 @@ public ResponseEntity updateQuestion( } @DeleteMapping("/{questionId}") + @AuthorizationQuestion public ResponseEntity deleteQuestion(@PathVariable Long questionId) { questionService.deleteQuestion(questionId); return ResponseEntity.status(HttpStatus.NO_CONTENT).body(null); diff --git a/baebae-BE/src/main/java/com/web/baebaeBE/domain/question/controller/api/QuestionApi.java b/baebae-BE/src/main/java/com/web/baebaeBE/domain/question/controller/api/QuestionApi.java index deabf063..8e540198 100644 --- a/baebae-BE/src/main/java/com/web/baebaeBE/domain/question/controller/api/QuestionApi.java +++ b/baebae-BE/src/main/java/com/web/baebaeBE/domain/question/controller/api/QuestionApi.java @@ -41,8 +41,9 @@ public interface QuestionApi { "}"))) @PostMapping("/sender/{senderId}/receiver/{receiverId}") ResponseEntity createQuestion( - @RequestBody QuestionCreateRequest request, - @PathVariable Long senderId, @PathVariable Long receiverId); + @PathVariable Long senderId, @PathVariable Long receiverId, + @RequestBody QuestionCreateRequest request + ); @Operation( summary = "모든 질문 조회", From 9f8455b99ce88c761093a6a0e2d05586004290ce Mon Sep 17 00:00:00 2001 From: kimyechan Date: Tue, 21 May 2024 12:49:15 +0900 Subject: [PATCH 13/32] =?UTF-8?q?Feat:=20=EB=8B=B5=EB=B3=80=EA=B4=80?= =?UTF-8?q?=EB=A0=A8=20API=20=EC=9D=B8=EA=B0=80=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../baebaeBE/domain/answer/controller/AnswerController.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/baebae-BE/src/main/java/com/web/baebaeBE/domain/answer/controller/AnswerController.java b/baebae-BE/src/main/java/com/web/baebaeBE/domain/answer/controller/AnswerController.java index a8cfc57c..37341bac 100644 --- a/baebae-BE/src/main/java/com/web/baebaeBE/domain/answer/controller/AnswerController.java +++ b/baebae-BE/src/main/java/com/web/baebaeBE/domain/answer/controller/AnswerController.java @@ -7,6 +7,9 @@ import com.web.baebaeBE.domain.answer.service.AnswerService; import com.web.baebaeBE.domain.reaction.entity.ReactionValue; +import com.web.baebaeBE.global.authorization.annotation.AuthorizationAnswer; +import com.web.baebaeBE.global.authorization.annotation.AuthorizationMember; +import com.web.baebaeBE.global.authorization.annotation.AuthorizationQuestion; import lombok.AllArgsConstructor; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; @@ -26,6 +29,7 @@ public class AnswerController implements AnswerApi { private final AnswerService answerService; @PostMapping(value = "/{memberId}", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + @AuthorizationMember public ResponseEntity createAnswer(@PathVariable Long memberId, @RequestPart(value = "imageFile") MultipartFile imageFile, @RequestPart AnswerCreateRequest request) { @@ -43,6 +47,7 @@ public ResponseEntity> getAllAnswers( } @PutMapping(value = "/{answerId}", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + @AuthorizationAnswer public ResponseEntity updateAnswer(@PathVariable Long answerId, @RequestPart(value = "imageFile", required = false) MultipartFile imageFile, @RequestPart AnswerCreateRequest request) { @@ -51,6 +56,7 @@ public ResponseEntity updateAnswer(@PathVariable Long answ } @DeleteMapping("/{answerId}") + @AuthorizationAnswer public ResponseEntity deleteAnswer(@PathVariable Long answerId) { answerService.deleteAnswer(answerId); return ResponseEntity.status(HttpStatus.NO_CONTENT).body(null); From fc33ba06aef0e38f5a817901484b64008be530df Mon Sep 17 00:00:00 2001 From: kimyechan Date: Tue, 21 May 2024 12:49:28 +0900 Subject: [PATCH 14/32] =?UTF-8?q?Feat:=20=EC=B9=B4=ED=85=8C=EA=B3=A0?= =?UTF-8?q?=EB=A6=AC=EA=B4=80=EB=A0=A8=20API=20=EC=9D=B8=EA=B0=80=EC=B2=98?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/category/controller/CategoryController.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/baebae-BE/src/main/java/com/web/baebaeBE/domain/category/controller/CategoryController.java b/baebae-BE/src/main/java/com/web/baebaeBE/domain/category/controller/CategoryController.java index 104bd030..e33f4b93 100644 --- a/baebae-BE/src/main/java/com/web/baebaeBE/domain/category/controller/CategoryController.java +++ b/baebae-BE/src/main/java/com/web/baebaeBE/domain/category/controller/CategoryController.java @@ -6,6 +6,9 @@ import com.web.baebaeBE.domain.category.dto.CategoryResponse; import com.web.baebaeBE.domain.category.entity.Category; import com.web.baebaeBE.domain.category.service.CategoryService; +import com.web.baebaeBE.global.authorization.annotation.AuthorizationCategory; +import com.web.baebaeBE.global.authorization.annotation.AuthorizationCategoryAndAnswer; +import com.web.baebaeBE.global.authorization.annotation.AuthorizationMember; import lombok.RequiredArgsConstructor; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; @@ -21,6 +24,7 @@ public class CategoryController implements CategoryApi { private final CategoryService categoryService; @PostMapping(value = "/{memberId}", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + @AuthorizationMember public ResponseEntity createCategory( @PathVariable Long memberId, @RequestPart(value = "categoryImage", required = false) MultipartFile categoryImage, @@ -40,6 +44,7 @@ public ResponseEntity getCategoriesByMemb } @PostMapping("/{categoryId}/answers/{answerId}") + @AuthorizationCategoryAndAnswer public ResponseEntity addAnswerToCategory( @PathVariable Long categoryId, @PathVariable Long answerId @@ -49,7 +54,8 @@ public ResponseEntity addAnswerToCategory( return ResponseEntity.noContent().build(); } - @PatchMapping("/{categoryId}/image") + @PatchMapping(value = "/{categoryId}/image", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + @AuthorizationCategory public ResponseEntity updateCategoryImage( @PathVariable Long categoryId, @RequestPart("imageFile") MultipartFile imageFile @@ -60,6 +66,7 @@ public ResponseEntity updateCatego @PutMapping("/{categoryId}") + @AuthorizationCategory public ResponseEntity updateCategory( @PathVariable Long categoryId, @RequestBody CategoryRequest.UpdateCategory updateCategory @@ -70,6 +77,7 @@ public ResponseEntity updateCatego } @DeleteMapping("/{categoryId}") + @AuthorizationCategory public ResponseEntity deleteCategory( @PathVariable Long categoryId ) { From 150a425ec3f115be28dafe363ed9e4e36ce3a07e Mon Sep 17 00:00:00 2001 From: kimyechan Date: Tue, 21 May 2024 12:51:52 +0900 Subject: [PATCH 15/32] =?UTF-8?q?Fix:=20=ED=8C=8C=EC=9D=B4=EC=96=B4?= =?UTF-8?q?=EB=B2=A0=EC=9D=B4=EC=8A=A4=20=EC=98=A4=EB=A5=98=ED=95=B4?= =?UTF-8?q?=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../baebaeBE/global/firebase/FirebaseNotificationService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/baebae-BE/src/main/java/com/web/baebaeBE/global/firebase/FirebaseNotificationService.java b/baebae-BE/src/main/java/com/web/baebaeBE/global/firebase/FirebaseNotificationService.java index a501019b..934f5eb4 100644 --- a/baebae-BE/src/main/java/com/web/baebaeBE/global/firebase/FirebaseNotificationService.java +++ b/baebae-BE/src/main/java/com/web/baebaeBE/global/firebase/FirebaseNotificationService.java @@ -59,7 +59,7 @@ public void notifyNewAnswer(Member member, Question question, Answer answer) { // 알림 생성 및 전송 NotificationRequest.create notificationDto = new NotificationRequest.create( - question.getId(), + member.getId(), member.getNickname() + "님이 질문에 답변을 남겼어요!", question.getContent(), NotificationRequest.EventType.NEW_ANSWER, From d0f0ff32ad4098e416fe21b9c6d4d7e1fbda5e28 Mon Sep 17 00:00:00 2001 From: kimyechan Date: Tue, 21 May 2024 12:52:34 +0900 Subject: [PATCH 16/32] =?UTF-8?q?Chore:=20=EB=B3=B4=EC=9D=B8=20=ED=95=B4?= =?UTF-8?q?=EC=A0=9C=20API=20=EB=A6=AC=EC=8A=A4=ED=8A=B8=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/web/baebaeBE/global/config/SecurityConfig.java | 3 ++- .../com/web/baebaeBE/global/security/SecurityConstants.java | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/baebae-BE/src/main/java/com/web/baebaeBE/global/config/SecurityConfig.java b/baebae-BE/src/main/java/com/web/baebaeBE/global/config/SecurityConfig.java index 26cd3a5f..ba7e3453 100644 --- a/baebae-BE/src/main/java/com/web/baebaeBE/global/config/SecurityConfig.java +++ b/baebae-BE/src/main/java/com/web/baebaeBE/global/config/SecurityConfig.java @@ -44,7 +44,8 @@ public WebSecurityCustomizer configure() { return web -> { web.ignoring() .requestMatchers(HttpMethod.GET, "/api/member/profile-image/{memberId}") - //.requestMatchers(toH2Console()) + .requestMatchers(HttpMethod.GET, "/api/category/{memberId}") + .requestMatchers(toH2Console()) .requestMatchers(NO_AUTH_LIST); //.requestMatchers("/**"); }; diff --git a/baebae-BE/src/main/java/com/web/baebaeBE/global/security/SecurityConstants.java b/baebae-BE/src/main/java/com/web/baebaeBE/global/security/SecurityConstants.java index 357b6420..d887f49d 100644 --- a/baebae-BE/src/main/java/com/web/baebaeBE/global/security/SecurityConstants.java +++ b/baebae-BE/src/main/java/com/web/baebaeBE/global/security/SecurityConstants.java @@ -6,7 +6,7 @@ public final class SecurityConstants { // oAuth2 인증 제외 "/api/oauth/kakao", "/favicon.ico", "/oauth/kakao/callback", "/api/auth/login", "/api/auth/isExisting", "/api/auth/nickname/isExisting", - "/api/category/{memberId}", "/api/answers", "/api/answers/member/{memberId}", + "/api/answers", "/api/answers/member/{memberId}", "/api/reactionsCount/{answerId}/reactionsCount", // Swagger 제외 과정 "/v3/**", "/swagger-ui/**", From 7faf76d32e1be93573b8fb71d4267ee0043d6f7a Mon Sep 17 00:00:00 2001 From: kimyechan Date: Tue, 21 May 2024 12:53:03 +0900 Subject: [PATCH 17/32] =?UTF-8?q?Feat:=20Authorization=20=ED=8F=AC?= =?UTF-8?q?=EC=9D=B8=ED=8A=B8=EC=BB=B7=20=EB=A9=94=EC=84=9C=EB=93=9C=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../MemberAnswerReactionController.java | 1 - .../AuthorizationCategoryAndAnswer.java | 11 +++++++++++ .../authorization/aspect/AuthPolicyAspect.java | 17 +++++++++++++++++ 3 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 baebae-BE/src/main/java/com/web/baebaeBE/global/authorization/annotation/AuthorizationCategoryAndAnswer.java diff --git a/baebae-BE/src/main/java/com/web/baebaeBE/domain/reaction/controller/MemberAnswerReactionController.java b/baebae-BE/src/main/java/com/web/baebaeBE/domain/reaction/controller/MemberAnswerReactionController.java index 647548b8..c1e92456 100644 --- a/baebae-BE/src/main/java/com/web/baebaeBE/domain/reaction/controller/MemberAnswerReactionController.java +++ b/baebae-BE/src/main/java/com/web/baebaeBE/domain/reaction/controller/MemberAnswerReactionController.java @@ -3,7 +3,6 @@ import com.web.baebaeBE.domain.reaction.controller.api.MemberAnswerReactionApi; import com.web.baebaeBE.domain.reaction.dto.ReactionRequest; import com.web.baebaeBE.domain.reactioncount.dto.ReactionResponse; -import com.web.baebaeBE.domain.reaction.entity.MemberAnswerReaction; import com.web.baebaeBE.domain.reaction.service.MemberAnswerReactionService; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; diff --git a/baebae-BE/src/main/java/com/web/baebaeBE/global/authorization/annotation/AuthorizationCategoryAndAnswer.java b/baebae-BE/src/main/java/com/web/baebaeBE/global/authorization/annotation/AuthorizationCategoryAndAnswer.java new file mode 100644 index 00000000..cf6b4650 --- /dev/null +++ b/baebae-BE/src/main/java/com/web/baebaeBE/global/authorization/annotation/AuthorizationCategoryAndAnswer.java @@ -0,0 +1,11 @@ +package com.web.baebaeBE.global.authorization.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface AuthorizationCategoryAndAnswer { +} diff --git a/baebae-BE/src/main/java/com/web/baebaeBE/global/authorization/aspect/AuthPolicyAspect.java b/baebae-BE/src/main/java/com/web/baebaeBE/global/authorization/aspect/AuthPolicyAspect.java index e730bfef..82bff0c6 100644 --- a/baebae-BE/src/main/java/com/web/baebaeBE/global/authorization/aspect/AuthPolicyAspect.java +++ b/baebae-BE/src/main/java/com/web/baebaeBE/global/authorization/aspect/AuthPolicyAspect.java @@ -89,4 +89,21 @@ public void checkCategory(JoinPoint joinPoint, Long categoryId) { throw new BusinessException(MemberException.NOT_MATCH_MEMBER); } } + + @Before("@annotation(com.web.baebaeBE.global.authorization.annotation.AuthorizationCategoryAndAnswer) && args(categoryId,answerId,..)") + public void checkCategoryAndAnswer(JoinPoint joinPoint, Long categoryId, Long answerId) { + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + String currentEmail = authentication.getName(); + System.out.println("checkCategoryAndAnswer: " + categoryId + " " + answerId); + + Category category = categoryRepository.findById(categoryId) + .orElseThrow(() -> new BusinessException(CategoryException.CATEGORY_NOT_FOUND)); + + Answer answer = answerRepository.findByAnswerId(answerId) + .orElseThrow(() -> new BusinessException(AnswerError.NO_EXIST_ANSWER)); + + if (!category.getMember().getEmail().equals(currentEmail) && !answer.getMember().getEmail().equals(currentEmail)) { + throw new BusinessException(MemberException.NOT_MATCH_MEMBER); + } + } } \ No newline at end of file From 77279d8edc8bb96d1201441f54d346ac653873d2 Mon Sep 17 00:00:00 2001 From: kimyechan Date: Tue, 21 May 2024 14:35:57 +0900 Subject: [PATCH 18/32] =?UTF-8?q?Fix:=20ddl-auto=20=EC=88=98=EC=A0=95=20?= =?UTF-8?q?=EB=B0=B0=ED=8F=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/deploy-dev.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy-dev.yaml b/.github/workflows/deploy-dev.yaml index 22aed03b..cb73f4fa 100644 --- a/.github/workflows/deploy-dev.yaml +++ b/.github/workflows/deploy-dev.yaml @@ -18,7 +18,7 @@ jobs: with: java-version: '17' distribution: 'adopt' - +# - name: Create and configure application.yml run: | echo "${{ secrets.APPLICATION_YML }}" > baebae-BE/src/main/resources/application.yml From f4b2eeccd22c7e12bacd1505e785294be419338e Mon Sep 17 00:00:00 2001 From: kimyechan Date: Tue, 21 May 2024 14:43:37 +0900 Subject: [PATCH 19/32] =?UTF-8?q?Fix:=20ddl-auto=20=EC=88=98=EC=A0=95=20?= =?UTF-8?q?=EB=B0=B0=ED=8F=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/deploy-dev.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy-dev.yaml b/.github/workflows/deploy-dev.yaml index cb73f4fa..22aed03b 100644 --- a/.github/workflows/deploy-dev.yaml +++ b/.github/workflows/deploy-dev.yaml @@ -18,7 +18,7 @@ jobs: with: java-version: '17' distribution: 'adopt' -# + - name: Create and configure application.yml run: | echo "${{ secrets.APPLICATION_YML }}" > baebae-BE/src/main/resources/application.yml From 82f1fee3e6e5acfba998310edf18424f8b3b7224 Mon Sep 17 00:00:00 2001 From: kimyechan Date: Tue, 21 May 2024 16:32:37 +0900 Subject: [PATCH 20/32] =?UTF-8?q?Fix:=20h2=20=ED=99=98=EA=B2=BD=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/web/baebaeBE/global/config/SecurityConfig.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/baebae-BE/src/main/java/com/web/baebaeBE/global/config/SecurityConfig.java b/baebae-BE/src/main/java/com/web/baebaeBE/global/config/SecurityConfig.java index ba7e3453..490b2f5b 100644 --- a/baebae-BE/src/main/java/com/web/baebaeBE/global/config/SecurityConfig.java +++ b/baebae-BE/src/main/java/com/web/baebaeBE/global/config/SecurityConfig.java @@ -45,7 +45,7 @@ public WebSecurityCustomizer configure() { web.ignoring() .requestMatchers(HttpMethod.GET, "/api/member/profile-image/{memberId}") .requestMatchers(HttpMethod.GET, "/api/category/{memberId}") - .requestMatchers(toH2Console()) + //.requestMatchers(toH2Console()) .requestMatchers(NO_AUTH_LIST); //.requestMatchers("/**"); }; From 4224f5ee64f08e87bec9a05e3318d70e86e4202d Mon Sep 17 00:00:00 2001 From: kimyechan Date: Tue, 21 May 2024 19:12:42 +0900 Subject: [PATCH 21/32] =?UTF-8?q?Fix:=20=EB=8B=89=EB=84=A4=EC=9E=84?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=20=EC=8B=9C=ED=81=90=EB=A6=AC=ED=8B=B0=20?= =?UTF-8?q?=EC=A0=9C=EC=99=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/com/web/baebaeBE/global/config/SecurityConfig.java | 1 + 1 file changed, 1 insertion(+) diff --git a/baebae-BE/src/main/java/com/web/baebaeBE/global/config/SecurityConfig.java b/baebae-BE/src/main/java/com/web/baebaeBE/global/config/SecurityConfig.java index 490b2f5b..ddc44bd5 100644 --- a/baebae-BE/src/main/java/com/web/baebaeBE/global/config/SecurityConfig.java +++ b/baebae-BE/src/main/java/com/web/baebaeBE/global/config/SecurityConfig.java @@ -45,6 +45,7 @@ public WebSecurityCustomizer configure() { web.ignoring() .requestMatchers(HttpMethod.GET, "/api/member/profile-image/{memberId}") .requestMatchers(HttpMethod.GET, "/api/category/{memberId}") + .requestMatchers(HttpMethod.GET, "/api/member/nickname/{nickname}") //.requestMatchers(toH2Console()) .requestMatchers(NO_AUTH_LIST); //.requestMatchers("/**"); From e450ec86c21e9a0f950d090d72feadf03dd001ca Mon Sep 17 00:00:00 2001 From: kimyechan Date: Tue, 21 May 2024 21:11:07 +0900 Subject: [PATCH 22/32] =?UTF-8?q?Hotfix:=20notification=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20=EB=B2=84=EA=B7=B8=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../baebaeBE/global/firebase/FirebaseNotificationService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/baebae-BE/src/main/java/com/web/baebaeBE/global/firebase/FirebaseNotificationService.java b/baebae-BE/src/main/java/com/web/baebaeBE/global/firebase/FirebaseNotificationService.java index 934f5eb4..0ef5a668 100644 --- a/baebae-BE/src/main/java/com/web/baebaeBE/global/firebase/FirebaseNotificationService.java +++ b/baebae-BE/src/main/java/com/web/baebaeBE/global/firebase/FirebaseNotificationService.java @@ -97,7 +97,7 @@ public void notifyReaction(Member member, Answer answer, MemberAnswerReaction re //알림 생성 및 저장 NotificationRequest.create notificationDto = new NotificationRequest.create( - member.getId(), + answer.getMember().getId(), notificationTitle, answer.getContent(), NotificationRequest.EventType.NEW_ANSWER, From b48b2bb980625d6995c22f561e5ddbdf3d31d063 Mon Sep 17 00:00:00 2001 From: kimyechan Date: Tue, 21 May 2024 21:16:46 +0900 Subject: [PATCH 23/32] =?UTF-8?q?Hotfix:=20=EB=8B=B5=EB=B3=80=20=EC=A4=91?= =?UTF-8?q?=EB=B3=B5=20=EC=A0=80=EC=9E=A5=20=EB=AC=B8=EC=A0=9C=20=ED=95=B4?= =?UTF-8?q?=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../web/baebaeBE/domain/answer/exception/AnswerError.java | 3 ++- .../web/baebaeBE/domain/answer/service/AnswerService.java | 5 ++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/baebae-BE/src/main/java/com/web/baebaeBE/domain/answer/exception/AnswerError.java b/baebae-BE/src/main/java/com/web/baebaeBE/domain/answer/exception/AnswerError.java index e8f6fa43..8b8e7a42 100644 --- a/baebae-BE/src/main/java/com/web/baebaeBE/domain/answer/exception/AnswerError.java +++ b/baebae-BE/src/main/java/com/web/baebaeBE/domain/answer/exception/AnswerError.java @@ -13,7 +13,8 @@ public enum AnswerError implements ErrorCode { NO_EXIST_ANSWER(HttpStatus.NOT_FOUND, "M-002", "존재하지 않는 답변입니다."), NO_EXIST_QUESTION(HttpStatus.NOT_FOUND, "M-003", "존재하지 않는 질문입니다."), IMAGE_PROCESSING_ERROR(HttpStatus.NOT_FOUND, "M-004", "이미지 업로드 오류입니다."), - ALREADY_REACTED(HttpStatus.NOT_FOUND, "M-005", "이미 반응하였습니다."); + ALREADY_REACTED(HttpStatus.NOT_FOUND, "M-005", "이미 반응하였습니다."), + ALREADY_ANSWERED_QUESTION(HttpStatus.CONFLICT, "M-006", "이미 답변한 질문입니다."); private final HttpStatus httpStatus; private final String errorCode; diff --git a/baebae-BE/src/main/java/com/web/baebaeBE/domain/answer/service/AnswerService.java b/baebae-BE/src/main/java/com/web/baebaeBE/domain/answer/service/AnswerService.java index 9b5fd2a2..404cf71e 100644 --- a/baebae-BE/src/main/java/com/web/baebaeBE/domain/answer/service/AnswerService.java +++ b/baebae-BE/src/main/java/com/web/baebaeBE/domain/answer/service/AnswerService.java @@ -60,6 +60,9 @@ public AnswerDetailResponse createAnswer(AnswerCreateRequest request, Long membe Question question = questionRepository.findById(request.getQuestionId()) .orElseThrow(() -> new BusinessException(AnswerError.NO_EXIST_QUESTION)); + if(question.isAnswered() == true) + throw new BusinessException(AnswerError.ALREADY_ANSWERED_QUESTION); // 이미 답변한 질문이면 예외처리 + // Answer 생성 Answer answer = answerMapper.toEntity(request, question, member); Answer savedAnswer = answerRepository.save(answer); @@ -86,7 +89,7 @@ public AnswerDetailResponse createAnswer(AnswerCreateRequest request, Long membe question.setAnswered(true); questionRepository.save(question); - firebaseNotificationService.notifyNewAnswer(member, question,savedAnswer); // 푸시 메세지 전송 + firebaseNotificationService.notifyNewAnswer(question.getSender(), question,savedAnswer); // 푸시 메세지 전송 return answerMapper.toDomain(savedAnswer); } From 434eb87aa4d73862405fb27c07405c738e169b7a Mon Sep 17 00:00:00 2001 From: kimyechan Date: Tue, 21 May 2024 21:29:02 +0900 Subject: [PATCH 24/32] =?UTF-8?q?Hotfix:=20=ED=94=BC=EB=93=9C=20=EA=B8=B0?= =?UTF-8?q?=EB=B3=B8=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20=EC=A0=80=EC=9E=A5=20?= =?UTF-8?q?=EB=B2=84=EA=B7=B8=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../web/baebaeBE/domain/answer/service/AnswerService.java | 6 +++++- .../com/web/baebaeBE/domain/question/entity/Question.java | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/baebae-BE/src/main/java/com/web/baebaeBE/domain/answer/service/AnswerService.java b/baebae-BE/src/main/java/com/web/baebaeBE/domain/answer/service/AnswerService.java index 404cf71e..70a8870b 100644 --- a/baebae-BE/src/main/java/com/web/baebaeBE/domain/answer/service/AnswerService.java +++ b/baebae-BE/src/main/java/com/web/baebaeBE/domain/answer/service/AnswerService.java @@ -66,14 +66,18 @@ public AnswerDetailResponse createAnswer(AnswerCreateRequest request, Long membe // Answer 생성 Answer answer = answerMapper.toEntity(request, question, member); Answer savedAnswer = answerRepository.save(answer); + String imageUrl; if (!imageFile.isEmpty()) { try (InputStream inputStream = imageFile.getInputStream()) { - String imageUrl = s3ImageStorageService.uploadFile(member.getId().toString(), answer.getId().toString(), "image", 0, inputStream, imageFile.getSize(), imageFile.getContentType()); + imageUrl = s3ImageStorageService.uploadFile(member.getId().toString(), answer.getId().toString(), "image", 0, inputStream, imageFile.getSize(), imageFile.getContentType()); answer.setImageFile(imageUrl); } catch (IOException e) { throw new BusinessException(AnswerError.IMAGE_PROCESSING_ERROR); } + } else{ + imageUrl = s3ImageStorageService.getDefaultFileUrl(); // 기본이미지 } + answer.setImageFile(imageUrl); // ReactionCount 생성 ReactionCount reactionCount = ReactionCount.builder() diff --git a/baebae-BE/src/main/java/com/web/baebaeBE/domain/question/entity/Question.java b/baebae-BE/src/main/java/com/web/baebaeBE/domain/question/entity/Question.java index fbcb4c49..beab432e 100644 --- a/baebae-BE/src/main/java/com/web/baebaeBE/domain/question/entity/Question.java +++ b/baebae-BE/src/main/java/com/web/baebaeBE/domain/question/entity/Question.java @@ -42,7 +42,7 @@ public class Question { private LocalDateTime createdDate; @Column(name = "is_answered", nullable = false) - private boolean isAnswered = false; + private boolean isAnswered; public void updateContent(String content) { this.content = content; From e093d094eb22aca75e553b8303b9e486d1e04c81 Mon Sep 17 00:00:00 2001 From: kimyechan Date: Wed, 22 May 2024 01:59:42 +0900 Subject: [PATCH 25/32] =?UTF-8?q?Hotfix:=20=ED=94=BC=EB=93=9C=EC=83=9D?= =?UTF-8?q?=EC=84=B1=20=EB=A9=94=EC=84=B8=EC=A7=80=20=EC=98=A4=EB=A5=98=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/web/baebaeBE/domain/answer/service/AnswerService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/baebae-BE/src/main/java/com/web/baebaeBE/domain/answer/service/AnswerService.java b/baebae-BE/src/main/java/com/web/baebaeBE/domain/answer/service/AnswerService.java index 70a8870b..3802e9e9 100644 --- a/baebae-BE/src/main/java/com/web/baebaeBE/domain/answer/service/AnswerService.java +++ b/baebae-BE/src/main/java/com/web/baebaeBE/domain/answer/service/AnswerService.java @@ -93,7 +93,7 @@ public AnswerDetailResponse createAnswer(AnswerCreateRequest request, Long membe question.setAnswered(true); questionRepository.save(question); - firebaseNotificationService.notifyNewAnswer(question.getSender(), question,savedAnswer); // 푸시 메세지 전송 + firebaseNotificationService.notifyNewAnswer(member, question,savedAnswer); // 푸시 메세지 전송 return answerMapper.toDomain(savedAnswer); } From c81e7b6a1c4ab8affc4791a7334f086e01ee7aaf Mon Sep 17 00:00:00 2001 From: kimyechan Date: Wed, 22 May 2024 02:02:28 +0900 Subject: [PATCH 26/32] =?UTF-8?q?Refactor:=20AllowOrigins=20uri=EA=B0=92?= =?UTF-8?q?=20Value=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/web/baebaeBE/global/config/SecurityConfig.java | 10 ++++++++-- .../java/com/web/baebaeBE/global/config/WebConfig.java | 6 +++++- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/baebae-BE/src/main/java/com/web/baebaeBE/global/config/SecurityConfig.java b/baebae-BE/src/main/java/com/web/baebaeBE/global/config/SecurityConfig.java index ddc44bd5..ba198413 100644 --- a/baebae-BE/src/main/java/com/web/baebaeBE/global/config/SecurityConfig.java +++ b/baebae-BE/src/main/java/com/web/baebaeBE/global/config/SecurityConfig.java @@ -5,6 +5,9 @@ import com.web.baebaeBE.domain.login.service.OAuth2UserCustomService; import lombok.RequiredArgsConstructor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.security.servlet.PathRequest; import org.springframework.context.annotation.Bean; @@ -22,8 +25,10 @@ import org.springframework.web.cors.CorsConfigurationSource; import org.springframework.web.cors.UrlBasedCorsConfigurationSource; +import javax.annotation.PostConstruct; import java.util.Arrays; import java.util.Collections; +import java.util.List; import static com.web.baebaeBE.global.security.SecurityConstants.NO_AUTH_LIST; import static org.springframework.boot.autoconfigure.security.servlet.PathRequest.toH2Console; @@ -34,8 +39,9 @@ @EnableMethodSecurity public class SecurityConfig { private final JwtTokenProvider jwtTokenProvider; - private final OAuth2UserCustomService oAuth2UserCustomService; + @Value("${allowed.origins}") + private String[] allowedOrigins; // Spring Security 제외 목록 (인증,인가 검사 제외) @@ -84,7 +90,7 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { @Bean public CorsConfigurationSource corsConfigurationSource() { CorsConfiguration configuration = new CorsConfiguration(); - configuration.setAllowedOrigins(Arrays.asList("http://localhost:5173", "https://api.flipit.co.kr", "https://www.flipit.co.kr", "https://flipit.co.kr")); // 허용할 오리진 설정 + configuration.setAllowedOrigins(List.of(allowedOrigins)); // 허용할 오리진 설정 configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "PATCH")); // 허용할 HTTP 메소드 설정 configuration.setAllowedHeaders(Collections.singletonList("*")); // 허용할 HTTP 헤더 설정 configuration.setAllowCredentials(true); // 쿠키를 포함한 요청 허용 설정 diff --git a/baebae-BE/src/main/java/com/web/baebaeBE/global/config/WebConfig.java b/baebae-BE/src/main/java/com/web/baebaeBE/global/config/WebConfig.java index 54737547..f4a29b42 100644 --- a/baebae-BE/src/main/java/com/web/baebaeBE/global/config/WebConfig.java +++ b/baebae-BE/src/main/java/com/web/baebaeBE/global/config/WebConfig.java @@ -1,15 +1,19 @@ package com.web.baebaeBE.global.config; +import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.CorsRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration public class WebConfig implements WebMvcConfigurer { + @Value("${allowed.origins}") + private String[] allowedOrigins; + @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") - .allowedOrigins("http://localhost:5173", "https://api.flipit.co.kr", "https://www.flipit.co.kr", "https://flipit.co.kr") + .allowedOrigins(allowedOrigins) .allowedMethods("GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS") .allowedHeaders("*") // 모든 헤더 허용 .allowCredentials(true) From 9fbbbcdea800c8da40c06f165eb0906e16c1af23 Mon Sep 17 00:00:00 2001 From: kimyechan Date: Wed, 22 May 2024 02:48:19 +0900 Subject: [PATCH 27/32] =?UTF-8?q?Feat:=20=ED=95=B4=EB=8B=B9=20=ED=94=BC?= =?UTF-8?q?=EB=93=9C=20=EC=86=8D=ED=95=9C=20=EC=B9=B4=ED=85=8C=EA=B3=A0?= =?UTF-8?q?=EB=A6=AC=20=EB=A6=AC=EC=8A=A4=ED=8A=B8=20=EC=A1=B0=ED=9A=8C=20?= =?UTF-8?q?api=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CategorizedAnswerController.java | 33 +++++++++++++++++ .../controller/api/CategorizedAnswerApi.java | 37 +++++++++++++++++++ .../answer/dto/CategorizedAnswerRequest.java | 4 ++ .../answer/dto/CategorizedAnswerResponse.java | 31 ++++++++++++++++ .../CategorizedAnswerRepository.java | 2 + .../service/CategorizedAnswerService.java | 14 ++++++- 6 files changed, 120 insertions(+), 1 deletion(-) create mode 100644 baebae-BE/src/main/java/com/web/baebaeBE/domain/categorized/answer/controller/CategorizedAnswerController.java create mode 100644 baebae-BE/src/main/java/com/web/baebaeBE/domain/categorized/answer/controller/api/CategorizedAnswerApi.java create mode 100644 baebae-BE/src/main/java/com/web/baebaeBE/domain/categorized/answer/dto/CategorizedAnswerRequest.java create mode 100644 baebae-BE/src/main/java/com/web/baebaeBE/domain/categorized/answer/dto/CategorizedAnswerResponse.java diff --git a/baebae-BE/src/main/java/com/web/baebaeBE/domain/categorized/answer/controller/CategorizedAnswerController.java b/baebae-BE/src/main/java/com/web/baebaeBE/domain/categorized/answer/controller/CategorizedAnswerController.java new file mode 100644 index 00000000..857234ad --- /dev/null +++ b/baebae-BE/src/main/java/com/web/baebaeBE/domain/categorized/answer/controller/CategorizedAnswerController.java @@ -0,0 +1,33 @@ +package com.web.baebaeBE.domain.categorized.answer.controller; + +import com.web.baebaeBE.domain.categorized.answer.controller.api.CategorizedAnswerApi; +import com.web.baebaeBE.domain.categorized.answer.dto.CategorizedAnswerResponse; +import com.web.baebaeBE.domain.categorized.answer.service.CategorizedAnswerService; +import com.web.baebaeBE.global.authorization.annotation.AuthorizationAnswer; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/api/categorizedAnswer") +public class CategorizedAnswerController implements CategorizedAnswerApi { + + private final CategorizedAnswerService categorizedAnswerService; + + + @GetMapping("{answerId}") + @AuthorizationAnswer + public ResponseEntity> getCategoriesByAnswerId( + @PathVariable Long answerId + ) { + return ResponseEntity.ok(categorizedAnswerService.getCategoriesByAnswerId(answerId)); + } + + +} diff --git a/baebae-BE/src/main/java/com/web/baebaeBE/domain/categorized/answer/controller/api/CategorizedAnswerApi.java b/baebae-BE/src/main/java/com/web/baebaeBE/domain/categorized/answer/controller/api/CategorizedAnswerApi.java new file mode 100644 index 00000000..e0cb59ee --- /dev/null +++ b/baebae-BE/src/main/java/com/web/baebaeBE/domain/categorized/answer/controller/api/CategorizedAnswerApi.java @@ -0,0 +1,37 @@ +package com.web.baebaeBE.domain.categorized.answer.controller.api; + +import com.web.baebaeBE.domain.categorized.answer.dto.CategorizedAnswerResponse; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.enums.ParameterIn; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; + +import java.util.List; + +@Tag(name = "CategorizedAnswer", description = "카테고리 내부의 피드에 관련된 API") +public interface CategorizedAnswerApi { + + @Operation(summary = "피드가 속한 카테고리 조회", + description = "Answer ID를 받아 해당 Answer에 연결된 모든 카테고리를 조회합니다.", + security = @SecurityRequirement(name = "bearerAuth") + ) + @Parameter( + in = ParameterIn.HEADER, + name = "Authorization", required = true, + schema = @Schema(type = "string"), + description = "Bearer [Access 토큰]") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "조회 성공") + }) + @GetMapping("{answerId}") + ResponseEntity> getCategoriesByAnswerId( + @Parameter(description = "Answer의 ID", required = true) @PathVariable Long answerId + ); +} \ No newline at end of file diff --git a/baebae-BE/src/main/java/com/web/baebaeBE/domain/categorized/answer/dto/CategorizedAnswerRequest.java b/baebae-BE/src/main/java/com/web/baebaeBE/domain/categorized/answer/dto/CategorizedAnswerRequest.java new file mode 100644 index 00000000..c01a6942 --- /dev/null +++ b/baebae-BE/src/main/java/com/web/baebaeBE/domain/categorized/answer/dto/CategorizedAnswerRequest.java @@ -0,0 +1,4 @@ +package com.web.baebaeBE.domain.categorized.answer.dto; + +public class CategorizedAnswerRequest { +} diff --git a/baebae-BE/src/main/java/com/web/baebaeBE/domain/categorized/answer/dto/CategorizedAnswerResponse.java b/baebae-BE/src/main/java/com/web/baebaeBE/domain/categorized/answer/dto/CategorizedAnswerResponse.java new file mode 100644 index 00000000..2a0fbc99 --- /dev/null +++ b/baebae-BE/src/main/java/com/web/baebaeBE/domain/categorized/answer/dto/CategorizedAnswerResponse.java @@ -0,0 +1,31 @@ +package com.web.baebaeBE.domain.categorized.answer.dto; + +import com.web.baebaeBE.domain.category.dto.CategoryResponse; +import com.web.baebaeBE.domain.category.entity.Category; +import lombok.*; + +import java.util.List; +import java.util.stream.Collectors; + +public class CategorizedAnswerResponse { + + @Getter + @Setter + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class CategoryInformationResponse { + private Long categoryId; + private String categoryName; + private String categoryImage; + + public static CategorizedAnswerResponse.CategoryInformationResponse of(Category category) { + return CategorizedAnswerResponse.CategoryInformationResponse.builder() + .categoryId(category.getId()) + .categoryName(category.getCategoryName()) + .categoryImage(category.getCategoryImage()) + .build(); + } + } + +} diff --git a/baebae-BE/src/main/java/com/web/baebaeBE/domain/categorized/answer/repository/CategorizedAnswerRepository.java b/baebae-BE/src/main/java/com/web/baebaeBE/domain/categorized/answer/repository/CategorizedAnswerRepository.java index 9937f5ee..22617964 100644 --- a/baebae-BE/src/main/java/com/web/baebaeBE/domain/categorized/answer/repository/CategorizedAnswerRepository.java +++ b/baebae-BE/src/main/java/com/web/baebaeBE/domain/categorized/answer/repository/CategorizedAnswerRepository.java @@ -7,8 +7,10 @@ import org.springframework.data.jpa.repository.JpaRepository; import java.util.List; +import java.util.Optional; public interface CategorizedAnswerRepository extends JpaRepository { Page findByAnswer_Member_IdAndCategory_Id(Long memberId, Long categoryId, Pageable pageable); Page findByAnswer_Member_Id(Long memberId, Pageable pageable); + List findAllByAnswerId(Long answerId); } diff --git a/baebae-BE/src/main/java/com/web/baebaeBE/domain/categorized/answer/service/CategorizedAnswerService.java b/baebae-BE/src/main/java/com/web/baebaeBE/domain/categorized/answer/service/CategorizedAnswerService.java index 83287077..05abd7c9 100644 --- a/baebae-BE/src/main/java/com/web/baebaeBE/domain/categorized/answer/service/CategorizedAnswerService.java +++ b/baebae-BE/src/main/java/com/web/baebaeBE/domain/categorized/answer/service/CategorizedAnswerService.java @@ -2,8 +2,10 @@ import com.web.baebaeBE.domain.answer.dto.AnswerDetailResponse; import com.web.baebaeBE.domain.answer.repository.AnswerMapper; +import com.web.baebaeBE.domain.categorized.answer.dto.CategorizedAnswerResponse; import com.web.baebaeBE.domain.categorized.answer.entity.CategorizedAnswer; import com.web.baebaeBE.domain.categorized.answer.repository.CategorizedAnswerRepository; +import com.web.baebaeBE.domain.category.entity.Category; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.data.domain.Page; @@ -11,6 +13,8 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.util.List; + @Service @Slf4j @RequiredArgsConstructor @@ -19,6 +23,15 @@ public class CategorizedAnswerService { private final CategorizedAnswerRepository categorizedAnswerRepository; private final AnswerMapper answerMapper; + public List getCategoriesByAnswerId(Long answerId) { + List categorizedAnswers = categorizedAnswerRepository.findAllByAnswerId(answerId); + + List categories = categorizedAnswers.stream().map(CategorizedAnswer::getCategory).toList(); + return categories.stream().map(CategorizedAnswerResponse.CategoryInformationResponse::of).toList(); + + } + + public Page getAnswersByMemberAndCategory(Long memberId, Long categoryId, Pageable pageable) { Page categorizedAnswers; if (categoryId == null) { // 카테고리 정보가 없을 경우 -> 전체 피드 조회 @@ -27,6 +40,5 @@ public Page getAnswersByMemberAndCategory(Long memberId, L categorizedAnswers = categorizedAnswerRepository.findByAnswer_Member_IdAndCategory_Id(memberId, categoryId, pageable); return categorizedAnswers.map(categorizedAnswer -> answerMapper.toDomain(categorizedAnswer.getAnswer())); - } } \ No newline at end of file From 13b611c47a43c7b6767ff1644f7b307062f5e8fe Mon Sep 17 00:00:00 2001 From: kimyechan Date: Wed, 22 May 2024 03:57:42 +0900 Subject: [PATCH 28/32] =?UTF-8?q?Feat:=20=ED=94=BC=EB=93=9C=20=EA=B8=B0?= =?UTF-8?q?=EB=B0=98=20=EC=B9=B4=ED=85=8C=EA=B3=A0=EB=A6=AC=20=EA=B7=B8?= =?UTF-8?q?=EB=A3=B9=20=EC=88=98=EC=A0=95=20api=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .idea/dbnavigator.xml | 2 +- .../CategorizedAnswerController.java | 13 ++-- .../controller/api/CategorizedAnswerApi.java | 19 ++++++ .../answer/dto/CategorizedAnswerRequest.java | 16 +++++ .../answer/entity/CategorizedAnswer.java | 5 ++ .../service/CategorizedAnswerService.java | 68 ++++++++++++++++++- 6 files changed, 116 insertions(+), 7 deletions(-) diff --git a/.idea/dbnavigator.xml b/.idea/dbnavigator.xml index 8268975b..b5a23a02 100644 --- a/.idea/dbnavigator.xml +++ b/.idea/dbnavigator.xml @@ -13,7 +13,7 @@ - + diff --git a/baebae-BE/src/main/java/com/web/baebaeBE/domain/categorized/answer/controller/CategorizedAnswerController.java b/baebae-BE/src/main/java/com/web/baebaeBE/domain/categorized/answer/controller/CategorizedAnswerController.java index 857234ad..9f13d083 100644 --- a/baebae-BE/src/main/java/com/web/baebaeBE/domain/categorized/answer/controller/CategorizedAnswerController.java +++ b/baebae-BE/src/main/java/com/web/baebaeBE/domain/categorized/answer/controller/CategorizedAnswerController.java @@ -1,15 +1,13 @@ package com.web.baebaeBE.domain.categorized.answer.controller; import com.web.baebaeBE.domain.categorized.answer.controller.api.CategorizedAnswerApi; +import com.web.baebaeBE.domain.categorized.answer.dto.CategorizedAnswerRequest; import com.web.baebaeBE.domain.categorized.answer.dto.CategorizedAnswerResponse; import com.web.baebaeBE.domain.categorized.answer.service.CategorizedAnswerService; import com.web.baebaeBE.global.authorization.annotation.AuthorizationAnswer; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; import java.util.List; @@ -29,5 +27,12 @@ public ResponseEntity updateCategoriesByAnswerId(@PathVariable Long answerId, @RequestBody CategorizedAnswerRequest.CategoryList categoryIds) { + categorizedAnswerService.updateCategoriesByAnswerId(answerId, categoryIds); + return ResponseEntity.noContent().build(); + } + } diff --git a/baebae-BE/src/main/java/com/web/baebaeBE/domain/categorized/answer/controller/api/CategorizedAnswerApi.java b/baebae-BE/src/main/java/com/web/baebaeBE/domain/categorized/answer/controller/api/CategorizedAnswerApi.java index e0cb59ee..628d12ab 100644 --- a/baebae-BE/src/main/java/com/web/baebaeBE/domain/categorized/answer/controller/api/CategorizedAnswerApi.java +++ b/baebae-BE/src/main/java/com/web/baebaeBE/domain/categorized/answer/controller/api/CategorizedAnswerApi.java @@ -1,5 +1,6 @@ package com.web.baebaeBE.domain.categorized.answer.controller.api; +import com.web.baebaeBE.domain.categorized.answer.dto.CategorizedAnswerRequest; import com.web.baebaeBE.domain.categorized.answer.dto.CategorizedAnswerResponse; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; @@ -12,6 +13,8 @@ import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; import java.util.List; @@ -34,4 +37,20 @@ public interface CategorizedAnswerApi { ResponseEntity> getCategoriesByAnswerId( @Parameter(description = "Answer의 ID", required = true) @PathVariable Long answerId ); + + + @Operation(summary = "피드가 속한 카테고리 수정", + description = "Answer ID와 Category ID 리스트를 받아 피드가 속할 카테고리 정보를 수정합니다.", + security = @SecurityRequirement(name = "bearerAuth") + ) + @Parameter( + in = ParameterIn.HEADER, + name = "Authorization", required = true, + schema = @Schema(type = "string"), + description = "Bearer [Access 토큰]") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "수정 성공") + }) + @PutMapping("/{answerId}") + public ResponseEntity updateCategoriesByAnswerId(@PathVariable Long answerId, @RequestBody CategorizedAnswerRequest.CategoryList categoryIds) ; } \ No newline at end of file diff --git a/baebae-BE/src/main/java/com/web/baebaeBE/domain/categorized/answer/dto/CategorizedAnswerRequest.java b/baebae-BE/src/main/java/com/web/baebaeBE/domain/categorized/answer/dto/CategorizedAnswerRequest.java index c01a6942..7edbaf63 100644 --- a/baebae-BE/src/main/java/com/web/baebaeBE/domain/categorized/answer/dto/CategorizedAnswerRequest.java +++ b/baebae-BE/src/main/java/com/web/baebaeBE/domain/categorized/answer/dto/CategorizedAnswerRequest.java @@ -1,4 +1,20 @@ package com.web.baebaeBE.domain.categorized.answer.dto; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.util.List; + public class CategorizedAnswerRequest { + + @Getter + @Setter + @NoArgsConstructor + @AllArgsConstructor + public static class CategoryList{ + private List categoryIds; + + } } diff --git a/baebae-BE/src/main/java/com/web/baebaeBE/domain/categorized/answer/entity/CategorizedAnswer.java b/baebae-BE/src/main/java/com/web/baebaeBE/domain/categorized/answer/entity/CategorizedAnswer.java index 33a365e8..43735700 100644 --- a/baebae-BE/src/main/java/com/web/baebaeBE/domain/categorized/answer/entity/CategorizedAnswer.java +++ b/baebae-BE/src/main/java/com/web/baebaeBE/domain/categorized/answer/entity/CategorizedAnswer.java @@ -4,6 +4,8 @@ import com.web.baebaeBE.domain.category.entity.Category; import jakarta.persistence.*; import lombok.*; +import org.hibernate.annotations.OnDelete; +import org.hibernate.annotations.OnDeleteAction; import java.time.LocalDateTime; @@ -18,14 +20,17 @@ public class CategorizedAnswer { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "categorized_answer_id") private Long id; @ManyToOne @JoinColumn(name = "category_id", nullable = false) + @OnDelete(action = OnDeleteAction.CASCADE) private Category category; @ManyToOne @JoinColumn(name = "answer_id", nullable = false) + @OnDelete(action = OnDeleteAction.CASCADE) private Answer answer; } \ No newline at end of file diff --git a/baebae-BE/src/main/java/com/web/baebaeBE/domain/categorized/answer/service/CategorizedAnswerService.java b/baebae-BE/src/main/java/com/web/baebaeBE/domain/categorized/answer/service/CategorizedAnswerService.java index 05abd7c9..75ab5171 100644 --- a/baebae-BE/src/main/java/com/web/baebaeBE/domain/categorized/answer/service/CategorizedAnswerService.java +++ b/baebae-BE/src/main/java/com/web/baebaeBE/domain/categorized/answer/service/CategorizedAnswerService.java @@ -1,11 +1,18 @@ package com.web.baebaeBE.domain.categorized.answer.service; import com.web.baebaeBE.domain.answer.dto.AnswerDetailResponse; +import com.web.baebaeBE.domain.answer.entity.Answer; +import com.web.baebaeBE.domain.answer.exception.AnswerError; import com.web.baebaeBE.domain.answer.repository.AnswerMapper; +import com.web.baebaeBE.domain.answer.repository.AnswerRepository; +import com.web.baebaeBE.domain.categorized.answer.dto.CategorizedAnswerRequest; import com.web.baebaeBE.domain.categorized.answer.dto.CategorizedAnswerResponse; import com.web.baebaeBE.domain.categorized.answer.entity.CategorizedAnswer; import com.web.baebaeBE.domain.categorized.answer.repository.CategorizedAnswerRepository; import com.web.baebaeBE.domain.category.entity.Category; +import com.web.baebaeBE.domain.category.exception.CategoryException; +import com.web.baebaeBE.domain.category.repository.CategoryRepository; +import com.web.baebaeBE.global.error.exception.BusinessException; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.data.domain.Page; @@ -13,7 +20,10 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.util.ArrayList; import java.util.List; +import java.util.NoSuchElementException; +import java.util.stream.Collectors; @Service @Slf4j @@ -22,13 +32,23 @@ public class CategorizedAnswerService { private final CategorizedAnswerRepository categorizedAnswerRepository; private final AnswerMapper answerMapper; + private final CategoryRepository categoryRepository; + private final AnswerRepository answerRepository; public List getCategoriesByAnswerId(Long answerId) { List categorizedAnswers = categorizedAnswerRepository.findAllByAnswerId(answerId); - List categories = categorizedAnswers.stream().map(CategorizedAnswer::getCategory).toList(); - return categories.stream().map(CategorizedAnswerResponse.CategoryInformationResponse::of).toList(); + for(CategorizedAnswer categorizedAnswer : categorizedAnswers) { + log.info("categorizedAnswer : {}", categorizedAnswer.getCategory().getId()); + } + List categoryInformationResponses = new ArrayList<>(); + for(CategorizedAnswer categorizedAnswer : categorizedAnswers) { + categoryInformationResponses.add + (CategorizedAnswerResponse.CategoryInformationResponse.of(categorizedAnswer.getCategory())); + } + + return categoryInformationResponses; } @@ -41,4 +61,48 @@ public Page getAnswersByMemberAndCategory(Long memberId, L return categorizedAnswers.map(categorizedAnswer -> answerMapper.toDomain(categorizedAnswer.getAnswer())); } + + + @Transactional + public void updateCategoriesByAnswerId(Long answerId, CategorizedAnswerRequest.CategoryList categoryList) { + List existingCategorizedAnswers = categorizedAnswerRepository.findAllByAnswerId(answerId); + List existingCategoryIds = existingCategorizedAnswers.stream() + .map(categorizedAnswer -> categorizedAnswer.getCategory().getId()) + .collect(Collectors.toList()); + + List newCategoryIds = categoryList.getCategoryIds(); + + // 추가해야 할 카테고리 ID 계산 + List toAddCategoryIds = newCategoryIds.stream() + .filter(categoryId -> !existingCategoryIds.contains(categoryId)) + .collect(Collectors.toList()); + + // 삭제해야 할 카테고리 ID 계산 + List toRemoveCategoryIds = existingCategoryIds.stream() + .filter(categoryId -> !newCategoryIds.contains(categoryId)) + .collect(Collectors.toList()); + + Answer answer = answerRepository.findByAnswerId(answerId) + .orElseThrow(() -> new BusinessException(AnswerError.NO_EXIST_ANSWER)); + + // 새로운 카테고리 ID에 대해 반복하여 CategorizedAnswer를 생성하고 저장 + for (Long categoryId : toAddCategoryIds) { + Category category = categoryRepository.findById(categoryId) + .orElseThrow(() -> new BusinessException(CategoryException.CATEGORY_NOT_FOUND)); + CategorizedAnswer newCategorizedAnswer = CategorizedAnswer.builder() + .category(category) + .answer(answer) + .build(); + categorizedAnswerRepository.save(newCategorizedAnswer); + } + + // 삭제해야 할 카테고리 ID에 대해 반복하여 해당 CategorizedAnswer를 삭제 + for (Long categoryId : toRemoveCategoryIds) { + CategorizedAnswer categorizedAnswerToRemove = existingCategorizedAnswers.stream() + .filter(categorizedAnswer -> categorizedAnswer.getCategory().getId().equals(categoryId)) + .findFirst() + .orElseThrow(() -> new NoSuchElementException("No CategorizedAnswer found for the given category id")); + categorizedAnswerRepository.delete(categorizedAnswerToRemove); + } + } } \ No newline at end of file From 08fd862c4aa0946a97e38543e49a68d05a931196 Mon Sep 17 00:00:00 2001 From: kimyechan Date: Wed, 22 May 2024 04:52:31 +0900 Subject: [PATCH 29/32] =?UTF-8?q?Feat:=20=ED=94=BC=EB=93=9C=20=EA=B8=B0?= =?UTF-8?q?=EB=B0=98=20=EC=B9=B4=ED=85=8C=EA=B3=A0=EB=A6=AC=20=EA=B7=B8?= =?UTF-8?q?=EB=A3=B9=20=EC=88=98=EC=A0=95=20api=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../answer/repository/CategorizedAnswerRepository.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/baebae-BE/src/main/java/com/web/baebaeBE/domain/categorized/answer/repository/CategorizedAnswerRepository.java b/baebae-BE/src/main/java/com/web/baebaeBE/domain/categorized/answer/repository/CategorizedAnswerRepository.java index 22617964..fb2996a7 100644 --- a/baebae-BE/src/main/java/com/web/baebaeBE/domain/categorized/answer/repository/CategorizedAnswerRepository.java +++ b/baebae-BE/src/main/java/com/web/baebaeBE/domain/categorized/answer/repository/CategorizedAnswerRepository.java @@ -13,4 +13,4 @@ public interface CategorizedAnswerRepository extends JpaRepository findByAnswer_Member_IdAndCategory_Id(Long memberId, Long categoryId, Pageable pageable); Page findByAnswer_Member_Id(Long memberId, Pageable pageable); List findAllByAnswerId(Long answerId); -} +} \ No newline at end of file From a86b355cc1de4a8812b875b7f29c85514e02d109 Mon Sep 17 00:00:00 2001 From: jihyo-j <84886198+jihyo-j@users.noreply.github.com> Date: Wed, 22 May 2024 14:19:23 +0900 Subject: [PATCH 30/32] Update deploy-dev.yaml --- .github/workflows/deploy-dev.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/deploy-dev.yaml b/.github/workflows/deploy-dev.yaml index 22aed03b..bee36add 100644 --- a/.github/workflows/deploy-dev.yaml +++ b/.github/workflows/deploy-dev.yaml @@ -23,6 +23,7 @@ jobs: run: | echo "${{ secrets.APPLICATION_YML }}" > baebae-BE/src/main/resources/application.yml echo "${{ secrets.APPLICATION_DEPLOY_YML }}" > baebae-BE/src/main/resources/application-deploy.yml + echo "${{ secrets.APPLICATION_TEST_YML }}" > baebae-BE/src/test/resources/application-test.yml - name: create-json id: create-json @@ -63,4 +64,4 @@ jobs: run: | cd baebae-BE chmod +x request_source_deploy_start.sh - ./request_source_deploy_start.sh \ No newline at end of file + ./request_source_deploy_start.sh From 7782c1d362397e9a76588136f1bd29e5c2f77649 Mon Sep 17 00:00:00 2001 From: jihyo-j <84886198+jihyo-j@users.noreply.github.com> Date: Wed, 22 May 2024 14:31:09 +0900 Subject: [PATCH 31/32] Update deploy-dev.yaml MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit test경로 추가 --- .github/workflows/deploy-dev.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/deploy-dev.yaml b/.github/workflows/deploy-dev.yaml index bee36add..41b708a8 100644 --- a/.github/workflows/deploy-dev.yaml +++ b/.github/workflows/deploy-dev.yaml @@ -21,6 +21,8 @@ jobs: - name: Create and configure application.yml run: | + mkdir -p baebae-BE/src/main/resources + mkdir -p baebae-BE/src/test/resources echo "${{ secrets.APPLICATION_YML }}" > baebae-BE/src/main/resources/application.yml echo "${{ secrets.APPLICATION_DEPLOY_YML }}" > baebae-BE/src/main/resources/application-deploy.yml echo "${{ secrets.APPLICATION_TEST_YML }}" > baebae-BE/src/test/resources/application-test.yml From b17e9966b8e93cd076b3dc320b1dcd32acf2f18b Mon Sep 17 00:00:00 2001 From: kimyechan Date: Wed, 22 May 2024 14:47:03 +0900 Subject: [PATCH 32/32] =?UTF-8?q?Test:=20ci-cd=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=EC=BD=94=EB=93=9C=20=ED=8F=AC=ED=95=A8=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/build-test.yaml | 2 +- .github/workflows/deploy-dev.yaml | 2 +- .github/workflows/deploy-prod.yaml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build-test.yaml b/.github/workflows/build-test.yaml index 01030ce6..9022d9a7 100644 --- a/.github/workflows/build-test.yaml +++ b/.github/workflows/build-test.yaml @@ -36,4 +36,4 @@ jobs: run: | cd baebae-BE chmod +x ./gradlew - ./gradlew build -x test + ./gradlew build diff --git a/.github/workflows/deploy-dev.yaml b/.github/workflows/deploy-dev.yaml index 41b708a8..0d9eb6bc 100644 --- a/.github/workflows/deploy-dev.yaml +++ b/.github/workflows/deploy-dev.yaml @@ -42,7 +42,7 @@ jobs: run: | cd baebae-BE chmod +x ./gradlew - ./gradlew build -x test + ./gradlew build - name: Create deployment package run: | diff --git a/.github/workflows/deploy-prod.yaml b/.github/workflows/deploy-prod.yaml index f2499c23..36ffba7f 100644 --- a/.github/workflows/deploy-prod.yaml +++ b/.github/workflows/deploy-prod.yaml @@ -32,7 +32,7 @@ jobs: run: | cd baebae-BE chmod +x ./gradlew - ./gradlew build -x test + ./gradlew build - name: Create deployment package run: |