From e1b838cc17caa9a34f7eb07f4211c2d56ded3327 Mon Sep 17 00:00:00 2001 From: Sin Ye Rin <91180366+nyeroni@users.noreply.github.com> Date: Mon, 28 Oct 2024 16:56:28 +0900 Subject: [PATCH 01/55] =?UTF-8?q?feat:=20=EC=95=8C=EB=A6=BC=20=EC=8B=9C?= =?UTF-8?q?=EA=B0=84=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yerong/wedle/notification/service/NotificationService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/yerong/wedle/notification/service/NotificationService.java b/src/main/java/yerong/wedle/notification/service/NotificationService.java index dafcc4f..45097b5 100644 --- a/src/main/java/yerong/wedle/notification/service/NotificationService.java +++ b/src/main/java/yerong/wedle/notification/service/NotificationService.java @@ -55,7 +55,7 @@ private CalendarEvent getCalendarEventById(Long calendarId) { } @Transactional - @Scheduled(cron = "0 0 17 * * ?") + @Scheduled(cron = "0 0 10 * * ?") public void sendNotifications() { LocalDate today = LocalDate.now(); List dueNotifications = notificationRepository.findByNotificationDate(today); From 7241d47dca40f996264676360b520fe00b616aaf Mon Sep 17 00:00:00 2001 From: Sin Ye Rin <91180366+nyeroni@users.noreply.github.com> Date: Mon, 28 Oct 2024 17:14:19 +0900 Subject: [PATCH 02/55] =?UTF-8?q?feat:=20firebase=20=ED=99=98=EA=B2=BD=20?= =?UTF-8?q?=EB=B3=80=EC=88=98=20=EB=93=B1=EB=A1=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/yerong/wedle/common/config/FMCConfig.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main/java/yerong/wedle/common/config/FMCConfig.java b/src/main/java/yerong/wedle/common/config/FMCConfig.java index 1b20fef..263ef0f 100644 --- a/src/main/java/yerong/wedle/common/config/FMCConfig.java +++ b/src/main/java/yerong/wedle/common/config/FMCConfig.java @@ -3,6 +3,8 @@ import com.google.auth.oauth2.GoogleCredentials; import com.google.firebase.FirebaseApp; import com.google.firebase.FirebaseOptions; + +import java.io.ByteArrayInputStream; import java.io.IOException; import javax.annotation.PostConstruct; import org.springframework.context.annotation.Configuration; @@ -13,11 +15,11 @@ public class FMCConfig { @PostConstruct private void init() throws IOException { - String fileResourceURL = "security/wedle-94dad-firebase-adminsdk-3qavr-19fe7c2a51.json"; - ClassPathResource resource = new ClassPathResource(fileResourceURL); + String firebaseConfig = System.getenv("FIREBASE_CONFIG"); + ByteArrayInputStream serviceAccountStream = new ByteArrayInputStream(firebaseConfig.getBytes()); FirebaseOptions options = FirebaseOptions.builder() - .setCredentials(GoogleCredentials.fromStream(resource.getInputStream())) + .setCredentials(GoogleCredentials.fromStream(serviceAccountStream)) .build(); FirebaseApp.initializeApp(options); From b93b9d652709434b2b0f1b7ed918207c3557ae2f Mon Sep 17 00:00:00 2001 From: Sin Ye Rin <91180366+nyeroni@users.noreply.github.com> Date: Mon, 28 Oct 2024 17:33:45 +0900 Subject: [PATCH 03/55] Update gradle.yml --- .github/workflows/gradle.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 0c5e30a..c7a701e 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -42,6 +42,9 @@ jobs: aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} aws-region: ap-northeast-2 + - name: Set up environment variables + run: echo "FIREBASE_CONFIG=${{ secrets.FIREBASE_CONFIG }}" >> $GITHUB_ENV + - name: Zip the JAR file run: zip -r myapp.zip build/libs/*.jar appspec.yml scripts @@ -79,4 +82,4 @@ jobs: uses: actions/setup-java@v4 with: java-version: '17' - distribution: 'temurin' \ No newline at end of file + distribution: 'temurin' From 4a3539556a4154fb22294f7c247fe7520a1bd584 Mon Sep 17 00:00:00 2001 From: Sin Ye Rin <91180366+nyeroni@users.noreply.github.com> Date: Mon, 28 Oct 2024 17:35:58 +0900 Subject: [PATCH 04/55] =?UTF-8?q?feat:=20firebase=20=EB=93=B1=EB=A1=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/yerong/wedle/common/config/FMCConfig.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/java/yerong/wedle/common/config/FMCConfig.java b/src/main/java/yerong/wedle/common/config/FMCConfig.java index 263ef0f..654c382 100644 --- a/src/main/java/yerong/wedle/common/config/FMCConfig.java +++ b/src/main/java/yerong/wedle/common/config/FMCConfig.java @@ -7,15 +7,18 @@ import java.io.ByteArrayInputStream; import java.io.IOException; import javax.annotation.PostConstruct; + +import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Configuration; -import org.springframework.core.io.ClassPathResource; @Configuration public class FMCConfig { + @Value("${FIREBASE_CONFIG}") + private String firebaseConfig; + @PostConstruct private void init() throws IOException { - String firebaseConfig = System.getenv("FIREBASE_CONFIG"); ByteArrayInputStream serviceAccountStream = new ByteArrayInputStream(firebaseConfig.getBytes()); FirebaseOptions options = FirebaseOptions.builder() From 19c005ebd60b80330083607a527e138672539368 Mon Sep 17 00:00:00 2001 From: Sin Ye Rin <91180366+nyeroni@users.noreply.github.com> Date: Mon, 28 Oct 2024 17:54:34 +0900 Subject: [PATCH 05/55] Update gradle.yml --- .github/workflows/gradle.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index c7a701e..bf63e9b 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -42,8 +42,8 @@ jobs: aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} aws-region: ap-northeast-2 - - name: Set up environment variables - run: echo "FIREBASE_CONFIG=${{ secrets.FIREBASE_CONFIG }}" >> $GITHUB_ENV + - name: Set environment variables + run: echo "spring.cloud.gcp.firebase.config=${{ secrets.FIREBASE_CONFIG }}" - name: Zip the JAR file run: zip -r myapp.zip build/libs/*.jar appspec.yml scripts From ca174e1cd73fbed0b0c17828d401ef78301678c9 Mon Sep 17 00:00:00 2001 From: Sin Ye Rin <91180366+nyeroni@users.noreply.github.com> Date: Mon, 28 Oct 2024 17:57:07 +0900 Subject: [PATCH 06/55] =?UTF-8?q?refactor:=20=EC=A3=BC=EC=84=9D=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yerong/wedle/notification/service/NotificationService.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/yerong/wedle/notification/service/NotificationService.java b/src/main/java/yerong/wedle/notification/service/NotificationService.java index 45097b5..4d5c136 100644 --- a/src/main/java/yerong/wedle/notification/service/NotificationService.java +++ b/src/main/java/yerong/wedle/notification/service/NotificationService.java @@ -69,7 +69,6 @@ public void sendNotifications() { FcmUtils.broadCast(registrationTokens, title, body); notification.setActive(false); - log.info("알람이 울렸습니다."); } } } From ef2820b8509e856fb8bfa3fbae82b7fc956e0451 Mon Sep 17 00:00:00 2001 From: Sin Ye Rin <91180366+nyeroni@users.noreply.github.com> Date: Mon, 28 Oct 2024 18:04:28 +0900 Subject: [PATCH 07/55] Update gradle.yml --- .github/workflows/gradle.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index bf63e9b..0fbecbf 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -43,7 +43,7 @@ jobs: aws-region: ap-northeast-2 - name: Set environment variables - run: echo "spring.cloud.gcp.firebase.config=${{ secrets.FIREBASE_CONFIG }}" + run: echo "FIREBASE_CONFIG=${{ secrets.FIREBASE_CONFIG }}" - name: Zip the JAR file run: zip -r myapp.zip build/libs/*.jar appspec.yml scripts From f32896ad1fc282df3a623c9b541bcf46626178cb Mon Sep 17 00:00:00 2001 From: Sin Ye Rin <91180366+nyeroni@users.noreply.github.com> Date: Mon, 28 Oct 2024 18:05:25 +0900 Subject: [PATCH 08/55] =?UTF-8?q?feat:=20=ED=99=98=EA=B2=BD=EB=B3=80?= =?UTF-8?q?=EC=88=98=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/yerong/wedle/common/config/FMCConfig.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/yerong/wedle/common/config/FMCConfig.java b/src/main/java/yerong/wedle/common/config/FMCConfig.java index 654c382..ae48faa 100644 --- a/src/main/java/yerong/wedle/common/config/FMCConfig.java +++ b/src/main/java/yerong/wedle/common/config/FMCConfig.java @@ -14,7 +14,7 @@ @Configuration public class FMCConfig { - @Value("${FIREBASE_CONFIG}") + @Value("${firebase.config}") private String firebaseConfig; @PostConstruct From 90fc4bc75314e533ffa9314a9f2366f53061717b Mon Sep 17 00:00:00 2001 From: Sin Ye Rin <91180366+nyeroni@users.noreply.github.com> Date: Mon, 28 Oct 2024 20:18:47 +0900 Subject: [PATCH 09/55] =?UTF-8?q?feat:=20=EC=95=8C=EB=A6=BC=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/NotificationApiController.java | 23 +++++++++++++++---- .../service/NotificationService.java | 14 ++++------- 2 files changed, 22 insertions(+), 15 deletions(-) diff --git a/src/main/java/yerong/wedle/notification/controller/NotificationApiController.java b/src/main/java/yerong/wedle/notification/controller/NotificationApiController.java index 32d66ca..002518f 100644 --- a/src/main/java/yerong/wedle/notification/controller/NotificationApiController.java +++ b/src/main/java/yerong/wedle/notification/controller/NotificationApiController.java @@ -1,5 +1,8 @@ package yerong.wedle.notification.controller; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; @@ -9,7 +12,6 @@ import yerong.wedle.notification.service.NotificationService; import java.time.LocalDate; -import java.time.LocalDateTime; import java.util.List; @RestController @@ -18,6 +20,12 @@ public class NotificationApiController { private final NotificationService notificationService; + @Operation(summary = "알림 생성", description = "이벤트에 대한 새로운 알림을 생성합니다.") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "알림이 성공적으로 생성되었습니다."), + @ApiResponse(responseCode = "400", description = "잘못된 요청 데이터") + }) + @PostMapping public ResponseEntity createNotification(@RequestBody @Valid CreateNotificationRequest request){ @@ -25,9 +33,14 @@ public ResponseEntity createNotification(@RequestBody @Val return ResponseEntity.ok(notificationResponse); } - @GetMapping - public ResponseEntity> getNotifications(@RequestParam LocalDate date) { - List notificationResponses = notificationService.getNotificationsByDate(date); - return ResponseEntity.ok(notificationResponses); + @Operation(summary = "알림 삭제", description = "ID를 통해 특정 알림을 삭제합니다.") + @ApiResponses(value = { + @ApiResponse(responseCode = "204", description = "알림이 성공적으로 삭제되었습니다."), + @ApiResponse(responseCode = "404", description = "알림을 찾을 수 없습니다.") + }) + @DeleteMapping + public ResponseEntity deleteNotification(@RequestParam Long notificationId) { + notificationService.deleteNotification(notificationId); + return ResponseEntity.noContent().build(); } } diff --git a/src/main/java/yerong/wedle/notification/service/NotificationService.java b/src/main/java/yerong/wedle/notification/service/NotificationService.java index 4d5c136..ce15491 100644 --- a/src/main/java/yerong/wedle/notification/service/NotificationService.java +++ b/src/main/java/yerong/wedle/notification/service/NotificationService.java @@ -3,9 +3,7 @@ import jakarta.transaction.Transactional; import java.time.LocalDate; -import java.time.LocalDateTime; import java.util.List; -import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -43,13 +41,6 @@ public NotificationResponse createNotification(CreateNotificationRequest request return convertToResponse(notification); } - public List getNotificationsByDate(LocalDate date) { - return notificationRepository.findByNotificationDate(date) - .stream() - .map(this::convertToResponse) - .collect(Collectors.toList()); - } - private CalendarEvent getCalendarEventById(Long calendarId) { return calendarEventRepository.findById(calendarId).orElseThrow(CalendarEventNotFoundException::new); } @@ -82,5 +73,8 @@ private NotificationResponse convertToResponse(Notification notification) { .isActive(notification.isActive()) .build(); } - + @Transactional + public void deleteNotification(Long notificationId) { + notificationRepository.deleteById(notificationId); + } } From 1227eb6641f0bb8ea9b83651c4ef84f1a91b35ce Mon Sep 17 00:00:00 2001 From: Sin Ye Rin <91180366+nyeroni@users.noreply.github.com> Date: Tue, 29 Oct 2024 01:52:23 +0900 Subject: [PATCH 10/55] =?UTF-8?q?feat:=20=EC=95=8C=EB=A6=BC=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84=20=EB=B0=8F=20=ED=9A=8C=EC=9B=90=EA=B3=BC=20=EC=97=B0?= =?UTF-8?q?=EA=B4=80=EA=B4=80=EA=B3=84=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CalendarEventApiController.java | 1 - .../calendar/dto/CalendarEventResponse.java | 5 +++- .../service/CalendarEventService.java | 30 +++++++++++++++++-- .../exception/GlobalExceptionHandler.java | 11 +++++++ .../wedle/common/exception/ResponseCode.java | 3 ++ .../yerong/wedle/member/domain/Member.java | 6 ++++ .../controller/NotificationApiController.java | 12 +++++++- .../notification/domain/Notification.java | 4 +++ .../NotificationNotFoundException.java | 11 +++++++ .../repository/NotificationRepository.java | 8 +++++ .../service/NotificationService.java | 27 +++++++++++++++++ 11 files changed, 113 insertions(+), 5 deletions(-) create mode 100644 src/main/java/yerong/wedle/notification/exception/NotificationNotFoundException.java diff --git a/src/main/java/yerong/wedle/calendar/controller/CalendarEventApiController.java b/src/main/java/yerong/wedle/calendar/controller/CalendarEventApiController.java index f73e83c..c25a59d 100644 --- a/src/main/java/yerong/wedle/calendar/controller/CalendarEventApiController.java +++ b/src/main/java/yerong/wedle/calendar/controller/CalendarEventApiController.java @@ -34,5 +34,4 @@ public ResponseEntity> getAllEvents() { List responses = calendarEventService.getAll(); return ResponseEntity.ok(responses); } - } diff --git a/src/main/java/yerong/wedle/calendar/dto/CalendarEventResponse.java b/src/main/java/yerong/wedle/calendar/dto/CalendarEventResponse.java index 9c318ab..232b673 100644 --- a/src/main/java/yerong/wedle/calendar/dto/CalendarEventResponse.java +++ b/src/main/java/yerong/wedle/calendar/dto/CalendarEventResponse.java @@ -10,8 +10,11 @@ @AllArgsConstructor public class CalendarEventResponse { - private Long id; + private Long CalendarEventId; private String title; private LocalDate date; private String type; + + private boolean notificationActive; + private Long notificationId; } diff --git a/src/main/java/yerong/wedle/calendar/service/CalendarEventService.java b/src/main/java/yerong/wedle/calendar/service/CalendarEventService.java index d4c38ee..4ca8ead 100644 --- a/src/main/java/yerong/wedle/calendar/service/CalendarEventService.java +++ b/src/main/java/yerong/wedle/calendar/service/CalendarEventService.java @@ -1,12 +1,19 @@ package yerong.wedle.calendar.service; import lombok.RequiredArgsConstructor; +import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import yerong.wedle.calendar.domain.CalendarEvent; import yerong.wedle.calendar.dto.CalendarEventResponse; import yerong.wedle.calendar.exception.CalendarEventNotFoundException; import yerong.wedle.calendar.repository.CalendarEventRepository; +import yerong.wedle.member.domain.Member; +import yerong.wedle.member.exception.MemberNotFoundException; +import yerong.wedle.member.repository.MemberRepository; +import yerong.wedle.notification.domain.Notification; +import yerong.wedle.notification.exception.NotificationNotFoundException; +import yerong.wedle.notification.repository.NotificationRepository; import java.time.LocalDate; import java.util.List; @@ -19,6 +26,8 @@ public class CalendarEventService { private final CalendarEventRepository calendarEventRepository; + private final NotificationRepository notificationRepository; + private final MemberRepository memberRepository; public List getAll() { List calendarEvents = calendarEventRepository.findAll(); @@ -32,12 +41,23 @@ public List convertToDto(CalendarEvent calendarEvent) { LocalDate startDate = calendarEvent.getStartDate(); LocalDate endDate = calendarEvent.getEndDate(); + String socialId = getCurrentUserId(); + Member member = memberRepository.findBySocialId(socialId) + .orElseThrow(MemberNotFoundException::new); + + + Notification notification = notificationRepository.findByEventAndMember(calendarEvent, member).orElseThrow(NotificationNotFoundException::new);; + boolean notificationActive = notification != null && notification.isActive(); + Long notificationId = notification != null ? notification.getNotificationId() : null; + if (endDate == null) { return List.of(new CalendarEventResponse( calendarEvent.getId(), calendarEvent.getTitle(), startDate, - calendarEvent.getCalendarEventType().getDisplayName() + calendarEvent.getCalendarEventType().getDisplayName(), + notificationActive, + notificationId )); } @@ -46,9 +66,15 @@ public List convertToDto(CalendarEvent calendarEvent) { calendarEvent.getId(), calendarEvent.getTitle(), date, - calendarEvent.getCalendarEventType().getDisplayName() + calendarEvent.getCalendarEventType().getDisplayName(), + notificationActive, + notificationId )) .collect(Collectors.toList()); } + private String getCurrentUserId() { + String socialId = SecurityContextHolder.getContext().getAuthentication().getName(); + return socialId; + } } diff --git a/src/main/java/yerong/wedle/common/exception/GlobalExceptionHandler.java b/src/main/java/yerong/wedle/common/exception/GlobalExceptionHandler.java index 61eebdd..6f388be 100644 --- a/src/main/java/yerong/wedle/common/exception/GlobalExceptionHandler.java +++ b/src/main/java/yerong/wedle/common/exception/GlobalExceptionHandler.java @@ -15,6 +15,7 @@ import yerong.wedle.employmentRate.exception.EmploymentRateNotFoundException; import yerong.wedle.member.exception.MemberNotFoundException; import yerong.wedle.member.exception.MemberDuplicateException; +import yerong.wedle.notification.exception.NotificationNotFoundException; import yerong.wedle.oauth.exception.InvalidAuthorizationHeaderException; import yerong.wedle.oauth.exception.InvalidRefreshTokenException; import yerong.wedle.oauth.exception.InvalidTokenException; @@ -62,6 +63,16 @@ public ResponseEntity handleMemberDuplicateException(MemberDuplic return ResponseEntity.status(HttpStatus.CONFLICT).body(errorResponse); } + @ExceptionHandler(NotificationNotFoundException.class) + public ResponseEntity handleNotificationNotFoundException(NotificationNotFoundException ex) { + ErrorResponse errorResponse = new ErrorResponse( + ResponseCode.NOTIFICATION_NOT_FOUND.getCode(), + ResponseCode.NOTIFICATION_NOT_FOUND.getMessage(), + LocalDateTime.now().format(FORMATTER) + ); + return ResponseEntity.status(HttpStatus.NOT_FOUND).body(errorResponse); + } + @ExceptionHandler(InvalidRefreshTokenException.class) public ResponseEntity handleInvalidRefreshTokenException(InvalidRefreshTokenException ex) { ErrorResponse errorResponse = new ErrorResponse( diff --git a/src/main/java/yerong/wedle/common/exception/ResponseCode.java b/src/main/java/yerong/wedle/common/exception/ResponseCode.java index 893a186..dc3b819 100644 --- a/src/main/java/yerong/wedle/common/exception/ResponseCode.java +++ b/src/main/java/yerong/wedle/common/exception/ResponseCode.java @@ -11,6 +11,9 @@ public enum ResponseCode { MEMBER_NOT_FOUND("404", "회원이 존재하지 않습니다."), MEMBER_DUPLICATE("409", "이미 존재하는 회원입니다."), + // Member + NOTIFICATION_NOT_FOUND("404", "알림이 존재하지 않습니다."), + // OAuth INVALID_REFRESH_TOKEN("400", "유효하지 않은 Refresh Token입니다."), INVALID_TOKEN("400", "유효하지 않은 Access Token입니다."), diff --git a/src/main/java/yerong/wedle/member/domain/Member.java b/src/main/java/yerong/wedle/member/domain/Member.java index 76f9429..5f2286d 100644 --- a/src/main/java/yerong/wedle/member/domain/Member.java +++ b/src/main/java/yerong/wedle/member/domain/Member.java @@ -3,6 +3,9 @@ import jakarta.persistence.*; import lombok.*; import yerong.wedle.common.domain.BaseTimeEntity; +import yerong.wedle.notification.domain.Notification; + +import java.util.List; import static jakarta.persistence.GenerationType.IDENTITY; import static lombok.AccessLevel.*; @@ -27,4 +30,7 @@ public class Member extends BaseTimeEntity { @Enumerated(value = EnumType.STRING) private Role role; + + @OneToMany(mappedBy = "member", cascade = CascadeType.ALL, fetch = FetchType.LAZY) // Member와 Notification 간의 관계 설정 + private List notifications; } diff --git a/src/main/java/yerong/wedle/notification/controller/NotificationApiController.java b/src/main/java/yerong/wedle/notification/controller/NotificationApiController.java index 002518f..8679cbe 100644 --- a/src/main/java/yerong/wedle/notification/controller/NotificationApiController.java +++ b/src/main/java/yerong/wedle/notification/controller/NotificationApiController.java @@ -11,7 +11,6 @@ import yerong.wedle.notification.dto.NotificationResponse; import yerong.wedle.notification.service.NotificationService; -import java.time.LocalDate; import java.util.List; @RestController @@ -43,4 +42,15 @@ public ResponseEntity deleteNotification(@RequestParam Long notificationId notificationService.deleteNotification(notificationId); return ResponseEntity.noContent().build(); } + + @Operation(summary = "회원의 모든 알림 조회", description = "특정 회원의 모든 알림을 조회합니다.") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "회원의 알림 목록이 성공적으로 조회되었습니다."), + @ApiResponse(responseCode = "404", description = "회원이 존재하지 않습니다.") + }) + @GetMapping("/member") + public ResponseEntity> getNotificationsByMember() { + List notifications = notificationService.getNotificationsByMember(); + return ResponseEntity.ok(notifications); + } } diff --git a/src/main/java/yerong/wedle/notification/domain/Notification.java b/src/main/java/yerong/wedle/notification/domain/Notification.java index 1e78586..132f953 100644 --- a/src/main/java/yerong/wedle/notification/domain/Notification.java +++ b/src/main/java/yerong/wedle/notification/domain/Notification.java @@ -20,6 +20,7 @@ import lombok.NoArgsConstructor; import yerong.wedle.calendar.domain.CalendarEvent; import yerong.wedle.common.domain.BaseTimeEntity; +import yerong.wedle.member.domain.Member; @Getter @NoArgsConstructor(access = PROTECTED) @@ -45,6 +46,9 @@ public class Notification extends BaseTimeEntity { @Column(nullable = false) private boolean isActive; + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "member_id", nullable = false) + private Member member; public void setActive(boolean isActive) { this.isActive = isActive; diff --git a/src/main/java/yerong/wedle/notification/exception/NotificationNotFoundException.java b/src/main/java/yerong/wedle/notification/exception/NotificationNotFoundException.java new file mode 100644 index 0000000..a8562c0 --- /dev/null +++ b/src/main/java/yerong/wedle/notification/exception/NotificationNotFoundException.java @@ -0,0 +1,11 @@ +package yerong.wedle.notification.exception; + +import yerong.wedle.common.exception.CustomException; +import yerong.wedle.common.exception.ResponseCode; + +public class NotificationNotFoundException extends CustomException { + + public NotificationNotFoundException() { + super(ResponseCode.MEMBER_NOT_FOUND); + } +} diff --git a/src/main/java/yerong/wedle/notification/repository/NotificationRepository.java b/src/main/java/yerong/wedle/notification/repository/NotificationRepository.java index 45fcaf2..3c22c9c 100644 --- a/src/main/java/yerong/wedle/notification/repository/NotificationRepository.java +++ b/src/main/java/yerong/wedle/notification/repository/NotificationRepository.java @@ -2,10 +2,18 @@ import java.time.LocalDate; import java.util.List; +import java.util.Optional; + import org.springframework.data.jpa.repository.JpaRepository; +import yerong.wedle.calendar.domain.CalendarEvent; +import yerong.wedle.member.domain.Member; import yerong.wedle.notification.domain.Notification; public interface NotificationRepository extends JpaRepository { List findByNotificationDate(LocalDate notification); + + Optional findByEventAndMember(CalendarEvent calendarEvent, Member member); + + List findByMember(Member member); } diff --git a/src/main/java/yerong/wedle/notification/service/NotificationService.java b/src/main/java/yerong/wedle/notification/service/NotificationService.java index ce15491..ff80c98 100644 --- a/src/main/java/yerong/wedle/notification/service/NotificationService.java +++ b/src/main/java/yerong/wedle/notification/service/NotificationService.java @@ -8,11 +8,15 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Service; import yerong.wedle.calendar.domain.CalendarEvent; import yerong.wedle.calendar.exception.CalendarEventNotFoundException; import yerong.wedle.calendar.repository.CalendarEventRepository; import yerong.wedle.common.utils.FcmUtils; +import yerong.wedle.member.domain.Member; +import yerong.wedle.member.exception.MemberNotFoundException; +import yerong.wedle.member.repository.MemberRepository; import yerong.wedle.notification.domain.Notification; import yerong.wedle.notification.dto.CreateNotificationRequest; import yerong.wedle.notification.dto.NotificationResponse; @@ -25,9 +29,14 @@ public class NotificationService { private final NotificationRepository notificationRepository; private final CalendarEventRepository calendarEventRepository; + private final MemberRepository memberRepository; @Transactional public NotificationResponse createNotification(CreateNotificationRequest request) { + String socialId = getCurrentUserId(); + Member member = memberRepository.findBySocialId(socialId) + .orElseThrow(MemberNotFoundException::new); + CalendarEvent calendarEvent = getCalendarEventById(request.getEventId()); Notification notification = Notification.builder() @@ -35,6 +44,7 @@ public NotificationResponse createNotification(CreateNotificationRequest request .event(calendarEvent) .registrationTokens(request.getRegistrationTokens()) .isActive(true) + .member(member) .build(); notification = notificationRepository.save(notification); @@ -77,4 +87,21 @@ private NotificationResponse convertToResponse(Notification notification) { public void deleteNotification(Long notificationId) { notificationRepository.deleteById(notificationId); } + + @Transactional + public List getNotificationsByMember() { + String socialId = getCurrentUserId(); + Member member = memberRepository.findBySocialId(socialId) + .orElseThrow(MemberNotFoundException::new); + + List notifications = notificationRepository.findByMember(member); + return notifications.stream() + .map(this::convertToResponse) + .toList(); + } + private String getCurrentUserId() { + String socialId = SecurityContextHolder.getContext().getAuthentication().getName(); + + return socialId; + } } From 6bc7f846f72ef0dc285266cdc774549f6573c9e7 Mon Sep 17 00:00:00 2001 From: Sin Ye Rin <91180366+nyeroni@users.noreply.github.com> Date: Tue, 29 Oct 2024 01:54:59 +0900 Subject: [PATCH 11/55] =?UTF-8?q?refactor:=20=EC=98=A4=ED=83=80=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/yerong/wedle/calendar/dto/CalendarEventResponse.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/yerong/wedle/calendar/dto/CalendarEventResponse.java b/src/main/java/yerong/wedle/calendar/dto/CalendarEventResponse.java index 232b673..a9d7e6b 100644 --- a/src/main/java/yerong/wedle/calendar/dto/CalendarEventResponse.java +++ b/src/main/java/yerong/wedle/calendar/dto/CalendarEventResponse.java @@ -10,7 +10,7 @@ @AllArgsConstructor public class CalendarEventResponse { - private Long CalendarEventId; + private Long calendarEventId; private String title; private LocalDate date; private String type; From c82475519f27adaa2961f69b27377e4b1a77aa63 Mon Sep 17 00:00:00 2001 From: Sin Ye Rin <91180366+nyeroni@users.noreply.github.com> Date: Tue, 29 Oct 2024 02:09:26 +0900 Subject: [PATCH 12/55] =?UTF-8?q?refactor:=20=EC=95=8C=EB=A6=BC=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84=20=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../wedle/calendar/service/CalendarEventService.java | 9 +++++++-- .../notification/repository/NotificationRepository.java | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/main/java/yerong/wedle/calendar/service/CalendarEventService.java b/src/main/java/yerong/wedle/calendar/service/CalendarEventService.java index 4ca8ead..04e5a00 100644 --- a/src/main/java/yerong/wedle/calendar/service/CalendarEventService.java +++ b/src/main/java/yerong/wedle/calendar/service/CalendarEventService.java @@ -46,8 +46,13 @@ public List convertToDto(CalendarEvent calendarEvent) { .orElseThrow(MemberNotFoundException::new); - Notification notification = notificationRepository.findByEventAndMember(calendarEvent, member).orElseThrow(NotificationNotFoundException::new);; - boolean notificationActive = notification != null && notification.isActive(); + Notification notification = notificationRepository.findByEventAndMember(calendarEvent, member); + boolean notificationActive; + if(notification != null) { + notificationActive = notification.isActive(); + } else { + notificationActive = false; + } Long notificationId = notification != null ? notification.getNotificationId() : null; if (endDate == null) { diff --git a/src/main/java/yerong/wedle/notification/repository/NotificationRepository.java b/src/main/java/yerong/wedle/notification/repository/NotificationRepository.java index 3c22c9c..cec8d66 100644 --- a/src/main/java/yerong/wedle/notification/repository/NotificationRepository.java +++ b/src/main/java/yerong/wedle/notification/repository/NotificationRepository.java @@ -13,7 +13,7 @@ public interface NotificationRepository extends JpaRepository findByNotificationDate(LocalDate notification); - Optional findByEventAndMember(CalendarEvent calendarEvent, Member member); + Notification findByEventAndMember(CalendarEvent calendarEvent, Member member); List findByMember(Member member); } From dc515d3bf62c19b3e125528fde7a2a10b11dcd7d Mon Sep 17 00:00:00 2001 From: Sin Ye Rin <91180366+nyeroni@users.noreply.github.com> Date: Tue, 29 Oct 2024 17:17:08 +0900 Subject: [PATCH 13/55] =?UTF-8?q?feat:=20=EC=95=8C=EB=A6=BC=20=EC=A4=91?= =?UTF-8?q?=EB=B3=B5=20=EC=84=A4=EC=A0=95=20=EC=98=88=EC=99=B8=EC=B2=98?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/exception/GlobalExceptionHandler.java | 11 +++++++++++ .../yerong/wedle/common/exception/ResponseCode.java | 3 ++- .../controller/NotificationApiController.java | 4 ++-- .../exception/DuplicateNotificationException.java | 10 ++++++++++ .../repository/NotificationRepository.java | 2 ++ .../notification/service/NotificationService.java | 5 +++++ 6 files changed, 32 insertions(+), 3 deletions(-) create mode 100644 src/main/java/yerong/wedle/notification/exception/DuplicateNotificationException.java diff --git a/src/main/java/yerong/wedle/common/exception/GlobalExceptionHandler.java b/src/main/java/yerong/wedle/common/exception/GlobalExceptionHandler.java index 6f388be..b312c44 100644 --- a/src/main/java/yerong/wedle/common/exception/GlobalExceptionHandler.java +++ b/src/main/java/yerong/wedle/common/exception/GlobalExceptionHandler.java @@ -15,6 +15,7 @@ import yerong.wedle.employmentRate.exception.EmploymentRateNotFoundException; import yerong.wedle.member.exception.MemberNotFoundException; import yerong.wedle.member.exception.MemberDuplicateException; +import yerong.wedle.notification.exception.DuplicateNotificationException; import yerong.wedle.notification.exception.NotificationNotFoundException; import yerong.wedle.oauth.exception.InvalidAuthorizationHeaderException; import yerong.wedle.oauth.exception.InvalidRefreshTokenException; @@ -73,6 +74,16 @@ public ResponseEntity handleNotificationNotFoundException(Notific return ResponseEntity.status(HttpStatus.NOT_FOUND).body(errorResponse); } + @ExceptionHandler(DuplicateNotificationException.class) + public ResponseEntity handleDuplicateNotificationException(DuplicateNotificationException ex) { + ErrorResponse errorResponse = new ErrorResponse( + ResponseCode.DUPLICATION_NOTIFICATION.getCode(), + ResponseCode.DUPLICATION_NOTIFICATION.getMessage(), + LocalDateTime.now().format(FORMATTER) + ); + return ResponseEntity.status(HttpStatus.NOT_FOUND).body(errorResponse); + } + @ExceptionHandler(InvalidRefreshTokenException.class) public ResponseEntity handleInvalidRefreshTokenException(InvalidRefreshTokenException ex) { ErrorResponse errorResponse = new ErrorResponse( diff --git a/src/main/java/yerong/wedle/common/exception/ResponseCode.java b/src/main/java/yerong/wedle/common/exception/ResponseCode.java index dc3b819..af9eec0 100644 --- a/src/main/java/yerong/wedle/common/exception/ResponseCode.java +++ b/src/main/java/yerong/wedle/common/exception/ResponseCode.java @@ -11,8 +11,9 @@ public enum ResponseCode { MEMBER_NOT_FOUND("404", "회원이 존재하지 않습니다."), MEMBER_DUPLICATE("409", "이미 존재하는 회원입니다."), - // Member + // Notification NOTIFICATION_NOT_FOUND("404", "알림이 존재하지 않습니다."), + DUPLICATION_NOTIFICATION("409", "이미 존재하는 알림입니다."), // OAuth INVALID_REFRESH_TOKEN("400", "유효하지 않은 Refresh Token입니다."), diff --git a/src/main/java/yerong/wedle/notification/controller/NotificationApiController.java b/src/main/java/yerong/wedle/notification/controller/NotificationApiController.java index 8679cbe..0b51100 100644 --- a/src/main/java/yerong/wedle/notification/controller/NotificationApiController.java +++ b/src/main/java/yerong/wedle/notification/controller/NotificationApiController.java @@ -22,9 +22,9 @@ public class NotificationApiController { @Operation(summary = "알림 생성", description = "이벤트에 대한 새로운 알림을 생성합니다.") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "알림이 성공적으로 생성되었습니다."), - @ApiResponse(responseCode = "400", description = "잘못된 요청 데이터") + @ApiResponse(responseCode = "400", description = "잘못된 요청 데이터"), + @ApiResponse(responseCode = "409", description = "이미 존재하는 알림입니다.") }) - @PostMapping public ResponseEntity createNotification(@RequestBody @Valid CreateNotificationRequest request){ diff --git a/src/main/java/yerong/wedle/notification/exception/DuplicateNotificationException.java b/src/main/java/yerong/wedle/notification/exception/DuplicateNotificationException.java new file mode 100644 index 0000000..4671205 --- /dev/null +++ b/src/main/java/yerong/wedle/notification/exception/DuplicateNotificationException.java @@ -0,0 +1,10 @@ +package yerong.wedle.notification.exception; + +import yerong.wedle.common.exception.CustomException; +import yerong.wedle.common.exception.ResponseCode; + +public class DuplicateNotificationException extends CustomException { + public DuplicateNotificationException () { + super(ResponseCode.DUPLICATION_NOTIFICATION); + } +} diff --git a/src/main/java/yerong/wedle/notification/repository/NotificationRepository.java b/src/main/java/yerong/wedle/notification/repository/NotificationRepository.java index cec8d66..cf9b3c1 100644 --- a/src/main/java/yerong/wedle/notification/repository/NotificationRepository.java +++ b/src/main/java/yerong/wedle/notification/repository/NotificationRepository.java @@ -16,4 +16,6 @@ public interface NotificationRepository extends JpaRepository findByMember(Member member); + + boolean existsByMemberAndEvent(Member member, CalendarEvent calendarEvent); } diff --git a/src/main/java/yerong/wedle/notification/service/NotificationService.java b/src/main/java/yerong/wedle/notification/service/NotificationService.java index ff80c98..e0f5ca4 100644 --- a/src/main/java/yerong/wedle/notification/service/NotificationService.java +++ b/src/main/java/yerong/wedle/notification/service/NotificationService.java @@ -20,6 +20,7 @@ import yerong.wedle.notification.domain.Notification; import yerong.wedle.notification.dto.CreateNotificationRequest; import yerong.wedle.notification.dto.NotificationResponse; +import yerong.wedle.notification.exception.DuplicateNotificationException; import yerong.wedle.notification.repository.NotificationRepository; @Slf4j @@ -39,6 +40,10 @@ public NotificationResponse createNotification(CreateNotificationRequest request CalendarEvent calendarEvent = getCalendarEventById(request.getEventId()); + if (notificationRepository.existsByMemberAndEvent(member, calendarEvent)) { + throw new DuplicateNotificationException(); + } + Notification notification = Notification.builder() .notificationDate(request.getNotificationDate()) .event(calendarEvent) From 273b88373c80bb397ac15217b38fc3ba1e20a8de Mon Sep 17 00:00:00 2001 From: Sin Ye Rin <91180366+nyeroni@users.noreply.github.com> Date: Tue, 29 Oct 2024 17:37:22 +0900 Subject: [PATCH 14/55] =?UTF-8?q?feat:=20=EC=95=8C=EB=A6=BC=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95=20=EB=82=A0=EC=A7=9C=EA=B0=80=20=ED=95=B4=EB=8B=B9=20?= =?UTF-8?q?=EC=9D=B4=EB=B2=A4=ED=8A=B8=EC=9D=98=20=EB=82=A0=EC=A7=9C?= =?UTF-8?q?=EC=99=80=20=EB=A7=9E=EC=A7=80=20=EC=95=8A=EC=9C=BC=EB=A9=B4=20?= =?UTF-8?q?=EC=98=88=EC=99=B8=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../notification/service/NotificationService.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/main/java/yerong/wedle/notification/service/NotificationService.java b/src/main/java/yerong/wedle/notification/service/NotificationService.java index e0f5ca4..2c5e493 100644 --- a/src/main/java/yerong/wedle/notification/service/NotificationService.java +++ b/src/main/java/yerong/wedle/notification/service/NotificationService.java @@ -5,6 +5,7 @@ import java.time.LocalDate; import java.util.List; +import jakarta.validation.constraints.NotNull; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.scheduling.annotation.Scheduled; @@ -40,6 +41,10 @@ public NotificationResponse createNotification(CreateNotificationRequest request CalendarEvent calendarEvent = getCalendarEventById(request.getEventId()); + if (!isDateWithinEventRange(request.getNotificationDate(), calendarEvent)) { + throw new IllegalArgumentException("알림 날짜가 이벤트 기간에 포함되지 않습니다."); + } + if (notificationRepository.existsByMemberAndEvent(member, calendarEvent)) { throw new DuplicateNotificationException(); } @@ -56,6 +61,12 @@ public NotificationResponse createNotification(CreateNotificationRequest request return convertToResponse(notification); } + private boolean isDateWithinEventRange(LocalDate notificationDate, CalendarEvent calendarEvent) { + LocalDate startDate = calendarEvent.getStartDate(); + LocalDate endDate = calendarEvent.getEndDate() != null ? calendarEvent.getEndDate() : startDate; + return !notificationDate.isBefore(startDate) && !notificationDate.isAfter(endDate); + } + private CalendarEvent getCalendarEventById(Long calendarId) { return calendarEventRepository.findById(calendarId).orElseThrow(CalendarEventNotFoundException::new); } From 242dd614e24ecca9003db5e78800190d185d74e4 Mon Sep 17 00:00:00 2001 From: Sin Ye Rin <91180366+nyeroni@users.noreply.github.com> Date: Tue, 29 Oct 2024 17:48:53 +0900 Subject: [PATCH 15/55] =?UTF-8?q?feat:=20=ED=95=B4=EB=8B=B9=20=EB=82=A0?= =?UTF-8?q?=EC=A7=9C=EB=A7=8C=20=EC=95=8C=EB=A6=BC=20=ED=99=9C=EC=84=B1?= =?UTF-8?q?=ED=99=94=20=EB=B0=8F=20=EC=A4=91=EB=B3=B5=20=EC=95=8C=EB=A6=BC?= =?UTF-8?q?=20=EC=98=88=EC=99=B8=EC=B2=98=EB=A6=AC=20=EB=A6=AC=ED=8C=A9?= =?UTF-8?q?=ED=84=B0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../calendar/service/CalendarEventService.java | 13 +++---------- .../common/exception/GlobalExceptionHandler.java | 12 ++++++++++++ .../yerong/wedle/common/exception/ResponseCode.java | 1 + .../NotificationDateOutOfRangeException.java | 10 ++++++++++ .../repository/NotificationRepository.java | 6 ++++-- .../notification/service/NotificationService.java | 8 ++++---- 6 files changed, 34 insertions(+), 16 deletions(-) create mode 100644 src/main/java/yerong/wedle/notification/exception/NotificationDateOutOfRangeException.java diff --git a/src/main/java/yerong/wedle/calendar/service/CalendarEventService.java b/src/main/java/yerong/wedle/calendar/service/CalendarEventService.java index 04e5a00..ee4d9e1 100644 --- a/src/main/java/yerong/wedle/calendar/service/CalendarEventService.java +++ b/src/main/java/yerong/wedle/calendar/service/CalendarEventService.java @@ -45,14 +45,7 @@ public List convertToDto(CalendarEvent calendarEvent) { Member member = memberRepository.findBySocialId(socialId) .orElseThrow(MemberNotFoundException::new); - - Notification notification = notificationRepository.findByEventAndMember(calendarEvent, member); - boolean notificationActive; - if(notification != null) { - notificationActive = notification.isActive(); - } else { - notificationActive = false; - } + Notification notification = notificationRepository.findByEventAndMember(calendarEvent, member).orElse(null); Long notificationId = notification != null ? notification.getNotificationId() : null; if (endDate == null) { @@ -61,7 +54,7 @@ public List convertToDto(CalendarEvent calendarEvent) { calendarEvent.getTitle(), startDate, calendarEvent.getCalendarEventType().getDisplayName(), - notificationActive, + notification != null && notification.isActive() && notification.getNotificationDate().equals(startDate), notificationId )); } @@ -72,7 +65,7 @@ public List convertToDto(CalendarEvent calendarEvent) { calendarEvent.getTitle(), date, calendarEvent.getCalendarEventType().getDisplayName(), - notificationActive, + notification != null && notification.isActive() && notification.getNotificationDate().equals(date), notificationId )) .collect(Collectors.toList()); diff --git a/src/main/java/yerong/wedle/common/exception/GlobalExceptionHandler.java b/src/main/java/yerong/wedle/common/exception/GlobalExceptionHandler.java index b312c44..fa512fd 100644 --- a/src/main/java/yerong/wedle/common/exception/GlobalExceptionHandler.java +++ b/src/main/java/yerong/wedle/common/exception/GlobalExceptionHandler.java @@ -16,6 +16,7 @@ import yerong.wedle.member.exception.MemberNotFoundException; import yerong.wedle.member.exception.MemberDuplicateException; import yerong.wedle.notification.exception.DuplicateNotificationException; +import yerong.wedle.notification.exception.NotificationDateOutOfRangeException; import yerong.wedle.notification.exception.NotificationNotFoundException; import yerong.wedle.oauth.exception.InvalidAuthorizationHeaderException; import yerong.wedle.oauth.exception.InvalidRefreshTokenException; @@ -84,6 +85,17 @@ public ResponseEntity handleDuplicateNotificationException(Duplic return ResponseEntity.status(HttpStatus.NOT_FOUND).body(errorResponse); } + + @ExceptionHandler(NotificationDateOutOfRangeException.class) + public ResponseEntity handleNotificationDateOutOfRangeException(NotificationDateOutOfRangeException ex) { + ErrorResponse errorResponse = new ErrorResponse( + ResponseCode.NOTIFICATION_DATE_OUT_OF_RANGE.getCode(), + ResponseCode.NOTIFICATION_DATE_OUT_OF_RANGE.getMessage(), + LocalDateTime.now().format(FORMATTER) + ); + return ResponseEntity.status(HttpStatus.NOT_FOUND).body(errorResponse); + } + @ExceptionHandler(InvalidRefreshTokenException.class) public ResponseEntity handleInvalidRefreshTokenException(InvalidRefreshTokenException ex) { ErrorResponse errorResponse = new ErrorResponse( diff --git a/src/main/java/yerong/wedle/common/exception/ResponseCode.java b/src/main/java/yerong/wedle/common/exception/ResponseCode.java index af9eec0..16dd449 100644 --- a/src/main/java/yerong/wedle/common/exception/ResponseCode.java +++ b/src/main/java/yerong/wedle/common/exception/ResponseCode.java @@ -14,6 +14,7 @@ public enum ResponseCode { // Notification NOTIFICATION_NOT_FOUND("404", "알림이 존재하지 않습니다."), DUPLICATION_NOTIFICATION("409", "이미 존재하는 알림입니다."), + NOTIFICATION_DATE_OUT_OF_RANGE("400", "알림 날짜가 이벤트 기간에 포함되지 않습니다."), // OAuth INVALID_REFRESH_TOKEN("400", "유효하지 않은 Refresh Token입니다."), diff --git a/src/main/java/yerong/wedle/notification/exception/NotificationDateOutOfRangeException.java b/src/main/java/yerong/wedle/notification/exception/NotificationDateOutOfRangeException.java new file mode 100644 index 0000000..7896df7 --- /dev/null +++ b/src/main/java/yerong/wedle/notification/exception/NotificationDateOutOfRangeException.java @@ -0,0 +1,10 @@ +package yerong.wedle.notification.exception; + +import yerong.wedle.common.exception.CustomException; +import yerong.wedle.common.exception.ResponseCode; + +public class NotificationDateOutOfRangeException extends CustomException { + public NotificationDateOutOfRangeException() { + super(ResponseCode.NOTIFICATION_DATE_OUT_OF_RANGE); + } +} diff --git a/src/main/java/yerong/wedle/notification/repository/NotificationRepository.java b/src/main/java/yerong/wedle/notification/repository/NotificationRepository.java index cf9b3c1..c3f5384 100644 --- a/src/main/java/yerong/wedle/notification/repository/NotificationRepository.java +++ b/src/main/java/yerong/wedle/notification/repository/NotificationRepository.java @@ -4,6 +4,7 @@ import java.util.List; import java.util.Optional; +import jakarta.validation.constraints.NotNull; import org.springframework.data.jpa.repository.JpaRepository; import yerong.wedle.calendar.domain.CalendarEvent; import yerong.wedle.member.domain.Member; @@ -13,9 +14,10 @@ public interface NotificationRepository extends JpaRepository findByNotificationDate(LocalDate notification); - Notification findByEventAndMember(CalendarEvent calendarEvent, Member member); + Optional findByEventAndMember(CalendarEvent calendarEvent, Member member); List findByMember(Member member); - boolean existsByMemberAndEvent(Member member, CalendarEvent calendarEvent); + + Optional findByMemberAndEventAndNotificationDate(Member member, CalendarEvent calendarEvent, @NotNull LocalDate notificationDate); } diff --git a/src/main/java/yerong/wedle/notification/service/NotificationService.java b/src/main/java/yerong/wedle/notification/service/NotificationService.java index 2c5e493..4d872c8 100644 --- a/src/main/java/yerong/wedle/notification/service/NotificationService.java +++ b/src/main/java/yerong/wedle/notification/service/NotificationService.java @@ -22,6 +22,7 @@ import yerong.wedle.notification.dto.CreateNotificationRequest; import yerong.wedle.notification.dto.NotificationResponse; import yerong.wedle.notification.exception.DuplicateNotificationException; +import yerong.wedle.notification.exception.NotificationDateOutOfRangeException; import yerong.wedle.notification.repository.NotificationRepository; @Slf4j @@ -42,13 +43,12 @@ public NotificationResponse createNotification(CreateNotificationRequest request CalendarEvent calendarEvent = getCalendarEventById(request.getEventId()); if (!isDateWithinEventRange(request.getNotificationDate(), calendarEvent)) { - throw new IllegalArgumentException("알림 날짜가 이벤트 기간에 포함되지 않습니다."); + throw new NotificationDateOutOfRangeException(); } - - if (notificationRepository.existsByMemberAndEvent(member, calendarEvent)) { + + if (notificationRepository.findByMemberAndEventAndNotificationDate(member, calendarEvent, request.getNotificationDate()).isPresent()) { throw new DuplicateNotificationException(); } - Notification notification = Notification.builder() .notificationDate(request.getNotificationDate()) .event(calendarEvent) From b24276d40b00258085621d117656b28417bbf550 Mon Sep 17 00:00:00 2001 From: Sin Ye Rin <91180366+nyeroni@users.noreply.github.com> Date: Wed, 30 Oct 2024 13:37:05 +0900 Subject: [PATCH 16/55] =?UTF-8?q?feat:=20=EC=95=8C=EB=A6=BC=20=EC=B2=98?= =?UTF-8?q?=EB=A6=AC=20=EC=96=B4=EB=85=B8=ED=85=8C=EC=9D=B4=EC=85=98=20?= =?UTF-8?q?=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/yerong/wedle/WedleApplication.java | 2 ++ .../wedle/notification/service/NotificationService.java | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/java/yerong/wedle/WedleApplication.java b/src/main/java/yerong/wedle/WedleApplication.java index 11868dd..6902f4c 100644 --- a/src/main/java/yerong/wedle/WedleApplication.java +++ b/src/main/java/yerong/wedle/WedleApplication.java @@ -3,7 +3,9 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.data.jpa.repository.config.EnableJpaAuditing; +import org.springframework.scheduling.annotation.EnableScheduling; +@EnableScheduling @EnableJpaAuditing @SpringBootApplication public class WedleApplication { diff --git a/src/main/java/yerong/wedle/notification/service/NotificationService.java b/src/main/java/yerong/wedle/notification/service/NotificationService.java index 4d872c8..e4bb208 100644 --- a/src/main/java/yerong/wedle/notification/service/NotificationService.java +++ b/src/main/java/yerong/wedle/notification/service/NotificationService.java @@ -72,7 +72,7 @@ private CalendarEvent getCalendarEventById(Long calendarId) { } @Transactional - @Scheduled(cron = "0 0 10 * * ?") + @Scheduled(cron = "0 40 14 * * ?") public void sendNotifications() { LocalDate today = LocalDate.now(); List dueNotifications = notificationRepository.findByNotificationDate(today); @@ -81,7 +81,7 @@ public void sendNotifications() { if (notification.isActive()) { List registrationTokens = notification.getRegistrationTokens(); String title = notification.getEvent().getTitle(); - String body = ""; + String body = "일정 당일입니다."; FcmUtils.broadCast(registrationTokens, title, body); From 1ea80a03450e3ee27e3276d40212b7e3e437a3be Mon Sep 17 00:00:00 2001 From: Sin Ye Rin <91180366+nyeroni@users.noreply.github.com> Date: Wed, 30 Oct 2024 13:39:54 +0900 Subject: [PATCH 17/55] =?UTF-8?q?refactor:=20=EC=95=8C=EB=A6=BC=20?= =?UTF-8?q?=EC=B2=98=EB=A6=AC=20=EC=8B=9C=EA=B0=84=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yerong/wedle/notification/service/NotificationService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/yerong/wedle/notification/service/NotificationService.java b/src/main/java/yerong/wedle/notification/service/NotificationService.java index e4bb208..20f4cc6 100644 --- a/src/main/java/yerong/wedle/notification/service/NotificationService.java +++ b/src/main/java/yerong/wedle/notification/service/NotificationService.java @@ -72,7 +72,7 @@ private CalendarEvent getCalendarEventById(Long calendarId) { } @Transactional - @Scheduled(cron = "0 40 14 * * ?") + @Scheduled(cron = "0 50 14 * * ?") public void sendNotifications() { LocalDate today = LocalDate.now(); List dueNotifications = notificationRepository.findByNotificationDate(today); From 8111947f0b1694e019e6250b0092cc0ef26193fe Mon Sep 17 00:00:00 2001 From: Sin Ye Rin <91180366+nyeroni@users.noreply.github.com> Date: Wed, 30 Oct 2024 13:51:11 +0900 Subject: [PATCH 18/55] =?UTF-8?q?refactor:=20=EC=95=8C=EB=A6=BC=20?= =?UTF-8?q?=EC=B2=98=EB=A6=AC=20=EC=8B=9C=EA=B0=84=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yerong/wedle/notification/service/NotificationService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/yerong/wedle/notification/service/NotificationService.java b/src/main/java/yerong/wedle/notification/service/NotificationService.java index 20f4cc6..03c4fd2 100644 --- a/src/main/java/yerong/wedle/notification/service/NotificationService.java +++ b/src/main/java/yerong/wedle/notification/service/NotificationService.java @@ -72,7 +72,7 @@ private CalendarEvent getCalendarEventById(Long calendarId) { } @Transactional - @Scheduled(cron = "0 50 14 * * ?") + @Scheduled(cron = "0 0 14 * * ?") public void sendNotifications() { LocalDate today = LocalDate.now(); List dueNotifications = notificationRepository.findByNotificationDate(today); From a25f9bc2a79715e35e0317d36c51c7e1665f87e7 Mon Sep 17 00:00:00 2001 From: Sin Ye Rin <91180366+nyeroni@users.noreply.github.com> Date: Wed, 30 Oct 2024 14:02:51 +0900 Subject: [PATCH 19/55] =?UTF-8?q?refactor:=20=EC=95=8C=EB=A6=BC=20?= =?UTF-8?q?=EC=B2=98=EB=A6=AC=20=EC=8B=9C=EA=B0=84=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yerong/wedle/notification/service/NotificationService.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/yerong/wedle/notification/service/NotificationService.java b/src/main/java/yerong/wedle/notification/service/NotificationService.java index 03c4fd2..faf7c0b 100644 --- a/src/main/java/yerong/wedle/notification/service/NotificationService.java +++ b/src/main/java/yerong/wedle/notification/service/NotificationService.java @@ -5,7 +5,6 @@ import java.time.LocalDate; import java.util.List; -import jakarta.validation.constraints.NotNull; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.scheduling.annotation.Scheduled; @@ -72,7 +71,7 @@ private CalendarEvent getCalendarEventById(Long calendarId) { } @Transactional - @Scheduled(cron = "0 0 14 * * ?") + @Scheduled(cron = "0 10 14 * * ?", zone = "Asia/Seoul") public void sendNotifications() { LocalDate today = LocalDate.now(); List dueNotifications = notificationRepository.findByNotificationDate(today); From c4e3a2400955e1b858a770fde020c4c1b9fb37a6 Mon Sep 17 00:00:00 2001 From: Sin Ye Rin <91180366+nyeroni@users.noreply.github.com> Date: Wed, 30 Oct 2024 14:14:16 +0900 Subject: [PATCH 20/55] =?UTF-8?q?refactor:=20=EC=95=8C=EB=A6=BC=20?= =?UTF-8?q?=EC=B2=98=EB=A6=AC=20=EC=8B=9C=EA=B0=84=20=EB=B3=80=EA=B2=BD=20?= =?UTF-8?q?=EB=B0=8F=20=EC=98=88=EC=99=B8=EC=B2=98=EB=A6=AC=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yerong/wedle/common/utils/FcmUtils.java | 17 +++++++++++------ .../service/NotificationService.java | 2 +- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/main/java/yerong/wedle/common/utils/FcmUtils.java b/src/main/java/yerong/wedle/common/utils/FcmUtils.java index 6adfb6c..fcfe5ea 100644 --- a/src/main/java/yerong/wedle/common/utils/FcmUtils.java +++ b/src/main/java/yerong/wedle/common/utils/FcmUtils.java @@ -24,23 +24,22 @@ public static void broadCast(final List registrationTokens, String title MulticastMessage message = MulticastMessage.builder() .setNotification(Notification.builder() - .setTitle(title) // 알림 제목 - .setBody(body) // 알림 내용 + .setTitle(title) + .setBody(body) .build()) .setApnsConfig(ApnsConfig.builder() - .setAps(Aps.builder().setAlert(body).build()) // iOS에 맞춰 알림 내용 설정 + .setAps(Aps.builder().setAlert(body).build()) .putHeader("apns-expiration", Long.toString(EXPIRED_TIME_FOR_UNIX)) .build()) .addAllTokens(registrationTokens) .build(); - BatchResponse response; try { - response = FirebaseMessaging.getInstance().sendEachForMulticast(message); + BatchResponse response = FirebaseMessaging.getInstance().sendEachForMulticast(message); pushSuccessValidate(registrationTokens, response); } catch (FirebaseMessagingException e) { e.printStackTrace(); - // 예외 처리 추가 가능 + System.out.println("FCM 메시지 전송 중 예외 발생: " + e.getMessage()); } } @@ -54,6 +53,12 @@ private static void pushSuccessValidate(final List registrationTokens, f System.out.println(response.getSuccessCount() + " messages were sent successfully."); if (response.getFailureCount() > 0) { System.out.println(response.getFailureCount() + " messages failed to send."); + response.getResponses().forEach(sendResponse -> { + if (!sendResponse.isSuccessful()) { + System.out.println("Failed to send message to token: " + sendResponse.getMessageId()); + System.out.println("Error: " + sendResponse.getException().getMessage()); + } + }); } } } diff --git a/src/main/java/yerong/wedle/notification/service/NotificationService.java b/src/main/java/yerong/wedle/notification/service/NotificationService.java index faf7c0b..80ee471 100644 --- a/src/main/java/yerong/wedle/notification/service/NotificationService.java +++ b/src/main/java/yerong/wedle/notification/service/NotificationService.java @@ -71,7 +71,7 @@ private CalendarEvent getCalendarEventById(Long calendarId) { } @Transactional - @Scheduled(cron = "0 10 14 * * ?", zone = "Asia/Seoul") + @Scheduled(cron = "0 20 14 * * ?", zone = "Asia/Seoul") public void sendNotifications() { LocalDate today = LocalDate.now(); List dueNotifications = notificationRepository.findByNotificationDate(today); From 740b8b94b00e828d02e0ac50b4f65d6b6e4216c5 Mon Sep 17 00:00:00 2001 From: Sin Ye Rin <91180366+nyeroni@users.noreply.github.com> Date: Wed, 30 Oct 2024 14:26:20 +0900 Subject: [PATCH 21/55] =?UTF-8?q?refactor:=20fcm=20apns=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 --- src/main/java/yerong/wedle/common/utils/FcmUtils.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/yerong/wedle/common/utils/FcmUtils.java b/src/main/java/yerong/wedle/common/utils/FcmUtils.java index fcfe5ea..91f38b7 100644 --- a/src/main/java/yerong/wedle/common/utils/FcmUtils.java +++ b/src/main/java/yerong/wedle/common/utils/FcmUtils.java @@ -7,6 +7,7 @@ import com.google.firebase.messaging.FirebaseMessagingException; import com.google.firebase.messaging.MulticastMessage; import com.google.firebase.messaging.Notification; + import java.util.Date; import java.util.List; import lombok.AccessLevel; @@ -29,8 +30,8 @@ public static void broadCast(final List registrationTokens, String title .build()) .setApnsConfig(ApnsConfig.builder() .setAps(Aps.builder().setAlert(body).build()) - .putHeader("apns-expiration", Long.toString(EXPIRED_TIME_FOR_UNIX)) - .build()) + .putHeader("apns-expiration", Long.toString(EXPIRED_TIME_FOR_UNIX)) + .build()) .addAllTokens(registrationTokens) .build(); From 0d0a30e8548ab8863ab1958811bc975db1e7f32a Mon Sep 17 00:00:00 2001 From: Sin Ye Rin <91180366+nyeroni@users.noreply.github.com> Date: Wed, 30 Oct 2024 14:26:44 +0900 Subject: [PATCH 22/55] =?UTF-8?q?refactor:=20=EC=95=8C=EB=A6=BC=20?= =?UTF-8?q?=EC=8B=9C=EA=B0=84=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yerong/wedle/notification/service/NotificationService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/yerong/wedle/notification/service/NotificationService.java b/src/main/java/yerong/wedle/notification/service/NotificationService.java index 80ee471..502a03f 100644 --- a/src/main/java/yerong/wedle/notification/service/NotificationService.java +++ b/src/main/java/yerong/wedle/notification/service/NotificationService.java @@ -71,7 +71,7 @@ private CalendarEvent getCalendarEventById(Long calendarId) { } @Transactional - @Scheduled(cron = "0 20 14 * * ?", zone = "Asia/Seoul") + @Scheduled(cron = "0 35 14 * * ?", zone = "Asia/Seoul") public void sendNotifications() { LocalDate today = LocalDate.now(); List dueNotifications = notificationRepository.findByNotificationDate(today); From c08f3f224c4ae680b3e25e0a30322c28b7acf84d Mon Sep 17 00:00:00 2001 From: Sin Ye Rin <91180366+nyeroni@users.noreply.github.com> Date: Wed, 30 Oct 2024 14:38:05 +0900 Subject: [PATCH 23/55] =?UTF-8?q?refactor:=20=EC=95=8C=EB=A6=BC=20?= =?UTF-8?q?=EC=8B=9C=EA=B0=84=20=EC=88=98=EC=A0=95=20=EB=B0=8F=20fcm=20?= =?UTF-8?q?=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 --- src/main/java/yerong/wedle/common/utils/FcmUtils.java | 6 +++--- .../wedle/notification/service/NotificationService.java | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/yerong/wedle/common/utils/FcmUtils.java b/src/main/java/yerong/wedle/common/utils/FcmUtils.java index 91f38b7..28cdeaa 100644 --- a/src/main/java/yerong/wedle/common/utils/FcmUtils.java +++ b/src/main/java/yerong/wedle/common/utils/FcmUtils.java @@ -17,8 +17,8 @@ public class FcmUtils { private static final int FCM_PUSH_LIMIT_SIZE = 500; - private static final long ONE_WEEK = (long) 60 * 60 * 24 * 7; - private static final long EXPIRED_TIME_FOR_UNIX = new Date(new Date().getTime() + ONE_WEEK).getTime(); + private static final long ONE_WEEK_IN_SECONDS = 60 * 60 * 24 * 7; + private static final long EXPIRED_TIME_FOR_UNIX = new Date().getTime() / 1000 + ONE_WEEK_IN_SECONDS; public static void broadCast(final List registrationTokens, String title, String body) { limitSizeValidate(registrationTokens); @@ -30,7 +30,7 @@ public static void broadCast(final List registrationTokens, String title .build()) .setApnsConfig(ApnsConfig.builder() .setAps(Aps.builder().setAlert(body).build()) - .putHeader("apns-expiration", Long.toString(EXPIRED_TIME_FOR_UNIX)) + .putHeader("apns-expiration", Long.toString(EXPIRED_TIME_FOR_UNIX)) .build()) .addAllTokens(registrationTokens) .build(); diff --git a/src/main/java/yerong/wedle/notification/service/NotificationService.java b/src/main/java/yerong/wedle/notification/service/NotificationService.java index 502a03f..6a4c05e 100644 --- a/src/main/java/yerong/wedle/notification/service/NotificationService.java +++ b/src/main/java/yerong/wedle/notification/service/NotificationService.java @@ -71,7 +71,7 @@ private CalendarEvent getCalendarEventById(Long calendarId) { } @Transactional - @Scheduled(cron = "0 35 14 * * ?", zone = "Asia/Seoul") + @Scheduled(cron = "0 45 14 * * ?", zone = "Asia/Seoul") public void sendNotifications() { LocalDate today = LocalDate.now(); List dueNotifications = notificationRepository.findByNotificationDate(today); From ad21ba6335efdef8c8dbeedb519f4c03a8498e96 Mon Sep 17 00:00:00 2001 From: Sin Ye Rin <91180366+nyeroni@users.noreply.github.com> Date: Wed, 30 Oct 2024 15:15:26 +0900 Subject: [PATCH 24/55] =?UTF-8?q?refactor:=20fcm=20=EC=98=A4=EB=A5=98=20?= =?UTF-8?q?=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/yerong/wedle/common/utils/FcmUtils.java | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/main/java/yerong/wedle/common/utils/FcmUtils.java b/src/main/java/yerong/wedle/common/utils/FcmUtils.java index 28cdeaa..7b2f710 100644 --- a/src/main/java/yerong/wedle/common/utils/FcmUtils.java +++ b/src/main/java/yerong/wedle/common/utils/FcmUtils.java @@ -12,7 +12,9 @@ import java.util.List; import lombok.AccessLevel; import lombok.NoArgsConstructor; +import lombok.extern.slf4j.Slf4j; +@Slf4j @NoArgsConstructor(access = AccessLevel.PRIVATE) public class FcmUtils { @@ -29,9 +31,12 @@ public static void broadCast(final List registrationTokens, String title .setBody(body) .build()) .setApnsConfig(ApnsConfig.builder() - .setAps(Aps.builder().setAlert(body).build()) + .setAps(Aps + .builder() + .setAlert(body) + .build()) .putHeader("apns-expiration", Long.toString(EXPIRED_TIME_FOR_UNIX)) - .build()) + .build()) .addAllTokens(registrationTokens) .build(); @@ -39,11 +44,14 @@ public static void broadCast(final List registrationTokens, String title BatchResponse response = FirebaseMessaging.getInstance().sendEachForMulticast(message); pushSuccessValidate(registrationTokens, response); } catch (FirebaseMessagingException e) { - e.printStackTrace(); - System.out.println("FCM 메시지 전송 중 예외 발생: " + e.getMessage()); + log.error("FCM 메시지 전송 중 예외 발생: {}", e.getMessage(), e); + for (String token : registrationTokens) { + log.error("유효하지 않은 토큰: {}", token); + } } } + private static void limitSizeValidate(final List registrationTokens) { if (registrationTokens.size() > FCM_PUSH_LIMIT_SIZE) { throw new IllegalArgumentException("FCM push 알림 수신자는 최대 500명입니다."); From 41722464bde878ff8e3cb99832c69c78dedc5add Mon Sep 17 00:00:00 2001 From: Sin Ye Rin <91180366+nyeroni@users.noreply.github.com> Date: Wed, 30 Oct 2024 15:15:43 +0900 Subject: [PATCH 25/55] =?UTF-8?q?refactor:=20fcm=20=EC=98=A4=EB=A5=98=20?= =?UTF-8?q?=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yerong/wedle/notification/service/NotificationService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/yerong/wedle/notification/service/NotificationService.java b/src/main/java/yerong/wedle/notification/service/NotificationService.java index 6a4c05e..eeba638 100644 --- a/src/main/java/yerong/wedle/notification/service/NotificationService.java +++ b/src/main/java/yerong/wedle/notification/service/NotificationService.java @@ -71,7 +71,7 @@ private CalendarEvent getCalendarEventById(Long calendarId) { } @Transactional - @Scheduled(cron = "0 45 14 * * ?", zone = "Asia/Seoul") + @Scheduled(cron = "0 25 15 * * ?", zone = "Asia/Seoul") public void sendNotifications() { LocalDate today = LocalDate.now(); List dueNotifications = notificationRepository.findByNotificationDate(today); From 728f53d51c9d324110e5d8ba70b1f6e88a4af29b Mon Sep 17 00:00:00 2001 From: Sin Ye Rin <91180366+nyeroni@users.noreply.github.com> Date: Wed, 30 Oct 2024 15:18:41 +0900 Subject: [PATCH 26/55] =?UTF-8?q?refactor:=20fcm=20=EC=98=A4=EB=A5=98=20?= =?UTF-8?q?=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yerong/wedle/notification/service/NotificationService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/yerong/wedle/notification/service/NotificationService.java b/src/main/java/yerong/wedle/notification/service/NotificationService.java index eeba638..2cbe6fa 100644 --- a/src/main/java/yerong/wedle/notification/service/NotificationService.java +++ b/src/main/java/yerong/wedle/notification/service/NotificationService.java @@ -71,7 +71,7 @@ private CalendarEvent getCalendarEventById(Long calendarId) { } @Transactional - @Scheduled(cron = "0 25 15 * * ?", zone = "Asia/Seoul") + @Scheduled(cron = "0 30 15 * * ?", zone = "Asia/Seoul") public void sendNotifications() { LocalDate today = LocalDate.now(); List dueNotifications = notificationRepository.findByNotificationDate(today); From 1d7fbdde48a96f7e74ab85cbf3b6c5357143dd84 Mon Sep 17 00:00:00 2001 From: Sin Ye Rin <91180366+nyeroni@users.noreply.github.com> Date: Wed, 30 Oct 2024 15:24:38 +0900 Subject: [PATCH 27/55] =?UTF-8?q?refactor:=20=EC=95=8C=EB=A6=BC=20?= =?UTF-8?q?=EC=8B=9C=EA=B0=84=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yerong/wedle/notification/service/NotificationService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/yerong/wedle/notification/service/NotificationService.java b/src/main/java/yerong/wedle/notification/service/NotificationService.java index 2cbe6fa..7f904a0 100644 --- a/src/main/java/yerong/wedle/notification/service/NotificationService.java +++ b/src/main/java/yerong/wedle/notification/service/NotificationService.java @@ -71,7 +71,7 @@ private CalendarEvent getCalendarEventById(Long calendarId) { } @Transactional - @Scheduled(cron = "0 30 15 * * ?", zone = "Asia/Seoul") + @Scheduled(cron = "0 35 15 * * ?", zone = "Asia/Seoul") public void sendNotifications() { LocalDate today = LocalDate.now(); List dueNotifications = notificationRepository.findByNotificationDate(today); From c6a528cd21f52834f8ebc98ed581064049ae23ff Mon Sep 17 00:00:00 2001 From: Sin Ye Rin <91180366+nyeroni@users.noreply.github.com> Date: Wed, 30 Oct 2024 16:48:25 +0900 Subject: [PATCH 28/55] =?UTF-8?q?refactor:=20=EC=95=8C=EB=A6=BC=20?= =?UTF-8?q?=EC=8B=9C=EA=B0=84=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yerong/wedle/notification/service/NotificationService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/yerong/wedle/notification/service/NotificationService.java b/src/main/java/yerong/wedle/notification/service/NotificationService.java index 7f904a0..275b620 100644 --- a/src/main/java/yerong/wedle/notification/service/NotificationService.java +++ b/src/main/java/yerong/wedle/notification/service/NotificationService.java @@ -71,7 +71,7 @@ private CalendarEvent getCalendarEventById(Long calendarId) { } @Transactional - @Scheduled(cron = "0 35 15 * * ?", zone = "Asia/Seoul") + @Scheduled(cron = "0 0 17 * * ?", zone = "Asia/Seoul") public void sendNotifications() { LocalDate today = LocalDate.now(); List dueNotifications = notificationRepository.findByNotificationDate(today); From 34ec6456f1761f75d4fb1d39d365a6faf09bcb58 Mon Sep 17 00:00:00 2001 From: Sin Ye Rin <91180366+nyeroni@users.noreply.github.com> Date: Wed, 30 Oct 2024 19:02:59 +0900 Subject: [PATCH 29/55] =?UTF-8?q?refactor:=20=EC=95=8C=EB=A6=BC=20?= =?UTF-8?q?=EC=8B=9C=EA=B0=84=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yerong/wedle/notification/service/NotificationService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/yerong/wedle/notification/service/NotificationService.java b/src/main/java/yerong/wedle/notification/service/NotificationService.java index 275b620..f6bcd9d 100644 --- a/src/main/java/yerong/wedle/notification/service/NotificationService.java +++ b/src/main/java/yerong/wedle/notification/service/NotificationService.java @@ -71,7 +71,7 @@ private CalendarEvent getCalendarEventById(Long calendarId) { } @Transactional - @Scheduled(cron = "0 0 17 * * ?", zone = "Asia/Seoul") + @Scheduled(cron = "0 18 19 * * ?", zone = "Asia/Seoul") public void sendNotifications() { LocalDate today = LocalDate.now(); List dueNotifications = notificationRepository.findByNotificationDate(today); From 4bd25f9b73c25e77e428cce414c738bbde4e15d3 Mon Sep 17 00:00:00 2001 From: Sin Ye Rin <91180366+nyeroni@users.noreply.github.com> Date: Wed, 30 Oct 2024 22:34:55 +0900 Subject: [PATCH 30/55] =?UTF-8?q?refactor:=20=EC=95=8C=EB=A6=BC=20?= =?UTF-8?q?=EC=8B=9C=EA=B0=84=20=EB=B3=80=EA=B2=BD=20=EB=B0=8F=20=EC=98=88?= =?UTF-8?q?=EC=99=B8=EC=B2=98=EB=A6=AC=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yerong/wedle/common/config/FMCConfig.java | 23 ++++--- .../yerong/wedle/common/utils/FcmUtils.java | 60 +++++++++++++------ .../service/NotificationService.java | 2 +- 3 files changed, 58 insertions(+), 27 deletions(-) diff --git a/src/main/java/yerong/wedle/common/config/FMCConfig.java b/src/main/java/yerong/wedle/common/config/FMCConfig.java index ae48faa..98a9743 100644 --- a/src/main/java/yerong/wedle/common/config/FMCConfig.java +++ b/src/main/java/yerong/wedle/common/config/FMCConfig.java @@ -6,25 +6,32 @@ import java.io.ByteArrayInputStream; import java.io.IOException; +import java.io.InputStream; import javax.annotation.PostConstruct; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Configuration; +import org.springframework.core.io.ClassPathResource; @Configuration +@Slf4j public class FMCConfig { @Value("${firebase.config}") private String firebaseConfig; @PostConstruct - private void init() throws IOException { - ByteArrayInputStream serviceAccountStream = new ByteArrayInputStream(firebaseConfig.getBytes()); - - FirebaseOptions options = FirebaseOptions.builder() - .setCredentials(GoogleCredentials.fromStream(serviceAccountStream)) - .build(); - - FirebaseApp.initializeApp(options); + private void init() { + try (ByteArrayInputStream serviceAccount = new ByteArrayInputStream(firebaseConfig.getBytes())) { + FirebaseOptions options = FirebaseOptions.builder() + .setCredentials(GoogleCredentials.fromStream(serviceAccount)) + .build(); + + FirebaseApp.initializeApp(options); + log.info("파이어베이스 서버와의 연결에 성공했습니다."); + } catch (IOException e) { + log.error("파이어베이스 서버와의 연결에 실패했습니다.", e); + } } } diff --git a/src/main/java/yerong/wedle/common/utils/FcmUtils.java b/src/main/java/yerong/wedle/common/utils/FcmUtils.java index 7b2f710..58b33ff 100644 --- a/src/main/java/yerong/wedle/common/utils/FcmUtils.java +++ b/src/main/java/yerong/wedle/common/utils/FcmUtils.java @@ -10,6 +10,7 @@ import java.util.Date; import java.util.List; + import lombok.AccessLevel; import lombok.NoArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -25,33 +26,53 @@ public class FcmUtils { public static void broadCast(final List registrationTokens, String title, String body) { limitSizeValidate(registrationTokens); - MulticastMessage message = MulticastMessage.builder() + log.info("registrationTokens = {}", registrationTokens.get(0)); + log.info("title = {}", title); + log.info("body = {}", body); + + if (title == null || title.isEmpty()) { + log.error("제목이 비어 있습니다."); + return; + } + if (body == null || body.isEmpty()) { + log.error("본문이 비어 있습니다."); + return; + } + + long currentTime = new Date().getTime() / 1000; + if (EXPIRED_TIME_FOR_UNIX <= currentTime) { + log.error("APNs 만료 시간 설정이 잘못되었습니다."); + } + + MulticastMessage message = buildMessage(registrationTokens, title, body); + + try { + BatchResponse response = FirebaseMessaging.getInstance().sendEachForMulticast(message); + pushSuccessValidate(registrationTokens, response); + } catch (FirebaseMessagingException e) { + log.error("FCM 메시지 전송 중 예외 발생: {}", e.getMessage(), e); + if (e.getErrorCode() != null) { + log.error("Error Code: {}", e.getErrorCode()); + } + } + } + + private static MulticastMessage buildMessage(List registrationTokens, String title, String body) { + return MulticastMessage.builder() .setNotification(Notification.builder() .setTitle(title) .setBody(body) .build()) .setApnsConfig(ApnsConfig.builder() - .setAps(Aps - .builder() + .setAps(Aps.builder() .setAlert(body) .build()) .putHeader("apns-expiration", Long.toString(EXPIRED_TIME_FOR_UNIX)) .build()) .addAllTokens(registrationTokens) .build(); - - try { - BatchResponse response = FirebaseMessaging.getInstance().sendEachForMulticast(message); - pushSuccessValidate(registrationTokens, response); - } catch (FirebaseMessagingException e) { - log.error("FCM 메시지 전송 중 예외 발생: {}", e.getMessage(), e); - for (String token : registrationTokens) { - log.error("유효하지 않은 토큰: {}", token); - } - } } - private static void limitSizeValidate(final List registrationTokens) { if (registrationTokens.size() > FCM_PUSH_LIMIT_SIZE) { throw new IllegalArgumentException("FCM push 알림 수신자는 최대 500명입니다."); @@ -59,13 +80,16 @@ private static void limitSizeValidate(final List registrationTokens) { } private static void pushSuccessValidate(final List registrationTokens, final BatchResponse response) { - System.out.println(response.getSuccessCount() + " messages were sent successfully."); + log.info("{} messages were sent successfully.", response.getSuccessCount()); if (response.getFailureCount() > 0) { - System.out.println(response.getFailureCount() + " messages failed to send."); + log.error("{} messages failed to send.", response.getFailureCount()); response.getResponses().forEach(sendResponse -> { if (!sendResponse.isSuccessful()) { - System.out.println("Failed to send message to token: " + sendResponse.getMessageId()); - System.out.println("Error: " + sendResponse.getException().getMessage()); + String messageId = sendResponse.getMessageId(); + String errorMessage = sendResponse.getException() != null ? sendResponse.getException().getMessage() : "Unknown error"; + + log.error("Failed to send message to token: {}. Error: {}", + messageId != null ? messageId : "null", errorMessage); } }); } diff --git a/src/main/java/yerong/wedle/notification/service/NotificationService.java b/src/main/java/yerong/wedle/notification/service/NotificationService.java index f6bcd9d..4127475 100644 --- a/src/main/java/yerong/wedle/notification/service/NotificationService.java +++ b/src/main/java/yerong/wedle/notification/service/NotificationService.java @@ -71,7 +71,7 @@ private CalendarEvent getCalendarEventById(Long calendarId) { } @Transactional - @Scheduled(cron = "0 18 19 * * ?", zone = "Asia/Seoul") + @Scheduled(cron = "0 42 22 * * ?", zone = "Asia/Seoul") public void sendNotifications() { LocalDate today = LocalDate.now(); List dueNotifications = notificationRepository.findByNotificationDate(today); From 88c2cec236668b8ec92bb4e27abf34b984d79b66 Mon Sep 17 00:00:00 2001 From: Sin Ye Rin <91180366+nyeroni@users.noreply.github.com> Date: Wed, 30 Oct 2024 22:50:42 +0900 Subject: [PATCH 31/55] =?UTF-8?q?refactor:=20=EC=95=8C=EB=A6=BC=20?= =?UTF-8?q?=EC=8B=9C=EA=B0=84=20=EB=B3=80=EA=B2=BD=20=EB=B0=8F=20=ED=86=B5?= =?UTF-8?q?=EC=8B=A0=20=EC=9D=B8=EC=9E=90=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yerong/wedle/common/utils/FcmUtils.java | 33 ++++--------------- .../service/NotificationService.java | 2 +- 2 files changed, 8 insertions(+), 27 deletions(-) diff --git a/src/main/java/yerong/wedle/common/utils/FcmUtils.java b/src/main/java/yerong/wedle/common/utils/FcmUtils.java index 58b33ff..4e69d0b 100644 --- a/src/main/java/yerong/wedle/common/utils/FcmUtils.java +++ b/src/main/java/yerong/wedle/common/utils/FcmUtils.java @@ -1,12 +1,6 @@ package yerong.wedle.common.utils; -import com.google.firebase.messaging.ApnsConfig; -import com.google.firebase.messaging.Aps; -import com.google.firebase.messaging.BatchResponse; -import com.google.firebase.messaging.FirebaseMessaging; -import com.google.firebase.messaging.FirebaseMessagingException; -import com.google.firebase.messaging.MulticastMessage; -import com.google.firebase.messaging.Notification; +import com.google.firebase.messaging.*; import java.util.Date; import java.util.List; @@ -26,24 +20,6 @@ public class FcmUtils { public static void broadCast(final List registrationTokens, String title, String body) { limitSizeValidate(registrationTokens); - log.info("registrationTokens = {}", registrationTokens.get(0)); - log.info("title = {}", title); - log.info("body = {}", body); - - if (title == null || title.isEmpty()) { - log.error("제목이 비어 있습니다."); - return; - } - if (body == null || body.isEmpty()) { - log.error("본문이 비어 있습니다."); - return; - } - - long currentTime = new Date().getTime() / 1000; - if (EXPIRED_TIME_FOR_UNIX <= currentTime) { - log.error("APNs 만료 시간 설정이 잘못되었습니다."); - } - MulticastMessage message = buildMessage(registrationTokens, title, body); try { @@ -65,7 +41,12 @@ private static MulticastMessage buildMessage(List registrationTokens, St .build()) .setApnsConfig(ApnsConfig.builder() .setAps(Aps.builder() - .setAlert(body) + .setAlert( + ApsAlert.builder() + .setTitle(title) + .setBody(body) + .build() + ) .build()) .putHeader("apns-expiration", Long.toString(EXPIRED_TIME_FOR_UNIX)) .build()) diff --git a/src/main/java/yerong/wedle/notification/service/NotificationService.java b/src/main/java/yerong/wedle/notification/service/NotificationService.java index 4127475..07d52fc 100644 --- a/src/main/java/yerong/wedle/notification/service/NotificationService.java +++ b/src/main/java/yerong/wedle/notification/service/NotificationService.java @@ -71,7 +71,7 @@ private CalendarEvent getCalendarEventById(Long calendarId) { } @Transactional - @Scheduled(cron = "0 42 22 * * ?", zone = "Asia/Seoul") + @Scheduled(cron = "0 10 23 * * ?", zone = "Asia/Seoul") public void sendNotifications() { LocalDate today = LocalDate.now(); List dueNotifications = notificationRepository.findByNotificationDate(today); From 00eeff979bfdb3b9c5bc94c15c76b77e531b2c08 Mon Sep 17 00:00:00 2001 From: Sin Ye Rin <91180366+nyeroni@users.noreply.github.com> Date: Thu, 31 Oct 2024 20:22:10 +0900 Subject: [PATCH 32/55] =?UTF-8?q?feat:=20=EB=8B=89=EB=84=A4=EC=9E=84=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../exception/GlobalExceptionHandler.java | 21 ++++++++ .../wedle/common/exception/ResponseCode.java | 2 + .../controller/MemberApiController.java | 54 +++++++++++++++++++ .../yerong/wedle/member/domain/Member.java | 16 +++++- .../wedle/member/dto/NicknameRequest.java | 15 ++++++ .../wedle/member/dto/NicknameResponse.java | 15 ++++++ .../exception/ExistingNicknameException.java | 11 ++++ .../MemberNicknameDuplicateException.java | 11 ++++ .../member/repository/MemberRepository.java | 4 ++ .../wedle/member/service/MemberService.java | 50 ++++++++++++++++- .../oauth/controller/LoginController.java | 14 ++--- .../yerong/wedle/oauth/dto/LoginResponse.java | 15 ++++++ .../yerong/wedle/oauth/jwt/JwtProvider.java | 3 -- .../wedle/oauth/service/AuthService.java | 14 +++-- 14 files changed, 229 insertions(+), 16 deletions(-) create mode 100644 src/main/java/yerong/wedle/member/controller/MemberApiController.java create mode 100644 src/main/java/yerong/wedle/member/dto/NicknameRequest.java create mode 100644 src/main/java/yerong/wedle/member/dto/NicknameResponse.java create mode 100644 src/main/java/yerong/wedle/member/exception/ExistingNicknameException.java create mode 100644 src/main/java/yerong/wedle/member/exception/MemberNicknameDuplicateException.java create mode 100644 src/main/java/yerong/wedle/oauth/dto/LoginResponse.java diff --git a/src/main/java/yerong/wedle/common/exception/GlobalExceptionHandler.java b/src/main/java/yerong/wedle/common/exception/GlobalExceptionHandler.java index fa512fd..3d717f9 100644 --- a/src/main/java/yerong/wedle/common/exception/GlobalExceptionHandler.java +++ b/src/main/java/yerong/wedle/common/exception/GlobalExceptionHandler.java @@ -13,6 +13,8 @@ import yerong.wedle.competitionRate.exception.CompetitionRateNotFoundException; import yerong.wedle.department.exception.DepartmentNotFoundException; import yerong.wedle.employmentRate.exception.EmploymentRateNotFoundException; +import yerong.wedle.member.exception.ExistingNicknameException; +import yerong.wedle.member.exception.MemberNicknameDuplicateException; import yerong.wedle.member.exception.MemberNotFoundException; import yerong.wedle.member.exception.MemberDuplicateException; import yerong.wedle.notification.exception.DuplicateNotificationException; @@ -65,6 +67,16 @@ public ResponseEntity handleMemberDuplicateException(MemberDuplic return ResponseEntity.status(HttpStatus.CONFLICT).body(errorResponse); } + @ExceptionHandler(MemberNicknameDuplicateException.class) + public ResponseEntity handleMemberNicknameDuplicateException(MemberNicknameDuplicateException ex) { + ErrorResponse errorResponse = new ErrorResponse( + ResponseCode.MEMBER_NICKNAME_DUPLICATE.getCode(), + ResponseCode.MEMBER_NICKNAME_DUPLICATE.getMessage(), + LocalDateTime.now().format(FORMATTER) + ); + return ResponseEntity.status(HttpStatus.CONFLICT).body(errorResponse); + } + @ExceptionHandler(NotificationNotFoundException.class) public ResponseEntity handleNotificationNotFoundException(NotificationNotFoundException ex) { ErrorResponse errorResponse = new ErrorResponse( @@ -84,6 +96,15 @@ public ResponseEntity handleDuplicateNotificationException(Duplic ); return ResponseEntity.status(HttpStatus.NOT_FOUND).body(errorResponse); } + @ExceptionHandler(ExistingNicknameException.class) + public ResponseEntity handleExistingNicknameException(ExistingNicknameException ex) { + ErrorResponse errorResponse = new ErrorResponse( + ResponseCode.EXISTING_NICKNAME.getCode(), + ResponseCode.EXISTING_NICKNAME.getMessage(), + LocalDateTime.now().format(FORMATTER) + ); + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(errorResponse); + } @ExceptionHandler(NotificationDateOutOfRangeException.class) diff --git a/src/main/java/yerong/wedle/common/exception/ResponseCode.java b/src/main/java/yerong/wedle/common/exception/ResponseCode.java index 16dd449..95f9354 100644 --- a/src/main/java/yerong/wedle/common/exception/ResponseCode.java +++ b/src/main/java/yerong/wedle/common/exception/ResponseCode.java @@ -10,6 +10,8 @@ public enum ResponseCode { // Member MEMBER_NOT_FOUND("404", "회원이 존재하지 않습니다."), MEMBER_DUPLICATE("409", "이미 존재하는 회원입니다."), + MEMBER_NICKNAME_DUPLICATE("409", "이미 존재하는 닉네임입니다."), + EXISTING_NICKNAME("400", "기존 닉네임과 동일합니다."), // Notification NOTIFICATION_NOT_FOUND("404", "알림이 존재하지 않습니다."), diff --git a/src/main/java/yerong/wedle/member/controller/MemberApiController.java b/src/main/java/yerong/wedle/member/controller/MemberApiController.java new file mode 100644 index 0000000..b7765e5 --- /dev/null +++ b/src/main/java/yerong/wedle/member/controller/MemberApiController.java @@ -0,0 +1,54 @@ +package yerong.wedle.member.controller; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import yerong.wedle.department.dto.DepartmentResponse; +import yerong.wedle.department.service.DepartmentService; +import yerong.wedle.member.dto.NicknameRequest; +import yerong.wedle.member.dto.NicknameResponse; +import yerong.wedle.member.service.MemberService; + +import java.util.List; + +@Tag(name = "Member API", description = "회원 관련 API") +@RequiredArgsConstructor +@RestController +@RequestMapping("/api/members") +public class MemberApiController { + + private final MemberService memberService; + + @Operation(summary = "회원 닉네임 등록", description = "신규 회원의 닉네임을 등록합니다") + @ApiResponses({ + @ApiResponse(responseCode = "200", description = "닉네임 등록 성공"), + @ApiResponse(responseCode = "404", description = "회원을 찾을 수 없음"), + @ApiResponse(responseCode = "409", description = "중복된 닉네임이 있음") + + }) + @PostMapping("/nickname") + public ResponseEntity createNickname(NicknameRequest nickNameRequest) { + return ResponseEntity.ok().body(memberService.setNickname(nickNameRequest)); + } + + @Operation(summary = "닉네임 변경", description = "회원의 닉네임을 변경합니다") + @ApiResponses({ + @ApiResponse(responseCode = "200", description = "닉네임 변경 성공"), + @ApiResponse(responseCode = "400", description = "변경하려는 닉네임이 기존 닉네임과 같음"), + @ApiResponse(responseCode = "404", description = "회원을 찾을 수 없음"), + @ApiResponse(responseCode = "409", description = "중복된 닉네임이 있음") + }) + @PutMapping("/nickname") + public ResponseEntity updateNickname(@RequestBody NicknameRequest nicknameRequest) { + return ResponseEntity.ok(memberService.setNickname(nicknameRequest)); + } + + @GetMapping("/nickname") + public ResponseEntity getNickname() { + return ResponseEntity.ok(memberService.getNickname()); + } +} diff --git a/src/main/java/yerong/wedle/member/domain/Member.java b/src/main/java/yerong/wedle/member/domain/Member.java index 5f2286d..611ee69 100644 --- a/src/main/java/yerong/wedle/member/domain/Member.java +++ b/src/main/java/yerong/wedle/member/domain/Member.java @@ -8,7 +8,6 @@ import java.util.List; import static jakarta.persistence.GenerationType.IDENTITY; -import static lombok.AccessLevel.*; @AllArgsConstructor @NoArgsConstructor(access = AccessLevel.PROTECTED) @@ -26,11 +25,26 @@ public class Member extends BaseTimeEntity { private String username; private String email; + + @Column(nullable = false) private String socialId; + @Column(unique = true) + private String nickname; + @Enumerated(value = EnumType.STRING) private Role role; + private boolean isExistingMember; + @OneToMany(mappedBy = "member", cascade = CascadeType.ALL, fetch = FetchType.LAZY) // Member와 Notification 간의 관계 설정 private List notifications; + + public void setExistingMember(boolean isExistingMember) { + this.isExistingMember = isExistingMember; + } + + public void setNickname(String nickname) { + this.nickname = nickname; + } } diff --git a/src/main/java/yerong/wedle/member/dto/NicknameRequest.java b/src/main/java/yerong/wedle/member/dto/NicknameRequest.java new file mode 100644 index 0000000..4d15985 --- /dev/null +++ b/src/main/java/yerong/wedle/member/dto/NicknameRequest.java @@ -0,0 +1,15 @@ +package yerong.wedle.member.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@AllArgsConstructor +@Builder +@Data +@NoArgsConstructor +public class NicknameRequest { + + private String nickName; +} diff --git a/src/main/java/yerong/wedle/member/dto/NicknameResponse.java b/src/main/java/yerong/wedle/member/dto/NicknameResponse.java new file mode 100644 index 0000000..5178a6a --- /dev/null +++ b/src/main/java/yerong/wedle/member/dto/NicknameResponse.java @@ -0,0 +1,15 @@ +package yerong.wedle.member.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@AllArgsConstructor +@Builder +@Data +@NoArgsConstructor +public class NicknameResponse { + + private String nickName; +} diff --git a/src/main/java/yerong/wedle/member/exception/ExistingNicknameException.java b/src/main/java/yerong/wedle/member/exception/ExistingNicknameException.java new file mode 100644 index 0000000..44f83e7 --- /dev/null +++ b/src/main/java/yerong/wedle/member/exception/ExistingNicknameException.java @@ -0,0 +1,11 @@ +package yerong.wedle.member.exception; + +import yerong.wedle.common.exception.CustomException; +import yerong.wedle.common.exception.ResponseCode; + +public class ExistingNicknameException extends CustomException { + + public ExistingNicknameException() { + super(ResponseCode.EXISTING_NICKNAME); + } +} diff --git a/src/main/java/yerong/wedle/member/exception/MemberNicknameDuplicateException.java b/src/main/java/yerong/wedle/member/exception/MemberNicknameDuplicateException.java new file mode 100644 index 0000000..519b368 --- /dev/null +++ b/src/main/java/yerong/wedle/member/exception/MemberNicknameDuplicateException.java @@ -0,0 +1,11 @@ +package yerong.wedle.member.exception; + +import yerong.wedle.common.exception.CustomException; +import yerong.wedle.common.exception.ResponseCode; + +public class MemberNicknameDuplicateException extends CustomException { + + public MemberNicknameDuplicateException() { + super(ResponseCode.MEMBER_DUPLICATE); + } +} diff --git a/src/main/java/yerong/wedle/member/repository/MemberRepository.java b/src/main/java/yerong/wedle/member/repository/MemberRepository.java index 03596c4..12c8d79 100644 --- a/src/main/java/yerong/wedle/member/repository/MemberRepository.java +++ b/src/main/java/yerong/wedle/member/repository/MemberRepository.java @@ -8,4 +8,8 @@ public interface MemberRepository extends JpaRepository { Optional findBySocialId(String socialId); + + Optional findByNickname(String nickname); + + boolean existsByNickname(String nickname); } diff --git a/src/main/java/yerong/wedle/member/service/MemberService.java b/src/main/java/yerong/wedle/member/service/MemberService.java index 20227cd..15a8e16 100644 --- a/src/main/java/yerong/wedle/member/service/MemberService.java +++ b/src/main/java/yerong/wedle/member/service/MemberService.java @@ -1,11 +1,59 @@ package yerong.wedle.member.service; import lombok.RequiredArgsConstructor; -import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Service; +import yerong.wedle.member.domain.Member; +import yerong.wedle.member.dto.NicknameRequest; +import yerong.wedle.member.dto.NicknameResponse; +import yerong.wedle.member.exception.ExistingNicknameException; +import yerong.wedle.member.exception.MemberNicknameDuplicateException; +import yerong.wedle.member.exception.MemberNotFoundException; +import yerong.wedle.member.repository.MemberRepository; @RequiredArgsConstructor @Service public class MemberService { + private final MemberRepository memberRepository; + + private boolean isNicknameDuplicate(String nickname) { + return memberRepository.existsByNickname(nickname); + } + + public NicknameResponse setNickname(NicknameRequest nicknameRequest) { + String socialId = getCurrentUserId(); + Member member = memberRepository.findBySocialId(socialId) + .orElseThrow(MemberNotFoundException::new); + + if (member.getNickname() != null && member.getNickname().equals(nicknameRequest.getNickName())) { + throw new ExistingNicknameException(); + } + + if (isNicknameDuplicate(nicknameRequest.getNickName())) { + throw new MemberNicknameDuplicateException(); + } + + member.setNickname(nicknameRequest.getNickName()); + memberRepository.save(member); + + return NicknameResponse.builder() + .nickName(member.getNickname()).build(); + } + + + public NicknameResponse getNickname() { + String socialId = getCurrentUserId(); + Member member = memberRepository.findBySocialId(socialId) + .orElseThrow(MemberNotFoundException::new); + + return NicknameResponse.builder() + .nickName(member.getNickname()).build(); + } + + private String getCurrentUserId() { + String socialId = SecurityContextHolder.getContext().getAuthentication().getName(); + + return socialId; + } } diff --git a/src/main/java/yerong/wedle/oauth/controller/LoginController.java b/src/main/java/yerong/wedle/oauth/controller/LoginController.java index f0cd0ab..eed9f4a 100644 --- a/src/main/java/yerong/wedle/oauth/controller/LoginController.java +++ b/src/main/java/yerong/wedle/oauth/controller/LoginController.java @@ -8,7 +8,6 @@ import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpHeaders; @@ -17,9 +16,9 @@ import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.web.bind.annotation.*; import yerong.wedle.common.exception.ErrorResponse; -import yerong.wedle.common.exception.ResponseCode; import yerong.wedle.member.dto.MemberRequest; import yerong.wedle.member.exception.MemberNotFoundException; +import yerong.wedle.oauth.dto.LoginResponse; import yerong.wedle.oauth.dto.MemberLogoutResponse; import yerong.wedle.oauth.dto.TokenResponse; import yerong.wedle.oauth.exception.InvalidRefreshTokenException; @@ -50,11 +49,12 @@ public class LoginController { @PostMapping("/login/apple") public ResponseEntity login(@RequestBody MemberRequest memberRequest) throws Exception { try { - TokenResponse tokenResponse = authService.login(memberRequest); + LoginResponse loginResponse = authService.login(memberRequest); + HttpHeaders headers = authService.setTokenHeaders(TokenResponse.builder() + .accessToken(loginResponse.getAccessToken()) + .refreshToken(loginResponse.getRefreshToken()).build()); - HttpHeaders headers = authService.setTokenHeaders(tokenResponse); - - return ResponseEntity.ok().headers(headers).body(tokenResponse); + return ResponseEntity.ok().headers(headers).body(loginResponse); } catch (Exception e) { return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("로그인 중 오류가 발생했습니다."); } @@ -203,4 +203,6 @@ public ResponseEntity deleteMember() { return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("회원탈퇴 중 오류가 발생했습니다."); } } + + } diff --git a/src/main/java/yerong/wedle/oauth/dto/LoginResponse.java b/src/main/java/yerong/wedle/oauth/dto/LoginResponse.java new file mode 100644 index 0000000..2c087a5 --- /dev/null +++ b/src/main/java/yerong/wedle/oauth/dto/LoginResponse.java @@ -0,0 +1,15 @@ +package yerong.wedle.oauth.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; + +@Builder +@Data +@AllArgsConstructor +public class LoginResponse { + + private String accessToken; + private String refreshToken; + private boolean isExistingMember; +} diff --git a/src/main/java/yerong/wedle/oauth/jwt/JwtProvider.java b/src/main/java/yerong/wedle/oauth/jwt/JwtProvider.java index a5f90a1..b97377d 100644 --- a/src/main/java/yerong/wedle/oauth/jwt/JwtProvider.java +++ b/src/main/java/yerong/wedle/oauth/jwt/JwtProvider.java @@ -22,9 +22,6 @@ import java.security.Key; import java.text.ParseException; -import java.time.Instant; -import java.time.LocalDateTime; -import java.time.ZoneId; import java.util.Arrays; import java.util.Collection; import java.util.Date; diff --git a/src/main/java/yerong/wedle/oauth/service/AuthService.java b/src/main/java/yerong/wedle/oauth/service/AuthService.java index 078c4cd..99eb1ae 100644 --- a/src/main/java/yerong/wedle/oauth/service/AuthService.java +++ b/src/main/java/yerong/wedle/oauth/service/AuthService.java @@ -9,13 +9,13 @@ import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import yerong.wedle.common.exception.ResponseCode; import yerong.wedle.member.domain.Member; import yerong.wedle.member.domain.Role; import yerong.wedle.member.dto.MemberRequest; import yerong.wedle.member.exception.MemberNotFoundException; import yerong.wedle.member.repository.MemberRepository; import yerong.wedle.oauth.domain.RefreshToken; +import yerong.wedle.oauth.dto.LoginResponse; import yerong.wedle.oauth.dto.MemberLogoutResponse; import yerong.wedle.oauth.dto.TokenResponse; import yerong.wedle.oauth.exception.InvalidAuthorizationHeaderException; @@ -41,20 +41,23 @@ public class AuthService { @Transactional - public TokenResponse login(MemberRequest memberRequest){ + public LoginResponse login(MemberRequest memberRequest){ Member member = memberRepository.findBySocialId(memberRequest.getSocialId()).orElse(null); - if (member == null){ + if (member == null) { + member = Member.builder() .socialId(memberRequest.getSocialId()) .email(memberRequest.getEmail()) .username(memberRequest.getName()) .role(Role.USER) + .isExistingMember(false) .build(); memberRepository.save(member); + }else { + member.setExistingMember(true); } - TokenResponse tokenResponse = jwtProvider.generateTokenDto(memberRequest.getSocialId()); Optional existingRefreshToken = refreshTokenRepository.findByMemberId(member.getMemberId()); @@ -70,7 +73,8 @@ public TokenResponse login(MemberRequest memberRequest){ } redisTemplate.opsForValue().set("RT:" + member.getSocialId(), tokenResponse.getRefreshToken(), tokenResponse.getRefreshTokenExpiresIn(), TimeUnit.MILLISECONDS); - return tokenResponse; + + return new LoginResponse(tokenResponse.getAccessToken(), tokenResponse.getRefreshToken(), member.isExistingMember()); } @Transactional public TokenResponse refreshAccessToken(String refreshTokenValue){ From 2903bee0e6f904f85283c286e7af1fea9f55b3ee Mon Sep 17 00:00:00 2001 From: Sin Ye Rin <91180366+nyeroni@users.noreply.github.com> Date: Thu, 31 Oct 2024 20:29:12 +0900 Subject: [PATCH 33/55] =?UTF-8?q?refactor:=20=EC=95=8C=EB=A6=BC=20?= =?UTF-8?q?=EC=8B=9C=EA=B0=84=20=EC=88=98=EC=A0=95=20=EB=B0=8F=20=EB=AC=B8?= =?UTF-8?q?=EA=B5=AC=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../wedle/notification/service/NotificationService.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/yerong/wedle/notification/service/NotificationService.java b/src/main/java/yerong/wedle/notification/service/NotificationService.java index 07d52fc..53584a5 100644 --- a/src/main/java/yerong/wedle/notification/service/NotificationService.java +++ b/src/main/java/yerong/wedle/notification/service/NotificationService.java @@ -71,7 +71,7 @@ private CalendarEvent getCalendarEventById(Long calendarId) { } @Transactional - @Scheduled(cron = "0 10 23 * * ?", zone = "Asia/Seoul") + @Scheduled(cron = "0 0 10 * * ?", zone = "Asia/Seoul") public void sendNotifications() { LocalDate today = LocalDate.now(); List dueNotifications = notificationRepository.findByNotificationDate(today); @@ -79,8 +79,8 @@ public void sendNotifications() { for (Notification notification : dueNotifications) { if (notification.isActive()) { List registrationTokens = notification.getRegistrationTokens(); - String title = notification.getEvent().getTitle(); - String body = "일정 당일입니다."; + String title = "WEDLE"; + String body = notification.getEvent().getTitle() + " 일정 당일입니다"; FcmUtils.broadCast(registrationTokens, title, body); From 7bb4f463778217190866ac06a7198eb0de1aebb1 Mon Sep 17 00:00:00 2001 From: Sin Ye Rin <91180366+nyeroni@users.noreply.github.com> Date: Fri, 1 Nov 2024 16:16:36 +0900 Subject: [PATCH 34/55] =?UTF-8?q?refactor:=20=EC=95=8C=EB=A6=BC=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1=20=EB=B0=98=ED=99=98=20=ED=95=84=EB=93=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 --- .../yerong/wedle/notification/dto/NotificationResponse.java | 2 ++ .../yerong/wedle/notification/service/NotificationService.java | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/main/java/yerong/wedle/notification/dto/NotificationResponse.java b/src/main/java/yerong/wedle/notification/dto/NotificationResponse.java index 0c65342..4cea2f0 100644 --- a/src/main/java/yerong/wedle/notification/dto/NotificationResponse.java +++ b/src/main/java/yerong/wedle/notification/dto/NotificationResponse.java @@ -12,6 +12,8 @@ public class NotificationResponse { private Long notificationId; + private String title; + private String type; private LocalDate notificationDate; private Long eventId; private List registrationTokens; diff --git a/src/main/java/yerong/wedle/notification/service/NotificationService.java b/src/main/java/yerong/wedle/notification/service/NotificationService.java index 07d52fc..de7c19f 100644 --- a/src/main/java/yerong/wedle/notification/service/NotificationService.java +++ b/src/main/java/yerong/wedle/notification/service/NotificationService.java @@ -92,6 +92,8 @@ public void sendNotifications() { private NotificationResponse convertToResponse(Notification notification) { return NotificationResponse.builder() .notificationId(notification.getNotificationId()) + .title(notification.getEvent().getTitle()) + .type(notification.getEvent().getCalendarEventType().getDisplayName()) .notificationDate(notification.getNotificationDate()) .eventId(notification.getEvent().getId()) .registrationTokens(notification.getRegistrationTokens()) From 414399fa506a2ff88d810279c69ef7195955309d Mon Sep 17 00:00:00 2001 From: Sin Ye Rin <91180366+nyeroni@users.noreply.github.com> Date: Fri, 1 Nov 2024 20:44:21 +0900 Subject: [PATCH 35/55] =?UTF-8?q?feat:=20=EB=8B=89=EB=84=A4=EC=9E=84=20?= =?UTF-8?q?=EC=A4=91=EB=B3=B5=20=EC=B2=B4=ED=81=AC=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/MemberApiController.java | 11 ++++++++ .../member/dto/NicknameDuplicateResponse.java | 11 ++++++++ .../wedle/member/service/MemberService.java | 26 +++++++++++++++++++ 3 files changed, 48 insertions(+) create mode 100644 src/main/java/yerong/wedle/member/dto/NicknameDuplicateResponse.java diff --git a/src/main/java/yerong/wedle/member/controller/MemberApiController.java b/src/main/java/yerong/wedle/member/controller/MemberApiController.java index b7765e5..c61174d 100644 --- a/src/main/java/yerong/wedle/member/controller/MemberApiController.java +++ b/src/main/java/yerong/wedle/member/controller/MemberApiController.java @@ -9,6 +9,7 @@ import org.springframework.web.bind.annotation.*; import yerong.wedle.department.dto.DepartmentResponse; import yerong.wedle.department.service.DepartmentService; +import yerong.wedle.member.dto.NicknameDuplicateResponse; import yerong.wedle.member.dto.NicknameRequest; import yerong.wedle.member.dto.NicknameResponse; import yerong.wedle.member.service.MemberService; @@ -47,6 +48,16 @@ public ResponseEntity updateNickname(@RequestBody NicknameRequ return ResponseEntity.ok(memberService.setNickname(nicknameRequest)); } + @Operation(summary = "닉네임 중복 체크", description = "닉네임의 중복 여부를 확인합니다") + @ApiResponses({ + @ApiResponse(responseCode = "200", description = "닉네임 중복 확인 성공") + }) + @GetMapping("/nickname/check") + public ResponseEntity checkNicknameDuplicate(@RequestParam String nickname) { + NicknameDuplicateResponse nicknameDuplicateResponse = memberService.checkNicknameDuplicate(nickname); + return ResponseEntity.ok(nicknameDuplicateResponse); + } + @GetMapping("/nickname") public ResponseEntity getNickname() { return ResponseEntity.ok(memberService.getNickname()); diff --git a/src/main/java/yerong/wedle/member/dto/NicknameDuplicateResponse.java b/src/main/java/yerong/wedle/member/dto/NicknameDuplicateResponse.java new file mode 100644 index 0000000..e036169 --- /dev/null +++ b/src/main/java/yerong/wedle/member/dto/NicknameDuplicateResponse.java @@ -0,0 +1,11 @@ +package yerong.wedle.member.dto; + +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +public class NicknameDuplicateResponse { + private final boolean isDuplicate; + private final String message; +} diff --git a/src/main/java/yerong/wedle/member/service/MemberService.java b/src/main/java/yerong/wedle/member/service/MemberService.java index 15a8e16..0c6c677 100644 --- a/src/main/java/yerong/wedle/member/service/MemberService.java +++ b/src/main/java/yerong/wedle/member/service/MemberService.java @@ -4,6 +4,7 @@ import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Service; import yerong.wedle.member.domain.Member; +import yerong.wedle.member.dto.NicknameDuplicateResponse; import yerong.wedle.member.dto.NicknameRequest; import yerong.wedle.member.dto.NicknameResponse; import yerong.wedle.member.exception.ExistingNicknameException; @@ -56,4 +57,29 @@ private String getCurrentUserId() { return socialId; } + + public NicknameDuplicateResponse checkNicknameDuplicate(String nickname) { + String socialId = getCurrentUserId(); + Member member = memberRepository.findBySocialId(socialId) + .orElseThrow(MemberNotFoundException::new); + + String message = ""; + boolean isDuplicate = false; + + if (member.getNickname() != null && member.getNickname().equals(nickname)) { + message = "기존 닉네임과 동일합니다."; + isDuplicate = true; + } else if (isNicknameDuplicate(nickname)) { + message = "이미 존재하는 닉네임입니다."; + isDuplicate = true; + } else { + message = "사용 가능한 닉네임입니다."; + isDuplicate = false; + } + + return NicknameDuplicateResponse.builder() + .isDuplicate(isDuplicate) + .message(message) + .build(); + } } From 1ed11a729afd6b5430dd8360660d44eacd1dcd31 Mon Sep 17 00:00:00 2001 From: Sin Ye Rin <91180366+nyeroni@users.noreply.github.com> Date: Sat, 2 Nov 2024 15:24:44 +0900 Subject: [PATCH 36/55] =?UTF-8?q?feat:=20=EB=8B=89=EB=84=A4=EC=9E=84=20?= =?UTF-8?q?=EC=A1=B4=EC=9E=AC=20=EC=97=AC=EB=B6=80=20=EC=B2=B4=ED=81=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../oauth/controller/LoginController.java | 38 +++++++++---------- .../yerong/wedle/oauth/dto/LoginResponse.java | 1 + .../wedle/oauth/dto/LoginStatusResponse.java | 14 +++++++ .../wedle/oauth/service/AuthService.java | 8 +++- 4 files changed, 40 insertions(+), 21 deletions(-) create mode 100644 src/main/java/yerong/wedle/oauth/dto/LoginStatusResponse.java diff --git a/src/main/java/yerong/wedle/oauth/controller/LoginController.java b/src/main/java/yerong/wedle/oauth/controller/LoginController.java index eed9f4a..8f3a96c 100644 --- a/src/main/java/yerong/wedle/oauth/controller/LoginController.java +++ b/src/main/java/yerong/wedle/oauth/controller/LoginController.java @@ -19,6 +19,7 @@ import yerong.wedle.member.dto.MemberRequest; import yerong.wedle.member.exception.MemberNotFoundException; import yerong.wedle.oauth.dto.LoginResponse; +import yerong.wedle.oauth.dto.LoginStatusResponse; import yerong.wedle.oauth.dto.MemberLogoutResponse; import yerong.wedle.oauth.dto.TokenResponse; import yerong.wedle.oauth.exception.InvalidRefreshTokenException; @@ -86,41 +87,40 @@ public ResponseEntity refreshAccessToken(@RequestHeader("RefreshToken") Strin return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("토큰 갱신 중 오류가 발생했습니다."); } } - @Operation( - summary = "로그인 상태 확인", - description = "현재 사용자의 로그인 상태를 확인합니다." - ) - @ApiResponses({ - @ApiResponse(responseCode = "200", description = "로그인 상태 확인 성공"), - @ApiResponse(responseCode = "400", description = "유효하지 않은 토큰"), - @ApiResponse(responseCode = "500", description = "상태 확인 중 서버 오류 발생") - }) @PostMapping("/login/status") - public ResponseEntity checkLoginStatus(HttpServletRequest request) { + public ResponseEntity checkLoginStatus(HttpServletRequest request) { String authorizationHeader = request.getHeader("Authorization"); - String accessToken = authService.extractAccessTokenFromHeader(authorizationHeader); try { - if (jwtBlacklistService.isTokenBlacklisted(accessToken)) { - return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("토큰이 블랙리스트에 등록되어 있습니다."); + return ResponseEntity.status(HttpStatus.UNAUTHORIZED) + .body(new LoginStatusResponse(false, false, "토큰이 유효하지 않습니다. 다시 로그인 해주세요.")); } boolean isLoggedIn = authService.isLoggedIn(); - if (isLoggedIn) { - return ResponseEntity.ok().body("사용자는 로그인 상태입니다."); - } else { - return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("사용자는 로그인 상태가 아닙니다."); + if (!isLoggedIn) { + return ResponseEntity.status(HttpStatus.UNAUTHORIZED) + .body(new LoginStatusResponse(false, false, "사용자는 로그인 상태가 아닙니다.")); } + + boolean hasNickname = authService.hasNickname(); + String message = hasNickname ? "사용자는 완벽하게 회원가입 후 로그인된 상태입니다." : + "닉네임이 없는 상태로 회원가입이 완료되었습니다. 닉네임 입력이 필요합니다."; + + return ResponseEntity.ok(new LoginStatusResponse(true, hasNickname, message)); + } catch (InvalidTokenException e) { - return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("유효하지 않은 토큰입니다."); + return ResponseEntity.status(HttpStatus.BAD_REQUEST) + .body(new LoginStatusResponse(false, false, "유효하지 않은 토큰입니다.")); } catch (Exception e) { - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("상태 확인 중 오류가 발생했습니다."); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) + .body(new LoginStatusResponse(false, false, "상태 확인 중 오류가 발생했습니다.")); } } + @Operation( summary = "로그아웃", description = "현재 유효한 액세스 토큰과 리프레시 토큰을 블랙리스트에 등록하여 로그아웃 처리합니다." diff --git a/src/main/java/yerong/wedle/oauth/dto/LoginResponse.java b/src/main/java/yerong/wedle/oauth/dto/LoginResponse.java index 2c087a5..68b6ec3 100644 --- a/src/main/java/yerong/wedle/oauth/dto/LoginResponse.java +++ b/src/main/java/yerong/wedle/oauth/dto/LoginResponse.java @@ -12,4 +12,5 @@ public class LoginResponse { private String accessToken; private String refreshToken; private boolean isExistingMember; + private boolean hasNickname; } diff --git a/src/main/java/yerong/wedle/oauth/dto/LoginStatusResponse.java b/src/main/java/yerong/wedle/oauth/dto/LoginStatusResponse.java new file mode 100644 index 0000000..e29b54a --- /dev/null +++ b/src/main/java/yerong/wedle/oauth/dto/LoginStatusResponse.java @@ -0,0 +1,14 @@ +package yerong.wedle.oauth.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +@AllArgsConstructor +public class LoginStatusResponse { + private final boolean isLoggedIn; + private final boolean isNicknameSet; + private final String message; +} diff --git a/src/main/java/yerong/wedle/oauth/service/AuthService.java b/src/main/java/yerong/wedle/oauth/service/AuthService.java index 99eb1ae..43cd178 100644 --- a/src/main/java/yerong/wedle/oauth/service/AuthService.java +++ b/src/main/java/yerong/wedle/oauth/service/AuthService.java @@ -73,8 +73,7 @@ public LoginResponse login(MemberRequest memberRequest){ } redisTemplate.opsForValue().set("RT:" + member.getSocialId(), tokenResponse.getRefreshToken(), tokenResponse.getRefreshTokenExpiresIn(), TimeUnit.MILLISECONDS); - - return new LoginResponse(tokenResponse.getAccessToken(), tokenResponse.getRefreshToken(), member.isExistingMember()); + return new LoginResponse(tokenResponse.getAccessToken(), tokenResponse.getRefreshToken(), member.isExistingMember(), hasNickname()); } @Transactional public TokenResponse refreshAccessToken(String refreshTokenValue){ @@ -115,6 +114,11 @@ public boolean isLoggedIn() { return socialId != null && memberRepository.findBySocialId(socialId).isPresent(); } + public boolean hasNickname() { + String socialId = SecurityContextHolder.getContext().getAuthentication().getName(); + Member member = memberRepository.findBySocialId(socialId).orElse(null); + return member.getNickname() != null; + } @Transactional public MemberLogoutResponse logout(String socialId) { Member member = memberRepository.findBySocialId(socialId) From aede68c392c4d365de42dd1bf71d8ee95d139fba Mon Sep 17 00:00:00 2001 From: Sin Ye Rin <91180366+nyeroni@users.noreply.github.com> Date: Sat, 2 Nov 2024 15:30:34 +0900 Subject: [PATCH 37/55] =?UTF-8?q?feat:=20=EB=8B=89=EB=84=A4=EC=9E=84=20?= =?UTF-8?q?=EC=A1=B4=EC=9E=AC=20=EC=97=AC=EB=B6=80=20=EC=B2=B4=ED=81=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yerong/wedle/oauth/controller/LoginController.java | 3 +-- src/main/java/yerong/wedle/oauth/service/AuthService.java | 8 +++++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/main/java/yerong/wedle/oauth/controller/LoginController.java b/src/main/java/yerong/wedle/oauth/controller/LoginController.java index 8f3a96c..6e17c97 100644 --- a/src/main/java/yerong/wedle/oauth/controller/LoginController.java +++ b/src/main/java/yerong/wedle/oauth/controller/LoginController.java @@ -91,7 +91,6 @@ public ResponseEntity refreshAccessToken(@RequestHeader("RefreshToken") Strin public ResponseEntity checkLoginStatus(HttpServletRequest request) { String authorizationHeader = request.getHeader("Authorization"); String accessToken = authService.extractAccessTokenFromHeader(authorizationHeader); - try { if (jwtBlacklistService.isTokenBlacklisted(accessToken)) { return ResponseEntity.status(HttpStatus.UNAUTHORIZED) @@ -105,7 +104,7 @@ public ResponseEntity checkLoginStatus(HttpServletRequest r .body(new LoginStatusResponse(false, false, "사용자는 로그인 상태가 아닙니다.")); } - boolean hasNickname = authService.hasNickname(); + boolean hasNickname = authService.hasNickname(null); String message = hasNickname ? "사용자는 완벽하게 회원가입 후 로그인된 상태입니다." : "닉네임이 없는 상태로 회원가입이 완료되었습니다. 닉네임 입력이 필요합니다."; diff --git a/src/main/java/yerong/wedle/oauth/service/AuthService.java b/src/main/java/yerong/wedle/oauth/service/AuthService.java index 43cd178..ccf8e73 100644 --- a/src/main/java/yerong/wedle/oauth/service/AuthService.java +++ b/src/main/java/yerong/wedle/oauth/service/AuthService.java @@ -73,7 +73,7 @@ public LoginResponse login(MemberRequest memberRequest){ } redisTemplate.opsForValue().set("RT:" + member.getSocialId(), tokenResponse.getRefreshToken(), tokenResponse.getRefreshTokenExpiresIn(), TimeUnit.MILLISECONDS); - return new LoginResponse(tokenResponse.getAccessToken(), tokenResponse.getRefreshToken(), member.isExistingMember(), hasNickname()); + return new LoginResponse(tokenResponse.getAccessToken(), tokenResponse.getRefreshToken(), member.isExistingMember(), hasNickname(member.getSocialId())); } @Transactional public TokenResponse refreshAccessToken(String refreshTokenValue){ @@ -114,8 +114,10 @@ public boolean isLoggedIn() { return socialId != null && memberRepository.findBySocialId(socialId).isPresent(); } - public boolean hasNickname() { - String socialId = SecurityContextHolder.getContext().getAuthentication().getName(); + public boolean hasNickname(String socialId) { + if(socialId == null) { + socialId = SecurityContextHolder.getContext().getAuthentication().getName(); + } Member member = memberRepository.findBySocialId(socialId).orElse(null); return member.getNickname() != null; } From 43af2eaaa50b38d51312755a72257be9e51514e7 Mon Sep 17 00:00:00 2001 From: Sin Ye Rin <91180366+nyeroni@users.noreply.github.com> Date: Mon, 4 Nov 2024 16:47:35 +0900 Subject: [PATCH 38/55] =?UTF-8?q?feat:=20=ED=95=99=EA=B5=90=20=EB=A7=9B?= =?UTF-8?q?=EC=A7=91=20kakao=20api=20=EC=82=AC=EC=9A=A9=ED=95=98=EC=97=AC?= =?UTF-8?q?=20=EB=B0=9B=EC=95=84=EC=98=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/KakaoSearchApiController.java | 25 +++++ .../controller/RestaurantApiController.java | 44 -------- .../category/restaurant/domain/Hashtag.java | 23 ---- .../restaurant/domain/Restaurant.java | 44 -------- .../restaurant/dto/KakaoApiDocument.java | 13 +++ .../restaurant/dto/KakaoApiResponse.java | 9 ++ .../restaurant/dto/RestaurantResponse.java | 16 ++- .../restaurant/dto/TopRestaurantResponse.java | 19 ---- .../repository/RestaurantRepository.java | 11 -- .../service/KakaoSearchApiService.java | 62 +++++++++++ .../restaurant/service/RestaurantService.java | 101 ------------------ .../yerong/wedle/common/config/AppConfig.java | 13 +++ 12 files changed, 128 insertions(+), 252 deletions(-) create mode 100644 src/main/java/yerong/wedle/category/restaurant/controller/KakaoSearchApiController.java delete mode 100644 src/main/java/yerong/wedle/category/restaurant/controller/RestaurantApiController.java delete mode 100644 src/main/java/yerong/wedle/category/restaurant/domain/Hashtag.java delete mode 100644 src/main/java/yerong/wedle/category/restaurant/domain/Restaurant.java create mode 100644 src/main/java/yerong/wedle/category/restaurant/dto/KakaoApiDocument.java create mode 100644 src/main/java/yerong/wedle/category/restaurant/dto/KakaoApiResponse.java delete mode 100644 src/main/java/yerong/wedle/category/restaurant/dto/TopRestaurantResponse.java delete mode 100644 src/main/java/yerong/wedle/category/restaurant/repository/RestaurantRepository.java create mode 100644 src/main/java/yerong/wedle/category/restaurant/service/KakaoSearchApiService.java delete mode 100644 src/main/java/yerong/wedle/category/restaurant/service/RestaurantService.java create mode 100644 src/main/java/yerong/wedle/common/config/AppConfig.java diff --git a/src/main/java/yerong/wedle/category/restaurant/controller/KakaoSearchApiController.java b/src/main/java/yerong/wedle/category/restaurant/controller/KakaoSearchApiController.java new file mode 100644 index 0000000..96e0670 --- /dev/null +++ b/src/main/java/yerong/wedle/category/restaurant/controller/KakaoSearchApiController.java @@ -0,0 +1,25 @@ +package yerong.wedle.category.restaurant.controller; + +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import yerong.wedle.category.restaurant.dto.RestaurantResponse; +import yerong.wedle.category.restaurant.service.KakaoSearchApiService; + +@RequiredArgsConstructor +@RequestMapping("/api/kakao-search") +@RestController +public class KakaoSearchApiController { + + private final KakaoSearchApiService kakaoSearchApiService; + + @GetMapping("/kakao") + public ResponseEntity> kakaoSearchDynamic(@RequestParam String universityName) { + List restaurantResponses = kakaoSearchApiService.searchRestaurant(universityName + " 맛집"); + return ResponseEntity.ok(restaurantResponses); + } +} diff --git a/src/main/java/yerong/wedle/category/restaurant/controller/RestaurantApiController.java b/src/main/java/yerong/wedle/category/restaurant/controller/RestaurantApiController.java deleted file mode 100644 index d501112..0000000 --- a/src/main/java/yerong/wedle/category/restaurant/controller/RestaurantApiController.java +++ /dev/null @@ -1,44 +0,0 @@ -package yerong.wedle.category.restaurant.controller; - -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.tags.Tag; -import lombok.RequiredArgsConstructor; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; -import yerong.wedle.category.restaurant.dto.RestaurantResponse; -import yerong.wedle.category.restaurant.dto.TopRestaurantResponse; -import yerong.wedle.category.restaurant.service.RestaurantService; - -import java.util.List; - -@Tag(name = "Restaurant API", description = "대학교 주변 맛집 정보 관련 API") -@RequiredArgsConstructor -@RestController -@RequestMapping("/api/restaurants") -public class RestaurantApiController { - - private final RestaurantService restaurantService; - - @Operation( - summary = "대학교 주변 식당 조회", - description = "대학교 ID를 이용해 해당 대학교 주변의 식당 목록을 조회합니다." - ) - @GetMapping - public ResponseEntity> getRestaurantsByUniversityId(@RequestParam Long universityId) { - List restaurantResponses = restaurantService.getRestaurantsByUniversityId(universityId); - return ResponseEntity.ok(restaurantResponses); - } - - @Operation( - summary = "학교별 1위 맛집 조회", - description = "모든 학교별 1위 맛집을 조회합니다." - ) - @GetMapping("/top") - public ResponseEntity> getTopRestaurants() { - List topRestaurants = restaurantService.getTopRestaurants(); - return ResponseEntity.ok(topRestaurants); - } -} diff --git a/src/main/java/yerong/wedle/category/restaurant/domain/Hashtag.java b/src/main/java/yerong/wedle/category/restaurant/domain/Hashtag.java deleted file mode 100644 index a827c02..0000000 --- a/src/main/java/yerong/wedle/category/restaurant/domain/Hashtag.java +++ /dev/null @@ -1,23 +0,0 @@ -package yerong.wedle.category.restaurant.domain; - -import jakarta.persistence.*; -import lombok.Getter; -import lombok.NoArgsConstructor; - -@NoArgsConstructor -@Getter -@Entity -public class Hashtag { - - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - @Column(name = "hashtag_id") - private Long hashtagId; - - @Column(nullable = false) - private String name; - - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "restaurant_id") - private Restaurant restaurant; -} \ No newline at end of file diff --git a/src/main/java/yerong/wedle/category/restaurant/domain/Restaurant.java b/src/main/java/yerong/wedle/category/restaurant/domain/Restaurant.java deleted file mode 100644 index 2022f8f..0000000 --- a/src/main/java/yerong/wedle/category/restaurant/domain/Restaurant.java +++ /dev/null @@ -1,44 +0,0 @@ -package yerong.wedle.category.restaurant.domain; - -import jakarta.persistence.*; -import lombok.Getter; -import lombok.NoArgsConstructor; -import yerong.wedle.university.domain.University; - -import java.util.ArrayList; -import java.util.List; - -import static jakarta.persistence.FetchType.LAZY; -import static jakarta.persistence.GenerationType.IDENTITY; -import static lombok.AccessLevel.PROTECTED; - -@NoArgsConstructor(access = PROTECTED) -@Getter -@Entity -public class Restaurant { - - @Id - @GeneratedValue(strategy = IDENTITY) - @Column(name = "restaurant_id") - private Long restaurantId; - - @Column(nullable = false) - private String name; - - @Column(nullable = false) - private String location; - - private String placeUrl; - - @Column(nullable = false) - private int ranking; - - @ManyToOne(fetch = LAZY) - @JoinColumn(name = "university_id") - private University university; - - @OneToMany(mappedBy = "restaurant", cascade = CascadeType.ALL, orphanRemoval = true) - private List hashtags = new ArrayList<>(); - - private String imageUrl; -} diff --git a/src/main/java/yerong/wedle/category/restaurant/dto/KakaoApiDocument.java b/src/main/java/yerong/wedle/category/restaurant/dto/KakaoApiDocument.java new file mode 100644 index 0000000..e56ce46 --- /dev/null +++ b/src/main/java/yerong/wedle/category/restaurant/dto/KakaoApiDocument.java @@ -0,0 +1,13 @@ +package yerong.wedle.category.restaurant.dto; + +import lombok.Data; + +@Data +public class KakaoApiDocument { + private String place_name; + private String road_address_name; + private String address_name; + private String phone; + private String category_name; + private String place_url; +} diff --git a/src/main/java/yerong/wedle/category/restaurant/dto/KakaoApiResponse.java b/src/main/java/yerong/wedle/category/restaurant/dto/KakaoApiResponse.java new file mode 100644 index 0000000..2927237 --- /dev/null +++ b/src/main/java/yerong/wedle/category/restaurant/dto/KakaoApiResponse.java @@ -0,0 +1,9 @@ +package yerong.wedle.category.restaurant.dto; + +import java.util.List; +import lombok.Data; + +@Data +public class KakaoApiResponse { + private List documents; +} \ No newline at end of file diff --git a/src/main/java/yerong/wedle/category/restaurant/dto/RestaurantResponse.java b/src/main/java/yerong/wedle/category/restaurant/dto/RestaurantResponse.java index 97a0e8c..c05e23f 100644 --- a/src/main/java/yerong/wedle/category/restaurant/dto/RestaurantResponse.java +++ b/src/main/java/yerong/wedle/category/restaurant/dto/RestaurantResponse.java @@ -1,24 +1,20 @@ package yerong.wedle.category.restaurant.dto; -import jakarta.persistence.*; import lombok.AllArgsConstructor; +import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; -import yerong.wedle.category.restaurant.domain.Hashtag; -import yerong.wedle.university.domain.University; -import java.util.ArrayList; -import java.util.List; - -import static jakarta.persistence.FetchType.LAZY; @Getter @AllArgsConstructor @NoArgsConstructor +@Builder public class RestaurantResponse { private String name; - private String location; + private String roadAddressName; + private String addressName; + private String phone; + private String categoryName; private String placeUrl; - private List hashtags; - private String imageUrl; } diff --git a/src/main/java/yerong/wedle/category/restaurant/dto/TopRestaurantResponse.java b/src/main/java/yerong/wedle/category/restaurant/dto/TopRestaurantResponse.java deleted file mode 100644 index 28edf16..0000000 --- a/src/main/java/yerong/wedle/category/restaurant/dto/TopRestaurantResponse.java +++ /dev/null @@ -1,19 +0,0 @@ -package yerong.wedle.category.restaurant.dto; - -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; - -import java.util.List; - -@Getter -@AllArgsConstructor -@NoArgsConstructor -public class TopRestaurantResponse { - private String name; - private String location; - private String placeUrl; - private List hashtags; - private String imageUrl; - private String topMessage; -} diff --git a/src/main/java/yerong/wedle/category/restaurant/repository/RestaurantRepository.java b/src/main/java/yerong/wedle/category/restaurant/repository/RestaurantRepository.java deleted file mode 100644 index e26f995..0000000 --- a/src/main/java/yerong/wedle/category/restaurant/repository/RestaurantRepository.java +++ /dev/null @@ -1,11 +0,0 @@ -package yerong.wedle.category.restaurant.repository; - -import org.springframework.data.jpa.repository.JpaRepository; -import yerong.wedle.category.restaurant.domain.Restaurant; -import yerong.wedle.university.domain.University; - -import java.util.List; - -public interface RestaurantRepository extends JpaRepository { - List findByUniversity(University university); -} diff --git a/src/main/java/yerong/wedle/category/restaurant/service/KakaoSearchApiService.java b/src/main/java/yerong/wedle/category/restaurant/service/KakaoSearchApiService.java new file mode 100644 index 0000000..277ed8b --- /dev/null +++ b/src/main/java/yerong/wedle/category/restaurant/service/KakaoSearchApiService.java @@ -0,0 +1,62 @@ +package yerong.wedle.category.restaurant.service; + +import java.util.ArrayList; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import org.springframework.web.client.RestTemplate; +import yerong.wedle.category.restaurant.dto.RestaurantResponse; +import yerong.wedle.category.restaurant.dto.KakaoApiDocument; +import yerong.wedle.category.restaurant.dto.KakaoApiResponse; + +@Service +@RequiredArgsConstructor +public class KakaoSearchApiService { + + @Value("${kakao.client-id}") + private String KAKAO_CLIENT_ID; + + private final RestTemplate restTemplate; + + public List searchRestaurant(String query) { + String url = "https://dapi.kakao.com/v2/local/search/keyword.json?query=" + query + "&size=15"; + + HttpHeaders headers = new HttpHeaders(); + headers.set("Authorization", "KakaoAK " + KAKAO_CLIENT_ID); + + HttpEntity entity = new HttpEntity<>(headers); + ResponseEntity response = restTemplate.exchange(url, HttpMethod.GET, entity, KakaoApiResponse.class); + + List restaurantResponses = new ArrayList<>(); + + if (response.getBody() != null && response.getBody().getDocuments() != null) { + for (KakaoApiDocument document : response.getBody().getDocuments()) { + String fullCategoryName = document.getCategory_name(); + String lastCategory = null; + + if (fullCategoryName != null && !fullCategoryName.isEmpty()) { + String[] categories = fullCategoryName.split(" > "); + if (categories.length > 0) { + lastCategory = categories[categories.length - 1]; // 마지막 부분 + } + } + + restaurantResponses.add(RestaurantResponse.builder() + .name(document.getPlace_name()) + .roadAddressName(document.getRoad_address_name()) + .addressName(document.getAddress_name()) + .categoryName(lastCategory) + .phone(document.getPhone()) + .placeUrl(document.getPlace_url()) + .build()); + } + } + + return restaurantResponses; // 맛집 정보 반환 + } +} diff --git a/src/main/java/yerong/wedle/category/restaurant/service/RestaurantService.java b/src/main/java/yerong/wedle/category/restaurant/service/RestaurantService.java deleted file mode 100644 index 6f5bcf8..0000000 --- a/src/main/java/yerong/wedle/category/restaurant/service/RestaurantService.java +++ /dev/null @@ -1,101 +0,0 @@ -package yerong.wedle.category.restaurant.service; - -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; -import yerong.wedle.category.restaurant.domain.Hashtag; -import yerong.wedle.category.restaurant.domain.Restaurant; -import yerong.wedle.category.restaurant.dto.RestaurantResponse; -import yerong.wedle.category.restaurant.dto.TopRestaurantResponse; -import yerong.wedle.category.restaurant.exception.RestaurantNotFoundException; -import yerong.wedle.category.restaurant.repository.RestaurantRepository; -import yerong.wedle.university.domain.University; -import yerong.wedle.university.exception.UniversityNotFoundException; -import yerong.wedle.university.repository.UniversityRepository; - -import java.util.Comparator; -import java.util.List; -import java.util.stream.Collectors; - -@RequiredArgsConstructor -@Service -public class RestaurantService { - - private final RestaurantRepository restaurantRepository; - private final UniversityRepository universityRepository; - - @Transactional - public List getRestaurantsByUniversityId(Long universityId) { - University university = universityRepository.findById(universityId) - .orElseThrow(UniversityNotFoundException::new); - List restaurants = restaurantRepository.findByUniversity(university) - .stream() - .sorted(Comparator.comparingInt(Restaurant::getRanking)) - .collect(Collectors.toList()); - - return restaurants.stream() - .map(this::convertToDto) - .collect(Collectors.toList()); - } - - private RestaurantResponse convertToDto(Restaurant restaurant) { - List hashtags = restaurant.getHashtags().stream() - .map(Hashtag::getName) - .collect(Collectors.toList()); - - return new RestaurantResponse( - restaurant.getName(), - restaurant.getLocation(), - restaurant.getPlaceUrl(), - hashtags, - restaurant.getImageUrl() - ); - } - - @Transactional - public List getTopRestaurants() { - List universities = universityRepository.findAll(); - - return universities.stream() - .map(this::findTopRestaurantForUniversity) - .filter(topRestaurant -> topRestaurant != null) // null 체크하여 결과에서 제외 - .collect(Collectors.toList()); - } - - private TopRestaurantResponse findTopRestaurantForUniversity(University university) { - List restaurants = restaurantRepository.findByUniversity(university); - - if (restaurants.isEmpty()) { - return null; - } - - // 순위가 1인 식당만 찾기 - Restaurant topRestaurant = restaurants.stream() - .filter(restaurant -> restaurant.getRanking() == 1) - .findFirst() - .orElse(null); // 없으면 null 반환 - - if (topRestaurant == null) { - return null; // 순위가 1인 식당이 없으면 null 반환 - } - - return convertToTopDto(topRestaurant, university.getName()); - } - - private TopRestaurantResponse convertToTopDto(Restaurant restaurant, String universityName) { - List hashtags = restaurant.getHashtags().stream() - .map(Hashtag::getName) - .collect(Collectors.toList()); - - String topMessage = universityName + " 1등 맛집"; - - return new TopRestaurantResponse( - restaurant.getName(), - restaurant.getLocation(), - restaurant.getPlaceUrl(), - hashtags, - restaurant.getImageUrl(), - topMessage - ); - } -} \ No newline at end of file diff --git a/src/main/java/yerong/wedle/common/config/AppConfig.java b/src/main/java/yerong/wedle/common/config/AppConfig.java new file mode 100644 index 0000000..519c8b0 --- /dev/null +++ b/src/main/java/yerong/wedle/common/config/AppConfig.java @@ -0,0 +1,13 @@ +package yerong.wedle.common.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.client.RestTemplate; + +@Configuration +public class AppConfig { + @Bean + public RestTemplate restTemplate() { + return new RestTemplate(); + } +} From 3ad41b9e92374b60aadb0319ce46371cee524951 Mon Sep 17 00:00:00 2001 From: Sin Ye Rin <91180366+nyeroni@users.noreply.github.com> Date: Mon, 4 Nov 2024 16:53:10 +0900 Subject: [PATCH 39/55] =?UTF-8?q?feat:=20swagger=20=EB=AC=B8=EC=84=9C=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...ontroller.java => RestaurantApiController.java} | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) rename src/main/java/yerong/wedle/category/restaurant/controller/{KakaoSearchApiController.java => RestaurantApiController.java} (56%) diff --git a/src/main/java/yerong/wedle/category/restaurant/controller/KakaoSearchApiController.java b/src/main/java/yerong/wedle/category/restaurant/controller/RestaurantApiController.java similarity index 56% rename from src/main/java/yerong/wedle/category/restaurant/controller/KakaoSearchApiController.java rename to src/main/java/yerong/wedle/category/restaurant/controller/RestaurantApiController.java index 96e0670..34c18da 100644 --- a/src/main/java/yerong/wedle/category/restaurant/controller/KakaoSearchApiController.java +++ b/src/main/java/yerong/wedle/category/restaurant/controller/RestaurantApiController.java @@ -1,5 +1,9 @@ package yerong.wedle.category.restaurant.controller; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.tags.Tag; import java.util.List; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; @@ -10,13 +14,21 @@ import yerong.wedle.category.restaurant.dto.RestaurantResponse; import yerong.wedle.category.restaurant.service.KakaoSearchApiService; +@Tag(name = "Restaurant API", description = "맛집 검색 관련 API") @RequiredArgsConstructor @RequestMapping("/api/kakao-search") @RestController -public class KakaoSearchApiController { +public class RestaurantApiController { private final KakaoSearchApiService kakaoSearchApiService; + + @Operation(summary = "Kakao 맛집 검색", description = "주어진 대학 이름을 기준으로 Kakao API를 통해 맛집 정보를 검색합니다.") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "맛집 검색 성공"), + @ApiResponse(responseCode = "400", description = "잘못된 요청"), + @ApiResponse(responseCode = "500", description = "서버 오류") + }) @GetMapping("/kakao") public ResponseEntity> kakaoSearchDynamic(@RequestParam String universityName) { List restaurantResponses = kakaoSearchApiService.searchRestaurant(universityName + " 맛집"); From f42025d5f6c66ec5af7e0830592d15d83a56f490 Mon Sep 17 00:00:00 2001 From: Sin Ye Rin <91180366+nyeroni@users.noreply.github.com> Date: Tue, 5 Nov 2024 13:41:12 +0900 Subject: [PATCH 40/55] =?UTF-8?q?feat:=20=EC=95=8C=EB=A6=BC=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../calendar/controller/CalendarEventApiController.java | 3 --- .../notification/repository/NotificationRepository.java | 2 +- .../wedle/notification/service/NotificationService.java | 5 +++-- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/main/java/yerong/wedle/calendar/controller/CalendarEventApiController.java b/src/main/java/yerong/wedle/calendar/controller/CalendarEventApiController.java index c25a59d..1564d70 100644 --- a/src/main/java/yerong/wedle/calendar/controller/CalendarEventApiController.java +++ b/src/main/java/yerong/wedle/calendar/controller/CalendarEventApiController.java @@ -5,12 +5,9 @@ import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; -import org.springframework.http.HttpStatus; 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.RequestParam; import org.springframework.web.bind.annotation.RestController; import yerong.wedle.calendar.dto.CalendarEventResponse; import yerong.wedle.calendar.service.CalendarEventService; diff --git a/src/main/java/yerong/wedle/notification/repository/NotificationRepository.java b/src/main/java/yerong/wedle/notification/repository/NotificationRepository.java index c3f5384..956bdd8 100644 --- a/src/main/java/yerong/wedle/notification/repository/NotificationRepository.java +++ b/src/main/java/yerong/wedle/notification/repository/NotificationRepository.java @@ -18,6 +18,6 @@ public interface NotificationRepository extends JpaRepository findByMember(Member member); - + List findByMemberAndIsActiveTrue(Member member); Optional findByMemberAndEventAndNotificationDate(Member member, CalendarEvent calendarEvent, @NotNull LocalDate notificationDate); } diff --git a/src/main/java/yerong/wedle/notification/service/NotificationService.java b/src/main/java/yerong/wedle/notification/service/NotificationService.java index 7265ea6..e273d31 100644 --- a/src/main/java/yerong/wedle/notification/service/NotificationService.java +++ b/src/main/java/yerong/wedle/notification/service/NotificationService.java @@ -85,6 +85,7 @@ public void sendNotifications() { FcmUtils.broadCast(registrationTokens, title, body); notification.setActive(false); + notificationRepository.delete(notification); } } } @@ -110,11 +111,11 @@ public List getNotificationsByMember() { String socialId = getCurrentUserId(); Member member = memberRepository.findBySocialId(socialId) .orElseThrow(MemberNotFoundException::new); - - List notifications = notificationRepository.findByMember(member); + List notifications = notificationRepository.findByMemberAndIsActiveTrue(member); return notifications.stream() .map(this::convertToResponse) .toList(); + } private String getCurrentUserId() { String socialId = SecurityContextHolder.getContext().getAuthentication().getName(); From ee3a8ce0b285d7531d455225aec1264f7869bca6 Mon Sep 17 00:00:00 2001 From: Sin Ye Rin <91180366+nyeroni@users.noreply.github.com> Date: Tue, 5 Nov 2024 15:05:04 +0900 Subject: [PATCH 41/55] =?UTF-8?q?feat:=20=EB=A7=9B=EC=A7=91=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20=EC=9C=84=EB=8F=84=20=EA=B2=BD=EB=8F=84=20=EB=B0=98?= =?UTF-8?q?=ED=99=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yerong/wedle/category/restaurant/dto/KakaoApiDocument.java | 2 ++ .../wedle/category/restaurant/dto/RestaurantResponse.java | 2 ++ .../category/restaurant/service/KakaoSearchApiService.java | 2 ++ 3 files changed, 6 insertions(+) diff --git a/src/main/java/yerong/wedle/category/restaurant/dto/KakaoApiDocument.java b/src/main/java/yerong/wedle/category/restaurant/dto/KakaoApiDocument.java index e56ce46..246c979 100644 --- a/src/main/java/yerong/wedle/category/restaurant/dto/KakaoApiDocument.java +++ b/src/main/java/yerong/wedle/category/restaurant/dto/KakaoApiDocument.java @@ -10,4 +10,6 @@ public class KakaoApiDocument { private String phone; private String category_name; private String place_url; + private Double x; + private Double y; } diff --git a/src/main/java/yerong/wedle/category/restaurant/dto/RestaurantResponse.java b/src/main/java/yerong/wedle/category/restaurant/dto/RestaurantResponse.java index c05e23f..70fc8e9 100644 --- a/src/main/java/yerong/wedle/category/restaurant/dto/RestaurantResponse.java +++ b/src/main/java/yerong/wedle/category/restaurant/dto/RestaurantResponse.java @@ -17,4 +17,6 @@ public class RestaurantResponse { private String phone; private String categoryName; private String placeUrl; + private Double x; + private Double y; } diff --git a/src/main/java/yerong/wedle/category/restaurant/service/KakaoSearchApiService.java b/src/main/java/yerong/wedle/category/restaurant/service/KakaoSearchApiService.java index 277ed8b..a84b56d 100644 --- a/src/main/java/yerong/wedle/category/restaurant/service/KakaoSearchApiService.java +++ b/src/main/java/yerong/wedle/category/restaurant/service/KakaoSearchApiService.java @@ -53,6 +53,8 @@ public List searchRestaurant(String query) { .categoryName(lastCategory) .phone(document.getPhone()) .placeUrl(document.getPlace_url()) + .x(document.getX()) + .y(document.getY()) .build()); } } From b8ca7f49dd088e52b672ff9d6720121ee5993202 Mon Sep 17 00:00:00 2001 From: Sin Ye Rin <91180366+nyeroni@users.noreply.github.com> Date: Wed, 6 Nov 2024 15:18:49 +0900 Subject: [PATCH 42/55] =?UTF-8?q?feat:=20=EB=A7=9B=EC=A7=91=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20=EC=84=9C=EB=B8=8C=20=EB=84=A4=EC=9E=84=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/RestaurantApiController.java | 2 +- .../service/KakaoSearchApiService.java | 18 +++++++++++++++--- .../wedle/university/domain/University.java | 3 +++ .../university/dto/UniversityResponse.java | 2 +- .../repository/UniversityRepository.java | 1 + 5 files changed, 21 insertions(+), 5 deletions(-) diff --git a/src/main/java/yerong/wedle/category/restaurant/controller/RestaurantApiController.java b/src/main/java/yerong/wedle/category/restaurant/controller/RestaurantApiController.java index 34c18da..5de03dd 100644 --- a/src/main/java/yerong/wedle/category/restaurant/controller/RestaurantApiController.java +++ b/src/main/java/yerong/wedle/category/restaurant/controller/RestaurantApiController.java @@ -31,7 +31,7 @@ public class RestaurantApiController { }) @GetMapping("/kakao") public ResponseEntity> kakaoSearchDynamic(@RequestParam String universityName) { - List restaurantResponses = kakaoSearchApiService.searchRestaurant(universityName + " 맛집"); + List restaurantResponses = kakaoSearchApiService.searchRestaurant(universityName); return ResponseEntity.ok(restaurantResponses); } } diff --git a/src/main/java/yerong/wedle/category/restaurant/service/KakaoSearchApiService.java b/src/main/java/yerong/wedle/category/restaurant/service/KakaoSearchApiService.java index a84b56d..8624e45 100644 --- a/src/main/java/yerong/wedle/category/restaurant/service/KakaoSearchApiService.java +++ b/src/main/java/yerong/wedle/category/restaurant/service/KakaoSearchApiService.java @@ -13,6 +13,9 @@ import yerong.wedle.category.restaurant.dto.RestaurantResponse; import yerong.wedle.category.restaurant.dto.KakaoApiDocument; import yerong.wedle.category.restaurant.dto.KakaoApiResponse; +import yerong.wedle.university.domain.University; +import yerong.wedle.university.exception.UniversityNotFoundException; +import yerong.wedle.university.repository.UniversityRepository; @Service @RequiredArgsConstructor @@ -22,8 +25,17 @@ public class KakaoSearchApiService { private String KAKAO_CLIENT_ID; private final RestTemplate restTemplate; + private final UniversityRepository universityRepository; - public List searchRestaurant(String query) { + public List searchRestaurant(String universityName) { + University university = universityRepository.findByName(universityName) + .orElseThrow(UniversityNotFoundException::new); + String query; + if (university.getSubName() != null) { + query = university.getSubName() + " 맛집"; + } else { + query = universityName + " 맛집"; + } String url = "https://dapi.kakao.com/v2/local/search/keyword.json?query=" + query + "&size=15"; HttpHeaders headers = new HttpHeaders(); @@ -42,7 +54,7 @@ public List searchRestaurant(String query) { if (fullCategoryName != null && !fullCategoryName.isEmpty()) { String[] categories = fullCategoryName.split(" > "); if (categories.length > 0) { - lastCategory = categories[categories.length - 1]; // 마지막 부분 + lastCategory = categories[categories.length - 1]; } } @@ -59,6 +71,6 @@ public List searchRestaurant(String query) { } } - return restaurantResponses; // 맛집 정보 반환 + return restaurantResponses; } } diff --git a/src/main/java/yerong/wedle/university/domain/University.java b/src/main/java/yerong/wedle/university/domain/University.java index 52d65a2..a465199 100644 --- a/src/main/java/yerong/wedle/university/domain/University.java +++ b/src/main/java/yerong/wedle/university/domain/University.java @@ -20,6 +20,9 @@ public class University { @Column(nullable = false) private String name; + + private String subName; + @Column(nullable = false) private String location; private String type; //국립/사립 diff --git a/src/main/java/yerong/wedle/university/dto/UniversityResponse.java b/src/main/java/yerong/wedle/university/dto/UniversityResponse.java index 591836f..a070a18 100644 --- a/src/main/java/yerong/wedle/university/dto/UniversityResponse.java +++ b/src/main/java/yerong/wedle/university/dto/UniversityResponse.java @@ -15,5 +15,5 @@ public class UniversityResponse { private String fullName; private String logo; private Long starNum; - boolean isStarred; + private boolean isStarred; } diff --git a/src/main/java/yerong/wedle/university/repository/UniversityRepository.java b/src/main/java/yerong/wedle/university/repository/UniversityRepository.java index 5987144..4c9b9fc 100644 --- a/src/main/java/yerong/wedle/university/repository/UniversityRepository.java +++ b/src/main/java/yerong/wedle/university/repository/UniversityRepository.java @@ -9,6 +9,7 @@ public interface UniversityRepository extends JpaRepository { + Optional findByName(String name); List findByNameContainingOrLocationContaining(String name, String location); List findAllByOrderByNameAsc(); From dba92bc6eff0c1efc52a954ed92a44b2d4be32b0 Mon Sep 17 00:00:00 2001 From: Sin Ye Rin <91180366+nyeroni@users.noreply.github.com> Date: Wed, 13 Nov 2024 15:24:28 +0900 Subject: [PATCH 43/55] =?UTF-8?q?feat:=20=ED=95=AB=ED=94=8C=20TOP10=20?= =?UTF-8?q?=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yerong/wedle/category/activity/service/ActivityService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/yerong/wedle/category/activity/service/ActivityService.java b/src/main/java/yerong/wedle/category/activity/service/ActivityService.java index 85f582c..d8de43c 100644 --- a/src/main/java/yerong/wedle/category/activity/service/ActivityService.java +++ b/src/main/java/yerong/wedle/category/activity/service/ActivityService.java @@ -34,7 +34,7 @@ public List getActivitiesByUniversityId(Long universityId) { @Transactional public List getActivitiesByIds() { - List activityIds = Arrays.asList(45L, 51L, 94L, 59L, 48L, 18L, 16L, 11L, 19L, 114L); + List activityIds = Arrays.asList(45L, 50L, 101L, 59L, 138L, 18L, 53L, 13L, 19L, 128L); List activities = activityRepository.findByActivityIdIn(activityIds); From ae1ec1925a5592dc187a450cfe52f6f9ed9e60c3 Mon Sep 17 00:00:00 2001 From: Sin Ye Rin <91180366+nyeroni@users.noreply.github.com> Date: Thu, 14 Nov 2024 15:05:42 +0900 Subject: [PATCH 44/55] =?UTF-8?q?feat:=20=EA=B8=B0=ED=83=80=20=EC=B9=B4?= =?UTF-8?q?=ED=85=8C=EA=B3=A0=EB=A6=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/yerong/wedle/category/expo/domain/ExpoType.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/yerong/wedle/category/expo/domain/ExpoType.java b/src/main/java/yerong/wedle/category/expo/domain/ExpoType.java index 52a5ce2..a5e54cf 100644 --- a/src/main/java/yerong/wedle/category/expo/domain/ExpoType.java +++ b/src/main/java/yerong/wedle/category/expo/domain/ExpoType.java @@ -3,7 +3,8 @@ public enum ExpoType { ADMISSION_SESSION("입시설명회"), COOPERATIVE_ACTIVITIES("연계활동"), - MAJOR_EXPERIENCE("전공체험"); + MAJOR_EXPERIENCE("전공체험"), + OTHER("기타"); private final String displayName; From e0265fb5a857f9ffc3d14fb8087a5156f85bea90 Mon Sep 17 00:00:00 2001 From: Sin Ye Rin <91180366+nyeroni@users.noreply.github.com> Date: Thu, 14 Nov 2024 15:10:01 +0900 Subject: [PATCH 45/55] =?UTF-8?q?feat:=20link=20null=20=ED=97=88=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/yerong/wedle/category/expo/domain/Expo.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/yerong/wedle/category/expo/domain/Expo.java b/src/main/java/yerong/wedle/category/expo/domain/Expo.java index fdbf6db..05554d6 100644 --- a/src/main/java/yerong/wedle/category/expo/domain/Expo.java +++ b/src/main/java/yerong/wedle/category/expo/domain/Expo.java @@ -39,7 +39,6 @@ class Expo { @Column(nullable = false) private String title; - @Column(nullable = false) private String link; private String location; From 3b6f7f1c06ba5721ab022ee095f53e585689f00a Mon Sep 17 00:00:00 2001 From: Sin Ye Rin <91180366+nyeroni@users.noreply.github.com> Date: Thu, 14 Nov 2024 15:26:11 +0900 Subject: [PATCH 46/55] =?UTF-8?q?refactor:=20=EB=8B=89=EB=84=A4=EC=9E=84?= =?UTF-8?q?=20=EC=B2=98=EB=A6=AC=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../wedle/oauth/controller/LoginController.java | 5 ++--- .../yerong/wedle/oauth/service/AuthService.java | 13 +++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/main/java/yerong/wedle/oauth/controller/LoginController.java b/src/main/java/yerong/wedle/oauth/controller/LoginController.java index 6e17c97..af0b014 100644 --- a/src/main/java/yerong/wedle/oauth/controller/LoginController.java +++ b/src/main/java/yerong/wedle/oauth/controller/LoginController.java @@ -104,7 +104,7 @@ public ResponseEntity checkLoginStatus(HttpServletRequest r .body(new LoginStatusResponse(false, false, "사용자는 로그인 상태가 아닙니다.")); } - boolean hasNickname = authService.hasNickname(null); + boolean hasNickname = authService.hasNickname(); String message = hasNickname ? "사용자는 완벽하게 회원가입 후 로그인된 상태입니다." : "닉네임이 없는 상태로 회원가입이 완료되었습니다. 닉네임 입력이 필요합니다."; @@ -155,8 +155,7 @@ public ResponseEntity logout(Principal principal, HttpServletRequest request) return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(Map.of("error", "유효하지 않은 토큰입니다.")); } - String socialId = SecurityContextHolder.getContext().getAuthentication().getName(); - MemberLogoutResponse memberLogoutResponse = authService.logout(socialId); + MemberLogoutResponse memberLogoutResponse = authService.logout(); if (memberLogoutResponse == null) { log.warn("Social Id {}로 회원을 찾을 수 없음", principal.getName()); return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(Map.of("error", "Social Id로 회원을 찾을 수 없습니다.")); diff --git a/src/main/java/yerong/wedle/oauth/service/AuthService.java b/src/main/java/yerong/wedle/oauth/service/AuthService.java index ccf8e73..2756e6d 100644 --- a/src/main/java/yerong/wedle/oauth/service/AuthService.java +++ b/src/main/java/yerong/wedle/oauth/service/AuthService.java @@ -73,7 +73,7 @@ public LoginResponse login(MemberRequest memberRequest){ } redisTemplate.opsForValue().set("RT:" + member.getSocialId(), tokenResponse.getRefreshToken(), tokenResponse.getRefreshTokenExpiresIn(), TimeUnit.MILLISECONDS); - return new LoginResponse(tokenResponse.getAccessToken(), tokenResponse.getRefreshToken(), member.isExistingMember(), hasNickname(member.getSocialId())); + return new LoginResponse(tokenResponse.getAccessToken(), tokenResponse.getRefreshToken(), member.isExistingMember(), hasNickname()); } @Transactional public TokenResponse refreshAccessToken(String refreshTokenValue){ @@ -114,15 +114,16 @@ public boolean isLoggedIn() { return socialId != null && memberRepository.findBySocialId(socialId).isPresent(); } - public boolean hasNickname(String socialId) { - if(socialId == null) { - socialId = SecurityContextHolder.getContext().getAuthentication().getName(); - } + public boolean hasNickname() { + String socialId = SecurityContextHolder.getContext().getAuthentication().getName(); + Member member = memberRepository.findBySocialId(socialId).orElse(null); return member.getNickname() != null; } @Transactional - public MemberLogoutResponse logout(String socialId) { + public MemberLogoutResponse logout() { + String socialId = SecurityContextHolder.getContext().getAuthentication().getName(); + Member member = memberRepository.findBySocialId(socialId) .orElseThrow(MemberNotFoundException::new); RefreshToken refreshToken = refreshTokenRepository.findByMemberId(member.getMemberId()) From 5fdce6319196683228eecb55998f913f5bde25d7 Mon Sep 17 00:00:00 2001 From: Sin Ye Rin <91180366+nyeroni@users.noreply.github.com> Date: Thu, 14 Nov 2024 16:03:50 +0900 Subject: [PATCH 47/55] =?UTF-8?q?refactor:=20=EB=8B=89=EB=84=A4=EC=9E=84?= =?UTF-8?q?=20=EC=B2=98=EB=A6=AC=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/yerong/wedle/oauth/controller/LoginController.java | 2 +- src/main/java/yerong/wedle/oauth/service/AuthService.java | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/yerong/wedle/oauth/controller/LoginController.java b/src/main/java/yerong/wedle/oauth/controller/LoginController.java index af0b014..2fc602f 100644 --- a/src/main/java/yerong/wedle/oauth/controller/LoginController.java +++ b/src/main/java/yerong/wedle/oauth/controller/LoginController.java @@ -104,7 +104,7 @@ public ResponseEntity checkLoginStatus(HttpServletRequest r .body(new LoginStatusResponse(false, false, "사용자는 로그인 상태가 아닙니다.")); } - boolean hasNickname = authService.hasNickname(); + boolean hasNickname = authService.hasNickname(null); String message = hasNickname ? "사용자는 완벽하게 회원가입 후 로그인된 상태입니다." : "닉네임이 없는 상태로 회원가입이 완료되었습니다. 닉네임 입력이 필요합니다."; diff --git a/src/main/java/yerong/wedle/oauth/service/AuthService.java b/src/main/java/yerong/wedle/oauth/service/AuthService.java index 2756e6d..c0a61c8 100644 --- a/src/main/java/yerong/wedle/oauth/service/AuthService.java +++ b/src/main/java/yerong/wedle/oauth/service/AuthService.java @@ -73,7 +73,7 @@ public LoginResponse login(MemberRequest memberRequest){ } redisTemplate.opsForValue().set("RT:" + member.getSocialId(), tokenResponse.getRefreshToken(), tokenResponse.getRefreshTokenExpiresIn(), TimeUnit.MILLISECONDS); - return new LoginResponse(tokenResponse.getAccessToken(), tokenResponse.getRefreshToken(), member.isExistingMember(), hasNickname()); + return new LoginResponse(tokenResponse.getAccessToken(), tokenResponse.getRefreshToken(), member.isExistingMember(), hasNickname(member.getSocialId())); } @Transactional public TokenResponse refreshAccessToken(String refreshTokenValue){ @@ -114,8 +114,8 @@ public boolean isLoggedIn() { return socialId != null && memberRepository.findBySocialId(socialId).isPresent(); } - public boolean hasNickname() { - String socialId = SecurityContextHolder.getContext().getAuthentication().getName(); + public boolean hasNickname(String socialId) { + if (socialId == null) socialId = SecurityContextHolder.getContext().getAuthentication().getName(); Member member = memberRepository.findBySocialId(socialId).orElse(null); return member.getNickname() != null; From 07b3e48d949d7299806ac3d29575c5c84f5bd35d Mon Sep 17 00:00:00 2001 From: Sin Ye Rin <91180366+nyeroni@users.noreply.github.com> Date: Thu, 14 Nov 2024 16:04:10 +0900 Subject: [PATCH 48/55] =?UTF-8?q?refactor:=20=EB=8B=89=EB=84=A4=EC=9E=84?= =?UTF-8?q?=20=EC=B2=98=EB=A6=AC=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/yerong/wedle/oauth/service/AuthService.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/yerong/wedle/oauth/service/AuthService.java b/src/main/java/yerong/wedle/oauth/service/AuthService.java index c0a61c8..9e7d29c 100644 --- a/src/main/java/yerong/wedle/oauth/service/AuthService.java +++ b/src/main/java/yerong/wedle/oauth/service/AuthService.java @@ -114,6 +114,7 @@ public boolean isLoggedIn() { return socialId != null && memberRepository.findBySocialId(socialId).isPresent(); } + @Transactional public boolean hasNickname(String socialId) { if (socialId == null) socialId = SecurityContextHolder.getContext().getAuthentication().getName(); From 862f9447eb5d68586974b5c34a8614f5e81a454a Mon Sep 17 00:00:00 2001 From: Sin Ye Rin <91180366+nyeroni@users.noreply.github.com> Date: Thu, 14 Nov 2024 16:44:32 +0900 Subject: [PATCH 49/55] =?UTF-8?q?refactor:=20=EB=93=B1=EB=A1=9D=EA=B8=88?= =?UTF-8?q?=20=EB=B0=98=ED=99=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../university/dto/UniversityAllResponse.java | 6 +- .../university/service/UniversityService.java | 56 +++++++++++-------- 2 files changed, 34 insertions(+), 28 deletions(-) diff --git a/src/main/java/yerong/wedle/university/dto/UniversityAllResponse.java b/src/main/java/yerong/wedle/university/dto/UniversityAllResponse.java index 6c5a262..1b43d8f 100644 --- a/src/main/java/yerong/wedle/university/dto/UniversityAllResponse.java +++ b/src/main/java/yerong/wedle/university/dto/UniversityAllResponse.java @@ -1,16 +1,14 @@ package yerong.wedle.university.dto; +import java.util.List; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; import yerong.wedle.competitionRate.dto.CompetitionRateResponse; import yerong.wedle.department.dto.DepartmentResponse; import yerong.wedle.employmentRate.dto.EmploymentRateResponse; -import yerong.wedle.tuitionfee.dto.TuitionFeeResponse; import yerong.wedle.tuitionfee.dto.YearTuitionFeeResponse; -import java.util.List; - @AllArgsConstructor @NoArgsConstructor @Getter @@ -24,7 +22,7 @@ public class UniversityAllResponse { private String website; private String admissionSite; private Long starNum; - private YearTuitionFeeResponse tuitionFeeResponse; + private List tuitionFeeResponse; private List departmentResponses; private List competitionRateResponses; private List employmentRateResponses; diff --git a/src/main/java/yerong/wedle/university/service/UniversityService.java b/src/main/java/yerong/wedle/university/service/UniversityService.java index ef3df6c..f48b118 100644 --- a/src/main/java/yerong/wedle/university/service/UniversityService.java +++ b/src/main/java/yerong/wedle/university/service/UniversityService.java @@ -1,5 +1,9 @@ package yerong.wedle.university.service; +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Service; @@ -14,7 +18,6 @@ import yerong.wedle.employmentRate.domain.EmploymentRate; import yerong.wedle.employmentRate.dto.EmploymentRateResponse; import yerong.wedle.employmentRate.repository.EmploymentRateRepository; -import yerong.wedle.employmentRate.service.EmploymentRateService; import yerong.wedle.member.domain.Member; import yerong.wedle.member.exception.MemberNotFoundException; import yerong.wedle.member.repository.MemberRepository; @@ -29,10 +32,6 @@ import yerong.wedle.university.exception.UniversityNotFoundException; import yerong.wedle.university.repository.UniversityRepository; -import java.util.Comparator; -import java.util.List; -import java.util.stream.Collectors; - @RequiredArgsConstructor @Service public class UniversityService { @@ -60,14 +59,18 @@ public List searchUniversitiesSummary(String keyward) { @Transactional public UniversityResponse getUniversitySummaryById(Long universityId) { - University university = universityRepository.findById(universityId).orElseThrow(UniversityNotFoundException::new); + University university = universityRepository.findById(universityId) + .orElseThrow(UniversityNotFoundException::new); return convertToSummaryDto(university); } + @Transactional public UniversityAllResponse getUniversityDetailsById(Long universityId) { - University university = universityRepository.findById(universityId).orElseThrow(UniversityNotFoundException::new); + University university = universityRepository.findById(universityId) + .orElseThrow(UniversityNotFoundException::new); return convertToDetailDto(university); } + @Transactional public List getAllUniversitiesSummary() { List universities = universityRepository.findAllByOrderByNameAsc(); @@ -83,6 +86,7 @@ public List getAllUniversitiesDetails() { .map(this::convertToDetailDto) .collect(Collectors.toList()); } + private UniversityResponse convertToSummaryDto(University university) { Long starNum = starRepository.countByUniversityId(university.getUniversityId()); String socialId = getCurrentUserId(); @@ -100,6 +104,7 @@ private UniversityResponse convertToSummaryDto(University university) { isStarred ); } + private UniversityAllResponse convertToDetailDto(University university) { Long starNum = starRepository.countByUniversityId(university.getUniversityId()); @@ -108,14 +113,15 @@ private UniversityAllResponse convertToDetailDto(University university) { List employmentRates = employmentRateRepository.findByUniversity(university); List departments = departmentRepository.findByUniversity(university); - YearTuitionFeeResponse tuitionFeeResponses = getLatestTuitionFeeResponse(tuitionFees); + List allTuitionFeeResponses = getAllTuitionFeeResponses(tuitionFees); List competitionRateResponses = competitionRates.stream() - .map(rate -> new CompetitionRateResponse(rate.getEarlyAdmissionRate(), rate.getRegularAdmissionRate(), rate.getAverageAdmissionRate(), rate.getCompetitionYear())) + .map(rate -> new CompetitionRateResponse(rate.getEarlyAdmissionRate(), rate.getRegularAdmissionRate(), + rate.getAverageAdmissionRate(), rate.getCompetitionYear())) .collect(Collectors.toList()); List employmentRateResponses = employmentRates.stream() - .map(rate -> new EmploymentRateResponse(rate.getEmploymentYear(),rate.getEmploymentRate())) + .map(rate -> new EmploymentRateResponse(rate.getEmploymentYear(), rate.getEmploymentRate())) .collect(Collectors.toList()); List departmentResponses = departments.stream() @@ -135,26 +141,28 @@ private UniversityAllResponse convertToDetailDto(University university) { university.getWebsite(), university.getAdmissionSite(), starNum, - tuitionFeeResponses, + allTuitionFeeResponses, departmentResponses, competitionRateResponses, employmentRateResponses ); } - private YearTuitionFeeResponse getLatestTuitionFeeResponse(List tuitionFees) { - TuitionFee latestTuitionFee = tuitionFees.stream() - .max(Comparator.comparing(TuitionFee::getTuitionFeeYear)) - .orElse(null); - - if (latestTuitionFee != null) { - return new YearTuitionFeeResponse( - latestTuitionFee.getTuitionFeeYear(), - List.of(new TuitionFeeResponse(latestTuitionFee.getTuitionFeeType().getDisplayName(), latestTuitionFee.getFeeAmount())) // DTO 리스트 생성 - ); - } else { - return new YearTuitionFeeResponse(); - } + private List getAllTuitionFeeResponses(List tuitionFees) { + Map> groupedByYear = tuitionFees.stream() + .collect(Collectors.groupingBy(TuitionFee::getTuitionFeeYear)); + + return groupedByYear.entrySet().stream() + .map(entry -> { + String year = entry.getKey(); + List tuitionFeeResponses = entry.getValue().stream() + .map(tuitionFee -> new TuitionFeeResponse( + tuitionFee.getTuitionFeeType().name(), + tuitionFee.getFeeAmount())) + .collect(Collectors.toList()); + return new YearTuitionFeeResponse(year, tuitionFeeResponses); + }) + .collect(Collectors.toList()); } private DepartmentResponse convertToDepartmentDto(DepartmentType departmentType, List departments) { From 62281c67816c101c5134116b158779cc5d84563e Mon Sep 17 00:00:00 2001 From: Sin Ye Rin <91180366+nyeroni@users.noreply.github.com> Date: Thu, 14 Nov 2024 16:59:24 +0900 Subject: [PATCH 50/55] =?UTF-8?q?refactor:=20=EB=93=B1=EB=A1=9D=EA=B8=88?= =?UTF-8?q?=20=ED=83=80=EC=9E=85=EB=AA=85=20=ED=95=9C=EA=B8=80=EB=A1=9C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/yerong/wedle/university/service/UniversityService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/yerong/wedle/university/service/UniversityService.java b/src/main/java/yerong/wedle/university/service/UniversityService.java index f48b118..cc180df 100644 --- a/src/main/java/yerong/wedle/university/service/UniversityService.java +++ b/src/main/java/yerong/wedle/university/service/UniversityService.java @@ -157,7 +157,7 @@ private List getAllTuitionFeeResponses(List String year = entry.getKey(); List tuitionFeeResponses = entry.getValue().stream() .map(tuitionFee -> new TuitionFeeResponse( - tuitionFee.getTuitionFeeType().name(), + tuitionFee.getTuitionFeeType().getDisplayName(), tuitionFee.getFeeAmount())) .collect(Collectors.toList()); return new YearTuitionFeeResponse(year, tuitionFeeResponses); From dbc844ec25f51128a2ea4c3fc9ff9885e15c1dcf Mon Sep 17 00:00:00 2001 From: Sin Ye Rin <91180366+nyeroni@users.noreply.github.com> Date: Thu, 14 Nov 2024 17:58:48 +0900 Subject: [PATCH 51/55] =?UTF-8?q?refactor:=20=EC=95=8C=EB=A6=BC=20?= =?UTF-8?q?=EC=84=A4=EC=A0=95=20=EB=B0=98=ED=99=98=20=EB=A6=AC=ED=8C=A9?= =?UTF-8?q?=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/CalendarEventService.java | 80 +++++++++++-------- .../repository/NotificationRepository.java | 9 ++- 2 files changed, 50 insertions(+), 39 deletions(-) diff --git a/src/main/java/yerong/wedle/calendar/service/CalendarEventService.java b/src/main/java/yerong/wedle/calendar/service/CalendarEventService.java index ee4d9e1..a84170e 100644 --- a/src/main/java/yerong/wedle/calendar/service/CalendarEventService.java +++ b/src/main/java/yerong/wedle/calendar/service/CalendarEventService.java @@ -1,25 +1,21 @@ package yerong.wedle.calendar.service; +import java.time.LocalDate; +import java.util.ArrayList; +import java.util.List; import lombok.RequiredArgsConstructor; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import yerong.wedle.calendar.domain.CalendarEvent; import yerong.wedle.calendar.dto.CalendarEventResponse; -import yerong.wedle.calendar.exception.CalendarEventNotFoundException; import yerong.wedle.calendar.repository.CalendarEventRepository; import yerong.wedle.member.domain.Member; import yerong.wedle.member.exception.MemberNotFoundException; import yerong.wedle.member.repository.MemberRepository; import yerong.wedle.notification.domain.Notification; -import yerong.wedle.notification.exception.NotificationNotFoundException; import yerong.wedle.notification.repository.NotificationRepository; -import java.time.LocalDate; -import java.util.List; -import java.util.Optional; -import java.util.stream.Collectors; - @Service @RequiredArgsConstructor @Transactional(readOnly = true) @@ -31,45 +27,59 @@ public class CalendarEventService { public List getAll() { List calendarEvents = calendarEventRepository.findAll(); - - return calendarEvents.stream() - .flatMap(event -> convertToDto(event).stream()) - .collect(Collectors.toList()); + return convertToDto(calendarEvents); } - public List convertToDto(CalendarEvent calendarEvent) { - LocalDate startDate = calendarEvent.getStartDate(); - LocalDate endDate = calendarEvent.getEndDate(); - + public List convertToDto(List calendarEvents) { + List calendarEventResponses = new ArrayList<>(); String socialId = getCurrentUserId(); Member member = memberRepository.findBySocialId(socialId) .orElseThrow(MemberNotFoundException::new); + for (CalendarEvent calendarEvent : calendarEvents) { + LocalDate startDate = calendarEvent.getStartDate(); + LocalDate endDate = calendarEvent.getEndDate(); - Notification notification = notificationRepository.findByEventAndMember(calendarEvent, member).orElse(null); - Long notificationId = notification != null ? notification.getNotificationId() : null; - - if (endDate == null) { - return List.of(new CalendarEventResponse( - calendarEvent.getId(), - calendarEvent.getTitle(), - startDate, - calendarEvent.getCalendarEventType().getDisplayName(), - notification != null && notification.isActive() && notification.getNotificationDate().equals(startDate), - notificationId - )); - } - - return startDate.datesUntil(endDate.plusDays(1)) - .map(date -> new CalendarEventResponse( + if (endDate == null) { + Notification notification = notificationRepository.findByMemberAndEventAndNotificationDate(member, + calendarEvent, startDate).orElse(null); + Long notificationId = notification != null ? notification.getNotificationId() : null; + calendarEventResponses.add(new CalendarEventResponse( calendarEvent.getId(), calendarEvent.getTitle(), - date, + startDate, calendarEvent.getCalendarEventType().getDisplayName(), - notification != null && notification.isActive() && notification.getNotificationDate().equals(date), + notification != null && notification.isActive() && notification.getNotificationDate() + .equals(startDate), notificationId - )) - .collect(Collectors.toList()); + )); + } else { + System.out.println("=============="); + System.out.println("success"); + System.out.println("=============="); + + startDate.datesUntil(endDate.plusDays(1)) + .forEach(date -> { + Notification notification = notificationRepository.findByMemberAndEventAndNotificationDate( + member, calendarEvent, date).orElse(null); + Long notificationId = notification != null ? notification.getNotificationId() : null; + System.out.println("notificationId: " + notificationId); + boolean isActive = notification != null && notification.isActive() + && notification.getNotificationDate().equals(date); + + calendarEventResponses.add(new CalendarEventResponse( + calendarEvent.getId(), + calendarEvent.getTitle(), + date, + calendarEvent.getCalendarEventType().getDisplayName(), + isActive, + notificationId + )); + }); + } + } + return calendarEventResponses; } + private String getCurrentUserId() { String socialId = SecurityContextHolder.getContext().getAuthentication().getName(); diff --git a/src/main/java/yerong/wedle/notification/repository/NotificationRepository.java b/src/main/java/yerong/wedle/notification/repository/NotificationRepository.java index 956bdd8..54275fa 100644 --- a/src/main/java/yerong/wedle/notification/repository/NotificationRepository.java +++ b/src/main/java/yerong/wedle/notification/repository/NotificationRepository.java @@ -1,10 +1,9 @@ package yerong.wedle.notification.repository; +import jakarta.validation.constraints.NotNull; import java.time.LocalDate; import java.util.List; import java.util.Optional; - -import jakarta.validation.constraints.NotNull; import org.springframework.data.jpa.repository.JpaRepository; import yerong.wedle.calendar.domain.CalendarEvent; import yerong.wedle.member.domain.Member; @@ -14,10 +13,12 @@ public interface NotificationRepository extends JpaRepository findByNotificationDate(LocalDate notification); - Optional findByEventAndMember(CalendarEvent calendarEvent, Member member); + List findByEventAndMember(CalendarEvent calendarEvent, Member member); List findByMember(Member member); List findByMemberAndIsActiveTrue(Member member); - Optional findByMemberAndEventAndNotificationDate(Member member, CalendarEvent calendarEvent, @NotNull LocalDate notificationDate); + + Optional findByMemberAndEventAndNotificationDate(Member member, CalendarEvent calendarEvent, + @NotNull LocalDate notificationDate); } From 1872e312d8c70f3ce8a8ce94955d4f0d00ec406e Mon Sep 17 00:00:00 2001 From: Sin Ye Rin <91180366+nyeroni@users.noreply.github.com> Date: Thu, 14 Nov 2024 18:03:23 +0900 Subject: [PATCH 52/55] =?UTF-8?q?refactor:=20=EC=95=8C=EB=A6=BC=20?= =?UTF-8?q?=EC=84=A4=EC=A0=95=20=EB=B0=98=ED=99=98=20=EB=A6=AC=ED=8C=A9?= =?UTF-8?q?=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yerong/wedle/calendar/service/CalendarEventService.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/main/java/yerong/wedle/calendar/service/CalendarEventService.java b/src/main/java/yerong/wedle/calendar/service/CalendarEventService.java index a84170e..0306dfa 100644 --- a/src/main/java/yerong/wedle/calendar/service/CalendarEventService.java +++ b/src/main/java/yerong/wedle/calendar/service/CalendarEventService.java @@ -53,16 +53,11 @@ public List convertToDto(List calendarEven notificationId )); } else { - System.out.println("=============="); - System.out.println("success"); - System.out.println("=============="); - startDate.datesUntil(endDate.plusDays(1)) .forEach(date -> { Notification notification = notificationRepository.findByMemberAndEventAndNotificationDate( member, calendarEvent, date).orElse(null); Long notificationId = notification != null ? notification.getNotificationId() : null; - System.out.println("notificationId: " + notificationId); boolean isActive = notification != null && notification.isActive() && notification.getNotificationDate().equals(date); From eaf88af15dc7d98d91ef0c9fe9aac68a67f4a33f Mon Sep 17 00:00:00 2001 From: Sin Ye Rin <91180366+nyeroni@users.noreply.github.com> Date: Fri, 15 Nov 2024 16:36:31 +0900 Subject: [PATCH 53/55] =?UTF-8?q?refactor:=20=EC=A0=90=EC=88=98=20?= =?UTF-8?q?=EB=B2=94=EC=9C=84=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../category/questionnaire/service/MatchingService.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/main/java/yerong/wedle/category/questionnaire/service/MatchingService.java b/src/main/java/yerong/wedle/category/questionnaire/service/MatchingService.java index 4be2dc3..61b3724 100644 --- a/src/main/java/yerong/wedle/category/questionnaire/service/MatchingService.java +++ b/src/main/java/yerong/wedle/category/questionnaire/service/MatchingService.java @@ -28,6 +28,7 @@ public List getAllByCategory() { .map(this::convertQuestionnaireDto) .collect(Collectors.toList()); } + private QuestionnaireResponse convertQuestionnaireDto(QuestionnaireCategory category) { List questionnaires = questionnaireRepository.findAllByQuestionnaireCategory(category); @@ -52,13 +53,13 @@ public MatchingResultResponse getMatchingResult(int score) { private Optional findResultByScore(int score) { if (score >= 5 && score <= 30) { return matchingResultRepository.findByMatchingResultType(MatchingResultType.PRACTICAL_APPLICATION_TYPE); - } else if (score >= 31 && score <= 50) { + } else if (score >= 31 && score <= 56) { return matchingResultRepository.findByMatchingResultType(MatchingResultType.BALANCED_TYPE); - } else if (score >= 51 && score <= 70) { + } else if (score >= 57 && score <= 82) { return matchingResultRepository.findByMatchingResultType(MatchingResultType.ANALYTICAL_TYPE); - } else if (score >= 71 && score <= 90) { + } else if (score >= 83 && score <= 105) { return matchingResultRepository.findByMatchingResultType(MatchingResultType.CREATIVE_INNOVATOR_TYPE); - } else if (score >= 91 && score <= 100) { + } else if (score >= 106 && score <= 125) { return matchingResultRepository.findByMatchingResultType(MatchingResultType.PROFESSIONAL_RESEARCH_TYPE); } return Optional.empty(); From 77c02250e8eab78f108663d1be244a7b0df8b532 Mon Sep 17 00:00:00 2001 From: Sin Ye Rin <91180366+nyeroni@users.noreply.github.com> Date: Thu, 21 Nov 2024 01:21:45 +0900 Subject: [PATCH 54/55] =?UTF-8?q?feat:=20=EC=B6=95=EC=A0=9C=20=EC=A0=95?= =?UTF-8?q?=EB=B3=B4=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../event/controller/ArtistController.java | 31 +++++++++ .../event/controller/EventApiController.java | 32 --------- .../controller/FestivalApiController.java | 35 ++++++++++ .../wedle/category/event/domain/Artist.java | 35 ++++++++++ .../wedle/category/event/domain/Event.java | 47 ------------- .../category/event/domain/EventDetails.java | 26 ------- .../category/event/domain/EventImage.java | 26 ------- .../category/event/domain/EventType.java | 18 ----- .../wedle/category/event/domain/Festival.java | 48 +++++++++++++ .../category/event/domain/FestivalArtist.java | 35 ++++++++++ ...ImageResponse.java => ArtistResponse.java} | 13 ++-- .../event/dto/ArtistTop10Response.java | 16 +++++ .../category/event/dto/EventResponse.java | 22 ------ .../category/event/dto/FestivalResponse.java | 16 +++++ .../dto/FestivalWithArtistsResponse.java | 14 ++++ .../event/dto/UniversityFestivalResponse.java | 15 ++++ .../event/repository/ArtistRepository.java | 15 ++++ .../repository/EventImageRepository.java | 7 -- .../event/repository/EventRepository.java | 11 --- .../event/repository/FestivalRepository.java | 10 +++ .../category/event/service/ArtistService.java | 34 ++++++++++ .../category/event/service/EventService.java | 47 ------------- .../event/service/FestivalService.java | 68 +++++++++++++++++++ .../wedle/university/domain/University.java | 18 +++-- 24 files changed, 392 insertions(+), 247 deletions(-) create mode 100644 src/main/java/yerong/wedle/category/event/controller/ArtistController.java delete mode 100644 src/main/java/yerong/wedle/category/event/controller/EventApiController.java create mode 100644 src/main/java/yerong/wedle/category/event/controller/FestivalApiController.java create mode 100644 src/main/java/yerong/wedle/category/event/domain/Artist.java delete mode 100644 src/main/java/yerong/wedle/category/event/domain/Event.java delete mode 100644 src/main/java/yerong/wedle/category/event/domain/EventDetails.java delete mode 100644 src/main/java/yerong/wedle/category/event/domain/EventImage.java delete mode 100644 src/main/java/yerong/wedle/category/event/domain/EventType.java create mode 100644 src/main/java/yerong/wedle/category/event/domain/Festival.java create mode 100644 src/main/java/yerong/wedle/category/event/domain/FestivalArtist.java rename src/main/java/yerong/wedle/category/event/dto/{EventImageResponse.java => ArtistResponse.java} (52%) create mode 100644 src/main/java/yerong/wedle/category/event/dto/ArtistTop10Response.java delete mode 100644 src/main/java/yerong/wedle/category/event/dto/EventResponse.java create mode 100644 src/main/java/yerong/wedle/category/event/dto/FestivalResponse.java create mode 100644 src/main/java/yerong/wedle/category/event/dto/FestivalWithArtistsResponse.java create mode 100644 src/main/java/yerong/wedle/category/event/dto/UniversityFestivalResponse.java create mode 100644 src/main/java/yerong/wedle/category/event/repository/ArtistRepository.java delete mode 100644 src/main/java/yerong/wedle/category/event/repository/EventImageRepository.java delete mode 100644 src/main/java/yerong/wedle/category/event/repository/EventRepository.java create mode 100644 src/main/java/yerong/wedle/category/event/repository/FestivalRepository.java create mode 100644 src/main/java/yerong/wedle/category/event/service/ArtistService.java delete mode 100644 src/main/java/yerong/wedle/category/event/service/EventService.java create mode 100644 src/main/java/yerong/wedle/category/event/service/FestivalService.java diff --git a/src/main/java/yerong/wedle/category/event/controller/ArtistController.java b/src/main/java/yerong/wedle/category/event/controller/ArtistController.java new file mode 100644 index 0000000..d4c1cb1 --- /dev/null +++ b/src/main/java/yerong/wedle/category/event/controller/ArtistController.java @@ -0,0 +1,31 @@ +package yerong.wedle.category.event.controller; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import yerong.wedle.category.event.dto.ArtistTop10Response; +import yerong.wedle.category.event.service.ArtistService; + +@Tag(name = "Artist API", description = "아티스트 정보 API") +@RequiredArgsConstructor +@RestController +@RequestMapping("/api/artists") +public class ArtistController { + + private final ArtistService artistService; + + @Operation( + summary = "가장 많이 방문한 아티스트 조회", + description = "대학교에서 가장 많이 방문한 아티스트 TOP 10을 조회합니다." + ) + @GetMapping("/top") + public ResponseEntity> getTopArtists() { + List topArtists = artistService.getTopArtists(); + return ResponseEntity.ok(topArtists); + } +} diff --git a/src/main/java/yerong/wedle/category/event/controller/EventApiController.java b/src/main/java/yerong/wedle/category/event/controller/EventApiController.java deleted file mode 100644 index 7b1dc71..0000000 --- a/src/main/java/yerong/wedle/category/event/controller/EventApiController.java +++ /dev/null @@ -1,32 +0,0 @@ -package yerong.wedle.category.event.controller; - -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.tags.Tag; -import lombok.RequiredArgsConstructor; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; -import yerong.wedle.category.event.dto.EventResponse; -import yerong.wedle.category.event.service.EventService; - -import java.util.List; - -@Tag(name = "Event API", description = "대학교 관련 이벤트 정보 API") -@RequiredArgsConstructor -@RestController -@RequestMapping("/api/events") -public class EventApiController { - private final EventService eventService; - - @Operation( - summary = "대학교 이벤트 조회", - description = "대학교 ID를 이용해 해당 대학교에서 진행된 이벤트 목록을 조회합니다." - ) - @GetMapping - public ResponseEntity> getEventsByUniversityId(@RequestParam Long universityId) { - List events = eventService.getEventsByUniversityId(universityId); - return ResponseEntity.ok(events); - } -} \ No newline at end of file diff --git a/src/main/java/yerong/wedle/category/event/controller/FestivalApiController.java b/src/main/java/yerong/wedle/category/event/controller/FestivalApiController.java new file mode 100644 index 0000000..af4aa7e --- /dev/null +++ b/src/main/java/yerong/wedle/category/event/controller/FestivalApiController.java @@ -0,0 +1,35 @@ +package yerong.wedle.category.event.controller; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import yerong.wedle.category.event.dto.UniversityFestivalResponse; +import yerong.wedle.category.event.service.FestivalService; + +@Tag(name = "Festival API", description = "대학교 관련 축제 정보 API") +@RequiredArgsConstructor +@RestController +@RequestMapping("/api/festivals") +public class FestivalApiController { + private final FestivalService eventService; + + @Operation( + summary = "대학교 이벤트 조회", + description = "대학교 ID를 이용해 해당 대학교에서 진행된 이벤트 목록을 조회합니다.", + responses = { + @ApiResponse(responseCode = "200", description = "성공적으로 축제 목록 조회"), + @ApiResponse(responseCode = "404", description = "해당 대학교를 찾을 수 없음") + } + ) + @GetMapping + public ResponseEntity getEventsByUniversityId(@RequestParam Long universityId) { + UniversityFestivalResponse eventsByUniversityId = eventService.getFestivalsByUniversityId(universityId); + return ResponseEntity.ok(eventsByUniversityId); + } +} \ No newline at end of file diff --git a/src/main/java/yerong/wedle/category/event/domain/Artist.java b/src/main/java/yerong/wedle/category/event/domain/Artist.java new file mode 100644 index 0000000..5339df9 --- /dev/null +++ b/src/main/java/yerong/wedle/category/event/domain/Artist.java @@ -0,0 +1,35 @@ +package yerong.wedle.category.event.domain; + +import static lombok.AccessLevel.PROTECTED; + +import jakarta.persistence.CascadeType; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.OneToMany; +import java.util.Set; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor(access = PROTECTED) +@Entity +public class Artist { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "artist_id") + private Long artistId; + + @Column(nullable = false) + private String name; + @Column(nullable = false) + private String subname; + + private String image = ""; + + @OneToMany(mappedBy = "artist", cascade = CascadeType.ALL, orphanRemoval = true) + private Set eventArtists; +} \ No newline at end of file diff --git a/src/main/java/yerong/wedle/category/event/domain/Event.java b/src/main/java/yerong/wedle/category/event/domain/Event.java deleted file mode 100644 index b2a668b..0000000 --- a/src/main/java/yerong/wedle/category/event/domain/Event.java +++ /dev/null @@ -1,47 +0,0 @@ -package yerong.wedle.category.event.domain; - -import jakarta.persistence.*; -import lombok.Getter; -import lombok.NoArgsConstructor; -import yerong.wedle.university.domain.University; - -import java.time.LocalDateTime; -import java.util.Set; - -import static jakarta.persistence.FetchType.LAZY; -import static jakarta.persistence.GenerationType.IDENTITY; -import static lombok.AccessLevel.PROTECTED; - -@Getter -@NoArgsConstructor(access = PROTECTED) -@Entity -public class Event { - - @Id - @GeneratedValue(strategy = IDENTITY) - @Column(name = "event_id") - private Long eventId; - - @Column(nullable = false) - private String name; - - @Enumerated(EnumType.STRING) - private EventType eventType; - - - @Column(nullable = false) - private LocalDateTime startDate; - - @Column(nullable = false) - private LocalDateTime endDate; - - @ManyToOne(fetch = LAZY) - @JoinColumn(name = "university_id") - private University university; - - @OneToOne(mappedBy = "event", cascade = CascadeType.ALL, orphanRemoval = true) - private EventDetails eventDetails; - - @OneToMany(mappedBy = "event", cascade = CascadeType.ALL, orphanRemoval = true) - private Set photos; -} diff --git a/src/main/java/yerong/wedle/category/event/domain/EventDetails.java b/src/main/java/yerong/wedle/category/event/domain/EventDetails.java deleted file mode 100644 index a8c2969..0000000 --- a/src/main/java/yerong/wedle/category/event/domain/EventDetails.java +++ /dev/null @@ -1,26 +0,0 @@ -package yerong.wedle.category.event.domain; - -import jakarta.persistence.*; -import lombok.Getter; -import lombok.NoArgsConstructor; - -@Getter -@NoArgsConstructor -@Entity -public class EventDetails { - - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - @Column(name = "event_details_id") - private Long eventDetailsId; - - @OneToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "event_id") - private Event event; - - @Column - private String lineUp; - - @Column - private String descriptions; -} diff --git a/src/main/java/yerong/wedle/category/event/domain/EventImage.java b/src/main/java/yerong/wedle/category/event/domain/EventImage.java deleted file mode 100644 index 702d813..0000000 --- a/src/main/java/yerong/wedle/category/event/domain/EventImage.java +++ /dev/null @@ -1,26 +0,0 @@ -package yerong.wedle.category.event.domain; - -import jakarta.persistence.*; -import lombok.Getter; -import lombok.NoArgsConstructor; - -@Getter -@NoArgsConstructor -@Entity -public class EventImage { - - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - @Column(name = "photo_id") - private Long photoId; - - @Column(nullable = false) - private String imageUrl; - - @Column(nullable = false) - private String source; - - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "event_id") - private Event event; -} diff --git a/src/main/java/yerong/wedle/category/event/domain/EventType.java b/src/main/java/yerong/wedle/category/event/domain/EventType.java deleted file mode 100644 index 1c1e73f..0000000 --- a/src/main/java/yerong/wedle/category/event/domain/EventType.java +++ /dev/null @@ -1,18 +0,0 @@ -package yerong.wedle.category.event.domain; - -import lombok.Getter; - -@Getter -public enum EventType { - FESTIVAL("축제"), - SNACK_NIGHT_EVENT("간식/야식행사"), - MT("MT"), - SPORTS_DAY("운동회"), - OTHER("기타"); - - private final String displayName; - - EventType(String displayName) { - this.displayName = displayName; - } -} diff --git a/src/main/java/yerong/wedle/category/event/domain/Festival.java b/src/main/java/yerong/wedle/category/event/domain/Festival.java new file mode 100644 index 0000000..3e81e16 --- /dev/null +++ b/src/main/java/yerong/wedle/category/event/domain/Festival.java @@ -0,0 +1,48 @@ +package yerong.wedle.category.event.domain; + +import static jakarta.persistence.FetchType.LAZY; +import static lombok.AccessLevel.PROTECTED; + +import jakarta.persistence.CascadeType; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.OneToMany; +import java.util.Set; +import lombok.Getter; +import lombok.NoArgsConstructor; +import yerong.wedle.university.domain.University; + +@Getter +@NoArgsConstructor(access = PROTECTED) +@Entity +public class Festival { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "festival_id") + private Long festivalId; + + @Column(nullable = false) + private String name; + + @Column(nullable = false) + private Integer festivalYear; + + @Column(nullable = false) + private String date; + + @Column(nullable = false) + private String play; + + @ManyToOne(fetch = LAZY) + @JoinColumn(name = "university_id") + private University university; + + @OneToMany(mappedBy = "festival", cascade = CascadeType.ALL, orphanRemoval = true) + private Set festivalArtists; +} diff --git a/src/main/java/yerong/wedle/category/event/domain/FestivalArtist.java b/src/main/java/yerong/wedle/category/event/domain/FestivalArtist.java new file mode 100644 index 0000000..b1769a9 --- /dev/null +++ b/src/main/java/yerong/wedle/category/event/domain/FestivalArtist.java @@ -0,0 +1,35 @@ +package yerong.wedle.category.event.domain; + +import static jakarta.persistence.FetchType.LAZY; +import static lombok.AccessLevel.PROTECTED; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor(access = PROTECTED) +@Entity +public class FestivalArtist { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(nullable = false) + private Integer festivalDay; + + @ManyToOne(fetch = LAZY) + @JoinColumn(name = "festival_id", nullable = false) + private Festival festival; + + @ManyToOne(fetch = LAZY) + @JoinColumn(name = "artist_id", nullable = false) + private Artist artist; +} diff --git a/src/main/java/yerong/wedle/category/event/dto/EventImageResponse.java b/src/main/java/yerong/wedle/category/event/dto/ArtistResponse.java similarity index 52% rename from src/main/java/yerong/wedle/category/event/dto/EventImageResponse.java rename to src/main/java/yerong/wedle/category/event/dto/ArtistResponse.java index 9598c6d..f1dfff3 100644 --- a/src/main/java/yerong/wedle/category/event/dto/EventImageResponse.java +++ b/src/main/java/yerong/wedle/category/event/dto/ArtistResponse.java @@ -1,13 +1,14 @@ package yerong.wedle.category.event.dto; import lombok.AllArgsConstructor; -import lombok.Getter; +import lombok.Data; import lombok.NoArgsConstructor; -@AllArgsConstructor +@Data @NoArgsConstructor -@Getter -public class EventImageResponse { - private String url; - private String source; +@AllArgsConstructor +public class ArtistResponse { + private String name; + private String subname; + private String image; } diff --git a/src/main/java/yerong/wedle/category/event/dto/ArtistTop10Response.java b/src/main/java/yerong/wedle/category/event/dto/ArtistTop10Response.java new file mode 100644 index 0000000..ac4098d --- /dev/null +++ b/src/main/java/yerong/wedle/category/event/dto/ArtistTop10Response.java @@ -0,0 +1,16 @@ +package yerong.wedle.category.event.dto; + + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class ArtistTop10Response { + private String name; + private String subname; + private String image; + private Integer count; +} \ No newline at end of file diff --git a/src/main/java/yerong/wedle/category/event/dto/EventResponse.java b/src/main/java/yerong/wedle/category/event/dto/EventResponse.java deleted file mode 100644 index 2988faf..0000000 --- a/src/main/java/yerong/wedle/category/event/dto/EventResponse.java +++ /dev/null @@ -1,22 +0,0 @@ -package yerong.wedle.category.event.dto; - -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; - -import java.time.LocalDateTime; -import java.util.Set; - -@AllArgsConstructor -@NoArgsConstructor -@Getter -public class EventResponse { - - private String name; - private String eventType; - private LocalDateTime startDate; - private LocalDateTime endDate; - private String lineUp; - private String descriptions; - private Set photos; -} diff --git a/src/main/java/yerong/wedle/category/event/dto/FestivalResponse.java b/src/main/java/yerong/wedle/category/event/dto/FestivalResponse.java new file mode 100644 index 0000000..cef3911 --- /dev/null +++ b/src/main/java/yerong/wedle/category/event/dto/FestivalResponse.java @@ -0,0 +1,16 @@ +package yerong.wedle.category.event.dto; + +import java.util.List; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class FestivalResponse { + private String eventName; + private Integer year; + private String date; + private List dayLineup; +} diff --git a/src/main/java/yerong/wedle/category/event/dto/FestivalWithArtistsResponse.java b/src/main/java/yerong/wedle/category/event/dto/FestivalWithArtistsResponse.java new file mode 100644 index 0000000..13adee1 --- /dev/null +++ b/src/main/java/yerong/wedle/category/event/dto/FestivalWithArtistsResponse.java @@ -0,0 +1,14 @@ +package yerong.wedle.category.event.dto; + +import java.util.List; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class FestivalWithArtistsResponse { + private String day; + private List lineup; +} diff --git a/src/main/java/yerong/wedle/category/event/dto/UniversityFestivalResponse.java b/src/main/java/yerong/wedle/category/event/dto/UniversityFestivalResponse.java new file mode 100644 index 0000000..90dc674 --- /dev/null +++ b/src/main/java/yerong/wedle/category/event/dto/UniversityFestivalResponse.java @@ -0,0 +1,15 @@ +package yerong.wedle.category.event.dto; + +import java.util.List; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@AllArgsConstructor +@NoArgsConstructor +@Getter +public class UniversityFestivalResponse { + private Long universityId; + private String universityName; + private List events; +} diff --git a/src/main/java/yerong/wedle/category/event/repository/ArtistRepository.java b/src/main/java/yerong/wedle/category/event/repository/ArtistRepository.java new file mode 100644 index 0000000..f917c9e --- /dev/null +++ b/src/main/java/yerong/wedle/category/event/repository/ArtistRepository.java @@ -0,0 +1,15 @@ +package yerong.wedle.category.event.repository; + +import java.util.List; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import yerong.wedle.category.event.domain.Artist; + +public interface ArtistRepository extends JpaRepository { + + @Query("SELECT ea.artist.artistId AS artistId, ea.artist.name AS name, ea.artist.subname AS subname, ea.artist.image AS image, COUNT(ea.artist.artistId) AS visitCount " + + "FROM FestivalArtist ea " + + "GROUP BY ea.artist.artistId, ea.artist.name, ea.artist.subname, ea.artist.image " + + "ORDER BY visitCount DESC") + List findTopArtists(); +} diff --git a/src/main/java/yerong/wedle/category/event/repository/EventImageRepository.java b/src/main/java/yerong/wedle/category/event/repository/EventImageRepository.java deleted file mode 100644 index ac0a5c0..0000000 --- a/src/main/java/yerong/wedle/category/event/repository/EventImageRepository.java +++ /dev/null @@ -1,7 +0,0 @@ -package yerong.wedle.category.event.repository; - -import org.springframework.data.jpa.repository.JpaRepository; -import yerong.wedle.category.event.domain.EventImage; - -public interface EventImageRepository extends JpaRepository { -} diff --git a/src/main/java/yerong/wedle/category/event/repository/EventRepository.java b/src/main/java/yerong/wedle/category/event/repository/EventRepository.java deleted file mode 100644 index 6f5a721..0000000 --- a/src/main/java/yerong/wedle/category/event/repository/EventRepository.java +++ /dev/null @@ -1,11 +0,0 @@ -package yerong.wedle.category.event.repository; - -import org.springframework.data.jpa.repository.JpaRepository; -import yerong.wedle.category.event.domain.Event; -import yerong.wedle.university.domain.University; - -import java.util.List; - -public interface EventRepository extends JpaRepository { - List findByUniversity(University university); -} diff --git a/src/main/java/yerong/wedle/category/event/repository/FestivalRepository.java b/src/main/java/yerong/wedle/category/event/repository/FestivalRepository.java new file mode 100644 index 0000000..20887c6 --- /dev/null +++ b/src/main/java/yerong/wedle/category/event/repository/FestivalRepository.java @@ -0,0 +1,10 @@ +package yerong.wedle.category.event.repository; + +import java.util.List; +import org.springframework.data.jpa.repository.JpaRepository; +import yerong.wedle.category.event.domain.Festival; +import yerong.wedle.university.domain.University; + +public interface FestivalRepository extends JpaRepository { + List findByUniversity(University university); +} diff --git a/src/main/java/yerong/wedle/category/event/service/ArtistService.java b/src/main/java/yerong/wedle/category/event/service/ArtistService.java new file mode 100644 index 0000000..bea5e8f --- /dev/null +++ b/src/main/java/yerong/wedle/category/event/service/ArtistService.java @@ -0,0 +1,34 @@ +package yerong.wedle.category.event.service; + +import java.util.List; +import java.util.stream.Collectors; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import yerong.wedle.category.event.dto.ArtistTop10Response; +import yerong.wedle.category.event.repository.ArtistRepository; + +@RequiredArgsConstructor +@Service +public class ArtistService { + + private final ArtistRepository artistRepository; + + public List getTopArtists() { + List topArtistsData = artistRepository.findTopArtists(); + + List artistResponses = topArtistsData.stream() + .map(record -> { + ArtistTop10Response response = new ArtistTop10Response( + String.valueOf(record[1]), + String.valueOf(record[2]), + String.valueOf(record[3]), + ((Number) record[4]).intValue() + ); + return response; + }) + .limit(10) + .collect(Collectors.toList()); + + return artistResponses; + } +} \ No newline at end of file diff --git a/src/main/java/yerong/wedle/category/event/service/EventService.java b/src/main/java/yerong/wedle/category/event/service/EventService.java deleted file mode 100644 index f16f5d7..0000000 --- a/src/main/java/yerong/wedle/category/event/service/EventService.java +++ /dev/null @@ -1,47 +0,0 @@ -package yerong.wedle.category.event.service; - -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; -import yerong.wedle.category.event.domain.Event; -import yerong.wedle.category.event.dto.EventImageResponse; -import yerong.wedle.category.event.dto.EventResponse; -import yerong.wedle.category.event.repository.EventImageRepository; -import yerong.wedle.category.event.repository.EventRepository; -import yerong.wedle.university.domain.University; -import yerong.wedle.university.exception.UniversityNotFoundException; -import yerong.wedle.university.repository.UniversityRepository; - -import java.util.List; -import java.util.stream.Collectors; - -@RequiredArgsConstructor -@Service -public class EventService { - - private final EventRepository eventRepository; - private final UniversityRepository universityRepository; - - @Transactional - public List getEventsByUniversityId(Long universityId) { - University university = universityRepository.findById(universityId) - .orElseThrow(UniversityNotFoundException::new); - List events = eventRepository.findByUniversity(university); - return events.stream() - .map(this::convertToDto) - .collect(Collectors.toList()); - } - private EventResponse convertToDto(Event event) { - return new EventResponse( - event.getName(), - event.getEventType().getDisplayName(), - event.getStartDate(), - event.getEndDate(), - event.getEventDetails() != null ? event.getEventDetails().getLineUp() : null, - event.getEventDetails() != null ? event.getEventDetails().getDescriptions() : null, - event.getPhotos().stream() - .map(photo -> new EventImageResponse(photo.getImageUrl(), photo.getSource())) - .collect(Collectors.toSet()) - ); - } -} diff --git a/src/main/java/yerong/wedle/category/event/service/FestivalService.java b/src/main/java/yerong/wedle/category/event/service/FestivalService.java new file mode 100644 index 0000000..704ae87 --- /dev/null +++ b/src/main/java/yerong/wedle/category/event/service/FestivalService.java @@ -0,0 +1,68 @@ +package yerong.wedle.category.event.service; + +import java.util.List; +import java.util.stream.Collectors; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import yerong.wedle.category.event.domain.Artist; +import yerong.wedle.category.event.domain.Festival; +import yerong.wedle.category.event.domain.FestivalArtist; +import yerong.wedle.category.event.dto.ArtistResponse; +import yerong.wedle.category.event.dto.FestivalResponse; +import yerong.wedle.category.event.dto.FestivalWithArtistsResponse; +import yerong.wedle.category.event.dto.UniversityFestivalResponse; +import yerong.wedle.category.event.repository.FestivalRepository; +import yerong.wedle.university.domain.University; +import yerong.wedle.university.exception.UniversityNotFoundException; +import yerong.wedle.university.repository.UniversityRepository; + +@RequiredArgsConstructor +@Service +public class FestivalService { + + private final FestivalRepository eventRepository; + private final UniversityRepository universityRepository; + + @Transactional + public UniversityFestivalResponse getFestivalsByUniversityId(Long universityId) { + University university = universityRepository.findById(universityId) + .orElseThrow(UniversityNotFoundException::new); + + List festivalResponses = university.getFestivals().stream() + .map(this::convertToFestivalResponse) + .collect(Collectors.toList()); + + return new UniversityFestivalResponse( + university.getUniversityId(), + university.getName(), + festivalResponses + ); + } + + private FestivalResponse convertToFestivalResponse(Festival festival) { + List dayLineup = festival.getFestivalArtists().stream() + .collect(Collectors.groupingBy( + FestivalArtist::getFestivalDay, + Collectors.mapping(this::convertToArtistResponse, Collectors.toList()) + )) + .entrySet().stream() + .map(entry -> new FestivalWithArtistsResponse("Day" + entry.getKey(), entry.getValue())) + .collect(Collectors.toList()); + return new FestivalResponse( + festival.getName(), + festival.getFestivalYear(), + festival.getDate(), + dayLineup + ); + } + + private ArtistResponse convertToArtistResponse(FestivalArtist festivalArtist) { + Artist artist = festivalArtist.getArtist(); + return new ArtistResponse( + artist.getName(), + artist.getSubname(), + artist.getImage() + ); + } +} diff --git a/src/main/java/yerong/wedle/university/domain/University.java b/src/main/java/yerong/wedle/university/domain/University.java index a465199..84eb4ba 100644 --- a/src/main/java/yerong/wedle/university/domain/University.java +++ b/src/main/java/yerong/wedle/university/domain/University.java @@ -1,13 +1,18 @@ package yerong.wedle.university.domain; -import jakarta.persistence.*; -import lombok.Getter; -import yerong.wedle.category.activity.domain.ActivityUniversity; +import static jakarta.persistence.GenerationType.IDENTITY; +import jakarta.persistence.CascadeType; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import jakarta.persistence.OneToMany; import java.util.ArrayList; import java.util.List; - -import static jakarta.persistence.GenerationType.IDENTITY; +import lombok.Getter; +import yerong.wedle.category.activity.domain.ActivityUniversity; +import yerong.wedle.category.event.domain.Festival; @Entity @Getter @@ -36,4 +41,7 @@ public class University { @OneToMany(mappedBy = "university", cascade = CascadeType.ALL, orphanRemoval = true) private List activities = new ArrayList<>(); + + @OneToMany(mappedBy = "university", cascade = CascadeType.ALL, orphanRemoval = true) + private List festivals = new ArrayList<>(); } From a12f4244cfd43cfe05c89dadf7ae7b7520c1d979 Mon Sep 17 00:00:00 2001 From: Sin Ye Rin <91180366+nyeroni@users.noreply.github.com> Date: Thu, 21 Nov 2024 01:31:15 +0900 Subject: [PATCH 55/55] =?UTF-8?q?feat:=20=EC=B6=95=EC=A0=9C=20=EC=A0=95?= =?UTF-8?q?=EB=B3=B4=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/yerong/wedle/category/event/domain/Artist.java | 2 +- .../yerong/wedle/category/event/dto/FestivalResponse.java | 2 +- .../yerong/wedle/category/event/service/ArtistService.java | 6 ++++-- .../wedle/category/event/service/FestivalService.java | 2 +- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/main/java/yerong/wedle/category/event/domain/Artist.java b/src/main/java/yerong/wedle/category/event/domain/Artist.java index 5339df9..0879849 100644 --- a/src/main/java/yerong/wedle/category/event/domain/Artist.java +++ b/src/main/java/yerong/wedle/category/event/domain/Artist.java @@ -28,7 +28,7 @@ public class Artist { @Column(nullable = false) private String subname; - private String image = ""; + private final String image = ""; @OneToMany(mappedBy = "artist", cascade = CascadeType.ALL, orphanRemoval = true) private Set eventArtists; diff --git a/src/main/java/yerong/wedle/category/event/dto/FestivalResponse.java b/src/main/java/yerong/wedle/category/event/dto/FestivalResponse.java index cef3911..6b1624b 100644 --- a/src/main/java/yerong/wedle/category/event/dto/FestivalResponse.java +++ b/src/main/java/yerong/wedle/category/event/dto/FestivalResponse.java @@ -10,7 +10,7 @@ @AllArgsConstructor public class FestivalResponse { private String eventName; - private Integer year; + private String year; private String date; private List dayLineup; } diff --git a/src/main/java/yerong/wedle/category/event/service/ArtistService.java b/src/main/java/yerong/wedle/category/event/service/ArtistService.java index bea5e8f..3932033 100644 --- a/src/main/java/yerong/wedle/category/event/service/ArtistService.java +++ b/src/main/java/yerong/wedle/category/event/service/ArtistService.java @@ -18,17 +18,19 @@ public List getTopArtists() { List artistResponses = topArtistsData.stream() .map(record -> { + String image = (record[3] != null) ? String.valueOf(record[3]) : ""; ArtistTop10Response response = new ArtistTop10Response( String.valueOf(record[1]), String.valueOf(record[2]), - String.valueOf(record[3]), + image, + ((Number) record[4]).intValue() ); return response; }) .limit(10) .collect(Collectors.toList()); - + return artistResponses; } } \ No newline at end of file diff --git a/src/main/java/yerong/wedle/category/event/service/FestivalService.java b/src/main/java/yerong/wedle/category/event/service/FestivalService.java index 704ae87..eb8c74f 100644 --- a/src/main/java/yerong/wedle/category/event/service/FestivalService.java +++ b/src/main/java/yerong/wedle/category/event/service/FestivalService.java @@ -51,7 +51,7 @@ private FestivalResponse convertToFestivalResponse(Festival festival) { .collect(Collectors.toList()); return new FestivalResponse( festival.getName(), - festival.getFestivalYear(), + String.valueOf(festival.getFestivalYear()), festival.getDate(), dayLineup );