From 3eebdc3b1b7eb251781911add219d67c9c964b3b Mon Sep 17 00:00:00 2001 From: soohyun Date: Sun, 29 Oct 2023 17:49:10 +0900 Subject: [PATCH 01/78] =?UTF-8?q?[Refactor]=20=EC=95=8C=EB=A6=BC=EC=83=9D?= =?UTF-8?q?=EC=84=B1=EB=A1=9C=EC=A7=81=20=EA=B4=80=EB=A0=A8=20=EC=A4=91?= =?UTF-8?q?=EB=B3=B5=20=EC=BD=94=EB=93=9C=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/winey/server/service/FeedService.java | 91 +++++++------------ 1 file changed, 34 insertions(+), 57 deletions(-) diff --git a/src/main/java/org/winey/server/service/FeedService.java b/src/main/java/org/winey/server/service/FeedService.java index 22819ef..6c3a813 100644 --- a/src/main/java/org/winey/server/service/FeedService.java +++ b/src/main/java/org/winey/server/service/FeedService.java @@ -75,58 +75,21 @@ public CreateFeedResponseDto createFeed(CreateFeedRequestDto request, Long userI if (presentUser.getUserLevel().getLevelNumber() != checkUserLevelUp(presentUser)) {//userLevel 변동사항 체크, 만약에 레벨에 변동이 생겼다면? 레벨 강등 알림 생성. switch (checkUserLevelUp(presentUser)){ case 2: - Notification noti2 = Notification.builder() - .notiType(NotiType.RANKUPTO2) - .notiMessage(NotiType.RANKUPTO2.getType()) - .isChecked(false) - .notiReciver(presentUser) - .build(); - noti2.updateLinkId(null); - notiRepository.save(noti2); + notificationBuilderInFeed(NotiType.RANKUPTO2, presentUser); break; case 3: - Notification noti3 = Notification.builder() - .notiType(NotiType.RANKUPTO3) - .notiMessage(NotiType.RANKUPTO3.getType()) - .isChecked(false) - .notiReciver(presentUser) - .build(); - noti3.updateLinkId(null); - notiRepository.save(noti3); + notificationBuilderInFeed(NotiType.RANKUPTO3, presentUser); break; case 4: - Notification noti4 = Notification.builder() - .notiType(NotiType.RANKUPTO4) - .notiMessage(NotiType.RANKUPTO4.getType()) - .isChecked(false) - .notiReciver(presentUser) - .build(); - noti4.updateLinkId(null); - notiRepository.save(noti4); + notificationBuilderInFeed(NotiType.RANKUPTO4, presentUser); break; - } } } return CreateFeedResponseDto.of(feed.getFeedId(), feed.getCreatedAt()); } - private int checkUserLevelUp(User presentUser) { - int userAchievedGoals = goalRepository.countByUserAndIsAttained(presentUser, true); //Goal 중 userid가 맞고 isAttained true 개수 세기 - if (userAchievedGoals < 1) { - presentUser.updateUserLevel(UserLevel.COMMONER); - return 1; - } else if (userAchievedGoals < 3) { - presentUser.updateUserLevel(UserLevel.KNIGHT); - return 2; - } else if (userAchievedGoals < 9) { - presentUser.updateUserLevel(UserLevel.ARISTOCRAT); - return 3; - } else { - presentUser.updateUserLevel(UserLevel.EMPEROR); - return 4; - } - } + @Transactional public String deleteFeed(Long userId, Long feedId) { @@ -159,24 +122,10 @@ public String deleteFeed(Long userId, Long feedId) { if (presentUser.getUserLevel().getLevelNumber() != checkUserLevelUp(presentUser)) {//userLevel 변동사항 체크, 만약에 레벨에 변동이 생겼다면? 레벨 강등 알림 생성. switch (checkUserLevelUp(presentUser)){ case 3: - Notification noti3 = Notification.builder() - .notiType(NotiType.DELETERANKDOWNTO3) - .notiMessage(NotiType.DELETERANKDOWNTO3.getType()) - .isChecked(false) - .notiReciver(presentUser) - .build(); - noti3.updateLinkId(null); - notiRepository.save(noti3); + notificationBuilderInFeed(NotiType.DELETERANKDOWNTO3, presentUser); break; case 2: - Notification noti2 = Notification.builder() - .notiType(NotiType.DELETERANKDOWNTO2) - .notiMessage(NotiType.DELETERANKDOWNTO2.getType()) - .isChecked(false) - .notiReciver(presentUser) - .build(); - noti2.updateLinkId(null); - notiRepository.save(noti2); + notificationBuilderInFeed(NotiType.DELETERANKDOWNTO2, presentUser); break; } } @@ -308,4 +257,32 @@ private String getTimeAgo(LocalDateTime createdAt) { } return "지금"; } + + private void notificationBuilderInFeed(NotiType type, User user){ + Notification notification = Notification.builder() + .notiType(type) + .notiMessage(type.getType()) + .isChecked(false) + .notiReciver(user) + .build(); + notification.updateLinkId(null); + notiRepository.save(notification); + } + + private int checkUserLevelUp(User presentUser) { + int userAchievedGoals = goalRepository.countByUserAndIsAttained(presentUser, true); //Goal 중 userid가 맞고 isAttained true 개수 세기 + if (userAchievedGoals < 1) { + presentUser.updateUserLevel(UserLevel.COMMONER); + return 1; + } else if (userAchievedGoals < 3) { + presentUser.updateUserLevel(UserLevel.KNIGHT); + return 2; + } else if (userAchievedGoals < 9) { + presentUser.updateUserLevel(UserLevel.ARISTOCRAT); + return 3; + } else { + presentUser.updateUserLevel(UserLevel.EMPEROR); + return 4; + } + } } From bc0484abc49d870c42d390ed4d5daa9d0a343155 Mon Sep 17 00:00:00 2001 From: soohyun <49307946+sss4920@users.noreply.github.com> Date: Fri, 17 Nov 2023 21:23:33 +0900 Subject: [PATCH 02/78] =?UTF-8?q?[Feat]=20fcm=20=EA=B4=80=EB=A0=A8=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20=EA=B5=AC=ED=98=84=20(#187)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [Feat] fcm service 구현하기 * #159 [Feat] createMessage queue * [Feat] 좋아요, 댓글 생성시 알림 발송 * [Feat] 메시지 큐 실험 성공, fcm만 되는지 확인필요 * [Feat] 푸시sender transactional 제거 * [Feat] fcm토큰 준비 * [Feat] rabbitMQ server ec2연결 및 token response 요구사항 반영수정 * [Feat] updateFcmtoken 구현 및 notification success notFound -> no content수정 * [Merge] merge confict 해결 * [Fix] 닉네임 수정 로직이랑 줄을 헷갈려서 버그 수정 * [FIX] merge conflict * [FIX] stash 결과 적용 * [Feat] 동의여부 기능 추가 및 fcm 메시지 전송 변경 * [Feat] 댓글, 좋아요 알림로직에 동의 여부 수정 및 예외처리 * [Fix] notificationresponse Id 수정 * [Fix] logic 수정 --- .gitignore | 6 + build.gradle | 7 + .../common/message/MessageQueueReceiver.java | 82 ++++++++++++ .../common/message/MessageQueueSender.java | 46 +++++++ .../server/controller/UserController.java | 22 +++ .../request/UpdateAllowedPushDto.java | 16 +++ .../controller/request/UpdateFcmTokenDto.java | 20 +++ .../request/auth/SignInRequestDto.java | 2 + .../response/auth/SignInResponseDto.java | 6 +- .../response/user/UserResponseUserDto.java | 5 +- .../org/winey/server/domain/user/User.java | 11 ++ .../org/winey/server/exception/Error.java | 1 + .../org/winey/server/exception/Success.java | 4 +- .../winey/server/service/CommentService.java | 125 +++++++++++------- .../org/winey/server/service/FcmService.java | 116 ++++++++++++++++ .../winey/server/service/FeedLikeService.java | 36 +++-- .../org/winey/server/service/NotiService.java | 1 + .../org/winey/server/service/UserService.java | 34 +++-- .../server/service/auth/AuthService.java | 9 +- .../server/service/message/FcmRequestDto.java | 26 ++++ 20 files changed, 494 insertions(+), 81 deletions(-) create mode 100644 src/main/java/org/winey/server/common/message/MessageQueueReceiver.java create mode 100644 src/main/java/org/winey/server/common/message/MessageQueueSender.java create mode 100644 src/main/java/org/winey/server/controller/request/UpdateAllowedPushDto.java create mode 100644 src/main/java/org/winey/server/controller/request/UpdateFcmTokenDto.java create mode 100644 src/main/java/org/winey/server/service/FcmService.java create mode 100644 src/main/java/org/winey/server/service/message/FcmRequestDto.java diff --git a/.gitignore b/.gitignore index 9e143b5..162f436 100644 --- a/.gitignore +++ b/.gitignore @@ -43,5 +43,11 @@ application.yaml ### DS_Store .DS_Store +### fcm +winey-firebase.json + +### banner.txt +banner.txt + ### log file ### logs/ \ No newline at end of file diff --git a/build.gradle b/build.gradle index aa7316a..e6ddff3 100644 --- a/build.gradle +++ b/build.gradle @@ -56,6 +56,13 @@ dependencies { implementation group: 'io.jsonwebtoken', name: 'jjwt-jackson', version: '0.11.2' implementation 'org.springframework.cloud:spring-cloud-starter-openfeign:3.1.7' + //FCM + implementation group: 'com.google.firebase', name: 'firebase-admin', version: '6.8.1' + + //rabbitmq + implementation 'org.springframework.boot:spring-boot-starter-amqp' + + // ShedLock implementation 'net.javacrumbs.shedlock:shedlock-spring:4.14.0' implementation 'net.javacrumbs.shedlock:shedlock-provider-jdbc-template:4.14.0' diff --git a/src/main/java/org/winey/server/common/message/MessageQueueReceiver.java b/src/main/java/org/winey/server/common/message/MessageQueueReceiver.java new file mode 100644 index 0000000..dd1c546 --- /dev/null +++ b/src/main/java/org/winey/server/common/message/MessageQueueReceiver.java @@ -0,0 +1,82 @@ +package org.winey.server.common.message; + +import lombok.AllArgsConstructor; +import org.springframework.amqp.core.ExchangeTypes; +import org.springframework.amqp.rabbit.annotation.Exchange; +import org.springframework.amqp.rabbit.annotation.Queue; +import org.springframework.amqp.rabbit.annotation.QueueBinding; +import org.springframework.amqp.rabbit.annotation.RabbitListener; +import org.springframework.stereotype.Component; +import org.winey.server.domain.notification.Notification; +import org.winey.server.service.FcmService; +import org.winey.server.service.message.FcmRequestDto; + +import java.io.*; + +@Component +@AllArgsConstructor +public class MessageQueueReceiver { + + private final FcmService fcmService; + + @RabbitListener(bindings = @QueueBinding( + exchange = @Exchange(name = "like", type = ExchangeTypes.TOPIC), + value = @Queue(name = "like-notification"), + key = "like-noti") + ) + public void likeReceiver(byte[] likeNoti){ + System.out.println("좋아요 noti receiver"); + ByteArrayInputStream bis = new ByteArrayInputStream(likeNoti); + ObjectInput in = null; + try { + in = new ObjectInputStream(bis); + Object obj = in.readObject(); + System.out.println("여기까진 오는가"); + if (obj instanceof FcmRequestDto){ + FcmRequestDto notification = (FcmRequestDto) obj; + fcmService.sendByToken(notification); + } + }catch (IOException | ClassNotFoundException e){ + e.printStackTrace(); + }finally { + try{ + bis.close(); + if (in != null){ + in.close(); + } + }catch (IOException ex){ + ex.printStackTrace(); + } + } + } + + @RabbitListener(bindings = @QueueBinding( + exchange = @Exchange(name = "comment", type = ExchangeTypes.TOPIC), + value = @Queue(name = "comment-notification"), + key = "comment-noti") + ) + public void commentReceiver(byte[] commentNoti){ + System.out.println("댓글 noti receiver"); + ByteArrayInputStream bis = new ByteArrayInputStream(commentNoti); + ObjectInput in = null; + try { + in = new ObjectInputStream(bis); + Object obj = in.readObject(); + if (obj instanceof Notification){ + FcmRequestDto notification = (FcmRequestDto) obj; + fcmService.sendByToken(notification); + } + }catch (IOException | ClassNotFoundException e){ + e.printStackTrace(); + }finally { + try{ + bis.close(); + if (in != null){ + in.close(); + } + }catch (IOException ex){ + ex.printStackTrace(); + } + } + } +} diff --git a/src/main/java/org/winey/server/common/message/MessageQueueSender.java b/src/main/java/org/winey/server/common/message/MessageQueueSender.java new file mode 100644 index 0000000..a26d968 --- /dev/null +++ b/src/main/java/org/winey/server/common/message/MessageQueueSender.java @@ -0,0 +1,46 @@ +package org.winey.server.common.message; + +import com.google.cloud.ByteArray; +import lombok.AllArgsConstructor; +import org.springframework.amqp.AmqpException; +import org.springframework.amqp.rabbit.core.RabbitTemplate; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.transaction.annotation.Transactional; +import org.winey.server.domain.notification.NotiType; +import org.winey.server.domain.notification.Notification; +import org.winey.server.service.message.FcmRequestDto; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectOutputStream; +import java.time.LocalDateTime; + +@Configuration +@AllArgsConstructor +public class MessageQueueSender { + + final RabbitTemplate rabbitTemplate; + + + public void pushSender(FcmRequestDto notification){ + System.out.println("여기는 오는건가?"); + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + ObjectOutputStream out; + try { + out = new ObjectOutputStream(bos); + out.writeObject(notification); + out.flush(); + byte[] data = bos.toByteArray(); + if (notification.getType() == NotiType.COMMENTNOTI) { + rabbitTemplate.convertAndSend("comment", "comment-noti", data); + } else if (notification.getType() == NotiType.LIKENOTI) { + rabbitTemplate.convertAndSend("like", "like-noti", data); + } + }catch (AmqpException e){ + System.out.println("메시지 전송 중 오류가 발생했습니다." + e.getMessage()); + }catch (IOException e){ + e.printStackTrace(); + } + } +} diff --git a/src/main/java/org/winey/server/controller/UserController.java b/src/main/java/org/winey/server/controller/UserController.java index c38a023..aa3fc9b 100644 --- a/src/main/java/org/winey/server/controller/UserController.java +++ b/src/main/java/org/winey/server/controller/UserController.java @@ -7,6 +7,8 @@ import org.springframework.web.bind.annotation.*; import org.winey.server.common.dto.ApiResponse; import org.winey.server.config.resolver.UserId; +import org.winey.server.controller.request.UpdateAllowedPushDto; +import org.winey.server.controller.request.UpdateFcmTokenDto; import org.winey.server.controller.request.UpdateUserNicknameDto; import org.winey.server.controller.response.user.UserResponseDto; import org.winey.server.exception.Error; @@ -55,4 +57,24 @@ public ApiResponse checkNicknameDuplicate(@RequestParam String nickname) { }; return ApiResponse.success(Success.CHECK_NICKNAME_DUPLICATE_SUCCESS, result); } + + @PatchMapping("/fcmtoken") + @ResponseStatus(HttpStatus.OK) + @Operation(summary = "fcm토큰 변경 API", description = "유저의 fcm token을 변경합니다.") + public ApiResponse updateFcmToken (@UserId Long userId, @RequestBody @Valid final UpdateFcmTokenDto requestDto) { + userService.updateFcmToken(userId, requestDto); + return ApiResponse.success(Success.FCM_TOKEN_UPDATE_SUCCESS); + } + + @PatchMapping("/notification") + @ResponseStatus(HttpStatus.OK) + @Operation(summary = "푸시 알림 동의 변경 API", description = "유저의 알림 동의 여부를 변경합니다.") + public ApiResponse updateAllowedNotification (@UserId Long userId, @RequestBody @Valid final UpdateAllowedPushDto requestDto) { + Map result = new HashMap() { + { + put("isAllowed", userService.allowedPushNotification(userId, requestDto.getAllowedPush())); + } + }; + return ApiResponse.success(Success.UPDATE_PUSH_ALLOWED_SUCCESS, result); + } } diff --git a/src/main/java/org/winey/server/controller/request/UpdateAllowedPushDto.java b/src/main/java/org/winey/server/controller/request/UpdateAllowedPushDto.java new file mode 100644 index 0000000..02c81c2 --- /dev/null +++ b/src/main/java/org/winey/server/controller/request/UpdateAllowedPushDto.java @@ -0,0 +1,16 @@ +package org.winey.server.controller.request; + +import javax.validation.constraints.NotNull; + +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor(access = AccessLevel.PRIVATE) +@AllArgsConstructor +public class UpdateAllowedPushDto { + @NotNull + private Boolean allowedPush; +} diff --git a/src/main/java/org/winey/server/controller/request/UpdateFcmTokenDto.java b/src/main/java/org/winey/server/controller/request/UpdateFcmTokenDto.java new file mode 100644 index 0000000..5f36fdd --- /dev/null +++ b/src/main/java/org/winey/server/controller/request/UpdateFcmTokenDto.java @@ -0,0 +1,20 @@ +package org.winey.server.controller.request; + +import javax.validation.constraints.NotNull; + +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@AllArgsConstructor +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class UpdateFcmTokenDto { + @NotNull + private String token; + + public UpdateFcmTokenDto of(String token) { + return new UpdateFcmTokenDto(token); + } +} diff --git a/src/main/java/org/winey/server/controller/request/auth/SignInRequestDto.java b/src/main/java/org/winey/server/controller/request/auth/SignInRequestDto.java index 773ae0f..17c77d9 100644 --- a/src/main/java/org/winey/server/controller/request/auth/SignInRequestDto.java +++ b/src/main/java/org/winey/server/controller/request/auth/SignInRequestDto.java @@ -13,4 +13,6 @@ public class SignInRequestDto { @NotNull private String socialType; + + private String fcmToken; } \ No newline at end of file diff --git a/src/main/java/org/winey/server/controller/response/auth/SignInResponseDto.java b/src/main/java/org/winey/server/controller/response/auth/SignInResponseDto.java index 51402da..255fe14 100644 --- a/src/main/java/org/winey/server/controller/response/auth/SignInResponseDto.java +++ b/src/main/java/org/winey/server/controller/response/auth/SignInResponseDto.java @@ -12,9 +12,11 @@ public class SignInResponseDto { private Long userId; private String accessToken; private String refreshToken; + private String fcmToken; private Boolean isRegistered; + private Boolean fcmIsAllowed; - public static SignInResponseDto of(Long userId, String accessToken, String refreshToken, Boolean isRegistered) { - return new SignInResponseDto(userId, accessToken, refreshToken, isRegistered); + public static SignInResponseDto of(Long userId, String accessToken, String refreshToken, String fcmToken, Boolean isRegistered, Boolean fcmIsAllowed) { + return new SignInResponseDto(userId, accessToken, refreshToken, fcmToken, isRegistered, fcmIsAllowed); } } diff --git a/src/main/java/org/winey/server/controller/response/user/UserResponseUserDto.java b/src/main/java/org/winey/server/controller/response/user/UserResponseUserDto.java index 49e5d6e..ad40ac9 100644 --- a/src/main/java/org/winey/server/controller/response/user/UserResponseUserDto.java +++ b/src/main/java/org/winey/server/controller/response/user/UserResponseUserDto.java @@ -12,8 +12,9 @@ public class UserResponseUserDto { private Long userId; private String nickname; private String userLevel; + private Boolean fcmIsAllowed; - public static UserResponseUserDto of(Long userId, String nickname, String userLevel) { - return new UserResponseUserDto(userId, nickname, userLevel); + public static UserResponseUserDto of(Long userId, String nickname, String userLevel, Boolean fcmIsAllowed) { + return new UserResponseUserDto(userId, nickname, userLevel, fcmIsAllowed); } } \ No newline at end of file diff --git a/src/main/java/org/winey/server/domain/user/User.java b/src/main/java/org/winey/server/domain/user/User.java index 55f76ba..e3ad858 100644 --- a/src/main/java/org/winey/server/domain/user/User.java +++ b/src/main/java/org/winey/server/domain/user/User.java @@ -3,6 +3,7 @@ import lombok.*; import org.hibernate.annotations.ColumnDefault; import org.hibernate.annotations.DynamicInsert; +import org.hibernate.annotations.DynamicUpdate; import org.winey.server.domain.AuditingTimeEntity; import org.winey.server.domain.comment.Comment; import org.winey.server.domain.feed.Feed; @@ -41,6 +42,12 @@ public class User extends AuditingTimeEntity { @Enumerated(EnumType.STRING) private SocialType socialType; + @Column(nullable = true) + private String fcmToken; + + @Column(nullable = true) + private Boolean fcmIsAllowed = true; + @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.REMOVE, mappedBy = "user", orphanRemoval = true) private List goals; @@ -77,4 +84,8 @@ public void updateRefreshToken(String refreshToken) { public void updateNickname(String nickname) { this.nickname = nickname; } + + public void updateFcmToken(String fcmToken) { this.fcmToken = fcmToken; } + + public void updateFcmIsAllowed(Boolean isAllowed){this.fcmIsAllowed = isAllowed;} } diff --git a/src/main/java/org/winey/server/exception/Error.java b/src/main/java/org/winey/server/exception/Error.java index 4fbfd47..343ba9b 100644 --- a/src/main/java/org/winey/server/exception/Error.java +++ b/src/main/java/org/winey/server/exception/Error.java @@ -13,6 +13,7 @@ public enum Error { * 400 BAD REQUEST */ REQUEST_VALIDATION_EXCEPTION(HttpStatus.BAD_REQUEST, "잘못된 요청입니다"), + INVALID_FCMTOKEN_EXCEPTION(HttpStatus.BAD_REQUEST, "푸시알림 동의를 했는데, fcmtoken이 null이거나 문제가 있습니다."), LOGIN_SUCCESS(HttpStatus.OK, "로그인에 성공했습니다."), INVALID_PASSWORD_EXCEPTION(HttpStatus.BAD_REQUEST, "잘못된 비밀번호가 입력됐습니다."), NOT_FOUND_IMAGE_EXCEPTION(HttpStatus.BAD_REQUEST, "잘못된 이미지 파일입니다"), diff --git a/src/main/java/org/winey/server/exception/Success.java b/src/main/java/org/winey/server/exception/Success.java index c451f94..15fdc40 100644 --- a/src/main/java/org/winey/server/exception/Success.java +++ b/src/main/java/org/winey/server/exception/Success.java @@ -23,6 +23,7 @@ public enum Success { RE_ISSUE_TOKEN_SUCCESS(HttpStatus.OK, "토큰 재발급 성공"), UPDATE_NICKNAME_SUCCESS(HttpStatus.OK, "닉네임 변경 성공"), + UPDATE_PUSH_ALLOWED_SUCCESS(HttpStatus.OK, "푸시 알림 동의 여부 변경 성공"), CHECK_NICKNAME_DUPLICATE_SUCCESS(HttpStatus.OK, "닉네임 중복 확인 성공"), CHECK_NEW_NOTIFICATION_SUCCESS(HttpStatus.OK, "새 알림 여부 조회 성공"), BLOCK_USER_SUCCESS(HttpStatus.OK, "유저 차단 성공"), @@ -44,7 +45,8 @@ public enum Success { */ DELETE_FEED_SUCCESS(HttpStatus.NO_CONTENT, "피드가 정상적으로 삭제되었습니다."), DELETE_COMMENT_SUCCESS(HttpStatus.NO_CONTENT, "댓글이 정상적으로 삭제되었습니다."), - NOTIFICATION_EMPTY_SUCCESS(HttpStatus.NOT_FOUND, "알림이 한통도 없어요. 힝~구"), + NOTIFICATION_EMPTY_SUCCESS(HttpStatus.NO_CONTENT, "알림이 한통도 없어요. 힝~구"), + FCM_TOKEN_UPDATE_SUCCESS(HttpStatus.NO_CONTENT, "fcm 수정이 완료 됐습니다."), DELETE_USER_SUCCESS(HttpStatus.NO_CONTENT, "회원 탈퇴가 정상적으로 이루어졌습니다.") ; diff --git a/src/main/java/org/winey/server/service/CommentService.java b/src/main/java/org/winey/server/service/CommentService.java index 5cae39c..423d18b 100644 --- a/src/main/java/org/winey/server/service/CommentService.java +++ b/src/main/java/org/winey/server/service/CommentService.java @@ -1,13 +1,17 @@ package org.winey.server.service; import lombok.RequiredArgsConstructor; + import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import org.winey.server.common.message.MessageQueueSender; import org.winey.server.controller.response.comment.CommentResponseDto; import org.winey.server.controller.response.comment.DeleteCommentResponseDto; import org.winey.server.domain.comment.Comment; import org.winey.server.domain.notification.NotiType; import org.winey.server.domain.notification.Notification; +import org.winey.server.exception.model.BadRequestException; +import org.winey.server.exception.model.CustomException; import org.winey.server.exception.model.UnauthorizedException; import org.winey.server.exception.model.UnprocessableEntityException; import org.winey.server.infrastructure.CommentRepository; @@ -18,68 +22,89 @@ import org.winey.server.infrastructure.FeedRepository; import org.winey.server.infrastructure.NotiRepository; import org.winey.server.infrastructure.UserRepository; - +import org.winey.server.service.message.FcmRequestDto; @Service @RequiredArgsConstructor public class CommentService { - private final UserRepository userRepository; - private final FeedRepository feedRepository; - private final CommentRepository commentRepository; - private final NotiRepository notiRepository; + private final UserRepository userRepository; + private final FeedRepository feedRepository; + private final CommentRepository commentRepository; + private final NotiRepository notiRepository; - @Transactional - public CommentResponseDto createComment(Long userId, Long feedId, String content) { - User user = userRepository.findByUserId(userId) - .orElseThrow(() -> new NotFoundException(Error.NOT_FOUND_USER_EXCEPTION, Error.NOT_FOUND_USER_EXCEPTION.getMessage())); - Feed feed = feedRepository.findByFeedId(feedId) - .orElseThrow(() -> new NotFoundException(Error.NOT_FOUND_FEED_EXCEPTION, Error.NOT_FOUND_FEED_EXCEPTION.getMessage())); + private final MessageQueueSender messageQueueSender; + @Transactional + public CommentResponseDto createComment(Long userId, Long feedId, String content) { + User user = userRepository.findByUserId(userId) + .orElseThrow(() -> new NotFoundException(Error.NOT_FOUND_USER_EXCEPTION, + Error.NOT_FOUND_USER_EXCEPTION.getMessage())); + Feed feed = feedRepository.findByFeedId(feedId) + .orElseThrow(() -> new NotFoundException(Error.NOT_FOUND_FEED_EXCEPTION, + Error.NOT_FOUND_FEED_EXCEPTION.getMessage())); - Comment comment = Comment.builder() - .user(user) - .content(content) - .feed(feed) - .build(); - commentRepository.save(comment); + Comment comment = Comment.builder() + .user(user) + .content(content) + .feed(feed) + .build(); + commentRepository.save(comment); - if (user.getUserId() != feed.getUser().getUserId()) { - Notification notification = Notification.builder() //누군가가 내 피드에 댓글을 달았어요~ - .notiReciver(feed.getUser()) - .notiType(NotiType.COMMENTNOTI) - .notiMessage(comment.getUser().getNickname() + NotiType.COMMENTNOTI.getType()) - .isChecked(false) - .build(); - notification.updateLinkId(feedId); - notification.updateResponseId(comment.getCommentId()); - notification.updateRequestUserId(userId); - notiRepository.save(notification); - } - return CommentResponseDto.of(comment.getCommentId(), userId, user.getNickname(), comment.getContent(),user.getUserLevel().getLevelNumber(),comment.getCreatedAt()); - } + if (user.getUserId() != feed.getUser().getUserId()) { + createNotificationInComment(feed, comment, user); //알림 생성 후 message queue에 task 등록 + } + return CommentResponseDto.of(comment.getCommentId(), userId, user.getNickname(), comment.getContent(), + user.getUserLevel().getLevelNumber(), comment.getCreatedAt()); + } - @Transactional - public DeleteCommentResponseDto deleteComment(Long userId, Long commentId){ - User user = userRepository.findByUserId(userId) - .orElseThrow(()-> new NotFoundException(Error.NOT_FOUND_USER_EXCEPTION, Error.NOT_FOUND_USER_EXCEPTION.getMessage())); - Comment wantDeleteComment = commentRepository.findByCommentId(commentId) - .orElseThrow(()-> new NotFoundException(Error.NOT_FOUND_COMMENT_EXCEPTION, Error.NOT_FOUND_COMMENT_EXCEPTION.getMessage())); - Feed commentFeed = feedRepository.findByFeedId(wantDeleteComment.getFeed().getFeedId()) - .orElseThrow(() -> new NotFoundException(Error.NOT_FOUND_FEED_EXCEPTION, Error.NOT_FOUND_FEED_EXCEPTION.getMessage())); + @Transactional + public DeleteCommentResponseDto deleteComment(Long userId, Long commentId) { + User user = userRepository.findByUserId(userId) + .orElseThrow(() -> new NotFoundException(Error.NOT_FOUND_USER_EXCEPTION, + Error.NOT_FOUND_USER_EXCEPTION.getMessage())); + Comment wantDeleteComment = commentRepository.findByCommentId(commentId) + .orElseThrow(() -> new NotFoundException(Error.NOT_FOUND_COMMENT_EXCEPTION, + Error.NOT_FOUND_COMMENT_EXCEPTION.getMessage())); + Feed commentFeed = feedRepository.findByFeedId(wantDeleteComment.getFeed().getFeedId()) + .orElseThrow(() -> new NotFoundException(Error.NOT_FOUND_FEED_EXCEPTION, + Error.NOT_FOUND_FEED_EXCEPTION.getMessage())); - if (user.getUserId() != wantDeleteComment.getUser().getUserId() && user.getUserId() != commentFeed.getUser().getUserId() ){ //만약 현재 유저가 피드 주인도 아니고, 댓글 주인도 아니면? - throw new UnauthorizedException(Error.DELETE_UNAUTHORIZED, Error.DELETE_UNAUTHORIZED.getMessage()); //지울 수 있는 권한이 없다. - } + if (user.getUserId() != wantDeleteComment.getUser().getUserId() && user.getUserId() != commentFeed.getUser() + .getUserId()) { //만약 현재 유저가 피드 주인도 아니고, 댓글 주인도 아니면? + throw new UnauthorizedException(Error.DELETE_UNAUTHORIZED, + Error.DELETE_UNAUTHORIZED.getMessage()); //지울 수 있는 권한이 없다. + } - // 관련 알림 삭제 - notiRepository.deleteByNotiTypeAndResponseId(NotiType.COMMENTNOTI, wantDeleteComment.getCommentId()); + // 관련 알림 삭제 + notiRepository.deleteByNotiTypeAndResponseId(NotiType.COMMENTNOTI, wantDeleteComment.getCommentId()); - Long res = commentRepository.deleteByCommentId(commentId); - if (res != 1){ - throw new UnprocessableEntityException(Error.UNPROCESSABLE_ENTITY_DELETE_EXCEPTION,Error.UNPROCESSABLE_ENTITY_DELETE_EXCEPTION.getMessage()); - } - return DeleteCommentResponseDto.of(commentId); - } + Long res = commentRepository.deleteByCommentId(commentId); + if (res != 1) { + throw new UnprocessableEntityException(Error.UNPROCESSABLE_ENTITY_DELETE_EXCEPTION, + Error.UNPROCESSABLE_ENTITY_DELETE_EXCEPTION.getMessage()); + } + return DeleteCommentResponseDto.of(commentId); + } + private void createNotificationInComment(Feed feed, Comment comment, User user) { + Notification notification = Notification.builder() //누군가가 내 피드에 댓글을 달았어요~ + .notiReciver(feed.getUser()) + .notiType(NotiType.COMMENTNOTI) + .notiMessage(comment.getUser().getNickname() + NotiType.COMMENTNOTI.getType()) + .isChecked(false) + .build(); + notification.updateLinkId(feed.getFeedId()); + notification.updateResponseId(comment.getCommentId()); + notification.updateRequestUserId(user.getUserId()); + notiRepository.save(notification); + if (feed.getUser().getFcmIsAllowed() && !notification.getNotiReceiver().getFcmToken().isEmpty()) { //푸시알림에 동의했을 경우. + messageQueueSender.pushSender( + FcmRequestDto.of( + notification.getNotiMessage(), + notification.getNotiReceiver().getFcmToken(), + notification.getNotiType(), + feed.getFeedId())); + } + } } diff --git a/src/main/java/org/winey/server/service/FcmService.java b/src/main/java/org/winey/server/service/FcmService.java new file mode 100644 index 0000000..c9b0363 --- /dev/null +++ b/src/main/java/org/winey/server/service/FcmService.java @@ -0,0 +1,116 @@ +package org.winey.server.service; + +import com.google.auth.oauth2.GoogleCredentials; +import com.google.firebase.FirebaseApp; +import com.google.firebase.FirebaseOptions; +import com.google.firebase.database.DatabaseReference; +import com.google.firebase.database.FirebaseDatabase; +import com.google.firebase.messaging.*; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.core.io.ClassPathResource; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.winey.server.service.message.FcmRequestDto; + +import javax.annotation.PostConstruct; +import java.io.IOException; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +@Slf4j +@Service +public class FcmService{ + + @Value("${fcm.key.path}") + private String FCM_PRIVATE_KEY_PATH; + + // + // 메시징만 권한 설정 + @Value("${fcm.key.scope}") + private String fireBaseScope; + + // fcm 기본 설정 진행 + @PostConstruct + public void init() { + try { + FirebaseOptions options = new FirebaseOptions.Builder() + .setCredentials( + GoogleCredentials + .fromStream(new ClassPathResource(FCM_PRIVATE_KEY_PATH).getInputStream()) + .createScoped(List.of(fireBaseScope))) + .build(); + if (FirebaseApp.getApps().isEmpty()) { + FirebaseApp.initializeApp(options); + log.info("Firebase application has been initialized"); + } + } catch (IOException e) { + log.error(e.getMessage()); + // spring 뜰때 알림 서버가 잘 동작하지 않는 것이므로 바로 죽임 + throw new RuntimeException(e.getMessage()); + } + } + + + // 알림 보내기 + public void sendByTokenList(List tokenList) { + + // 메시지 만들기 + List messages = tokenList.stream().map(token -> Message.builder() + .putData("time", LocalDateTime.now().toString()) + .setNotification(new Notification("제목", "알림 내용")) + .setToken(token) + .build()).collect(Collectors.toList()); + + // 요청에 대한 응답을 받을 response + BatchResponse response; + try { + + // 알림 발송 + response = FirebaseMessaging.getInstance().sendAll(messages); + + // 요청에 대한 응답 처리 + if (response.getFailureCount() > 0) { + List responses = response.getResponses(); + List failedTokens = new ArrayList<>(); + + for (int i = 0; i < responses.size(); i++) { + if (!responses.get(i).isSuccessful()) { + failedTokens.add(tokenList.get(i)); + } + } + log.error("List of tokens are not valid FCM token : " + failedTokens); + } + } catch (FirebaseMessagingException e) { + log.error("cannot send to memberList push message. error info : {}", e.getMessage()); + } + } + // 좋아요나 댓글 관련 알림 로직 작성 + @Async + public void sendByToken(FcmRequestDto wineyNotification) { + // 메시지 만들기 + Message message = Message.builder() + .putData("feedId", String.valueOf(wineyNotification.getFeedId())) + .putData("notiType", String.valueOf(wineyNotification.getType())) + .putData("token", wineyNotification.getToken()) + .setNotification(new Notification("위니 제국의 편지가 도착했어요.", wineyNotification.getMessage())) + .setToken(wineyNotification.getToken()) + .build(); + + // 요청에 대한 응답을 받을 response + String response; + try { + System.out.println("여까지는 왔다."); + // 알림 발송 + response = FirebaseMessaging.getInstance().send(message); + System.out.println(response); + } catch (FirebaseMessagingException e) { + log.error("푸시 알림을 보내다가 오류가 발생했습니다. error info : {}", e.getMessage()); + } + } + +} + diff --git a/src/main/java/org/winey/server/service/FeedLikeService.java b/src/main/java/org/winey/server/service/FeedLikeService.java index a722120..8e80f12 100644 --- a/src/main/java/org/winey/server/service/FeedLikeService.java +++ b/src/main/java/org/winey/server/service/FeedLikeService.java @@ -3,6 +3,8 @@ import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import org.winey.server.common.message.MessageQueueSender; +import org.winey.server.service.message.FcmRequestDto; import org.winey.server.controller.response.feedLike.CreateFeedLikeResponseDto; import org.winey.server.domain.feed.Feed; import org.winey.server.domain.feed.FeedLike; @@ -26,6 +28,8 @@ public class FeedLikeService { private final NotiRepository notiRepository; + private final MessageQueueSender messageQueueSender; + @Transactional public CreateFeedLikeResponseDto createFeedLike(Long userId, Long feedId, boolean feedLike) { User user = userRepository.findByUserId(userId) @@ -47,16 +51,7 @@ public CreateFeedLikeResponseDto createFeedLike(Long userId, Long feedId, boolea feedLikeRepository.save(like); if (user.getUserId() != feed.getUser().getUserId()){ //만약 좋아요를 누르는 사람이랑 피드 주인이랑 다르면 알림 생성 - Notification noti = Notification.builder() // 좋아요 알림 생성 - .notiType(NotiType.LIKENOTI) - .notiMessage(user.getNickname()+NotiType.LIKENOTI.getType()) - .isChecked(false) - .notiReciver(feed.getUser()) - .build(); - noti.updateLinkId(feedId); - noti.updateResponseId(like.getId()); - noti.updateRequestUserId(userId); - notiRepository.save(noti); + createNotificationInLike(feed, like, user); } } else { // 좋아요 취소 FeedLike deletedFeedLike = feedLikeRepository.deleteByFeedAndUser(feed, user).get(0); @@ -68,4 +63,25 @@ public CreateFeedLikeResponseDto createFeedLike(Long userId, Long feedId, boolea } return CreateFeedLikeResponseDto.of(feedId, feedLike, (long) feedLikeRepository.countByFeed(feed)); } + + private void createNotificationInLike(Feed feed, FeedLike like, User user) { + Notification notification = Notification.builder() // 좋아요 알림 생성 + .notiType(NotiType.LIKENOTI) + .notiMessage(user.getNickname()+NotiType.LIKENOTI.getType()) + .isChecked(false) + .notiReciver(feed.getUser()) + .build(); + notification.updateLinkId(feed.getFeedId()); + notification.updateResponseId(like.getId()); + notification.updateRequestUserId(user.getUserId()); + notiRepository.save(notification); + if (feed.getUser().getFcmIsAllowed() && !notification.getNotiReceiver().getFcmToken().isEmpty()) { //푸시알림에 동의했을 경우. 피드 주인에게 알림 + messageQueueSender.pushSender( + FcmRequestDto.of( + notification.getNotiMessage(), + notification.getNotiReceiver().getFcmToken(), + notification.getNotiType(), + feed.getFeedId())); + } + } } diff --git a/src/main/java/org/winey/server/service/NotiService.java b/src/main/java/org/winey/server/service/NotiService.java index 44b6695..68c96e3 100644 --- a/src/main/java/org/winey/server/service/NotiService.java +++ b/src/main/java/org/winey/server/service/NotiService.java @@ -23,6 +23,7 @@ import org.winey.server.domain.notification.Notification; import org.winey.server.domain.user.User; import org.winey.server.exception.Error; +import org.winey.server.exception.model.BadRequestException; import org.winey.server.exception.model.NotFoundException; import org.winey.server.infrastructure.GoalRepository; import org.winey.server.infrastructure.NotiRepository; diff --git a/src/main/java/org/winey/server/service/UserService.java b/src/main/java/org/winey/server/service/UserService.java index 59169d1..d6d93ce 100644 --- a/src/main/java/org/winey/server/service/UserService.java +++ b/src/main/java/org/winey/server/service/UserService.java @@ -4,6 +4,7 @@ import org.joda.time.LocalDateTime; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import org.winey.server.controller.request.UpdateFcmTokenDto; import org.winey.server.controller.request.UpdateUserNicknameDto; import org.winey.server.controller.response.user.UserResponseDto; import org.winey.server.controller.response.user.UserResponseGoalDto; @@ -13,6 +14,7 @@ import org.winey.server.domain.notification.Notification; import org.winey.server.domain.user.User; import org.winey.server.exception.Error; +import org.winey.server.exception.model.BadRequestException; import org.winey.server.exception.model.NotFoundException; import org.winey.server.infrastructure.GoalRepository; import org.winey.server.infrastructure.NotiRepository; @@ -29,14 +31,13 @@ public class UserService { private final UserRepository userRepository; private final GoalRepository goalRepository; - private final NotiRepository notiRepository; @Transactional(readOnly = true) public UserResponseDto getUser(Long userId) { User user = userRepository.findByUserId(userId) .orElseThrow(() -> new NotFoundException(Error.NOT_FOUND_USER_EXCEPTION, Error.NOT_FOUND_USER_EXCEPTION.getMessage())); - UserResponseUserDto userDto = UserResponseUserDto.of(user.getUserId(), user.getNickname(), user.getUserLevel().getName()); + UserResponseUserDto userDto = UserResponseUserDto.of(user.getUserId(), user.getNickname(), user.getUserLevel().getName(),user.getFcmIsAllowed()); List goalList = goalRepository.findByUserOrderByCreatedAtDesc(user); @@ -57,18 +58,27 @@ public UserResponseDto getUser(Long userId) { public void updateNickname(Long userId, UpdateUserNicknameDto requestDto) { User user = userRepository.findByUserId(userId) .orElseThrow(() -> new NotFoundException(Error.NOT_FOUND_USER_EXCEPTION, Error.NOT_FOUND_USER_EXCEPTION.getMessage())); -// List notifications = notiRepository.findByRequestUserId(userId); -// if (!notifications.isEmpty()) { -// notifications.forEach(notification -> { -// if (notification.getNotiType() == NotiType.COMMENTNOTI) { -// notification.updateNotiMessage(requestDto.getNickname() + NotiType.COMMENTNOTI.getType()); -// } else { -// notification.updateNotiMessage(requestDto.getNickname() + NotiType.LIKENOTI.getType()); -// } -// }); -// } user.updateNickname(requestDto.getNickname()); } + @Transactional + public void updateFcmToken(Long userId, UpdateFcmTokenDto updateFcmTokenDto){ + User user = userRepository.findByUserId(userId) + .orElseThrow(() -> new NotFoundException(Error.NOT_FOUND_USER_EXCEPTION, Error.NOT_FOUND_USER_EXCEPTION.getMessage())); + user.updateFcmToken(updateFcmTokenDto.getToken()); + } + + //푸시알림 동의 여부 수정 api + @Transactional + public Boolean allowedPushNotification(Long userId, Boolean fcmIsAllowed){ + User user = userRepository.findByUserId(userId) + .orElseThrow(()-> new NotFoundException(Error.NOT_FOUND_USER_EXCEPTION, Error.NOT_FOUND_USER_EXCEPTION.getMessage())); + if (fcmIsAllowed == user.getFcmIsAllowed()) { //같은 경우면 에러가 날 수 있으니 에러 띄움. + throw new BadRequestException(Error.REQUEST_VALIDATION_EXCEPTION, + Error.REQUEST_VALIDATION_EXCEPTION.getMessage()); + } + user.updateFcmIsAllowed(fcmIsAllowed); + return fcmIsAllowed; + } public Boolean checkNicknameDuplicate(String nickname) { return userRepository.existsByNickname(nickname); diff --git a/src/main/java/org/winey/server/service/auth/AuthService.java b/src/main/java/org/winey/server/service/auth/AuthService.java index 6d5087a..4ce32b1 100644 --- a/src/main/java/org/winey/server/service/auth/AuthService.java +++ b/src/main/java/org/winey/server/service/auth/AuthService.java @@ -51,7 +51,6 @@ public class AuthService { public SignInResponseDto signIn(String socialAccessToken, SignInRequestDto requestDto) { SocialType socialType = SocialType.valueOf(requestDto.getSocialType()); String socialId = login(socialType, socialAccessToken); - System.out.println("여기2"); Boolean isRegistered = userRepository.existsBySocialIdAndSocialType(socialId, socialType); @@ -65,6 +64,7 @@ public SignInResponseDto signIn(String socialAccessToken, SignInRequestDto reque .nickname("위니"+randomString) .socialId(socialId) .socialType(socialType).build(); + newUser.updateFcmIsAllowed(true); //신규 유저면 true박고 userRepository.save(newUser); @@ -84,10 +84,12 @@ public SignInResponseDto signIn(String socialAccessToken, SignInRequestDto reque // jwt 발급 (액세스 토큰, 리프레쉬 토큰) String accessToken = jwtService.issuedToken(String.valueOf(user.getUserId()), TOKEN_EXPIRATION_TIME_ACCESS); String refreshToken = jwtService.issuedToken(String.valueOf(user.getUserId()), TOKEN_EXPIRATION_TIME_REFRESH); + String fcmToken = requestDto.getFcmToken(); user.updateRefreshToken(refreshToken); + user.updateFcmToken(fcmToken); - return SignInResponseDto.of(user.getUserId(), accessToken, refreshToken, isRegistered); + return SignInResponseDto.of(user.getUserId(), accessToken, refreshToken, fcmToken, isRegistered,user.getFcmIsAllowed()); } @Transactional @@ -110,8 +112,8 @@ public TokenResponseDto issueToken(String refreshToken) { public void signOut(Long userId) { User user = userRepository.findByUserId(userId) .orElseThrow(() -> new NotFoundException(Error.NOT_FOUND_USER_EXCEPTION, Error.NOT_FOUND_USER_EXCEPTION.getMessage())); - user.updateRefreshToken(null); + user.updateFcmToken(null); } private String login(SocialType socialType, String socialAccessToken) { @@ -119,7 +121,6 @@ private String login(SocialType socialType, String socialAccessToken) { return appleSignInService.getAppleId(socialAccessToken); } else if (socialType.toString() == "KAKAO") { - System.out.println("여기1"); return kakaoSignInService.getKaKaoId(socialAccessToken); } else{ diff --git a/src/main/java/org/winey/server/service/message/FcmRequestDto.java b/src/main/java/org/winey/server/service/message/FcmRequestDto.java new file mode 100644 index 0000000..9c0bced --- /dev/null +++ b/src/main/java/org/winey/server/service/message/FcmRequestDto.java @@ -0,0 +1,26 @@ +package org.winey.server.service.message; + +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import org.winey.server.domain.notification.NotiType; +import org.winey.server.domain.notification.Notification; + +import java.io.Serializable; + +import javax.annotation.Nullable; + +@Getter +@NoArgsConstructor(access = AccessLevel.PRIVATE) +@AllArgsConstructor(access = AccessLevel.PRIVATE) +public class FcmRequestDto implements Serializable { + private String message; + private String token; //fcm + private NotiType type; + private Long feedId; + + public static FcmRequestDto of(String message, String token, NotiType type, Long feedId){ + return new FcmRequestDto(message,token,type,feedId); + } +} From 75689d3e8ef070bdc0a5c7e27e0a8b27027ff799 Mon Sep 17 00:00:00 2001 From: Seonheui Jeon <88873302+funnysunny08@users.noreply.github.com> Date: Fri, 17 Nov 2023 21:27:02 +0900 Subject: [PATCH 03/78] Update cd.yml --- .github/workflows/cd.yml | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index 8af0c4b..287a958 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -36,13 +36,23 @@ jobs: cd ./src/main/resources # application.yml 파일 생성 - touch ./application.yml + touch ./application.yml # GitHub-Actions 에서 설정한 값을 application.yml 파일에 쓰기 echo "${{ secrets.WINEY_APPLICATION }}" >> ./application.yml # application.yml 파일 확인 cat ./application.yml + + # winey-firebase.json 파일 생성 + touch ./winey-firebase.json + + # GitHub-Actions 에서 설정한 값을 winey-firebase.json 파일에 쓰기 + echo "${{ secrets.WINEY_FIREBASE }}" >> ./winey-firebase.json + + # winey-firebase.json 파일 확인 + cat ./winey-firebase.json + shell: bash # 이 워크플로우는 gradle build From f922e3e452c98c58b1bd011df21141a9c1f7438e Mon Sep 17 00:00:00 2001 From: Seonheui Jeon <88873302+funnysunny08@users.noreply.github.com> Date: Fri, 17 Nov 2023 21:27:29 +0900 Subject: [PATCH 04/78] Update gradle.yml --- .github/workflows/gradle.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index ba3f44c..cbc9901 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -54,6 +54,16 @@ jobs: # application.yml 파일 확인 cat ./application.yml + + # winey-firebase.json 파일 생성 + touch ./winey-firebase.json + + # GitHub-Actions 에서 설정한 값을 winey-firebase.json 파일에 쓰기 + echo "${{ secrets.WINEY_FIREBASE }}" >> ./winey-firebase.json + + # winey-firebase.json 파일 확인 + cat ./winey-firebase.json + shell: bash # 이 워크플로우는 gradle build From fe3ab876e1c90895cc405b5bc7745c4bd93b671e Mon Sep 17 00:00:00 2001 From: Sunny Date: Fri, 17 Nov 2023 21:51:33 +0900 Subject: [PATCH 05/78] =?UTF-8?q?Revert=20"[Feat]=20fcm=20=EA=B4=80?= =?UTF-8?q?=EB=A0=A8=20=EB=A1=9C=EC=A7=81=20=EA=B5=AC=ED=98=84=20(#187)"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 5672dbdd2ab278e3dab128e916f24256ff1295e4. --- .gitignore | 6 - build.gradle | 7 - .../common/message/MessageQueueReceiver.java | 82 ------------ .../common/message/MessageQueueSender.java | 46 ------- .../server/controller/UserController.java | 22 --- .../request/UpdateAllowedPushDto.java | 16 --- .../controller/request/UpdateFcmTokenDto.java | 20 --- .../request/auth/SignInRequestDto.java | 2 - .../response/auth/SignInResponseDto.java | 6 +- .../response/user/UserResponseUserDto.java | 5 +- .../org/winey/server/domain/user/User.java | 11 -- .../org/winey/server/exception/Error.java | 1 - .../org/winey/server/exception/Success.java | 4 +- .../winey/server/service/CommentService.java | 125 +++++++----------- .../org/winey/server/service/FcmService.java | 116 ---------------- .../winey/server/service/FeedLikeService.java | 36 ++--- .../org/winey/server/service/NotiService.java | 1 - .../org/winey/server/service/UserService.java | 34 ++--- .../server/service/auth/AuthService.java | 9 +- .../server/service/message/FcmRequestDto.java | 26 ---- 20 files changed, 81 insertions(+), 494 deletions(-) delete mode 100644 src/main/java/org/winey/server/common/message/MessageQueueReceiver.java delete mode 100644 src/main/java/org/winey/server/common/message/MessageQueueSender.java delete mode 100644 src/main/java/org/winey/server/controller/request/UpdateAllowedPushDto.java delete mode 100644 src/main/java/org/winey/server/controller/request/UpdateFcmTokenDto.java delete mode 100644 src/main/java/org/winey/server/service/FcmService.java delete mode 100644 src/main/java/org/winey/server/service/message/FcmRequestDto.java diff --git a/.gitignore b/.gitignore index 162f436..9e143b5 100644 --- a/.gitignore +++ b/.gitignore @@ -43,11 +43,5 @@ application.yaml ### DS_Store .DS_Store -### fcm -winey-firebase.json - -### banner.txt -banner.txt - ### log file ### logs/ \ No newline at end of file diff --git a/build.gradle b/build.gradle index e6ddff3..aa7316a 100644 --- a/build.gradle +++ b/build.gradle @@ -56,13 +56,6 @@ dependencies { implementation group: 'io.jsonwebtoken', name: 'jjwt-jackson', version: '0.11.2' implementation 'org.springframework.cloud:spring-cloud-starter-openfeign:3.1.7' - //FCM - implementation group: 'com.google.firebase', name: 'firebase-admin', version: '6.8.1' - - //rabbitmq - implementation 'org.springframework.boot:spring-boot-starter-amqp' - - // ShedLock implementation 'net.javacrumbs.shedlock:shedlock-spring:4.14.0' implementation 'net.javacrumbs.shedlock:shedlock-provider-jdbc-template:4.14.0' diff --git a/src/main/java/org/winey/server/common/message/MessageQueueReceiver.java b/src/main/java/org/winey/server/common/message/MessageQueueReceiver.java deleted file mode 100644 index dd1c546..0000000 --- a/src/main/java/org/winey/server/common/message/MessageQueueReceiver.java +++ /dev/null @@ -1,82 +0,0 @@ -package org.winey.server.common.message; - -import lombok.AllArgsConstructor; -import org.springframework.amqp.core.ExchangeTypes; -import org.springframework.amqp.rabbit.annotation.Exchange; -import org.springframework.amqp.rabbit.annotation.Queue; -import org.springframework.amqp.rabbit.annotation.QueueBinding; -import org.springframework.amqp.rabbit.annotation.RabbitListener; -import org.springframework.stereotype.Component; -import org.winey.server.domain.notification.Notification; -import org.winey.server.service.FcmService; -import org.winey.server.service.message.FcmRequestDto; - -import java.io.*; - -@Component -@AllArgsConstructor -public class MessageQueueReceiver { - - private final FcmService fcmService; - - @RabbitListener(bindings = @QueueBinding( - exchange = @Exchange(name = "like", type = ExchangeTypes.TOPIC), - value = @Queue(name = "like-notification"), - key = "like-noti") - ) - public void likeReceiver(byte[] likeNoti){ - System.out.println("좋아요 noti receiver"); - ByteArrayInputStream bis = new ByteArrayInputStream(likeNoti); - ObjectInput in = null; - try { - in = new ObjectInputStream(bis); - Object obj = in.readObject(); - System.out.println("여기까진 오는가"); - if (obj instanceof FcmRequestDto){ - FcmRequestDto notification = (FcmRequestDto) obj; - fcmService.sendByToken(notification); - } - }catch (IOException | ClassNotFoundException e){ - e.printStackTrace(); - }finally { - try{ - bis.close(); - if (in != null){ - in.close(); - } - }catch (IOException ex){ - ex.printStackTrace(); - } - } - } - - @RabbitListener(bindings = @QueueBinding( - exchange = @Exchange(name = "comment", type = ExchangeTypes.TOPIC), - value = @Queue(name = "comment-notification"), - key = "comment-noti") - ) - public void commentReceiver(byte[] commentNoti){ - System.out.println("댓글 noti receiver"); - ByteArrayInputStream bis = new ByteArrayInputStream(commentNoti); - ObjectInput in = null; - try { - in = new ObjectInputStream(bis); - Object obj = in.readObject(); - if (obj instanceof Notification){ - FcmRequestDto notification = (FcmRequestDto) obj; - fcmService.sendByToken(notification); - } - }catch (IOException | ClassNotFoundException e){ - e.printStackTrace(); - }finally { - try{ - bis.close(); - if (in != null){ - in.close(); - } - }catch (IOException ex){ - ex.printStackTrace(); - } - } - } -} diff --git a/src/main/java/org/winey/server/common/message/MessageQueueSender.java b/src/main/java/org/winey/server/common/message/MessageQueueSender.java deleted file mode 100644 index a26d968..0000000 --- a/src/main/java/org/winey/server/common/message/MessageQueueSender.java +++ /dev/null @@ -1,46 +0,0 @@ -package org.winey.server.common.message; - -import com.google.cloud.ByteArray; -import lombok.AllArgsConstructor; -import org.springframework.amqp.AmqpException; -import org.springframework.amqp.rabbit.core.RabbitTemplate; -import org.springframework.context.annotation.Configuration; -import org.springframework.scheduling.annotation.Scheduled; -import org.springframework.transaction.annotation.Transactional; -import org.winey.server.domain.notification.NotiType; -import org.winey.server.domain.notification.Notification; -import org.winey.server.service.message.FcmRequestDto; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.ObjectOutputStream; -import java.time.LocalDateTime; - -@Configuration -@AllArgsConstructor -public class MessageQueueSender { - - final RabbitTemplate rabbitTemplate; - - - public void pushSender(FcmRequestDto notification){ - System.out.println("여기는 오는건가?"); - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - ObjectOutputStream out; - try { - out = new ObjectOutputStream(bos); - out.writeObject(notification); - out.flush(); - byte[] data = bos.toByteArray(); - if (notification.getType() == NotiType.COMMENTNOTI) { - rabbitTemplate.convertAndSend("comment", "comment-noti", data); - } else if (notification.getType() == NotiType.LIKENOTI) { - rabbitTemplate.convertAndSend("like", "like-noti", data); - } - }catch (AmqpException e){ - System.out.println("메시지 전송 중 오류가 발생했습니다." + e.getMessage()); - }catch (IOException e){ - e.printStackTrace(); - } - } -} diff --git a/src/main/java/org/winey/server/controller/UserController.java b/src/main/java/org/winey/server/controller/UserController.java index aa3fc9b..c38a023 100644 --- a/src/main/java/org/winey/server/controller/UserController.java +++ b/src/main/java/org/winey/server/controller/UserController.java @@ -7,8 +7,6 @@ import org.springframework.web.bind.annotation.*; import org.winey.server.common.dto.ApiResponse; import org.winey.server.config.resolver.UserId; -import org.winey.server.controller.request.UpdateAllowedPushDto; -import org.winey.server.controller.request.UpdateFcmTokenDto; import org.winey.server.controller.request.UpdateUserNicknameDto; import org.winey.server.controller.response.user.UserResponseDto; import org.winey.server.exception.Error; @@ -57,24 +55,4 @@ public ApiResponse checkNicknameDuplicate(@RequestParam String nickname) { }; return ApiResponse.success(Success.CHECK_NICKNAME_DUPLICATE_SUCCESS, result); } - - @PatchMapping("/fcmtoken") - @ResponseStatus(HttpStatus.OK) - @Operation(summary = "fcm토큰 변경 API", description = "유저의 fcm token을 변경합니다.") - public ApiResponse updateFcmToken (@UserId Long userId, @RequestBody @Valid final UpdateFcmTokenDto requestDto) { - userService.updateFcmToken(userId, requestDto); - return ApiResponse.success(Success.FCM_TOKEN_UPDATE_SUCCESS); - } - - @PatchMapping("/notification") - @ResponseStatus(HttpStatus.OK) - @Operation(summary = "푸시 알림 동의 변경 API", description = "유저의 알림 동의 여부를 변경합니다.") - public ApiResponse updateAllowedNotification (@UserId Long userId, @RequestBody @Valid final UpdateAllowedPushDto requestDto) { - Map result = new HashMap() { - { - put("isAllowed", userService.allowedPushNotification(userId, requestDto.getAllowedPush())); - } - }; - return ApiResponse.success(Success.UPDATE_PUSH_ALLOWED_SUCCESS, result); - } } diff --git a/src/main/java/org/winey/server/controller/request/UpdateAllowedPushDto.java b/src/main/java/org/winey/server/controller/request/UpdateAllowedPushDto.java deleted file mode 100644 index 02c81c2..0000000 --- a/src/main/java/org/winey/server/controller/request/UpdateAllowedPushDto.java +++ /dev/null @@ -1,16 +0,0 @@ -package org.winey.server.controller.request; - -import javax.validation.constraints.NotNull; - -import lombok.AccessLevel; -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; - -@Getter -@NoArgsConstructor(access = AccessLevel.PRIVATE) -@AllArgsConstructor -public class UpdateAllowedPushDto { - @NotNull - private Boolean allowedPush; -} diff --git a/src/main/java/org/winey/server/controller/request/UpdateFcmTokenDto.java b/src/main/java/org/winey/server/controller/request/UpdateFcmTokenDto.java deleted file mode 100644 index 5f36fdd..0000000 --- a/src/main/java/org/winey/server/controller/request/UpdateFcmTokenDto.java +++ /dev/null @@ -1,20 +0,0 @@ -package org.winey.server.controller.request; - -import javax.validation.constraints.NotNull; - -import lombok.AccessLevel; -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; - -@Getter -@AllArgsConstructor -@NoArgsConstructor(access = AccessLevel.PRIVATE) -public class UpdateFcmTokenDto { - @NotNull - private String token; - - public UpdateFcmTokenDto of(String token) { - return new UpdateFcmTokenDto(token); - } -} diff --git a/src/main/java/org/winey/server/controller/request/auth/SignInRequestDto.java b/src/main/java/org/winey/server/controller/request/auth/SignInRequestDto.java index 17c77d9..773ae0f 100644 --- a/src/main/java/org/winey/server/controller/request/auth/SignInRequestDto.java +++ b/src/main/java/org/winey/server/controller/request/auth/SignInRequestDto.java @@ -13,6 +13,4 @@ public class SignInRequestDto { @NotNull private String socialType; - - private String fcmToken; } \ No newline at end of file diff --git a/src/main/java/org/winey/server/controller/response/auth/SignInResponseDto.java b/src/main/java/org/winey/server/controller/response/auth/SignInResponseDto.java index 255fe14..51402da 100644 --- a/src/main/java/org/winey/server/controller/response/auth/SignInResponseDto.java +++ b/src/main/java/org/winey/server/controller/response/auth/SignInResponseDto.java @@ -12,11 +12,9 @@ public class SignInResponseDto { private Long userId; private String accessToken; private String refreshToken; - private String fcmToken; private Boolean isRegistered; - private Boolean fcmIsAllowed; - public static SignInResponseDto of(Long userId, String accessToken, String refreshToken, String fcmToken, Boolean isRegistered, Boolean fcmIsAllowed) { - return new SignInResponseDto(userId, accessToken, refreshToken, fcmToken, isRegistered, fcmIsAllowed); + public static SignInResponseDto of(Long userId, String accessToken, String refreshToken, Boolean isRegistered) { + return new SignInResponseDto(userId, accessToken, refreshToken, isRegistered); } } diff --git a/src/main/java/org/winey/server/controller/response/user/UserResponseUserDto.java b/src/main/java/org/winey/server/controller/response/user/UserResponseUserDto.java index ad40ac9..49e5d6e 100644 --- a/src/main/java/org/winey/server/controller/response/user/UserResponseUserDto.java +++ b/src/main/java/org/winey/server/controller/response/user/UserResponseUserDto.java @@ -12,9 +12,8 @@ public class UserResponseUserDto { private Long userId; private String nickname; private String userLevel; - private Boolean fcmIsAllowed; - public static UserResponseUserDto of(Long userId, String nickname, String userLevel, Boolean fcmIsAllowed) { - return new UserResponseUserDto(userId, nickname, userLevel, fcmIsAllowed); + public static UserResponseUserDto of(Long userId, String nickname, String userLevel) { + return new UserResponseUserDto(userId, nickname, userLevel); } } \ No newline at end of file diff --git a/src/main/java/org/winey/server/domain/user/User.java b/src/main/java/org/winey/server/domain/user/User.java index e3ad858..55f76ba 100644 --- a/src/main/java/org/winey/server/domain/user/User.java +++ b/src/main/java/org/winey/server/domain/user/User.java @@ -3,7 +3,6 @@ import lombok.*; import org.hibernate.annotations.ColumnDefault; import org.hibernate.annotations.DynamicInsert; -import org.hibernate.annotations.DynamicUpdate; import org.winey.server.domain.AuditingTimeEntity; import org.winey.server.domain.comment.Comment; import org.winey.server.domain.feed.Feed; @@ -42,12 +41,6 @@ public class User extends AuditingTimeEntity { @Enumerated(EnumType.STRING) private SocialType socialType; - @Column(nullable = true) - private String fcmToken; - - @Column(nullable = true) - private Boolean fcmIsAllowed = true; - @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.REMOVE, mappedBy = "user", orphanRemoval = true) private List goals; @@ -84,8 +77,4 @@ public void updateRefreshToken(String refreshToken) { public void updateNickname(String nickname) { this.nickname = nickname; } - - public void updateFcmToken(String fcmToken) { this.fcmToken = fcmToken; } - - public void updateFcmIsAllowed(Boolean isAllowed){this.fcmIsAllowed = isAllowed;} } diff --git a/src/main/java/org/winey/server/exception/Error.java b/src/main/java/org/winey/server/exception/Error.java index 343ba9b..4fbfd47 100644 --- a/src/main/java/org/winey/server/exception/Error.java +++ b/src/main/java/org/winey/server/exception/Error.java @@ -13,7 +13,6 @@ public enum Error { * 400 BAD REQUEST */ REQUEST_VALIDATION_EXCEPTION(HttpStatus.BAD_REQUEST, "잘못된 요청입니다"), - INVALID_FCMTOKEN_EXCEPTION(HttpStatus.BAD_REQUEST, "푸시알림 동의를 했는데, fcmtoken이 null이거나 문제가 있습니다."), LOGIN_SUCCESS(HttpStatus.OK, "로그인에 성공했습니다."), INVALID_PASSWORD_EXCEPTION(HttpStatus.BAD_REQUEST, "잘못된 비밀번호가 입력됐습니다."), NOT_FOUND_IMAGE_EXCEPTION(HttpStatus.BAD_REQUEST, "잘못된 이미지 파일입니다"), diff --git a/src/main/java/org/winey/server/exception/Success.java b/src/main/java/org/winey/server/exception/Success.java index 15fdc40..c451f94 100644 --- a/src/main/java/org/winey/server/exception/Success.java +++ b/src/main/java/org/winey/server/exception/Success.java @@ -23,7 +23,6 @@ public enum Success { RE_ISSUE_TOKEN_SUCCESS(HttpStatus.OK, "토큰 재발급 성공"), UPDATE_NICKNAME_SUCCESS(HttpStatus.OK, "닉네임 변경 성공"), - UPDATE_PUSH_ALLOWED_SUCCESS(HttpStatus.OK, "푸시 알림 동의 여부 변경 성공"), CHECK_NICKNAME_DUPLICATE_SUCCESS(HttpStatus.OK, "닉네임 중복 확인 성공"), CHECK_NEW_NOTIFICATION_SUCCESS(HttpStatus.OK, "새 알림 여부 조회 성공"), BLOCK_USER_SUCCESS(HttpStatus.OK, "유저 차단 성공"), @@ -45,8 +44,7 @@ public enum Success { */ DELETE_FEED_SUCCESS(HttpStatus.NO_CONTENT, "피드가 정상적으로 삭제되었습니다."), DELETE_COMMENT_SUCCESS(HttpStatus.NO_CONTENT, "댓글이 정상적으로 삭제되었습니다."), - NOTIFICATION_EMPTY_SUCCESS(HttpStatus.NO_CONTENT, "알림이 한통도 없어요. 힝~구"), - FCM_TOKEN_UPDATE_SUCCESS(HttpStatus.NO_CONTENT, "fcm 수정이 완료 됐습니다."), + NOTIFICATION_EMPTY_SUCCESS(HttpStatus.NOT_FOUND, "알림이 한통도 없어요. 힝~구"), DELETE_USER_SUCCESS(HttpStatus.NO_CONTENT, "회원 탈퇴가 정상적으로 이루어졌습니다.") ; diff --git a/src/main/java/org/winey/server/service/CommentService.java b/src/main/java/org/winey/server/service/CommentService.java index 423d18b..5cae39c 100644 --- a/src/main/java/org/winey/server/service/CommentService.java +++ b/src/main/java/org/winey/server/service/CommentService.java @@ -1,17 +1,13 @@ package org.winey.server.service; import lombok.RequiredArgsConstructor; - import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import org.winey.server.common.message.MessageQueueSender; import org.winey.server.controller.response.comment.CommentResponseDto; import org.winey.server.controller.response.comment.DeleteCommentResponseDto; import org.winey.server.domain.comment.Comment; import org.winey.server.domain.notification.NotiType; import org.winey.server.domain.notification.Notification; -import org.winey.server.exception.model.BadRequestException; -import org.winey.server.exception.model.CustomException; import org.winey.server.exception.model.UnauthorizedException; import org.winey.server.exception.model.UnprocessableEntityException; import org.winey.server.infrastructure.CommentRepository; @@ -22,89 +18,68 @@ import org.winey.server.infrastructure.FeedRepository; import org.winey.server.infrastructure.NotiRepository; import org.winey.server.infrastructure.UserRepository; -import org.winey.server.service.message.FcmRequestDto; + @Service @RequiredArgsConstructor public class CommentService { - private final UserRepository userRepository; - private final FeedRepository feedRepository; - private final CommentRepository commentRepository; - private final NotiRepository notiRepository; + private final UserRepository userRepository; + private final FeedRepository feedRepository; + private final CommentRepository commentRepository; + private final NotiRepository notiRepository; - private final MessageQueueSender messageQueueSender; + @Transactional + public CommentResponseDto createComment(Long userId, Long feedId, String content) { + User user = userRepository.findByUserId(userId) + .orElseThrow(() -> new NotFoundException(Error.NOT_FOUND_USER_EXCEPTION, Error.NOT_FOUND_USER_EXCEPTION.getMessage())); + Feed feed = feedRepository.findByFeedId(feedId) + .orElseThrow(() -> new NotFoundException(Error.NOT_FOUND_FEED_EXCEPTION, Error.NOT_FOUND_FEED_EXCEPTION.getMessage())); - @Transactional - public CommentResponseDto createComment(Long userId, Long feedId, String content) { - User user = userRepository.findByUserId(userId) - .orElseThrow(() -> new NotFoundException(Error.NOT_FOUND_USER_EXCEPTION, - Error.NOT_FOUND_USER_EXCEPTION.getMessage())); - Feed feed = feedRepository.findByFeedId(feedId) - .orElseThrow(() -> new NotFoundException(Error.NOT_FOUND_FEED_EXCEPTION, - Error.NOT_FOUND_FEED_EXCEPTION.getMessage())); - Comment comment = Comment.builder() - .user(user) - .content(content) - .feed(feed) - .build(); - commentRepository.save(comment); + Comment comment = Comment.builder() + .user(user) + .content(content) + .feed(feed) + .build(); + commentRepository.save(comment); - if (user.getUserId() != feed.getUser().getUserId()) { - createNotificationInComment(feed, comment, user); //알림 생성 후 message queue에 task 등록 - } - return CommentResponseDto.of(comment.getCommentId(), userId, user.getNickname(), comment.getContent(), - user.getUserLevel().getLevelNumber(), comment.getCreatedAt()); - } + if (user.getUserId() != feed.getUser().getUserId()) { + Notification notification = Notification.builder() //누군가가 내 피드에 댓글을 달았어요~ + .notiReciver(feed.getUser()) + .notiType(NotiType.COMMENTNOTI) + .notiMessage(comment.getUser().getNickname() + NotiType.COMMENTNOTI.getType()) + .isChecked(false) + .build(); + notification.updateLinkId(feedId); + notification.updateResponseId(comment.getCommentId()); + notification.updateRequestUserId(userId); + notiRepository.save(notification); + } + return CommentResponseDto.of(comment.getCommentId(), userId, user.getNickname(), comment.getContent(),user.getUserLevel().getLevelNumber(),comment.getCreatedAt()); + } - @Transactional - public DeleteCommentResponseDto deleteComment(Long userId, Long commentId) { - User user = userRepository.findByUserId(userId) - .orElseThrow(() -> new NotFoundException(Error.NOT_FOUND_USER_EXCEPTION, - Error.NOT_FOUND_USER_EXCEPTION.getMessage())); - Comment wantDeleteComment = commentRepository.findByCommentId(commentId) - .orElseThrow(() -> new NotFoundException(Error.NOT_FOUND_COMMENT_EXCEPTION, - Error.NOT_FOUND_COMMENT_EXCEPTION.getMessage())); - Feed commentFeed = feedRepository.findByFeedId(wantDeleteComment.getFeed().getFeedId()) - .orElseThrow(() -> new NotFoundException(Error.NOT_FOUND_FEED_EXCEPTION, - Error.NOT_FOUND_FEED_EXCEPTION.getMessage())); + @Transactional + public DeleteCommentResponseDto deleteComment(Long userId, Long commentId){ + User user = userRepository.findByUserId(userId) + .orElseThrow(()-> new NotFoundException(Error.NOT_FOUND_USER_EXCEPTION, Error.NOT_FOUND_USER_EXCEPTION.getMessage())); + Comment wantDeleteComment = commentRepository.findByCommentId(commentId) + .orElseThrow(()-> new NotFoundException(Error.NOT_FOUND_COMMENT_EXCEPTION, Error.NOT_FOUND_COMMENT_EXCEPTION.getMessage())); + Feed commentFeed = feedRepository.findByFeedId(wantDeleteComment.getFeed().getFeedId()) + .orElseThrow(() -> new NotFoundException(Error.NOT_FOUND_FEED_EXCEPTION, Error.NOT_FOUND_FEED_EXCEPTION.getMessage())); - if (user.getUserId() != wantDeleteComment.getUser().getUserId() && user.getUserId() != commentFeed.getUser() - .getUserId()) { //만약 현재 유저가 피드 주인도 아니고, 댓글 주인도 아니면? - throw new UnauthorizedException(Error.DELETE_UNAUTHORIZED, - Error.DELETE_UNAUTHORIZED.getMessage()); //지울 수 있는 권한이 없다. - } + if (user.getUserId() != wantDeleteComment.getUser().getUserId() && user.getUserId() != commentFeed.getUser().getUserId() ){ //만약 현재 유저가 피드 주인도 아니고, 댓글 주인도 아니면? + throw new UnauthorizedException(Error.DELETE_UNAUTHORIZED, Error.DELETE_UNAUTHORIZED.getMessage()); //지울 수 있는 권한이 없다. + } - // 관련 알림 삭제 - notiRepository.deleteByNotiTypeAndResponseId(NotiType.COMMENTNOTI, wantDeleteComment.getCommentId()); + // 관련 알림 삭제 + notiRepository.deleteByNotiTypeAndResponseId(NotiType.COMMENTNOTI, wantDeleteComment.getCommentId()); - Long res = commentRepository.deleteByCommentId(commentId); - if (res != 1) { - throw new UnprocessableEntityException(Error.UNPROCESSABLE_ENTITY_DELETE_EXCEPTION, - Error.UNPROCESSABLE_ENTITY_DELETE_EXCEPTION.getMessage()); - } - return DeleteCommentResponseDto.of(commentId); - } + Long res = commentRepository.deleteByCommentId(commentId); + if (res != 1){ + throw new UnprocessableEntityException(Error.UNPROCESSABLE_ENTITY_DELETE_EXCEPTION,Error.UNPROCESSABLE_ENTITY_DELETE_EXCEPTION.getMessage()); + } + return DeleteCommentResponseDto.of(commentId); + } - private void createNotificationInComment(Feed feed, Comment comment, User user) { - Notification notification = Notification.builder() //누군가가 내 피드에 댓글을 달았어요~ - .notiReciver(feed.getUser()) - .notiType(NotiType.COMMENTNOTI) - .notiMessage(comment.getUser().getNickname() + NotiType.COMMENTNOTI.getType()) - .isChecked(false) - .build(); - notification.updateLinkId(feed.getFeedId()); - notification.updateResponseId(comment.getCommentId()); - notification.updateRequestUserId(user.getUserId()); - notiRepository.save(notification); - if (feed.getUser().getFcmIsAllowed() && !notification.getNotiReceiver().getFcmToken().isEmpty()) { //푸시알림에 동의했을 경우. - messageQueueSender.pushSender( - FcmRequestDto.of( - notification.getNotiMessage(), - notification.getNotiReceiver().getFcmToken(), - notification.getNotiType(), - feed.getFeedId())); - } - } } diff --git a/src/main/java/org/winey/server/service/FcmService.java b/src/main/java/org/winey/server/service/FcmService.java deleted file mode 100644 index c9b0363..0000000 --- a/src/main/java/org/winey/server/service/FcmService.java +++ /dev/null @@ -1,116 +0,0 @@ -package org.winey.server.service; - -import com.google.auth.oauth2.GoogleCredentials; -import com.google.firebase.FirebaseApp; -import com.google.firebase.FirebaseOptions; -import com.google.firebase.database.DatabaseReference; -import com.google.firebase.database.FirebaseDatabase; -import com.google.firebase.messaging.*; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.core.io.ClassPathResource; -import org.springframework.scheduling.annotation.Async; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; -import org.winey.server.service.message.FcmRequestDto; - -import javax.annotation.PostConstruct; -import java.io.IOException; -import java.time.LocalDateTime; -import java.util.ArrayList; -import java.util.List; -import java.util.stream.Collectors; - -@Slf4j -@Service -public class FcmService{ - - @Value("${fcm.key.path}") - private String FCM_PRIVATE_KEY_PATH; - - // - // 메시징만 권한 설정 - @Value("${fcm.key.scope}") - private String fireBaseScope; - - // fcm 기본 설정 진행 - @PostConstruct - public void init() { - try { - FirebaseOptions options = new FirebaseOptions.Builder() - .setCredentials( - GoogleCredentials - .fromStream(new ClassPathResource(FCM_PRIVATE_KEY_PATH).getInputStream()) - .createScoped(List.of(fireBaseScope))) - .build(); - if (FirebaseApp.getApps().isEmpty()) { - FirebaseApp.initializeApp(options); - log.info("Firebase application has been initialized"); - } - } catch (IOException e) { - log.error(e.getMessage()); - // spring 뜰때 알림 서버가 잘 동작하지 않는 것이므로 바로 죽임 - throw new RuntimeException(e.getMessage()); - } - } - - - // 알림 보내기 - public void sendByTokenList(List tokenList) { - - // 메시지 만들기 - List messages = tokenList.stream().map(token -> Message.builder() - .putData("time", LocalDateTime.now().toString()) - .setNotification(new Notification("제목", "알림 내용")) - .setToken(token) - .build()).collect(Collectors.toList()); - - // 요청에 대한 응답을 받을 response - BatchResponse response; - try { - - // 알림 발송 - response = FirebaseMessaging.getInstance().sendAll(messages); - - // 요청에 대한 응답 처리 - if (response.getFailureCount() > 0) { - List responses = response.getResponses(); - List failedTokens = new ArrayList<>(); - - for (int i = 0; i < responses.size(); i++) { - if (!responses.get(i).isSuccessful()) { - failedTokens.add(tokenList.get(i)); - } - } - log.error("List of tokens are not valid FCM token : " + failedTokens); - } - } catch (FirebaseMessagingException e) { - log.error("cannot send to memberList push message. error info : {}", e.getMessage()); - } - } - // 좋아요나 댓글 관련 알림 로직 작성 - @Async - public void sendByToken(FcmRequestDto wineyNotification) { - // 메시지 만들기 - Message message = Message.builder() - .putData("feedId", String.valueOf(wineyNotification.getFeedId())) - .putData("notiType", String.valueOf(wineyNotification.getType())) - .putData("token", wineyNotification.getToken()) - .setNotification(new Notification("위니 제국의 편지가 도착했어요.", wineyNotification.getMessage())) - .setToken(wineyNotification.getToken()) - .build(); - - // 요청에 대한 응답을 받을 response - String response; - try { - System.out.println("여까지는 왔다."); - // 알림 발송 - response = FirebaseMessaging.getInstance().send(message); - System.out.println(response); - } catch (FirebaseMessagingException e) { - log.error("푸시 알림을 보내다가 오류가 발생했습니다. error info : {}", e.getMessage()); - } - } - -} - diff --git a/src/main/java/org/winey/server/service/FeedLikeService.java b/src/main/java/org/winey/server/service/FeedLikeService.java index 8e80f12..a722120 100644 --- a/src/main/java/org/winey/server/service/FeedLikeService.java +++ b/src/main/java/org/winey/server/service/FeedLikeService.java @@ -3,8 +3,6 @@ import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import org.winey.server.common.message.MessageQueueSender; -import org.winey.server.service.message.FcmRequestDto; import org.winey.server.controller.response.feedLike.CreateFeedLikeResponseDto; import org.winey.server.domain.feed.Feed; import org.winey.server.domain.feed.FeedLike; @@ -28,8 +26,6 @@ public class FeedLikeService { private final NotiRepository notiRepository; - private final MessageQueueSender messageQueueSender; - @Transactional public CreateFeedLikeResponseDto createFeedLike(Long userId, Long feedId, boolean feedLike) { User user = userRepository.findByUserId(userId) @@ -51,7 +47,16 @@ public CreateFeedLikeResponseDto createFeedLike(Long userId, Long feedId, boolea feedLikeRepository.save(like); if (user.getUserId() != feed.getUser().getUserId()){ //만약 좋아요를 누르는 사람이랑 피드 주인이랑 다르면 알림 생성 - createNotificationInLike(feed, like, user); + Notification noti = Notification.builder() // 좋아요 알림 생성 + .notiType(NotiType.LIKENOTI) + .notiMessage(user.getNickname()+NotiType.LIKENOTI.getType()) + .isChecked(false) + .notiReciver(feed.getUser()) + .build(); + noti.updateLinkId(feedId); + noti.updateResponseId(like.getId()); + noti.updateRequestUserId(userId); + notiRepository.save(noti); } } else { // 좋아요 취소 FeedLike deletedFeedLike = feedLikeRepository.deleteByFeedAndUser(feed, user).get(0); @@ -63,25 +68,4 @@ public CreateFeedLikeResponseDto createFeedLike(Long userId, Long feedId, boolea } return CreateFeedLikeResponseDto.of(feedId, feedLike, (long) feedLikeRepository.countByFeed(feed)); } - - private void createNotificationInLike(Feed feed, FeedLike like, User user) { - Notification notification = Notification.builder() // 좋아요 알림 생성 - .notiType(NotiType.LIKENOTI) - .notiMessage(user.getNickname()+NotiType.LIKENOTI.getType()) - .isChecked(false) - .notiReciver(feed.getUser()) - .build(); - notification.updateLinkId(feed.getFeedId()); - notification.updateResponseId(like.getId()); - notification.updateRequestUserId(user.getUserId()); - notiRepository.save(notification); - if (feed.getUser().getFcmIsAllowed() && !notification.getNotiReceiver().getFcmToken().isEmpty()) { //푸시알림에 동의했을 경우. 피드 주인에게 알림 - messageQueueSender.pushSender( - FcmRequestDto.of( - notification.getNotiMessage(), - notification.getNotiReceiver().getFcmToken(), - notification.getNotiType(), - feed.getFeedId())); - } - } } diff --git a/src/main/java/org/winey/server/service/NotiService.java b/src/main/java/org/winey/server/service/NotiService.java index 68c96e3..44b6695 100644 --- a/src/main/java/org/winey/server/service/NotiService.java +++ b/src/main/java/org/winey/server/service/NotiService.java @@ -23,7 +23,6 @@ import org.winey.server.domain.notification.Notification; import org.winey.server.domain.user.User; import org.winey.server.exception.Error; -import org.winey.server.exception.model.BadRequestException; import org.winey.server.exception.model.NotFoundException; import org.winey.server.infrastructure.GoalRepository; import org.winey.server.infrastructure.NotiRepository; diff --git a/src/main/java/org/winey/server/service/UserService.java b/src/main/java/org/winey/server/service/UserService.java index d6d93ce..59169d1 100644 --- a/src/main/java/org/winey/server/service/UserService.java +++ b/src/main/java/org/winey/server/service/UserService.java @@ -4,7 +4,6 @@ import org.joda.time.LocalDateTime; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import org.winey.server.controller.request.UpdateFcmTokenDto; import org.winey.server.controller.request.UpdateUserNicknameDto; import org.winey.server.controller.response.user.UserResponseDto; import org.winey.server.controller.response.user.UserResponseGoalDto; @@ -14,7 +13,6 @@ import org.winey.server.domain.notification.Notification; import org.winey.server.domain.user.User; import org.winey.server.exception.Error; -import org.winey.server.exception.model.BadRequestException; import org.winey.server.exception.model.NotFoundException; import org.winey.server.infrastructure.GoalRepository; import org.winey.server.infrastructure.NotiRepository; @@ -31,13 +29,14 @@ public class UserService { private final UserRepository userRepository; private final GoalRepository goalRepository; + private final NotiRepository notiRepository; @Transactional(readOnly = true) public UserResponseDto getUser(Long userId) { User user = userRepository.findByUserId(userId) .orElseThrow(() -> new NotFoundException(Error.NOT_FOUND_USER_EXCEPTION, Error.NOT_FOUND_USER_EXCEPTION.getMessage())); - UserResponseUserDto userDto = UserResponseUserDto.of(user.getUserId(), user.getNickname(), user.getUserLevel().getName(),user.getFcmIsAllowed()); + UserResponseUserDto userDto = UserResponseUserDto.of(user.getUserId(), user.getNickname(), user.getUserLevel().getName()); List goalList = goalRepository.findByUserOrderByCreatedAtDesc(user); @@ -58,27 +57,18 @@ public UserResponseDto getUser(Long userId) { public void updateNickname(Long userId, UpdateUserNicknameDto requestDto) { User user = userRepository.findByUserId(userId) .orElseThrow(() -> new NotFoundException(Error.NOT_FOUND_USER_EXCEPTION, Error.NOT_FOUND_USER_EXCEPTION.getMessage())); +// List notifications = notiRepository.findByRequestUserId(userId); +// if (!notifications.isEmpty()) { +// notifications.forEach(notification -> { +// if (notification.getNotiType() == NotiType.COMMENTNOTI) { +// notification.updateNotiMessage(requestDto.getNickname() + NotiType.COMMENTNOTI.getType()); +// } else { +// notification.updateNotiMessage(requestDto.getNickname() + NotiType.LIKENOTI.getType()); +// } +// }); +// } user.updateNickname(requestDto.getNickname()); } - @Transactional - public void updateFcmToken(Long userId, UpdateFcmTokenDto updateFcmTokenDto){ - User user = userRepository.findByUserId(userId) - .orElseThrow(() -> new NotFoundException(Error.NOT_FOUND_USER_EXCEPTION, Error.NOT_FOUND_USER_EXCEPTION.getMessage())); - user.updateFcmToken(updateFcmTokenDto.getToken()); - } - - //푸시알림 동의 여부 수정 api - @Transactional - public Boolean allowedPushNotification(Long userId, Boolean fcmIsAllowed){ - User user = userRepository.findByUserId(userId) - .orElseThrow(()-> new NotFoundException(Error.NOT_FOUND_USER_EXCEPTION, Error.NOT_FOUND_USER_EXCEPTION.getMessage())); - if (fcmIsAllowed == user.getFcmIsAllowed()) { //같은 경우면 에러가 날 수 있으니 에러 띄움. - throw new BadRequestException(Error.REQUEST_VALIDATION_EXCEPTION, - Error.REQUEST_VALIDATION_EXCEPTION.getMessage()); - } - user.updateFcmIsAllowed(fcmIsAllowed); - return fcmIsAllowed; - } public Boolean checkNicknameDuplicate(String nickname) { return userRepository.existsByNickname(nickname); diff --git a/src/main/java/org/winey/server/service/auth/AuthService.java b/src/main/java/org/winey/server/service/auth/AuthService.java index 4ce32b1..6d5087a 100644 --- a/src/main/java/org/winey/server/service/auth/AuthService.java +++ b/src/main/java/org/winey/server/service/auth/AuthService.java @@ -51,6 +51,7 @@ public class AuthService { public SignInResponseDto signIn(String socialAccessToken, SignInRequestDto requestDto) { SocialType socialType = SocialType.valueOf(requestDto.getSocialType()); String socialId = login(socialType, socialAccessToken); + System.out.println("여기2"); Boolean isRegistered = userRepository.existsBySocialIdAndSocialType(socialId, socialType); @@ -64,7 +65,6 @@ public SignInResponseDto signIn(String socialAccessToken, SignInRequestDto reque .nickname("위니"+randomString) .socialId(socialId) .socialType(socialType).build(); - newUser.updateFcmIsAllowed(true); //신규 유저면 true박고 userRepository.save(newUser); @@ -84,12 +84,10 @@ public SignInResponseDto signIn(String socialAccessToken, SignInRequestDto reque // jwt 발급 (액세스 토큰, 리프레쉬 토큰) String accessToken = jwtService.issuedToken(String.valueOf(user.getUserId()), TOKEN_EXPIRATION_TIME_ACCESS); String refreshToken = jwtService.issuedToken(String.valueOf(user.getUserId()), TOKEN_EXPIRATION_TIME_REFRESH); - String fcmToken = requestDto.getFcmToken(); user.updateRefreshToken(refreshToken); - user.updateFcmToken(fcmToken); - return SignInResponseDto.of(user.getUserId(), accessToken, refreshToken, fcmToken, isRegistered,user.getFcmIsAllowed()); + return SignInResponseDto.of(user.getUserId(), accessToken, refreshToken, isRegistered); } @Transactional @@ -112,8 +110,8 @@ public TokenResponseDto issueToken(String refreshToken) { public void signOut(Long userId) { User user = userRepository.findByUserId(userId) .orElseThrow(() -> new NotFoundException(Error.NOT_FOUND_USER_EXCEPTION, Error.NOT_FOUND_USER_EXCEPTION.getMessage())); + user.updateRefreshToken(null); - user.updateFcmToken(null); } private String login(SocialType socialType, String socialAccessToken) { @@ -121,6 +119,7 @@ private String login(SocialType socialType, String socialAccessToken) { return appleSignInService.getAppleId(socialAccessToken); } else if (socialType.toString() == "KAKAO") { + System.out.println("여기1"); return kakaoSignInService.getKaKaoId(socialAccessToken); } else{ diff --git a/src/main/java/org/winey/server/service/message/FcmRequestDto.java b/src/main/java/org/winey/server/service/message/FcmRequestDto.java deleted file mode 100644 index 9c0bced..0000000 --- a/src/main/java/org/winey/server/service/message/FcmRequestDto.java +++ /dev/null @@ -1,26 +0,0 @@ -package org.winey.server.service.message; - -import lombok.AccessLevel; -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; -import org.winey.server.domain.notification.NotiType; -import org.winey.server.domain.notification.Notification; - -import java.io.Serializable; - -import javax.annotation.Nullable; - -@Getter -@NoArgsConstructor(access = AccessLevel.PRIVATE) -@AllArgsConstructor(access = AccessLevel.PRIVATE) -public class FcmRequestDto implements Serializable { - private String message; - private String token; //fcm - private NotiType type; - private Long feedId; - - public static FcmRequestDto of(String message, String token, NotiType type, Long feedId){ - return new FcmRequestDto(message,token,type,feedId); - } -} From 8877d0fece6c0f28985a89b4d6e245d48952da38 Mon Sep 17 00:00:00 2001 From: Seonheui Jeon <88873302+funnysunny08@users.noreply.github.com> Date: Fri, 17 Nov 2023 22:07:54 +0900 Subject: [PATCH 06/78] Update cd.yml --- .github/workflows/cd.yml | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index 287a958..9171f96 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -43,16 +43,6 @@ jobs: # application.yml 파일 확인 cat ./application.yml - - # winey-firebase.json 파일 생성 - touch ./winey-firebase.json - - # GitHub-Actions 에서 설정한 값을 winey-firebase.json 파일에 쓰기 - echo "${{ secrets.WINEY_FIREBASE }}" >> ./winey-firebase.json - - # winey-firebase.json 파일 확인 - cat ./winey-firebase.json - shell: bash # 이 워크플로우는 gradle build From af0543bda207c1acc4c50791398dfc1975f27958 Mon Sep 17 00:00:00 2001 From: Seonheui Jeon <88873302+funnysunny08@users.noreply.github.com> Date: Fri, 17 Nov 2023 22:08:16 +0900 Subject: [PATCH 07/78] Update gradle.yml --- .github/workflows/gradle.yml | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index cbc9901..ba3f44c 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -54,16 +54,6 @@ jobs: # application.yml 파일 확인 cat ./application.yml - - # winey-firebase.json 파일 생성 - touch ./winey-firebase.json - - # GitHub-Actions 에서 설정한 값을 winey-firebase.json 파일에 쓰기 - echo "${{ secrets.WINEY_FIREBASE }}" >> ./winey-firebase.json - - # winey-firebase.json 파일 확인 - cat ./winey-firebase.json - shell: bash # 이 워크플로우는 gradle build From b115b911453777029fe5c77c061055f013e33d30 Mon Sep 17 00:00:00 2001 From: Seonheui Jeon <88873302+funnysunny08@users.noreply.github.com> Date: Fri, 17 Nov 2023 22:33:34 +0900 Subject: [PATCH 08/78] =?UTF-8?q?[Fix]=20=EB=B0=B0=ED=8F=AC=20=EC=98=A4?= =?UTF-8?q?=EB=A5=98=EB=A5=BC=20=ED=95=B4=EA=B2=B0=ED=95=9C=EB=8B=A4.=20(#?= =?UTF-8?q?193)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [Feat] fcm service 구현하기 * #159 [Feat] createMessage queue * [Feat] 좋아요, 댓글 생성시 알림 발송 * [Feat] 메시지 큐 실험 성공, fcm만 되는지 확인필요 * [Feat] 푸시sender transactional 제거 * [Feat] fcm토큰 준비 * [Feat] rabbitMQ server ec2연결 및 token response 요구사항 반영수정 * [Feat] updateFcmtoken 구현 및 notification success notFound -> no content수정 * [Merge] merge confict 해결 * [Fix] 닉네임 수정 로직이랑 줄을 헷갈려서 버그 수정 * [FIX] merge conflict * [FIX] stash 결과 적용 * [Feat] 동의여부 기능 추가 및 fcm 메시지 전송 변경 * [Feat] 댓글, 좋아요 알림로직에 동의 여부 수정 및 예외처리 * [Fix] notificationresponse Id 수정 * [Fix] logic 수정 --------- Co-authored-by: soohyun Co-authored-by: soohyun <49307946+sss4920@users.noreply.github.com> --- .gitignore | 6 + build.gradle | 7 + .../common/message/MessageQueueReceiver.java | 82 ++++++++++++ .../common/message/MessageQueueSender.java | 46 +++++++ .../server/controller/UserController.java | 22 +++ .../request/UpdateAllowedPushDto.java | 16 +++ .../controller/request/UpdateFcmTokenDto.java | 20 +++ .../request/auth/SignInRequestDto.java | 2 + .../response/auth/SignInResponseDto.java | 6 +- .../response/user/UserResponseUserDto.java | 5 +- .../org/winey/server/domain/user/User.java | 11 ++ .../org/winey/server/exception/Error.java | 1 + .../org/winey/server/exception/Success.java | 4 +- .../winey/server/service/CommentService.java | 125 +++++++++++------- .../org/winey/server/service/FcmService.java | 116 ++++++++++++++++ .../winey/server/service/FeedLikeService.java | 36 +++-- .../org/winey/server/service/NotiService.java | 1 + .../org/winey/server/service/UserService.java | 34 +++-- .../server/service/auth/AuthService.java | 9 +- .../server/service/message/FcmRequestDto.java | 26 ++++ 20 files changed, 494 insertions(+), 81 deletions(-) create mode 100644 src/main/java/org/winey/server/common/message/MessageQueueReceiver.java create mode 100644 src/main/java/org/winey/server/common/message/MessageQueueSender.java create mode 100644 src/main/java/org/winey/server/controller/request/UpdateAllowedPushDto.java create mode 100644 src/main/java/org/winey/server/controller/request/UpdateFcmTokenDto.java create mode 100644 src/main/java/org/winey/server/service/FcmService.java create mode 100644 src/main/java/org/winey/server/service/message/FcmRequestDto.java diff --git a/.gitignore b/.gitignore index 9e143b5..162f436 100644 --- a/.gitignore +++ b/.gitignore @@ -43,5 +43,11 @@ application.yaml ### DS_Store .DS_Store +### fcm +winey-firebase.json + +### banner.txt +banner.txt + ### log file ### logs/ \ No newline at end of file diff --git a/build.gradle b/build.gradle index aa7316a..e6ddff3 100644 --- a/build.gradle +++ b/build.gradle @@ -56,6 +56,13 @@ dependencies { implementation group: 'io.jsonwebtoken', name: 'jjwt-jackson', version: '0.11.2' implementation 'org.springframework.cloud:spring-cloud-starter-openfeign:3.1.7' + //FCM + implementation group: 'com.google.firebase', name: 'firebase-admin', version: '6.8.1' + + //rabbitmq + implementation 'org.springframework.boot:spring-boot-starter-amqp' + + // ShedLock implementation 'net.javacrumbs.shedlock:shedlock-spring:4.14.0' implementation 'net.javacrumbs.shedlock:shedlock-provider-jdbc-template:4.14.0' diff --git a/src/main/java/org/winey/server/common/message/MessageQueueReceiver.java b/src/main/java/org/winey/server/common/message/MessageQueueReceiver.java new file mode 100644 index 0000000..dd1c546 --- /dev/null +++ b/src/main/java/org/winey/server/common/message/MessageQueueReceiver.java @@ -0,0 +1,82 @@ +package org.winey.server.common.message; + +import lombok.AllArgsConstructor; +import org.springframework.amqp.core.ExchangeTypes; +import org.springframework.amqp.rabbit.annotation.Exchange; +import org.springframework.amqp.rabbit.annotation.Queue; +import org.springframework.amqp.rabbit.annotation.QueueBinding; +import org.springframework.amqp.rabbit.annotation.RabbitListener; +import org.springframework.stereotype.Component; +import org.winey.server.domain.notification.Notification; +import org.winey.server.service.FcmService; +import org.winey.server.service.message.FcmRequestDto; + +import java.io.*; + +@Component +@AllArgsConstructor +public class MessageQueueReceiver { + + private final FcmService fcmService; + + @RabbitListener(bindings = @QueueBinding( + exchange = @Exchange(name = "like", type = ExchangeTypes.TOPIC), + value = @Queue(name = "like-notification"), + key = "like-noti") + ) + public void likeReceiver(byte[] likeNoti){ + System.out.println("좋아요 noti receiver"); + ByteArrayInputStream bis = new ByteArrayInputStream(likeNoti); + ObjectInput in = null; + try { + in = new ObjectInputStream(bis); + Object obj = in.readObject(); + System.out.println("여기까진 오는가"); + if (obj instanceof FcmRequestDto){ + FcmRequestDto notification = (FcmRequestDto) obj; + fcmService.sendByToken(notification); + } + }catch (IOException | ClassNotFoundException e){ + e.printStackTrace(); + }finally { + try{ + bis.close(); + if (in != null){ + in.close(); + } + }catch (IOException ex){ + ex.printStackTrace(); + } + } + } + + @RabbitListener(bindings = @QueueBinding( + exchange = @Exchange(name = "comment", type = ExchangeTypes.TOPIC), + value = @Queue(name = "comment-notification"), + key = "comment-noti") + ) + public void commentReceiver(byte[] commentNoti){ + System.out.println("댓글 noti receiver"); + ByteArrayInputStream bis = new ByteArrayInputStream(commentNoti); + ObjectInput in = null; + try { + in = new ObjectInputStream(bis); + Object obj = in.readObject(); + if (obj instanceof Notification){ + FcmRequestDto notification = (FcmRequestDto) obj; + fcmService.sendByToken(notification); + } + }catch (IOException | ClassNotFoundException e){ + e.printStackTrace(); + }finally { + try{ + bis.close(); + if (in != null){ + in.close(); + } + }catch (IOException ex){ + ex.printStackTrace(); + } + } + } +} diff --git a/src/main/java/org/winey/server/common/message/MessageQueueSender.java b/src/main/java/org/winey/server/common/message/MessageQueueSender.java new file mode 100644 index 0000000..a26d968 --- /dev/null +++ b/src/main/java/org/winey/server/common/message/MessageQueueSender.java @@ -0,0 +1,46 @@ +package org.winey.server.common.message; + +import com.google.cloud.ByteArray; +import lombok.AllArgsConstructor; +import org.springframework.amqp.AmqpException; +import org.springframework.amqp.rabbit.core.RabbitTemplate; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.transaction.annotation.Transactional; +import org.winey.server.domain.notification.NotiType; +import org.winey.server.domain.notification.Notification; +import org.winey.server.service.message.FcmRequestDto; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectOutputStream; +import java.time.LocalDateTime; + +@Configuration +@AllArgsConstructor +public class MessageQueueSender { + + final RabbitTemplate rabbitTemplate; + + + public void pushSender(FcmRequestDto notification){ + System.out.println("여기는 오는건가?"); + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + ObjectOutputStream out; + try { + out = new ObjectOutputStream(bos); + out.writeObject(notification); + out.flush(); + byte[] data = bos.toByteArray(); + if (notification.getType() == NotiType.COMMENTNOTI) { + rabbitTemplate.convertAndSend("comment", "comment-noti", data); + } else if (notification.getType() == NotiType.LIKENOTI) { + rabbitTemplate.convertAndSend("like", "like-noti", data); + } + }catch (AmqpException e){ + System.out.println("메시지 전송 중 오류가 발생했습니다." + e.getMessage()); + }catch (IOException e){ + e.printStackTrace(); + } + } +} diff --git a/src/main/java/org/winey/server/controller/UserController.java b/src/main/java/org/winey/server/controller/UserController.java index c38a023..aa3fc9b 100644 --- a/src/main/java/org/winey/server/controller/UserController.java +++ b/src/main/java/org/winey/server/controller/UserController.java @@ -7,6 +7,8 @@ import org.springframework.web.bind.annotation.*; import org.winey.server.common.dto.ApiResponse; import org.winey.server.config.resolver.UserId; +import org.winey.server.controller.request.UpdateAllowedPushDto; +import org.winey.server.controller.request.UpdateFcmTokenDto; import org.winey.server.controller.request.UpdateUserNicknameDto; import org.winey.server.controller.response.user.UserResponseDto; import org.winey.server.exception.Error; @@ -55,4 +57,24 @@ public ApiResponse checkNicknameDuplicate(@RequestParam String nickname) { }; return ApiResponse.success(Success.CHECK_NICKNAME_DUPLICATE_SUCCESS, result); } + + @PatchMapping("/fcmtoken") + @ResponseStatus(HttpStatus.OK) + @Operation(summary = "fcm토큰 변경 API", description = "유저의 fcm token을 변경합니다.") + public ApiResponse updateFcmToken (@UserId Long userId, @RequestBody @Valid final UpdateFcmTokenDto requestDto) { + userService.updateFcmToken(userId, requestDto); + return ApiResponse.success(Success.FCM_TOKEN_UPDATE_SUCCESS); + } + + @PatchMapping("/notification") + @ResponseStatus(HttpStatus.OK) + @Operation(summary = "푸시 알림 동의 변경 API", description = "유저의 알림 동의 여부를 변경합니다.") + public ApiResponse updateAllowedNotification (@UserId Long userId, @RequestBody @Valid final UpdateAllowedPushDto requestDto) { + Map result = new HashMap() { + { + put("isAllowed", userService.allowedPushNotification(userId, requestDto.getAllowedPush())); + } + }; + return ApiResponse.success(Success.UPDATE_PUSH_ALLOWED_SUCCESS, result); + } } diff --git a/src/main/java/org/winey/server/controller/request/UpdateAllowedPushDto.java b/src/main/java/org/winey/server/controller/request/UpdateAllowedPushDto.java new file mode 100644 index 0000000..02c81c2 --- /dev/null +++ b/src/main/java/org/winey/server/controller/request/UpdateAllowedPushDto.java @@ -0,0 +1,16 @@ +package org.winey.server.controller.request; + +import javax.validation.constraints.NotNull; + +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor(access = AccessLevel.PRIVATE) +@AllArgsConstructor +public class UpdateAllowedPushDto { + @NotNull + private Boolean allowedPush; +} diff --git a/src/main/java/org/winey/server/controller/request/UpdateFcmTokenDto.java b/src/main/java/org/winey/server/controller/request/UpdateFcmTokenDto.java new file mode 100644 index 0000000..5f36fdd --- /dev/null +++ b/src/main/java/org/winey/server/controller/request/UpdateFcmTokenDto.java @@ -0,0 +1,20 @@ +package org.winey.server.controller.request; + +import javax.validation.constraints.NotNull; + +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@AllArgsConstructor +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class UpdateFcmTokenDto { + @NotNull + private String token; + + public UpdateFcmTokenDto of(String token) { + return new UpdateFcmTokenDto(token); + } +} diff --git a/src/main/java/org/winey/server/controller/request/auth/SignInRequestDto.java b/src/main/java/org/winey/server/controller/request/auth/SignInRequestDto.java index 773ae0f..17c77d9 100644 --- a/src/main/java/org/winey/server/controller/request/auth/SignInRequestDto.java +++ b/src/main/java/org/winey/server/controller/request/auth/SignInRequestDto.java @@ -13,4 +13,6 @@ public class SignInRequestDto { @NotNull private String socialType; + + private String fcmToken; } \ No newline at end of file diff --git a/src/main/java/org/winey/server/controller/response/auth/SignInResponseDto.java b/src/main/java/org/winey/server/controller/response/auth/SignInResponseDto.java index 51402da..255fe14 100644 --- a/src/main/java/org/winey/server/controller/response/auth/SignInResponseDto.java +++ b/src/main/java/org/winey/server/controller/response/auth/SignInResponseDto.java @@ -12,9 +12,11 @@ public class SignInResponseDto { private Long userId; private String accessToken; private String refreshToken; + private String fcmToken; private Boolean isRegistered; + private Boolean fcmIsAllowed; - public static SignInResponseDto of(Long userId, String accessToken, String refreshToken, Boolean isRegistered) { - return new SignInResponseDto(userId, accessToken, refreshToken, isRegistered); + public static SignInResponseDto of(Long userId, String accessToken, String refreshToken, String fcmToken, Boolean isRegistered, Boolean fcmIsAllowed) { + return new SignInResponseDto(userId, accessToken, refreshToken, fcmToken, isRegistered, fcmIsAllowed); } } diff --git a/src/main/java/org/winey/server/controller/response/user/UserResponseUserDto.java b/src/main/java/org/winey/server/controller/response/user/UserResponseUserDto.java index 49e5d6e..ad40ac9 100644 --- a/src/main/java/org/winey/server/controller/response/user/UserResponseUserDto.java +++ b/src/main/java/org/winey/server/controller/response/user/UserResponseUserDto.java @@ -12,8 +12,9 @@ public class UserResponseUserDto { private Long userId; private String nickname; private String userLevel; + private Boolean fcmIsAllowed; - public static UserResponseUserDto of(Long userId, String nickname, String userLevel) { - return new UserResponseUserDto(userId, nickname, userLevel); + public static UserResponseUserDto of(Long userId, String nickname, String userLevel, Boolean fcmIsAllowed) { + return new UserResponseUserDto(userId, nickname, userLevel, fcmIsAllowed); } } \ No newline at end of file diff --git a/src/main/java/org/winey/server/domain/user/User.java b/src/main/java/org/winey/server/domain/user/User.java index 55f76ba..e3ad858 100644 --- a/src/main/java/org/winey/server/domain/user/User.java +++ b/src/main/java/org/winey/server/domain/user/User.java @@ -3,6 +3,7 @@ import lombok.*; import org.hibernate.annotations.ColumnDefault; import org.hibernate.annotations.DynamicInsert; +import org.hibernate.annotations.DynamicUpdate; import org.winey.server.domain.AuditingTimeEntity; import org.winey.server.domain.comment.Comment; import org.winey.server.domain.feed.Feed; @@ -41,6 +42,12 @@ public class User extends AuditingTimeEntity { @Enumerated(EnumType.STRING) private SocialType socialType; + @Column(nullable = true) + private String fcmToken; + + @Column(nullable = true) + private Boolean fcmIsAllowed = true; + @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.REMOVE, mappedBy = "user", orphanRemoval = true) private List goals; @@ -77,4 +84,8 @@ public void updateRefreshToken(String refreshToken) { public void updateNickname(String nickname) { this.nickname = nickname; } + + public void updateFcmToken(String fcmToken) { this.fcmToken = fcmToken; } + + public void updateFcmIsAllowed(Boolean isAllowed){this.fcmIsAllowed = isAllowed;} } diff --git a/src/main/java/org/winey/server/exception/Error.java b/src/main/java/org/winey/server/exception/Error.java index 4fbfd47..343ba9b 100644 --- a/src/main/java/org/winey/server/exception/Error.java +++ b/src/main/java/org/winey/server/exception/Error.java @@ -13,6 +13,7 @@ public enum Error { * 400 BAD REQUEST */ REQUEST_VALIDATION_EXCEPTION(HttpStatus.BAD_REQUEST, "잘못된 요청입니다"), + INVALID_FCMTOKEN_EXCEPTION(HttpStatus.BAD_REQUEST, "푸시알림 동의를 했는데, fcmtoken이 null이거나 문제가 있습니다."), LOGIN_SUCCESS(HttpStatus.OK, "로그인에 성공했습니다."), INVALID_PASSWORD_EXCEPTION(HttpStatus.BAD_REQUEST, "잘못된 비밀번호가 입력됐습니다."), NOT_FOUND_IMAGE_EXCEPTION(HttpStatus.BAD_REQUEST, "잘못된 이미지 파일입니다"), diff --git a/src/main/java/org/winey/server/exception/Success.java b/src/main/java/org/winey/server/exception/Success.java index c451f94..15fdc40 100644 --- a/src/main/java/org/winey/server/exception/Success.java +++ b/src/main/java/org/winey/server/exception/Success.java @@ -23,6 +23,7 @@ public enum Success { RE_ISSUE_TOKEN_SUCCESS(HttpStatus.OK, "토큰 재발급 성공"), UPDATE_NICKNAME_SUCCESS(HttpStatus.OK, "닉네임 변경 성공"), + UPDATE_PUSH_ALLOWED_SUCCESS(HttpStatus.OK, "푸시 알림 동의 여부 변경 성공"), CHECK_NICKNAME_DUPLICATE_SUCCESS(HttpStatus.OK, "닉네임 중복 확인 성공"), CHECK_NEW_NOTIFICATION_SUCCESS(HttpStatus.OK, "새 알림 여부 조회 성공"), BLOCK_USER_SUCCESS(HttpStatus.OK, "유저 차단 성공"), @@ -44,7 +45,8 @@ public enum Success { */ DELETE_FEED_SUCCESS(HttpStatus.NO_CONTENT, "피드가 정상적으로 삭제되었습니다."), DELETE_COMMENT_SUCCESS(HttpStatus.NO_CONTENT, "댓글이 정상적으로 삭제되었습니다."), - NOTIFICATION_EMPTY_SUCCESS(HttpStatus.NOT_FOUND, "알림이 한통도 없어요. 힝~구"), + NOTIFICATION_EMPTY_SUCCESS(HttpStatus.NO_CONTENT, "알림이 한통도 없어요. 힝~구"), + FCM_TOKEN_UPDATE_SUCCESS(HttpStatus.NO_CONTENT, "fcm 수정이 완료 됐습니다."), DELETE_USER_SUCCESS(HttpStatus.NO_CONTENT, "회원 탈퇴가 정상적으로 이루어졌습니다.") ; diff --git a/src/main/java/org/winey/server/service/CommentService.java b/src/main/java/org/winey/server/service/CommentService.java index 5cae39c..423d18b 100644 --- a/src/main/java/org/winey/server/service/CommentService.java +++ b/src/main/java/org/winey/server/service/CommentService.java @@ -1,13 +1,17 @@ package org.winey.server.service; import lombok.RequiredArgsConstructor; + import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import org.winey.server.common.message.MessageQueueSender; import org.winey.server.controller.response.comment.CommentResponseDto; import org.winey.server.controller.response.comment.DeleteCommentResponseDto; import org.winey.server.domain.comment.Comment; import org.winey.server.domain.notification.NotiType; import org.winey.server.domain.notification.Notification; +import org.winey.server.exception.model.BadRequestException; +import org.winey.server.exception.model.CustomException; import org.winey.server.exception.model.UnauthorizedException; import org.winey.server.exception.model.UnprocessableEntityException; import org.winey.server.infrastructure.CommentRepository; @@ -18,68 +22,89 @@ import org.winey.server.infrastructure.FeedRepository; import org.winey.server.infrastructure.NotiRepository; import org.winey.server.infrastructure.UserRepository; - +import org.winey.server.service.message.FcmRequestDto; @Service @RequiredArgsConstructor public class CommentService { - private final UserRepository userRepository; - private final FeedRepository feedRepository; - private final CommentRepository commentRepository; - private final NotiRepository notiRepository; + private final UserRepository userRepository; + private final FeedRepository feedRepository; + private final CommentRepository commentRepository; + private final NotiRepository notiRepository; - @Transactional - public CommentResponseDto createComment(Long userId, Long feedId, String content) { - User user = userRepository.findByUserId(userId) - .orElseThrow(() -> new NotFoundException(Error.NOT_FOUND_USER_EXCEPTION, Error.NOT_FOUND_USER_EXCEPTION.getMessage())); - Feed feed = feedRepository.findByFeedId(feedId) - .orElseThrow(() -> new NotFoundException(Error.NOT_FOUND_FEED_EXCEPTION, Error.NOT_FOUND_FEED_EXCEPTION.getMessage())); + private final MessageQueueSender messageQueueSender; + @Transactional + public CommentResponseDto createComment(Long userId, Long feedId, String content) { + User user = userRepository.findByUserId(userId) + .orElseThrow(() -> new NotFoundException(Error.NOT_FOUND_USER_EXCEPTION, + Error.NOT_FOUND_USER_EXCEPTION.getMessage())); + Feed feed = feedRepository.findByFeedId(feedId) + .orElseThrow(() -> new NotFoundException(Error.NOT_FOUND_FEED_EXCEPTION, + Error.NOT_FOUND_FEED_EXCEPTION.getMessage())); - Comment comment = Comment.builder() - .user(user) - .content(content) - .feed(feed) - .build(); - commentRepository.save(comment); + Comment comment = Comment.builder() + .user(user) + .content(content) + .feed(feed) + .build(); + commentRepository.save(comment); - if (user.getUserId() != feed.getUser().getUserId()) { - Notification notification = Notification.builder() //누군가가 내 피드에 댓글을 달았어요~ - .notiReciver(feed.getUser()) - .notiType(NotiType.COMMENTNOTI) - .notiMessage(comment.getUser().getNickname() + NotiType.COMMENTNOTI.getType()) - .isChecked(false) - .build(); - notification.updateLinkId(feedId); - notification.updateResponseId(comment.getCommentId()); - notification.updateRequestUserId(userId); - notiRepository.save(notification); - } - return CommentResponseDto.of(comment.getCommentId(), userId, user.getNickname(), comment.getContent(),user.getUserLevel().getLevelNumber(),comment.getCreatedAt()); - } + if (user.getUserId() != feed.getUser().getUserId()) { + createNotificationInComment(feed, comment, user); //알림 생성 후 message queue에 task 등록 + } + return CommentResponseDto.of(comment.getCommentId(), userId, user.getNickname(), comment.getContent(), + user.getUserLevel().getLevelNumber(), comment.getCreatedAt()); + } - @Transactional - public DeleteCommentResponseDto deleteComment(Long userId, Long commentId){ - User user = userRepository.findByUserId(userId) - .orElseThrow(()-> new NotFoundException(Error.NOT_FOUND_USER_EXCEPTION, Error.NOT_FOUND_USER_EXCEPTION.getMessage())); - Comment wantDeleteComment = commentRepository.findByCommentId(commentId) - .orElseThrow(()-> new NotFoundException(Error.NOT_FOUND_COMMENT_EXCEPTION, Error.NOT_FOUND_COMMENT_EXCEPTION.getMessage())); - Feed commentFeed = feedRepository.findByFeedId(wantDeleteComment.getFeed().getFeedId()) - .orElseThrow(() -> new NotFoundException(Error.NOT_FOUND_FEED_EXCEPTION, Error.NOT_FOUND_FEED_EXCEPTION.getMessage())); + @Transactional + public DeleteCommentResponseDto deleteComment(Long userId, Long commentId) { + User user = userRepository.findByUserId(userId) + .orElseThrow(() -> new NotFoundException(Error.NOT_FOUND_USER_EXCEPTION, + Error.NOT_FOUND_USER_EXCEPTION.getMessage())); + Comment wantDeleteComment = commentRepository.findByCommentId(commentId) + .orElseThrow(() -> new NotFoundException(Error.NOT_FOUND_COMMENT_EXCEPTION, + Error.NOT_FOUND_COMMENT_EXCEPTION.getMessage())); + Feed commentFeed = feedRepository.findByFeedId(wantDeleteComment.getFeed().getFeedId()) + .orElseThrow(() -> new NotFoundException(Error.NOT_FOUND_FEED_EXCEPTION, + Error.NOT_FOUND_FEED_EXCEPTION.getMessage())); - if (user.getUserId() != wantDeleteComment.getUser().getUserId() && user.getUserId() != commentFeed.getUser().getUserId() ){ //만약 현재 유저가 피드 주인도 아니고, 댓글 주인도 아니면? - throw new UnauthorizedException(Error.DELETE_UNAUTHORIZED, Error.DELETE_UNAUTHORIZED.getMessage()); //지울 수 있는 권한이 없다. - } + if (user.getUserId() != wantDeleteComment.getUser().getUserId() && user.getUserId() != commentFeed.getUser() + .getUserId()) { //만약 현재 유저가 피드 주인도 아니고, 댓글 주인도 아니면? + throw new UnauthorizedException(Error.DELETE_UNAUTHORIZED, + Error.DELETE_UNAUTHORIZED.getMessage()); //지울 수 있는 권한이 없다. + } - // 관련 알림 삭제 - notiRepository.deleteByNotiTypeAndResponseId(NotiType.COMMENTNOTI, wantDeleteComment.getCommentId()); + // 관련 알림 삭제 + notiRepository.deleteByNotiTypeAndResponseId(NotiType.COMMENTNOTI, wantDeleteComment.getCommentId()); - Long res = commentRepository.deleteByCommentId(commentId); - if (res != 1){ - throw new UnprocessableEntityException(Error.UNPROCESSABLE_ENTITY_DELETE_EXCEPTION,Error.UNPROCESSABLE_ENTITY_DELETE_EXCEPTION.getMessage()); - } - return DeleteCommentResponseDto.of(commentId); - } + Long res = commentRepository.deleteByCommentId(commentId); + if (res != 1) { + throw new UnprocessableEntityException(Error.UNPROCESSABLE_ENTITY_DELETE_EXCEPTION, + Error.UNPROCESSABLE_ENTITY_DELETE_EXCEPTION.getMessage()); + } + return DeleteCommentResponseDto.of(commentId); + } + private void createNotificationInComment(Feed feed, Comment comment, User user) { + Notification notification = Notification.builder() //누군가가 내 피드에 댓글을 달았어요~ + .notiReciver(feed.getUser()) + .notiType(NotiType.COMMENTNOTI) + .notiMessage(comment.getUser().getNickname() + NotiType.COMMENTNOTI.getType()) + .isChecked(false) + .build(); + notification.updateLinkId(feed.getFeedId()); + notification.updateResponseId(comment.getCommentId()); + notification.updateRequestUserId(user.getUserId()); + notiRepository.save(notification); + if (feed.getUser().getFcmIsAllowed() && !notification.getNotiReceiver().getFcmToken().isEmpty()) { //푸시알림에 동의했을 경우. + messageQueueSender.pushSender( + FcmRequestDto.of( + notification.getNotiMessage(), + notification.getNotiReceiver().getFcmToken(), + notification.getNotiType(), + feed.getFeedId())); + } + } } diff --git a/src/main/java/org/winey/server/service/FcmService.java b/src/main/java/org/winey/server/service/FcmService.java new file mode 100644 index 0000000..c9b0363 --- /dev/null +++ b/src/main/java/org/winey/server/service/FcmService.java @@ -0,0 +1,116 @@ +package org.winey.server.service; + +import com.google.auth.oauth2.GoogleCredentials; +import com.google.firebase.FirebaseApp; +import com.google.firebase.FirebaseOptions; +import com.google.firebase.database.DatabaseReference; +import com.google.firebase.database.FirebaseDatabase; +import com.google.firebase.messaging.*; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.core.io.ClassPathResource; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.winey.server.service.message.FcmRequestDto; + +import javax.annotation.PostConstruct; +import java.io.IOException; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +@Slf4j +@Service +public class FcmService{ + + @Value("${fcm.key.path}") + private String FCM_PRIVATE_KEY_PATH; + + // + // 메시징만 권한 설정 + @Value("${fcm.key.scope}") + private String fireBaseScope; + + // fcm 기본 설정 진행 + @PostConstruct + public void init() { + try { + FirebaseOptions options = new FirebaseOptions.Builder() + .setCredentials( + GoogleCredentials + .fromStream(new ClassPathResource(FCM_PRIVATE_KEY_PATH).getInputStream()) + .createScoped(List.of(fireBaseScope))) + .build(); + if (FirebaseApp.getApps().isEmpty()) { + FirebaseApp.initializeApp(options); + log.info("Firebase application has been initialized"); + } + } catch (IOException e) { + log.error(e.getMessage()); + // spring 뜰때 알림 서버가 잘 동작하지 않는 것이므로 바로 죽임 + throw new RuntimeException(e.getMessage()); + } + } + + + // 알림 보내기 + public void sendByTokenList(List tokenList) { + + // 메시지 만들기 + List messages = tokenList.stream().map(token -> Message.builder() + .putData("time", LocalDateTime.now().toString()) + .setNotification(new Notification("제목", "알림 내용")) + .setToken(token) + .build()).collect(Collectors.toList()); + + // 요청에 대한 응답을 받을 response + BatchResponse response; + try { + + // 알림 발송 + response = FirebaseMessaging.getInstance().sendAll(messages); + + // 요청에 대한 응답 처리 + if (response.getFailureCount() > 0) { + List responses = response.getResponses(); + List failedTokens = new ArrayList<>(); + + for (int i = 0; i < responses.size(); i++) { + if (!responses.get(i).isSuccessful()) { + failedTokens.add(tokenList.get(i)); + } + } + log.error("List of tokens are not valid FCM token : " + failedTokens); + } + } catch (FirebaseMessagingException e) { + log.error("cannot send to memberList push message. error info : {}", e.getMessage()); + } + } + // 좋아요나 댓글 관련 알림 로직 작성 + @Async + public void sendByToken(FcmRequestDto wineyNotification) { + // 메시지 만들기 + Message message = Message.builder() + .putData("feedId", String.valueOf(wineyNotification.getFeedId())) + .putData("notiType", String.valueOf(wineyNotification.getType())) + .putData("token", wineyNotification.getToken()) + .setNotification(new Notification("위니 제국의 편지가 도착했어요.", wineyNotification.getMessage())) + .setToken(wineyNotification.getToken()) + .build(); + + // 요청에 대한 응답을 받을 response + String response; + try { + System.out.println("여까지는 왔다."); + // 알림 발송 + response = FirebaseMessaging.getInstance().send(message); + System.out.println(response); + } catch (FirebaseMessagingException e) { + log.error("푸시 알림을 보내다가 오류가 발생했습니다. error info : {}", e.getMessage()); + } + } + +} + diff --git a/src/main/java/org/winey/server/service/FeedLikeService.java b/src/main/java/org/winey/server/service/FeedLikeService.java index a722120..8e80f12 100644 --- a/src/main/java/org/winey/server/service/FeedLikeService.java +++ b/src/main/java/org/winey/server/service/FeedLikeService.java @@ -3,6 +3,8 @@ import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import org.winey.server.common.message.MessageQueueSender; +import org.winey.server.service.message.FcmRequestDto; import org.winey.server.controller.response.feedLike.CreateFeedLikeResponseDto; import org.winey.server.domain.feed.Feed; import org.winey.server.domain.feed.FeedLike; @@ -26,6 +28,8 @@ public class FeedLikeService { private final NotiRepository notiRepository; + private final MessageQueueSender messageQueueSender; + @Transactional public CreateFeedLikeResponseDto createFeedLike(Long userId, Long feedId, boolean feedLike) { User user = userRepository.findByUserId(userId) @@ -47,16 +51,7 @@ public CreateFeedLikeResponseDto createFeedLike(Long userId, Long feedId, boolea feedLikeRepository.save(like); if (user.getUserId() != feed.getUser().getUserId()){ //만약 좋아요를 누르는 사람이랑 피드 주인이랑 다르면 알림 생성 - Notification noti = Notification.builder() // 좋아요 알림 생성 - .notiType(NotiType.LIKENOTI) - .notiMessage(user.getNickname()+NotiType.LIKENOTI.getType()) - .isChecked(false) - .notiReciver(feed.getUser()) - .build(); - noti.updateLinkId(feedId); - noti.updateResponseId(like.getId()); - noti.updateRequestUserId(userId); - notiRepository.save(noti); + createNotificationInLike(feed, like, user); } } else { // 좋아요 취소 FeedLike deletedFeedLike = feedLikeRepository.deleteByFeedAndUser(feed, user).get(0); @@ -68,4 +63,25 @@ public CreateFeedLikeResponseDto createFeedLike(Long userId, Long feedId, boolea } return CreateFeedLikeResponseDto.of(feedId, feedLike, (long) feedLikeRepository.countByFeed(feed)); } + + private void createNotificationInLike(Feed feed, FeedLike like, User user) { + Notification notification = Notification.builder() // 좋아요 알림 생성 + .notiType(NotiType.LIKENOTI) + .notiMessage(user.getNickname()+NotiType.LIKENOTI.getType()) + .isChecked(false) + .notiReciver(feed.getUser()) + .build(); + notification.updateLinkId(feed.getFeedId()); + notification.updateResponseId(like.getId()); + notification.updateRequestUserId(user.getUserId()); + notiRepository.save(notification); + if (feed.getUser().getFcmIsAllowed() && !notification.getNotiReceiver().getFcmToken().isEmpty()) { //푸시알림에 동의했을 경우. 피드 주인에게 알림 + messageQueueSender.pushSender( + FcmRequestDto.of( + notification.getNotiMessage(), + notification.getNotiReceiver().getFcmToken(), + notification.getNotiType(), + feed.getFeedId())); + } + } } diff --git a/src/main/java/org/winey/server/service/NotiService.java b/src/main/java/org/winey/server/service/NotiService.java index 44b6695..68c96e3 100644 --- a/src/main/java/org/winey/server/service/NotiService.java +++ b/src/main/java/org/winey/server/service/NotiService.java @@ -23,6 +23,7 @@ import org.winey.server.domain.notification.Notification; import org.winey.server.domain.user.User; import org.winey.server.exception.Error; +import org.winey.server.exception.model.BadRequestException; import org.winey.server.exception.model.NotFoundException; import org.winey.server.infrastructure.GoalRepository; import org.winey.server.infrastructure.NotiRepository; diff --git a/src/main/java/org/winey/server/service/UserService.java b/src/main/java/org/winey/server/service/UserService.java index 59169d1..d6d93ce 100644 --- a/src/main/java/org/winey/server/service/UserService.java +++ b/src/main/java/org/winey/server/service/UserService.java @@ -4,6 +4,7 @@ import org.joda.time.LocalDateTime; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import org.winey.server.controller.request.UpdateFcmTokenDto; import org.winey.server.controller.request.UpdateUserNicknameDto; import org.winey.server.controller.response.user.UserResponseDto; import org.winey.server.controller.response.user.UserResponseGoalDto; @@ -13,6 +14,7 @@ import org.winey.server.domain.notification.Notification; import org.winey.server.domain.user.User; import org.winey.server.exception.Error; +import org.winey.server.exception.model.BadRequestException; import org.winey.server.exception.model.NotFoundException; import org.winey.server.infrastructure.GoalRepository; import org.winey.server.infrastructure.NotiRepository; @@ -29,14 +31,13 @@ public class UserService { private final UserRepository userRepository; private final GoalRepository goalRepository; - private final NotiRepository notiRepository; @Transactional(readOnly = true) public UserResponseDto getUser(Long userId) { User user = userRepository.findByUserId(userId) .orElseThrow(() -> new NotFoundException(Error.NOT_FOUND_USER_EXCEPTION, Error.NOT_FOUND_USER_EXCEPTION.getMessage())); - UserResponseUserDto userDto = UserResponseUserDto.of(user.getUserId(), user.getNickname(), user.getUserLevel().getName()); + UserResponseUserDto userDto = UserResponseUserDto.of(user.getUserId(), user.getNickname(), user.getUserLevel().getName(),user.getFcmIsAllowed()); List goalList = goalRepository.findByUserOrderByCreatedAtDesc(user); @@ -57,18 +58,27 @@ public UserResponseDto getUser(Long userId) { public void updateNickname(Long userId, UpdateUserNicknameDto requestDto) { User user = userRepository.findByUserId(userId) .orElseThrow(() -> new NotFoundException(Error.NOT_FOUND_USER_EXCEPTION, Error.NOT_FOUND_USER_EXCEPTION.getMessage())); -// List notifications = notiRepository.findByRequestUserId(userId); -// if (!notifications.isEmpty()) { -// notifications.forEach(notification -> { -// if (notification.getNotiType() == NotiType.COMMENTNOTI) { -// notification.updateNotiMessage(requestDto.getNickname() + NotiType.COMMENTNOTI.getType()); -// } else { -// notification.updateNotiMessage(requestDto.getNickname() + NotiType.LIKENOTI.getType()); -// } -// }); -// } user.updateNickname(requestDto.getNickname()); } + @Transactional + public void updateFcmToken(Long userId, UpdateFcmTokenDto updateFcmTokenDto){ + User user = userRepository.findByUserId(userId) + .orElseThrow(() -> new NotFoundException(Error.NOT_FOUND_USER_EXCEPTION, Error.NOT_FOUND_USER_EXCEPTION.getMessage())); + user.updateFcmToken(updateFcmTokenDto.getToken()); + } + + //푸시알림 동의 여부 수정 api + @Transactional + public Boolean allowedPushNotification(Long userId, Boolean fcmIsAllowed){ + User user = userRepository.findByUserId(userId) + .orElseThrow(()-> new NotFoundException(Error.NOT_FOUND_USER_EXCEPTION, Error.NOT_FOUND_USER_EXCEPTION.getMessage())); + if (fcmIsAllowed == user.getFcmIsAllowed()) { //같은 경우면 에러가 날 수 있으니 에러 띄움. + throw new BadRequestException(Error.REQUEST_VALIDATION_EXCEPTION, + Error.REQUEST_VALIDATION_EXCEPTION.getMessage()); + } + user.updateFcmIsAllowed(fcmIsAllowed); + return fcmIsAllowed; + } public Boolean checkNicknameDuplicate(String nickname) { return userRepository.existsByNickname(nickname); diff --git a/src/main/java/org/winey/server/service/auth/AuthService.java b/src/main/java/org/winey/server/service/auth/AuthService.java index 6d5087a..4ce32b1 100644 --- a/src/main/java/org/winey/server/service/auth/AuthService.java +++ b/src/main/java/org/winey/server/service/auth/AuthService.java @@ -51,7 +51,6 @@ public class AuthService { public SignInResponseDto signIn(String socialAccessToken, SignInRequestDto requestDto) { SocialType socialType = SocialType.valueOf(requestDto.getSocialType()); String socialId = login(socialType, socialAccessToken); - System.out.println("여기2"); Boolean isRegistered = userRepository.existsBySocialIdAndSocialType(socialId, socialType); @@ -65,6 +64,7 @@ public SignInResponseDto signIn(String socialAccessToken, SignInRequestDto reque .nickname("위니"+randomString) .socialId(socialId) .socialType(socialType).build(); + newUser.updateFcmIsAllowed(true); //신규 유저면 true박고 userRepository.save(newUser); @@ -84,10 +84,12 @@ public SignInResponseDto signIn(String socialAccessToken, SignInRequestDto reque // jwt 발급 (액세스 토큰, 리프레쉬 토큰) String accessToken = jwtService.issuedToken(String.valueOf(user.getUserId()), TOKEN_EXPIRATION_TIME_ACCESS); String refreshToken = jwtService.issuedToken(String.valueOf(user.getUserId()), TOKEN_EXPIRATION_TIME_REFRESH); + String fcmToken = requestDto.getFcmToken(); user.updateRefreshToken(refreshToken); + user.updateFcmToken(fcmToken); - return SignInResponseDto.of(user.getUserId(), accessToken, refreshToken, isRegistered); + return SignInResponseDto.of(user.getUserId(), accessToken, refreshToken, fcmToken, isRegistered,user.getFcmIsAllowed()); } @Transactional @@ -110,8 +112,8 @@ public TokenResponseDto issueToken(String refreshToken) { public void signOut(Long userId) { User user = userRepository.findByUserId(userId) .orElseThrow(() -> new NotFoundException(Error.NOT_FOUND_USER_EXCEPTION, Error.NOT_FOUND_USER_EXCEPTION.getMessage())); - user.updateRefreshToken(null); + user.updateFcmToken(null); } private String login(SocialType socialType, String socialAccessToken) { @@ -119,7 +121,6 @@ private String login(SocialType socialType, String socialAccessToken) { return appleSignInService.getAppleId(socialAccessToken); } else if (socialType.toString() == "KAKAO") { - System.out.println("여기1"); return kakaoSignInService.getKaKaoId(socialAccessToken); } else{ diff --git a/src/main/java/org/winey/server/service/message/FcmRequestDto.java b/src/main/java/org/winey/server/service/message/FcmRequestDto.java new file mode 100644 index 0000000..9c0bced --- /dev/null +++ b/src/main/java/org/winey/server/service/message/FcmRequestDto.java @@ -0,0 +1,26 @@ +package org.winey.server.service.message; + +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import org.winey.server.domain.notification.NotiType; +import org.winey.server.domain.notification.Notification; + +import java.io.Serializable; + +import javax.annotation.Nullable; + +@Getter +@NoArgsConstructor(access = AccessLevel.PRIVATE) +@AllArgsConstructor(access = AccessLevel.PRIVATE) +public class FcmRequestDto implements Serializable { + private String message; + private String token; //fcm + private NotiType type; + private Long feedId; + + public static FcmRequestDto of(String message, String token, NotiType type, Long feedId){ + return new FcmRequestDto(message,token,type,feedId); + } +} From daa6acbe458bd11278c106cc9017b0a870526550 Mon Sep 17 00:00:00 2001 From: Seonheui Jeon <88873302+funnysunny08@users.noreply.github.com> Date: Fri, 17 Nov 2023 22:38:32 +0900 Subject: [PATCH 09/78] Update cd.yml --- .github/workflows/cd.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index 9171f96..b65d77f 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -43,6 +43,15 @@ jobs: # application.yml 파일 확인 cat ./application.yml + + # winey-firebase.json 파일 생성 + touch ./winey-firebase.json + + # GitHub-Actions 에서 설정한 값을 winey-firebase.json 파일에 쓰기 + echo "${{ secrets.WINEY_FIREBASE }}" >> ./winey-firebase.json + + # winey-firebase.json 파일 확인 + cat ./winey-firebase.json shell: bash # 이 워크플로우는 gradle build From db1190655b344c96a9491025acc8355d95d5f7ee Mon Sep 17 00:00:00 2001 From: Seonheui Jeon <88873302+funnysunny08@users.noreply.github.com> Date: Fri, 17 Nov 2023 22:38:55 +0900 Subject: [PATCH 10/78] Update gradle.yml --- .github/workflows/gradle.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index ba3f44c..7fd3784 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -54,6 +54,15 @@ jobs: # application.yml 파일 확인 cat ./application.yml + + # winey-firebase.json 파일 생성 + touch ./winey-firebase.json + + # GitHub-Actions 에서 설정한 값을 winey-firebase.json 파일에 쓰기 + echo "${{ secrets.WINEY_FIREBASE }}" >> ./winey-firebase.json + + # winey-firebase.json 파일 확인 + cat ./winey-firebase.json shell: bash # 이 워크플로우는 gradle build From a72ef3fa535d38740428186db6a7590887b28f74 Mon Sep 17 00:00:00 2001 From: Seonheui Jeon <88873302+funnysunny08@users.noreply.github.com> Date: Fri, 17 Nov 2023 22:48:12 +0900 Subject: [PATCH 11/78] Update appspec.yml --- appspec.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appspec.yml b/appspec.yml index 44c46f6..2b9ffff 100644 --- a/appspec.yml +++ b/appspec.yml @@ -15,5 +15,5 @@ permissions: hooks: AfterInstall: - location: deploy.sh - timeout: 60 + timeout: 300 runas: ubuntu From f313fd5bf0ab958b8dcece0653530c398e8c42de Mon Sep 17 00:00:00 2001 From: Seonheui Jeon <88873302+funnysunny08@users.noreply.github.com> Date: Sat, 18 Nov 2023 00:42:18 +0900 Subject: [PATCH 12/78] =?UTF-8?q?feat:=20getFCMToken=20=EB=A9=94=EC=84=9C?= =?UTF-8?q?=EB=93=9C=EB=A5=BC=20=EC=98=A4=EB=B2=84=EB=9D=BC=EC=9D=B4?= =?UTF-8?q?=EB=93=9C=ED=95=9C=EB=8B=A4.=20(#196)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/winey/server/domain/user/User.java | 29 +++++++++++++++---- .../winey/server/service/CommentService.java | 14 ++++----- .../winey/server/service/FeedLikeService.java | 3 +- 3 files changed, 31 insertions(+), 15 deletions(-) diff --git a/src/main/java/org/winey/server/domain/user/User.java b/src/main/java/org/winey/server/domain/user/User.java index e3ad858..f20fdc0 100644 --- a/src/main/java/org/winey/server/domain/user/User.java +++ b/src/main/java/org/winey/server/domain/user/User.java @@ -1,9 +1,22 @@ package org.winey.server.domain.user; -import lombok.*; -import org.hibernate.annotations.ColumnDefault; +import java.util.List; +import java.util.Objects; +import javax.persistence.CascadeType; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.OneToMany; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; import org.hibernate.annotations.DynamicInsert; -import org.hibernate.annotations.DynamicUpdate; import org.winey.server.domain.AuditingTimeEntity; import org.winey.server.domain.comment.Comment; import org.winey.server.domain.feed.Feed; @@ -12,9 +25,6 @@ import org.winey.server.domain.notification.Notification; import org.winey.server.domain.recommend.Recommend; -import javax.persistence.*; -import java.util.List; - @Entity @Getter @DynamicInsert @@ -88,4 +98,11 @@ public void updateNickname(String nickname) { public void updateFcmToken(String fcmToken) { this.fcmToken = fcmToken; } public void updateFcmIsAllowed(Boolean isAllowed){this.fcmIsAllowed = isAllowed;} + + public String getFcmToken() { + if (Objects.nonNull(this.fcmToken)) { + return this.fcmToken; + } + return null; + } } diff --git a/src/main/java/org/winey/server/service/CommentService.java b/src/main/java/org/winey/server/service/CommentService.java index 423d18b..c33c15a 100644 --- a/src/main/java/org/winey/server/service/CommentService.java +++ b/src/main/java/org/winey/server/service/CommentService.java @@ -1,24 +1,22 @@ package org.winey.server.service; +import java.util.Objects; import lombok.RequiredArgsConstructor; - import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.winey.server.common.message.MessageQueueSender; import org.winey.server.controller.response.comment.CommentResponseDto; import org.winey.server.controller.response.comment.DeleteCommentResponseDto; import org.winey.server.domain.comment.Comment; +import org.winey.server.domain.feed.Feed; import org.winey.server.domain.notification.NotiType; import org.winey.server.domain.notification.Notification; -import org.winey.server.exception.model.BadRequestException; -import org.winey.server.exception.model.CustomException; -import org.winey.server.exception.model.UnauthorizedException; -import org.winey.server.exception.model.UnprocessableEntityException; -import org.winey.server.infrastructure.CommentRepository; -import org.winey.server.domain.feed.Feed; import org.winey.server.domain.user.User; import org.winey.server.exception.Error; import org.winey.server.exception.model.NotFoundException; +import org.winey.server.exception.model.UnauthorizedException; +import org.winey.server.exception.model.UnprocessableEntityException; +import org.winey.server.infrastructure.CommentRepository; import org.winey.server.infrastructure.FeedRepository; import org.winey.server.infrastructure.NotiRepository; import org.winey.server.infrastructure.UserRepository; @@ -97,7 +95,7 @@ private void createNotificationInComment(Feed feed, Comment comment, User user) notification.updateResponseId(comment.getCommentId()); notification.updateRequestUserId(user.getUserId()); notiRepository.save(notification); - if (feed.getUser().getFcmIsAllowed() && !notification.getNotiReceiver().getFcmToken().isEmpty()) { //푸시알림에 동의했을 경우. + if (feed.getUser().getFcmIsAllowed() && Objects.nonNull(notification.getNotiReceiver().getFcmToken())) { //푸시알림에 동의했을 경우. messageQueueSender.pushSender( FcmRequestDto.of( notification.getNotiMessage(), diff --git a/src/main/java/org/winey/server/service/FeedLikeService.java b/src/main/java/org/winey/server/service/FeedLikeService.java index 8e80f12..1a3486e 100644 --- a/src/main/java/org/winey/server/service/FeedLikeService.java +++ b/src/main/java/org/winey/server/service/FeedLikeService.java @@ -1,5 +1,6 @@ package org.winey.server.service; +import java.util.Objects; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -75,7 +76,7 @@ private void createNotificationInLike(Feed feed, FeedLike like, User user) { notification.updateResponseId(like.getId()); notification.updateRequestUserId(user.getUserId()); notiRepository.save(notification); - if (feed.getUser().getFcmIsAllowed() && !notification.getNotiReceiver().getFcmToken().isEmpty()) { //푸시알림에 동의했을 경우. 피드 주인에게 알림 + if (feed.getUser().getFcmIsAllowed() && Objects.nonNull(notification.getNotiReceiver().getFcmToken())) { //푸시알림에 동의했을 경우. 피드 주인에게 알림 messageQueueSender.pushSender( FcmRequestDto.of( notification.getNotiMessage(), From c6b2a5ae967d6bc506d6215380172abf00d2bf09 Mon Sep 17 00:00:00 2001 From: soohyun Date: Sun, 3 Dec 2023 23:20:33 +0900 Subject: [PATCH 13/78] =?UTF-8?q?[Fix]=20fcm=20=ED=8E=98=EC=9D=B4=EB=A1=9C?= =?UTF-8?q?=EB=93=9C=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/org/winey/server/service/FcmService.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/winey/server/service/FcmService.java b/src/main/java/org/winey/server/service/FcmService.java index c9b0363..b537691 100644 --- a/src/main/java/org/winey/server/service/FcmService.java +++ b/src/main/java/org/winey/server/service/FcmService.java @@ -96,14 +96,14 @@ public void sendByToken(FcmRequestDto wineyNotification) { .putData("feedId", String.valueOf(wineyNotification.getFeedId())) .putData("notiType", String.valueOf(wineyNotification.getType())) .putData("token", wineyNotification.getToken()) - .setNotification(new Notification("위니 제국의 편지가 도착했어요.", wineyNotification.getMessage())) + .putData("title", "위니 제국의 편지가 도착했어요.") + .putData("message" ,wineyNotification.getMessage()) .setToken(wineyNotification.getToken()) .build(); // 요청에 대한 응답을 받을 response String response; try { - System.out.println("여까지는 왔다."); // 알림 발송 response = FirebaseMessaging.getInstance().send(message); System.out.println(response); From 79554057dfedc1f5ddf8a58bdd368f172bdbffca Mon Sep 17 00:00:00 2001 From: soohyun Date: Sun, 3 Dec 2023 23:25:38 +0900 Subject: [PATCH 14/78] =?UTF-8?q?[Fix]=20fcmtoken=20response=EC=97=90?= =?UTF-8?q?=EC=84=9C=20=EC=A0=9C=EC=99=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/org/winey/server/service/FcmService.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/org/winey/server/service/FcmService.java b/src/main/java/org/winey/server/service/FcmService.java index b537691..fd94295 100644 --- a/src/main/java/org/winey/server/service/FcmService.java +++ b/src/main/java/org/winey/server/service/FcmService.java @@ -95,7 +95,6 @@ public void sendByToken(FcmRequestDto wineyNotification) { Message message = Message.builder() .putData("feedId", String.valueOf(wineyNotification.getFeedId())) .putData("notiType", String.valueOf(wineyNotification.getType())) - .putData("token", wineyNotification.getToken()) .putData("title", "위니 제국의 편지가 도착했어요.") .putData("message" ,wineyNotification.getMessage()) .setToken(wineyNotification.getToken()) From 42fd9481ef81b308a320703c45505c5c406124ca Mon Sep 17 00:00:00 2001 From: soohyun Date: Wed, 6 Dec 2023 02:41:15 +0900 Subject: [PATCH 15/78] =?UTF-8?q?[Fix]=EB=8C=93=EA=B8=80=EC=95=8C=EB=A6=BC?= =?UTF-8?q?=20=EB=B2=84=EA=B7=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/winey/server/common/message/MessageQueueReceiver.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/winey/server/common/message/MessageQueueReceiver.java b/src/main/java/org/winey/server/common/message/MessageQueueReceiver.java index dd1c546..b72244c 100644 --- a/src/main/java/org/winey/server/common/message/MessageQueueReceiver.java +++ b/src/main/java/org/winey/server/common/message/MessageQueueReceiver.java @@ -62,7 +62,7 @@ public void commentReceiver(byte[] commentNoti){ try { in = new ObjectInputStream(bis); Object obj = in.readObject(); - if (obj instanceof Notification){ + if (obj instanceof FcmRequestDto){ FcmRequestDto notification = (FcmRequestDto) obj; fcmService.sendByToken(notification); } From 2fb7f140e70c9e0c2e393083ec3f6c255ae825cc Mon Sep 17 00:00:00 2001 From: soohyun Date: Fri, 8 Dec 2023 18:04:27 +0900 Subject: [PATCH 16/78] =?UTF-8?q?[Refactor]=20=EC=95=84=EC=9A=94=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=99=84=EB=A3=8C=20=EC=95=88?= =?UTF-8?q?=EB=93=9C=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=A0=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 1 + .../common/message/MessageQueueReceiver.java | 19 +++- .../org/winey/server/service/FcmService.java | 105 +++++++++++++++--- .../server/service/message/FcmMessage.java | 46 ++++++++ 4 files changed, 151 insertions(+), 20 deletions(-) create mode 100644 src/main/java/org/winey/server/service/message/FcmMessage.java diff --git a/build.gradle b/build.gradle index e6ddff3..fe72069 100644 --- a/build.gradle +++ b/build.gradle @@ -58,6 +58,7 @@ dependencies { //FCM implementation group: 'com.google.firebase', name: 'firebase-admin', version: '6.8.1' + implementation 'com.squareup.okhttp3:okhttp:4.10.0' // Firebase 서버로 푸시 메시지 전송 시 필요 //rabbitmq implementation 'org.springframework.boot:spring-boot-starter-amqp' diff --git a/src/main/java/org/winey/server/common/message/MessageQueueReceiver.java b/src/main/java/org/winey/server/common/message/MessageQueueReceiver.java index b72244c..b23a3fd 100644 --- a/src/main/java/org/winey/server/common/message/MessageQueueReceiver.java +++ b/src/main/java/org/winey/server/common/message/MessageQueueReceiver.java @@ -6,6 +6,8 @@ import org.springframework.amqp.rabbit.annotation.Queue; import org.springframework.amqp.rabbit.annotation.QueueBinding; import org.springframework.amqp.rabbit.annotation.RabbitListener; +import org.springframework.amqp.support.AmqpHeaders; +import org.springframework.messaging.handler.annotation.Header; import org.springframework.stereotype.Component; import org.winey.server.domain.notification.Notification; import org.winey.server.service.FcmService; @@ -13,6 +15,9 @@ import java.io.*; +import com.rabbitmq.client.AMQP; +import com.rabbitmq.client.Channel; + @Component @AllArgsConstructor public class MessageQueueReceiver { @@ -24,7 +29,8 @@ public class MessageQueueReceiver { value = @Queue(name = "like-notification"), key = "like-noti") ) - public void likeReceiver(byte[] likeNoti){ + public void likeReceiver(byte[] likeNoti, Channel channel,@Header(AmqpHeaders.DELIVERY_TAG) long tag) throws + IOException { System.out.println("좋아요 noti receiver"); ByteArrayInputStream bis = new ByteArrayInputStream(likeNoti); ObjectInput in = null; @@ -34,10 +40,14 @@ public void likeReceiver(byte[] likeNoti){ System.out.println("여기까진 오는가"); if (obj instanceof FcmRequestDto){ FcmRequestDto notification = (FcmRequestDto) obj; - fcmService.sendByToken(notification); + String response = String.valueOf(fcmService.sendByToken(notification)); + if (response.isEmpty()){ + throw new IOException(); + } } }catch (IOException | ClassNotFoundException e){ e.printStackTrace(); + channel.basicNack(tag,false,true); }finally { try{ bis.close(); @@ -64,7 +74,10 @@ public void commentReceiver(byte[] commentNoti){ Object obj = in.readObject(); if (obj instanceof FcmRequestDto){ FcmRequestDto notification = (FcmRequestDto) obj; - fcmService.sendByToken(notification); + String response = String.valueOf(fcmService.sendByToken(notification)); + if (response.isEmpty()){ + throw new IOException(); + } } }catch (IOException | ClassNotFoundException e){ e.printStackTrace(); diff --git a/src/main/java/org/winey/server/service/FcmService.java b/src/main/java/org/winey/server/service/FcmService.java index fd94295..404be38 100644 --- a/src/main/java/org/winey/server/service/FcmService.java +++ b/src/main/java/org/winey/server/service/FcmService.java @@ -1,17 +1,24 @@ package org.winey.server.service; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; import com.google.auth.oauth2.GoogleCredentials; +import com.google.common.net.HttpHeaders; import com.google.firebase.FirebaseApp; import com.google.firebase.FirebaseOptions; -import com.google.firebase.database.DatabaseReference; -import com.google.firebase.database.FirebaseDatabase; import com.google.firebase.messaging.*; import lombok.extern.slf4j.Slf4j; +import okhttp3.MediaType; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.RequestBody; +import okhttp3.Response; + import org.springframework.beans.factory.annotation.Value; import org.springframework.core.io.ClassPathResource; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; +import org.winey.server.service.message.FcmMessage; import org.winey.server.service.message.FcmRequestDto; import javax.annotation.PostConstruct; @@ -19,6 +26,7 @@ import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.CompletableFuture; import java.util.stream.Collectors; @Slf4j @@ -33,6 +41,9 @@ public class FcmService{ @Value("${fcm.key.scope}") private String fireBaseScope; + @Value("${fcm.api.url}") + private String FCM_API_URL; + // fcm 기본 설정 진행 @PostConstruct public void init() { @@ -55,6 +66,7 @@ public void init() { } + // 알림 보내기 public void sendByTokenList(List tokenList) { @@ -90,24 +102,83 @@ public void sendByTokenList(List tokenList) { } // 좋아요나 댓글 관련 알림 로직 작성 @Async - public void sendByToken(FcmRequestDto wineyNotification) { + public CompletableFuture sendByToken(FcmRequestDto wineyNotification) throws JsonProcessingException { // 메시지 만들기 - Message message = Message.builder() - .putData("feedId", String.valueOf(wineyNotification.getFeedId())) - .putData("notiType", String.valueOf(wineyNotification.getType())) - .putData("title", "위니 제국의 편지가 도착했어요.") - .putData("message" ,wineyNotification.getMessage()) - .setToken(wineyNotification.getToken()) + // Message message = Message.builder() + // .putData("feedId", String.valueOf(wineyNotification.getFeedId())) + // .putData("notiType", String.valueOf(wineyNotification.getType())) + // .putData("title", "위니 제국의 편지가 도착했어요.") + // .putData("message" ,wineyNotification.getMessage()) + // .setToken(wineyNotification.getToken()) <- 만약 여기 destination 부분만 수정해도 될 수도 있음. 가능성 생각하기 + // .build(); + String jsonMessage = makeSingleMessage(wineyNotification); + // 요청에 대한 응답을 받을 response + Response response; + // 알림 발송 + response = sendPushMessage(jsonMessage); + return CompletableFuture.completedFuture(response); + } + + // 실제 파이어베이스 서버로 푸시 메시지를 전송하는 메서드 + private Response sendPushMessage(String message) { + + try { + OkHttpClient client = new OkHttpClient(); + RequestBody requestBody = RequestBody.create(message, MediaType.get("application/json; charset=utf-8")); + Request httpRequest = new Request.Builder() + .url(FCM_API_URL) // 요청을 보낼 위치 (to 파이어베이스 서버) + .post(requestBody) + .addHeader(HttpHeaders.AUTHORIZATION, "Bearer " + getAccessToken()) + .addHeader(HttpHeaders.CONTENT_TYPE, "application/json; UTF-8") .build(); - // 요청에 대한 응답을 받을 response - String response; + Response response = client.newCall(httpRequest).execute(); + + log.info("단일 기기 알림 전송 성공 ! successCount: 1 messages were sent successfully"); + log.info("알림 전송: {}", response.body().string()); + return response; + } catch (IOException e) { + throw new IllegalArgumentException("파일을 읽는 데 실패했습니다."); + } + } + + // Firebase에서 Access Token 가져오기 + private String getAccessToken() { + try { - // 알림 발송 - response = FirebaseMessaging.getInstance().send(message); - System.out.println(response); - } catch (FirebaseMessagingException e) { - log.error("푸시 알림을 보내다가 오류가 발생했습니다. error info : {}", e.getMessage()); + GoogleCredentials googleCredentials = GoogleCredentials + .fromStream(new ClassPathResource(FCM_PRIVATE_KEY_PATH).getInputStream()) + .createScoped(List.of("https://www.googleapis.com/auth/cloud-platform")); + googleCredentials.refreshIfExpired(); + log.info("getAccessToken() - googleCredentials: {} ", googleCredentials.getAccessToken().getTokenValue()); + + return googleCredentials.getAccessToken().getTokenValue(); + } catch (IOException e) { + throw new IllegalArgumentException("파일을 읽는 데 실패했습니다."); + } + } + private String makeSingleMessage(FcmRequestDto wineyNotification) throws JsonProcessingException { + + try { + FcmMessage fcmMessage = FcmMessage.builder() + .message(FcmMessage.Message.builder() + .token(wineyNotification.getToken()) // 1:1 전송 시 반드시 필요한 대상 토큰 설정 + .data(FcmMessage.Data.builder() + .title("위니 제국의 편지가 도착했어요.") + .message(wineyNotification.getMessage()) + .feedId(String.valueOf(wineyNotification.getFeedId())) + .notiType(String.valueOf(wineyNotification.getType())) + .build()) + .notification(FcmMessage.Notification.builder() + .title("위니 제국의 편지가 도착했어요.") + .body(wineyNotification.getMessage()) + .build()) + .build() + ).validateOnly(false) + .build(); + return new ObjectMapper().writeValueAsString(fcmMessage); + } catch (JsonProcessingException e) { + throw new IllegalArgumentException("JSON 처리 도중에 예외가 발생했습니다."); } } diff --git a/src/main/java/org/winey/server/service/message/FcmMessage.java b/src/main/java/org/winey/server/service/message/FcmMessage.java new file mode 100644 index 0000000..fe11ba3 --- /dev/null +++ b/src/main/java/org/winey/server/service/message/FcmMessage.java @@ -0,0 +1,46 @@ +package org.winey.server.service.message; + + + +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; + +@Builder +@Getter +@AllArgsConstructor(access = AccessLevel.PRIVATE) +public class FcmMessage { + + private boolean validateOnly; + private Message message; + + @Builder + @AllArgsConstructor + @Getter + public static class Message{ + private Data data; + private String token; + private Notification notification; + } + + @Builder + @AllArgsConstructor + @Getter + public static class Data{ + private String title; + private String message; + private String feedId; + private String notiType; + } + + @Builder + @AllArgsConstructor + @Getter + public static class Notification{ + private String title; + private String body; + } + + +} From 1f2d969d29e94a46997e96597a34e0cf918d7fda Mon Sep 17 00:00:00 2001 From: soohyun <49307946+sss4920@users.noreply.github.com> Date: Sat, 9 Dec 2023 19:16:21 +0900 Subject: [PATCH 17/78] Update deploy.sh --- scripts/deploy.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/deploy.sh b/scripts/deploy.sh index b905050..41efaeb 100644 --- a/scripts/deploy.sh +++ b/scripts/deploy.sh @@ -50,7 +50,7 @@ nohup java -jar -Duser.timezone=Asia/Seoul -Dspring.profiles.active=$IDLE_PROFIL echo "> $IDLE_PROFILE 10초 후 Health check 시작" echo "> curl -s http://localhost:$IDLE_PORT/health " -sleep 10 +sleep 120 for retry_count in {1..10} do From bf7d56a4788caaaaaa085a4a1f6589249f3b5463 Mon Sep 17 00:00:00 2001 From: Seonheui Jeon <88873302+funnysunny08@users.noreply.github.com> Date: Mon, 11 Dec 2023 21:12:13 +0900 Subject: [PATCH 18/78] Update deploy.sh --- scripts/deploy.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/deploy.sh b/scripts/deploy.sh index 41efaeb..b905050 100644 --- a/scripts/deploy.sh +++ b/scripts/deploy.sh @@ -50,7 +50,7 @@ nohup java -jar -Duser.timezone=Asia/Seoul -Dspring.profiles.active=$IDLE_PROFIL echo "> $IDLE_PROFILE 10초 후 Health check 시작" echo "> curl -s http://localhost:$IDLE_PORT/health " -sleep 120 +sleep 10 for retry_count in {1..10} do From 9406593fb1cc8f25ddb169423d7a60993463045e Mon Sep 17 00:00:00 2001 From: Seonheui Jeon <88873302+funnysunny08@users.noreply.github.com> Date: Mon, 11 Dec 2023 21:18:11 +0900 Subject: [PATCH 19/78] Create dev-ci.yml --- .github/workflows/dev-ci.yml | 54 ++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 .github/workflows/dev-ci.yml diff --git a/.github/workflows/dev-ci.yml b/.github/workflows/dev-ci.yml new file mode 100644 index 0000000..356098c --- /dev/null +++ b/.github/workflows/dev-ci.yml @@ -0,0 +1,54 @@ +# This workflow uses actions that are not certified by GitHub. +# They are provided by a third-party and are governed by +# separate terms of service, privacy policy, and support +# documentation. +# This workflow will build a Java project with Gradle and cache/restore any dependencies to improve the workflow execution time +# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-gradle + +name: WINEY DEV CI + +on: + pull_request: + branches: [ "dev" ] + +permissions: + contents: read + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + - name: Set up JDK 11 + uses: actions/setup-java@v3 + with: + java-version: '11' + distribution: 'temurin' + + - name: make application.properties 파일 생성 + run: | + ## create application.yml + mkdir ./src/main/resources + cd ./src/main/resources + + # application.yml 파일 생성 + touch ./application.yml + + # GitHub-Actions 에서 설정한 값을 application.yml 파일에 쓰기 + echo "${{ secrets.WINEY_DEV_APPLICATION }}" >> ./application.yml + + # Firebase secrets 파일 복사 + echo "${{ secrets.WINEY_FIREBASE }}" >> ./winey-firebase.json + + # application.yml 파일 확인 + cat ./application.yml + shell: bash + + # 이 워크플로우는 gradle build + - name: Grant execute permission for gradlew + run: chmod +x gradlew + + - name: Build with Gradle # 실제 application build + run: ./gradlew build -PactiveProfiles=local From 5797c1c8f9cbac9ad7e635636918225ac41cf5c7 Mon Sep 17 00:00:00 2001 From: Seonheui Jeon <88873302+funnysunny08@users.noreply.github.com> Date: Mon, 11 Dec 2023 21:43:27 +0900 Subject: [PATCH 20/78] Create dev-cd.yml --- .github/workflows/dev-cd.yml | 100 +++++++++++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 .github/workflows/dev-cd.yml diff --git a/.github/workflows/dev-cd.yml b/.github/workflows/dev-cd.yml new file mode 100644 index 0000000..fbbeaef --- /dev/null +++ b/.github/workflows/dev-cd.yml @@ -0,0 +1,100 @@ +# 워크플로우의 이름 지정 +name: WINEY-DEV-CD + +# 해당 workflow가 언제 실행될 것인지에 대한 트리거를 지정 +on: + push: + branches: [ dev ] # dev branch로 push 될 때 실행됩니다. + +env: + S3_BUCKET_NAME: winey-dev + +jobs: + build: + name: Code deployment + + # 실행 환경 + runs-on: ubuntu-latest + + steps: + + # 1) 워크플로우 실행 전 기본적으로 체크아웃 필요 + - name: checkout + uses: actions/checkout@v3 + + # 2) JDK 11버전 설치, 다른 JDK 버전을 사용하다면 수정 + - name: Set up JDK 11 + uses: actions/setup-java@v3 + with: + java-version: '11' + distribution: 'temurin' + + # 3) 환경변수 파일 생성 + - name: make application.properties 파일 생성 + run: | + ## create application.yml + mkdir ./src/main/resources + cd ./src/main/resources + + # application.yml 파일 생성 + touch ./application.yml + + # GitHub-Actions 에서 설정한 값을 application.yml 파일에 쓰기 + echo "${{ secrets.WINEY_DEV_APPLICATION }}" >> ./application.yml + + # Firebase secrets 파일 복사 + echo "${{ secrets.WINEY_FIREBASE }}" >> ./winey-firebase.json + + # application.yml 파일 확인 + cat ./application.yml + shell: bash + + # 이 워크플로우는 gradle build + - name: Grant execute permission for gradlew + run: chmod +x gradlew + + - name: Build with Gradle # 실제 application build 테스트 제외 + run: ./gradlew build -x test + + # 디렉토리 생성 + - name: Make Directory + run: mkdir -p deploy + + # Jar 파일 복사 + - name: Copy Jar + run: cp ./build/libs/*.jar ./deploy + + # appspec.yml 파일 복사 + - name: Copy appspec.yml + run: cp appspec.yml ./deploy + + # script files 복사 + - name: Copy script + run: cp ./scripts/*.sh ./deploy + + - name: Make zip file + run: zip -r ./winey_dev_server.zip ./deploy + shell: bash + + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v1 + with: + aws-access-key-id: ${{ secrets.AWS_DEV_ACCESS_KEY }} + aws-secret-access-key: ${{ secrets.AWS_DEV_SECRET_KEY }} + aws-region: ap-northeast-2 + + - name: Upload to S3 + run: aws s3 cp --region ap-northeast-2 ./winey_dev_server.zip s3://$S3_BUCKET_NAME/ + + # Deploy + - name: Deploy + env: + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_DEV_ACCESS_KEY }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_DEV_SECRET_KEY }} + run: + aws deploy create-deployment + --application-name winey-dev-codedeploy + --deployment-group-name winey-dev-codedeploy-group + --file-exists-behavior OVERWRITE + --s3-location bucket=winey-dev,bundleType=zip,key=winey_dev_server.zip + --region ap-northeast-2 From 2a036cb85dafc6fded890e550699d57bc149dd86 Mon Sep 17 00:00:00 2001 From: Seonheui Jeon <88873302+funnysunny08@users.noreply.github.com> Date: Mon, 11 Dec 2023 21:44:50 +0900 Subject: [PATCH 21/78] Update dev-ci.yml --- .github/workflows/dev-ci.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/dev-ci.yml b/.github/workflows/dev-ci.yml index 356098c..0b93fa8 100644 --- a/.github/workflows/dev-ci.yml +++ b/.github/workflows/dev-ci.yml @@ -30,7 +30,6 @@ jobs: - name: make application.properties 파일 생성 run: | ## create application.yml - mkdir ./src/main/resources cd ./src/main/resources # application.yml 파일 생성 From 1972957d1106ac488096a550eec3469db318f708 Mon Sep 17 00:00:00 2001 From: Seonheui Jeon <88873302+funnysunny08@users.noreply.github.com> Date: Mon, 11 Dec 2023 21:45:01 +0900 Subject: [PATCH 22/78] Update dev-cd.yml --- .github/workflows/dev-cd.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/dev-cd.yml b/.github/workflows/dev-cd.yml index fbbeaef..abb0fc1 100644 --- a/.github/workflows/dev-cd.yml +++ b/.github/workflows/dev-cd.yml @@ -33,7 +33,6 @@ jobs: - name: make application.properties 파일 생성 run: | ## create application.yml - mkdir ./src/main/resources cd ./src/main/resources # application.yml 파일 생성 From 99708db62f4bf3f117706fabeace81006d2fe722 Mon Sep 17 00:00:00 2001 From: Seonheui Jeon <88873302+funnysunny08@users.noreply.github.com> Date: Mon, 11 Dec 2023 22:10:22 +0900 Subject: [PATCH 23/78] Update dev-ci.yml --- .github/workflows/dev-ci.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/dev-ci.yml b/.github/workflows/dev-ci.yml index 0b93fa8..f5a3dfb 100644 --- a/.github/workflows/dev-ci.yml +++ b/.github/workflows/dev-ci.yml @@ -38,6 +38,9 @@ jobs: # GitHub-Actions 에서 설정한 값을 application.yml 파일에 쓰기 echo "${{ secrets.WINEY_DEV_APPLICATION }}" >> ./application.yml + # winey-firebase.json 파일 생성 + touch ./winey-firebase.json + # Firebase secrets 파일 복사 echo "${{ secrets.WINEY_FIREBASE }}" >> ./winey-firebase.json From 0bd78000417020e8e8b86416a2acb302072f3301 Mon Sep 17 00:00:00 2001 From: Seonheui Jeon <88873302+funnysunny08@users.noreply.github.com> Date: Mon, 11 Dec 2023 22:10:35 +0900 Subject: [PATCH 24/78] Update dev-cd.yml --- .github/workflows/dev-cd.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/dev-cd.yml b/.github/workflows/dev-cd.yml index abb0fc1..1b454b0 100644 --- a/.github/workflows/dev-cd.yml +++ b/.github/workflows/dev-cd.yml @@ -41,6 +41,9 @@ jobs: # GitHub-Actions 에서 설정한 값을 application.yml 파일에 쓰기 echo "${{ secrets.WINEY_DEV_APPLICATION }}" >> ./application.yml + # winey-firebase.json 파일 생성 + touch ./winey-firebase.json + # Firebase secrets 파일 복사 echo "${{ secrets.WINEY_FIREBASE }}" >> ./winey-firebase.json From 4b6fec47c9200a4a92cb48c772923f26b5132413 Mon Sep 17 00:00:00 2001 From: Seonheui Jeon <88873302+funnysunny08@users.noreply.github.com> Date: Mon, 11 Dec 2023 22:21:27 +0900 Subject: [PATCH 25/78] Update dev-ci.yml --- .github/workflows/dev-ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/dev-ci.yml b/.github/workflows/dev-ci.yml index f5a3dfb..8b5adf4 100644 --- a/.github/workflows/dev-ci.yml +++ b/.github/workflows/dev-ci.yml @@ -30,6 +30,7 @@ jobs: - name: make application.properties 파일 생성 run: | ## create application.yml + mkdir ./src/main/resources cd ./src/main/resources # application.yml 파일 생성 From 6ef0dbc26a8cb81e605b4ffb36da011be107bbb0 Mon Sep 17 00:00:00 2001 From: Seonheui Jeon <88873302+funnysunny08@users.noreply.github.com> Date: Tue, 12 Dec 2023 00:03:48 +0900 Subject: [PATCH 26/78] Update dev-ci.yml --- .github/workflows/dev-ci.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/dev-ci.yml b/.github/workflows/dev-ci.yml index 8b5adf4..356098c 100644 --- a/.github/workflows/dev-ci.yml +++ b/.github/workflows/dev-ci.yml @@ -39,9 +39,6 @@ jobs: # GitHub-Actions 에서 설정한 값을 application.yml 파일에 쓰기 echo "${{ secrets.WINEY_DEV_APPLICATION }}" >> ./application.yml - # winey-firebase.json 파일 생성 - touch ./winey-firebase.json - # Firebase secrets 파일 복사 echo "${{ secrets.WINEY_FIREBASE }}" >> ./winey-firebase.json From f3e9a57c1056caa2680830d1bae7c91db5dc494c Mon Sep 17 00:00:00 2001 From: Seonheui Jeon <88873302+funnysunny08@users.noreply.github.com> Date: Tue, 12 Dec 2023 00:05:18 +0900 Subject: [PATCH 27/78] Update dev-ci.yml --- .github/workflows/dev-ci.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/dev-ci.yml b/.github/workflows/dev-ci.yml index 356098c..0b93fa8 100644 --- a/.github/workflows/dev-ci.yml +++ b/.github/workflows/dev-ci.yml @@ -30,7 +30,6 @@ jobs: - name: make application.properties 파일 생성 run: | ## create application.yml - mkdir ./src/main/resources cd ./src/main/resources # application.yml 파일 생성 From be94ae106d9dfd23952e2823472313d25ec5e1d5 Mon Sep 17 00:00:00 2001 From: Seonheui Jeon <88873302+funnysunny08@users.noreply.github.com> Date: Wed, 10 Jan 2024 00:11:55 +0900 Subject: [PATCH 28/78] =?UTF-8?q?[Feat]=20=EB=AA=A9=ED=91=9C=20=EC=B3=AC?= =?UTF-8?q?=EA=B3=84=20=EA=B0=9C=ED=8E=B8=20(#214)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [Feat] GoalType enum 클래스 생성 * [Feat] Goal entity 변경사항 적용 * [Feat] 신규 유저 가입시 목표 자동 설정 * [Feat] 피드 생성시 목표 달성 로직 수정 * [Feat] 목표 달성 시 새로운 목표 자동 시작 * [Feat] 마이페이지에서 현재 진행중인 목표 반환 로직 수정 * [Feat] 목표 달성시 새로운 목표 자동 시작 * [Feat] 피드 삭제 시 목표 달성 체크 로직 수정 * [Feat] 마이페이지 조회에 transaction read-only 해제 * [Feat] 목표 체계 개편 --- .../response/user/UserResponseDto.java | 13 +- .../response/user/UserResponseGoalDto.java | 25 --- .../response/user/UserResponseUserDto.java | 20 --- .../org/winey/server/domain/feed/Feed.java | 22 ++- .../org/winey/server/domain/goal/Goal.java | 50 ++++-- .../winey/server/domain/goal/GoalType.java | 28 +++ .../server/domain/notification/NotiType.java | 7 +- .../org/winey/server/domain/user/User.java | 38 +++- .../winey/server/domain/user/UserLevel.java | 21 ++- .../org/winey/server/exception/Error.java | 1 + .../org/winey/server/service/FeedService.java | 165 ++++++++---------- .../org/winey/server/service/GoalService.java | 15 +- .../org/winey/server/service/NotiService.java | 63 +++---- .../org/winey/server/service/UserService.java | 53 ++---- .../server/service/auth/AuthService.java | 21 +-- 15 files changed, 276 insertions(+), 266 deletions(-) delete mode 100644 src/main/java/org/winey/server/controller/response/user/UserResponseGoalDto.java delete mode 100644 src/main/java/org/winey/server/controller/response/user/UserResponseUserDto.java create mode 100644 src/main/java/org/winey/server/domain/goal/GoalType.java diff --git a/src/main/java/org/winey/server/controller/response/user/UserResponseDto.java b/src/main/java/org/winey/server/controller/response/user/UserResponseDto.java index 7425703..932644e 100644 --- a/src/main/java/org/winey/server/controller/response/user/UserResponseDto.java +++ b/src/main/java/org/winey/server/controller/response/user/UserResponseDto.java @@ -10,11 +10,14 @@ @NoArgsConstructor(access = AccessLevel.PRIVATE) @AllArgsConstructor(access = AccessLevel.PRIVATE) public class UserResponseDto { - private UserResponseUserDto userResponseUserDto; - private UserResponseGoalDto userResponseGoalDto; + private Long userId; + private String nickname; + private String userLevel; + private Boolean fcmIsAllowed; + private Long savedAmount; + private Long savedCount; - public static UserResponseDto of(UserResponseUserDto userResponseUserDto, UserResponseGoalDto userResponseGoalDto) { - return new UserResponseDto(userResponseUserDto, userResponseGoalDto); + public static UserResponseDto of(Long userId, String nickname, String userLevel, Boolean fcmIsAllowed, Long savedAmount, Long savedCount) { + return new UserResponseDto(userId, nickname, userLevel, fcmIsAllowed, savedAmount, savedCount); } } - diff --git a/src/main/java/org/winey/server/controller/response/user/UserResponseGoalDto.java b/src/main/java/org/winey/server/controller/response/user/UserResponseGoalDto.java deleted file mode 100644 index 42c8a45..0000000 --- a/src/main/java/org/winey/server/controller/response/user/UserResponseGoalDto.java +++ /dev/null @@ -1,25 +0,0 @@ -package org.winey.server.controller.response.user; - -import com.fasterxml.jackson.annotation.JsonProperty; -import lombok.AccessLevel; -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; - -@Getter -@NoArgsConstructor(access = AccessLevel.PRIVATE) -@AllArgsConstructor(access = AccessLevel.PRIVATE) -public class UserResponseGoalDto { - private Long duringGoalAmount; - private Long duringGoalCount; - private Long targetMoney; - private int targetDay; - private int dDay; - private Boolean isOver; - private Boolean isAttained; - - public static UserResponseGoalDto of(Long duringGoalAmount, Long duringGoalCount, Long targetMoney, int targetDay, int dDay, boolean isOver, boolean isAttained) { - return new UserResponseGoalDto(duringGoalAmount, duringGoalCount, targetMoney, targetDay, dDay, isOver, isAttained); - } - -} diff --git a/src/main/java/org/winey/server/controller/response/user/UserResponseUserDto.java b/src/main/java/org/winey/server/controller/response/user/UserResponseUserDto.java deleted file mode 100644 index ad40ac9..0000000 --- a/src/main/java/org/winey/server/controller/response/user/UserResponseUserDto.java +++ /dev/null @@ -1,20 +0,0 @@ -package org.winey.server.controller.response.user; - -import lombok.AccessLevel; -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; - -@Getter -@NoArgsConstructor(access = AccessLevel.PRIVATE) -@AllArgsConstructor(access = AccessLevel.PRIVATE) -public class UserResponseUserDto { - private Long userId; - private String nickname; - private String userLevel; - private Boolean fcmIsAllowed; - - public static UserResponseUserDto of(Long userId, String nickname, String userLevel, Boolean fcmIsAllowed) { - return new UserResponseUserDto(userId, nickname, userLevel, fcmIsAllowed); - } -} \ No newline at end of file diff --git a/src/main/java/org/winey/server/domain/feed/Feed.java b/src/main/java/org/winey/server/domain/feed/Feed.java index f586daa..09a3cc8 100644 --- a/src/main/java/org/winey/server/domain/feed/Feed.java +++ b/src/main/java/org/winey/server/domain/feed/Feed.java @@ -1,27 +1,35 @@ package org.winey.server.domain.feed; +import java.util.List; +import javax.persistence.CascadeType; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.OneToMany; import lombok.AccessLevel; import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; -import org.springframework.data.annotation.CreatedDate; import org.winey.server.domain.AuditingTimeEntity; import org.winey.server.domain.comment.Comment; import org.winey.server.domain.goal.Goal; import org.winey.server.domain.user.User; -import javax.persistence.*; -import java.time.LocalDateTime; -import java.util.List; - @Getter @Entity @NoArgsConstructor(access = AccessLevel.PROTECTED) public class Feed extends AuditingTimeEntity { + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "feed_id") private Long feedId; + @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name="user_id") private User user; @@ -46,11 +54,11 @@ public class Feed extends AuditingTimeEntity { private List comments; @Builder - public Feed(User user, String feedTitle, String feedImage, Long feedMoney, Goal goal){ + public Feed(User user, String feedTitle, String feedImage, Long feedMoney) { this.user = user; this.feedTitle = feedTitle; this.feedImage = feedImage; this.feedMoney = feedMoney; - this.goal = goal; + this.goal = null; } } diff --git a/src/main/java/org/winey/server/domain/goal/Goal.java b/src/main/java/org/winey/server/domain/goal/Goal.java index d318f53..7ec1d9a 100644 --- a/src/main/java/org/winey/server/domain/goal/Goal.java +++ b/src/main/java/org/winey/server/domain/goal/Goal.java @@ -1,21 +1,34 @@ package org.winey.server.domain.goal; -import java.util.ArrayList; -import java.util.List; -import lombok.*; -import org.hibernate.annotations.ColumnDefault; +import java.time.LocalDate; +import javax.persistence.Column; +import javax.persistence.ConstraintMode; +import javax.persistence.Entity; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; +import javax.persistence.FetchType; +import javax.persistence.ForeignKey; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.Table; +import javax.persistence.UniqueConstraint; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; import org.hibernate.annotations.DynamicInsert; import org.winey.server.domain.AuditingTimeEntity; -import org.winey.server.domain.feed.Feed; -import org.winey.server.domain.notification.Notification; import org.winey.server.domain.user.User; -import javax.persistence.*; -import java.time.LocalDate; - @Entity @Getter @DynamicInsert +@Table(uniqueConstraints = { + @UniqueConstraint(columnNames = {"user_id", "goal_type"}) +}) @NoArgsConstructor(access = AccessLevel.PROTECTED) public class Goal extends AuditingTimeEntity { @@ -23,10 +36,14 @@ public class Goal extends AuditingTimeEntity { @GeneratedValue(strategy = GenerationType.IDENTITY) private Long goalId; - @Column(nullable = false) + @Column(name = "goal_type") + @Enumerated(EnumType.STRING) + private GoalType goalType; + + @Column private Long targetMoney; - @Column(nullable = false) + @Column private LocalDate targetDate; @Column(nullable = false) @@ -38,23 +55,20 @@ public class Goal extends AuditingTimeEntity { @Column(nullable = false) private Long duringGoalCount; - @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "user_id", nullable = false, foreignKey = @ForeignKey(ConstraintMode.CONSTRAINT)) private User user; - @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.REMOVE, mappedBy = "goal", orphanRemoval = true) - private List feeds = new ArrayList<>(); +// @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.REMOVE, mappedBy = "goal", orphanRemoval = true) +// private List feeds = new ArrayList<>(); @Builder - public Goal(Long targetMoney, LocalDate targetDate, User user) { - this.targetMoney = targetMoney; - this.targetDate = targetDate; + public Goal(GoalType goalType, User user) { + this.goalType = goalType; this.user = user; this.duringGoalCount = 0L; this.isAttained = false; this.duringGoalAmount = 0L; - } public void updateIsAttained(boolean isAttained) { diff --git a/src/main/java/org/winey/server/domain/goal/GoalType.java b/src/main/java/org/winey/server/domain/goal/GoalType.java new file mode 100644 index 0000000..dae96c5 --- /dev/null +++ b/src/main/java/org/winey/server/domain/goal/GoalType.java @@ -0,0 +1,28 @@ +package org.winey.server.domain.goal; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.winey.server.domain.user.UserLevel; + +@Getter +@AllArgsConstructor +public enum GoalType { + COMMONER_GOAL(UserLevel.COMMONER, 5000, 2, "아메리카노"), + KNIGHT_GOAL(UserLevel.KNIGHT, 30000, 7, "치킨"), + ARISTOCRAT_GOAL(UserLevel.ARISTOCRAT, 150000, 10, "운동화"), + EMPEROR_GOAL(UserLevel.EMPEROR, 300000, 20, "에어팟"); + + private final UserLevel userLevel; + private final int targetMoney; + private final int targetCount; + private final String targetProduct; + + public static GoalType findGoalTypeByUserLevel(UserLevel userLevel) { + for (GoalType goalType : GoalType.values()) { + if (goalType.getUserLevel() == userLevel) { + return goalType; + } + } + return null; + } +} diff --git a/src/main/java/org/winey/server/domain/notification/NotiType.java b/src/main/java/org/winey/server/domain/notification/NotiType.java index a99464d..fd9251c 100644 --- a/src/main/java/org/winey/server/domain/notification/NotiType.java +++ b/src/main/java/org/winey/server/domain/notification/NotiType.java @@ -2,10 +2,6 @@ import lombok.AllArgsConstructor; import lombok.Getter; -import lombok.RequiredArgsConstructor; -import lombok.Setter; - -import javax.validation.constraints.Null; @Getter @AllArgsConstructor @@ -18,8 +14,7 @@ public enum NotiType { //삭제로 등급 강등 DELETERANKDOWNTO3("게시글이 삭제되어 등급이 귀족으로 내려갔어요."), DELETERANKDOWNTO2("게시글이 삭제되어 등급이 기사로 내려갔어요."), - - DELETERANKDOWNTO1("게시글이 삭제되어 등급이 귀족으로 내려갔어요."), + DELETERANKDOWNTO1("게시글이 삭제되어 등급이 평민으로 내려갔어요."), //목표 달성 실패 GOALFAILED("이번에는 아쉽지만 힘내서 다음 목표를 세워볼까요?"), diff --git a/src/main/java/org/winey/server/domain/user/User.java b/src/main/java/org/winey/server/domain/user/User.java index f20fdc0..adff709 100644 --- a/src/main/java/org/winey/server/domain/user/User.java +++ b/src/main/java/org/winey/server/domain/user/User.java @@ -1,5 +1,7 @@ package org.winey.server.domain.user; +import static org.winey.server.domain.user.UserLevel.COMMONER; + import java.util.List; import java.util.Objects; import javax.persistence.CascadeType; @@ -58,6 +60,12 @@ public class User extends AuditingTimeEntity { @Column(nullable = true) private Boolean fcmIsAllowed = true; + @Column + private Long savedAmount; + + @Column + private Long savedCount; + @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.REMOVE, mappedBy = "user", orphanRemoval = true) private List goals; @@ -78,15 +86,33 @@ public class User extends AuditingTimeEntity { @Builder public User(String nickname, String socialId, SocialType socialType) { this.nickname = nickname; - this.userLevel = UserLevel.COMMONER; + this.userLevel = COMMONER; this.socialId = socialId; this.socialType = socialType; + this.savedCount = 0L; + this.savedAmount = 0L; } public void updateUserLevel(UserLevel userLevel){ this.userLevel = userLevel; } + public void upgradeUserLevel() { + switch (this.userLevel) { + case COMMONER: + this.userLevel = UserLevel.KNIGHT; + break; + case KNIGHT: + this.userLevel = UserLevel.ARISTOCRAT; + break; + case ARISTOCRAT: + this.userLevel = UserLevel.EMPEROR; + break; + case EMPEROR: + break; + } + } + public void updateRefreshToken(String refreshToken) { this.refreshToken = refreshToken; } @@ -99,6 +125,16 @@ public void updateNickname(String nickname) { public void updateFcmIsAllowed(Boolean isAllowed){this.fcmIsAllowed = isAllowed;} + public void increaseSavedAmountAndCount(Long money) { + this.savedAmount += money; + this.savedCount += 1; + } + + public void decreaseSavedAmountAndCount(Long money) { + this.savedCount -= money; + this.savedCount -= 1; + } + public String getFcmToken() { if (Objects.nonNull(this.fcmToken)) { return this.fcmToken; diff --git a/src/main/java/org/winey/server/domain/user/UserLevel.java b/src/main/java/org/winey/server/domain/user/UserLevel.java index 5303744..627fdd9 100644 --- a/src/main/java/org/winey/server/domain/user/UserLevel.java +++ b/src/main/java/org/winey/server/domain/user/UserLevel.java @@ -6,11 +6,24 @@ @Getter @AllArgsConstructor public enum UserLevel { - COMMONER("평민", 1), - KNIGHT("기사", 2), - ARISTOCRAT("귀족", 3), - EMPEROR("황제", 4); + COMMONER("평민", 1, 0L, 0L), + KNIGHT("기사", 2, 30000L, 2L), + ARISTOCRAT("귀족", 3, 150000L, 4L), + EMPEROR("황제", 4, 300000L, 6L); private final String name; private final int levelNumber; + private final Long minimumAmount; + private final Long minimumCount; + + public static UserLevel calculateUserLevel(Long amount, Long count) { + if (amount >= EMPEROR.minimumAmount && count >= EMPEROR.minimumCount) { + return EMPEROR; + } else if (amount >= ARISTOCRAT.minimumAmount && count >= ARISTOCRAT.minimumCount) { + return ARISTOCRAT; + } else if (amount >= KNIGHT.minimumAmount && count >= KNIGHT.minimumCount) { + return KNIGHT; + } + return COMMONER; + } } diff --git a/src/main/java/org/winey/server/exception/Error.java b/src/main/java/org/winey/server/exception/Error.java index 343ba9b..171f6ac 100644 --- a/src/main/java/org/winey/server/exception/Error.java +++ b/src/main/java/org/winey/server/exception/Error.java @@ -32,6 +32,7 @@ public enum Error { INVALID_APPLE_CLAIMS(HttpStatus.BAD_REQUEST, "Apple OAuth Claims 값이 올바르지 않습니다."), INVALID_ENCRYPT_COMMUNICATION(HttpStatus.BAD_REQUEST, "Apple OAuth 통신 암호화 과정 중 문제가 발생했습니다."), CREATE_PUBLIC_KEY_EXCEPTION(HttpStatus.BAD_REQUEST, "Apple OAuth 로그인 중 public verify 생성에 문제가 발생했습니다."), + INVALID_USER_LEVEL_EXCEPTION(HttpStatus.BAD_REQUEST, "존재하지 않는 유저 레벨입니다."), /** * 401 UNAUTHORIZED diff --git a/src/main/java/org/winey/server/service/FeedService.java b/src/main/java/org/winey/server/service/FeedService.java index 6c3a813..da9d0a1 100644 --- a/src/main/java/org/winey/server/service/FeedService.java +++ b/src/main/java/org/winey/server/service/FeedService.java @@ -1,6 +1,10 @@ package org.winey.server.service; +import java.time.LocalDateTime; +import java.time.temporal.ChronoUnit; +import java.util.List; import java.util.Objects; +import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; @@ -9,25 +13,25 @@ import org.winey.server.controller.request.CreateFeedRequestDto; import org.winey.server.controller.response.PageResponseDto; import org.winey.server.controller.response.comment.CommentResponseDto; -import org.winey.server.controller.response.feed.*; +import org.winey.server.controller.response.feed.CreateFeedResponseDto; +import org.winey.server.controller.response.feed.GetAllFeedResponseDto; +import org.winey.server.controller.response.feed.GetFeedDetailResponseDto; +import org.winey.server.controller.response.feed.GetFeedResponseDto; import org.winey.server.domain.block.BlockUser; import org.winey.server.domain.feed.Feed; -import org.winey.server.domain.goal.Goal; import org.winey.server.domain.notification.NotiType; import org.winey.server.domain.notification.Notification; import org.winey.server.domain.user.User; import org.winey.server.domain.user.UserLevel; import org.winey.server.exception.Error; -import org.winey.server.exception.model.ForbiddenException; import org.winey.server.exception.model.NotFoundException; import org.winey.server.exception.model.UnauthorizedException; -import org.winey.server.infrastructure.*; - -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.time.temporal.ChronoUnit; -import java.util.List; -import java.util.stream.Collectors; +import org.winey.server.infrastructure.BlockUserRepository; +import org.winey.server.infrastructure.CommentRepository; +import org.winey.server.infrastructure.FeedLikeRepository; +import org.winey.server.infrastructure.FeedRepository; +import org.winey.server.infrastructure.NotiRepository; +import org.winey.server.infrastructure.UserRepository; @Service @RequiredArgsConstructor @@ -35,7 +39,6 @@ public class FeedService { private final FeedRepository feedRepository; private final UserRepository userRepository; - private final GoalRepository goalRepository; private final FeedLikeRepository feedLikeRepository; private final CommentRepository commentRepository; private final NotiRepository notiRepository; @@ -43,93 +46,90 @@ public class FeedService { @Transactional public CreateFeedResponseDto createFeed(CreateFeedRequestDto request, Long userId, String imageUrl) { + // 1. 유저를 가져온다. User presentUser = userRepository.findByUserId(userId) .orElseThrow(() -> new NotFoundException(Error.NOT_FOUND_USER_EXCEPTION, Error.NOT_FOUND_USER_EXCEPTION.getMessage())); - Goal myGoal = goalRepository.findByUserOrderByCreatedAtDesc(presentUser).stream().findFirst() - .orElseThrow(() -> new ForbiddenException(Error.FEED_FORBIDDEN_EXCEPTION, Error.FEED_FORBIDDEN_EXCEPTION.getMessage())); //목표 설정 안하면 피드 못만듬 -> 에러처리 - + // 2. 피드를 생성한다. Feed feed = Feed.builder() - .feedImage(imageUrl) - .feedMoney(request.getFeedMoney()) - .feedTitle(request.getFeedTitle()) - .user(presentUser) - .goal(myGoal) - .build(); + .feedImage(imageUrl) + .feedMoney(request.getFeedMoney()) + .feedTitle(request.getFeedTitle()) + .user(presentUser) + .build(); feedRepository.save(feed); - myGoal.updateGoalCountAndAmount(feed.getFeedMoney(), true); // 절약 금액, 피드 횟수 업데이트. - - if (myGoal.isAttained()) { - System.out.println("이미 목표달성"); - return CreateFeedResponseDto.of(feed.getFeedId(), feed.getCreatedAt()); - } - - if (LocalDate.now().isAfter(myGoal.getTargetDate())){ - System.out.println("목표를 제한 시간 내에 이루지 못함."); - throw new ForbiddenException(Error.FEED_FORBIDDEN_EXCEPTION, Error.FEED_FORBIDDEN_EXCEPTION.getMessage()); //목표 설정 새로 하게 유도. - } - - if (myGoal.getDuringGoalAmount() >= myGoal.getTargetMoney()) { - myGoal.updateIsAttained(true); // 달성여부 체크 - if (presentUser.getUserLevel().getLevelNumber() != checkUserLevelUp(presentUser)) {//userLevel 변동사항 체크, 만약에 레벨에 변동이 생겼다면? 레벨 강등 알림 생성. - switch (checkUserLevelUp(presentUser)){ - case 2: - notificationBuilderInFeed(NotiType.RANKUPTO2, presentUser); - break; - case 3: - notificationBuilderInFeed(NotiType.RANKUPTO3, presentUser); - break; - case 4: - notificationBuilderInFeed(NotiType.RANKUPTO4, presentUser); - break; - } + // 3. 유저의 누적 절약 금액, 누적 절약 횟수를 업데이트한다. + presentUser.increaseSavedAmountAndCount(feed.getFeedMoney()); + + // 4. 레벨업을 체크한다. + UserLevel newUserLevel = UserLevel.calculateUserLevel(presentUser.getSavedAmount(), presentUser.getSavedCount()); + + if (presentUser.getUserLevel() != newUserLevel) { + // 4-1. 레벨업한다. + presentUser.updateUserLevel(newUserLevel); + + // 4-2. 레벨업 알림을 생성한다. + switch (newUserLevel) { + case KNIGHT: + notificationBuilderInFeed(NotiType.RANKUPTO2, presentUser); + break; + case ARISTOCRAT: + notificationBuilderInFeed(NotiType.RANKUPTO3, presentUser); + break; + case EMPEROR: + notificationBuilderInFeed(NotiType.RANKUPTO4, presentUser); + break; + default: + break; } } + return CreateFeedResponseDto.of(feed.getFeedId(), feed.getCreatedAt()); } - - @Transactional public String deleteFeed(Long userId, Long feedId) { + // 1. 유저를 가져온다. User presentUser = userRepository.findByUserId(userId) .orElseThrow(() -> new NotFoundException(Error.NOT_FOUND_USER_EXCEPTION, Error.NOT_FOUND_USER_EXCEPTION.getMessage())); - Goal presentGoal = goalRepository.findByUserOrderByCreatedAtDesc(presentUser).stream().findFirst() - .orElseThrow(() -> new NotFoundException(Error.NOT_FOUND_GOAL_EXCEPTION, Error.NOT_FOUND_GOAL_EXCEPTION.getMessage())); + + // 2. 지우고자 하는 피드를 가져온다. Feed wantDeleteFeed = feedRepository.findByFeedId(feedId) .orElseThrow(() -> new NotFoundException(Error.NOT_FOUND_FEED_EXCEPTION, Error.NOT_FOUND_FEED_EXCEPTION.getMessage())); + // 3. 피드를 작성한 유저와 현재 접속한 유저가 다르면 삭제할 수 없다. if (presentUser != wantDeleteFeed.getUser()) { throw new UnauthorizedException(Error.DELETE_UNAUTHORIZED, Error.DELETE_UNAUTHORIZED.getMessage()); // 삭제하는 사람 아니면 삭제 못함 처리. } - // 현재 삭제하고자 하는 피드의 goal 아이디 != 현재 진행 중인 goal 아이디 --> 넘어가! - if (wantDeleteFeed.getGoal().getGoalId() != presentGoal.getGoalId()) { - feedRepository.delete(wantDeleteFeed); - return wantDeleteFeed.getFeedImage(); - } - -// if ((!presentGoal.getCreatedAt().isBefore(wantDeleteFeed.getCreatedAt())) || (!presentGoal.getTargetDate().isAfter(wantDeleteFeed.getCreatedAt().toLocalDate()))) { -// feedRepository.delete(wantDeleteFeed); -// return wantDeleteFeed.getFeedImage(); -// } - - presentGoal.updateGoalCountAndAmount(wantDeleteFeed.getFeedMoney(), false); - - if (presentUser.getUserLevel().getLevelNumber() >= 3 && (presentGoal.getTargetMoney() > presentGoal.getDuringGoalAmount())) { //귀족 이상이면 강등로직. - presentGoal.updateIsAttained(false); // 달성여부 체크 - if (presentUser.getUserLevel().getLevelNumber() != checkUserLevelUp(presentUser)) {//userLevel 변동사항 체크, 만약에 레벨에 변동이 생겼다면? 레벨 강등 알림 생성. - switch (checkUserLevelUp(presentUser)){ - case 3: - notificationBuilderInFeed(NotiType.DELETERANKDOWNTO3, presentUser); - break; - case 2: - notificationBuilderInFeed(NotiType.DELETERANKDOWNTO2, presentUser); - break; - } + // 4. 유저의 누적 절약 금액, 누적 절약 횟수를 업데이트한다. + presentUser.decreaseSavedAmountAndCount(wantDeleteFeed.getFeedMoney()); + + // 5. 레벨다운을 체크한다. + UserLevel newUserLevel = UserLevel.calculateUserLevel(presentUser.getSavedAmount(), presentUser.getSavedCount()); + + if (presentUser.getUserLevel() != newUserLevel) { + // 4-1. 레벨다운한다. + presentUser.updateUserLevel(newUserLevel); + + // 4-2. 레벨다운 알림을 생성한다. + switch (newUserLevel) { + case COMMONER: + notificationBuilderInFeed(NotiType.DELETERANKDOWNTO1, presentUser); + break; + case KNIGHT: + notificationBuilderInFeed(NotiType.DELETERANKDOWNTO2, presentUser); + break; + case ARISTOCRAT: + notificationBuilderInFeed(NotiType.DELETERANKDOWNTO3, presentUser); + break; + default: + break; } } + + // 6. 피드를 삭제한다. notiRepository.deleteByLinkId(feedId); feedRepository.delete(wantDeleteFeed); return wantDeleteFeed.getFeedImage(); @@ -268,21 +268,4 @@ private void notificationBuilderInFeed(NotiType type, User user){ notification.updateLinkId(null); notiRepository.save(notification); } - - private int checkUserLevelUp(User presentUser) { - int userAchievedGoals = goalRepository.countByUserAndIsAttained(presentUser, true); //Goal 중 userid가 맞고 isAttained true 개수 세기 - if (userAchievedGoals < 1) { - presentUser.updateUserLevel(UserLevel.COMMONER); - return 1; - } else if (userAchievedGoals < 3) { - presentUser.updateUserLevel(UserLevel.KNIGHT); - return 2; - } else if (userAchievedGoals < 9) { - presentUser.updateUserLevel(UserLevel.ARISTOCRAT); - return 3; - } else { - presentUser.updateUserLevel(UserLevel.EMPEROR); - return 4; - } - } } diff --git a/src/main/java/org/winey/server/service/GoalService.java b/src/main/java/org/winey/server/service/GoalService.java index b9a073e..4aa1b2f 100644 --- a/src/main/java/org/winey/server/service/GoalService.java +++ b/src/main/java/org/winey/server/service/GoalService.java @@ -6,6 +6,7 @@ import org.winey.server.controller.request.goal.GoalRequestCreateDto; import org.winey.server.controller.response.goal.GoalResponseCreateDto; import org.winey.server.domain.goal.Goal; +import org.winey.server.domain.goal.GoalType; import org.winey.server.domain.user.User; import org.winey.server.exception.Error; import org.winey.server.exception.model.NotFoundException; @@ -17,21 +18,23 @@ @Service @RequiredArgsConstructor public class GoalService { + private final GoalRepository goalRepository; private final UserRepository userRepository; @Transactional public GoalResponseCreateDto createGoal(GoalRequestCreateDto requestDto, Long userId) { User user = userRepository.findByUserId(userId) - .orElseThrow(() -> new NotFoundException(Error.NOT_FOUND_USER_EXCEPTION, Error.NOT_FOUND_USER_EXCEPTION.getMessage())); + .orElseThrow(() -> new NotFoundException(Error.NOT_FOUND_USER_EXCEPTION, + Error.NOT_FOUND_USER_EXCEPTION.getMessage())); LocalDate targetDate = LocalDate.now().plusDays(requestDto.getTargetDay()); Goal createGoal = goalRepository.save(Goal.builder() - .targetMoney(requestDto.getTargetMoney()) - .targetDate(targetDate) - .user(user) - .build()); + .goalType(GoalType.findGoalTypeByUserLevel(user.getUserLevel())) + .user(user) + .build()); - return GoalResponseCreateDto.of(userId, createGoal.getTargetMoney(), createGoal.getTargetDate(), createGoal.getCreatedAt()); + return GoalResponseCreateDto.of(userId, createGoal.getTargetMoney(), + createGoal.getTargetDate(), createGoal.getCreatedAt()); } } diff --git a/src/main/java/org/winey/server/service/NotiService.java b/src/main/java/org/winey/server/service/NotiService.java index 68c96e3..e0bae86 100644 --- a/src/main/java/org/winey/server/service/NotiService.java +++ b/src/main/java/org/winey/server/service/NotiService.java @@ -1,6 +1,5 @@ package org.winey.server.service; -import java.time.LocalDate; import java.time.LocalDateTime; import java.time.temporal.ChronoUnit; import java.util.ArrayList; @@ -9,23 +8,17 @@ import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import net.javacrumbs.shedlock.core.SchedulerLock; import net.javacrumbs.shedlock.spring.annotation.EnableSchedulerLock; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.winey.server.controller.response.notification.GetAllNotiResponseDto; import org.winey.server.controller.response.notification.GetNotiResponseDto; -import org.winey.server.domain.goal.Goal; import org.winey.server.domain.notification.NotiType; import org.winey.server.domain.notification.Notification; import org.winey.server.domain.user.User; import org.winey.server.exception.Error; -import org.winey.server.exception.model.BadRequestException; import org.winey.server.exception.model.NotFoundException; -import org.winey.server.infrastructure.GoalRepository; import org.winey.server.infrastructure.NotiRepository; import org.winey.server.infrastructure.UserRepository; @@ -37,8 +30,6 @@ public class NotiService { private final NotiRepository notiRepository; private final UserRepository userRepository; - private final GoalRepository goalRepository; - private static final Logger logger = LoggerFactory.getLogger(NotiService.class); @Transactional(readOnly = true) public GetAllNotiResponseDto getAllNoti(Long userId) { @@ -77,33 +68,33 @@ public Boolean checkNewNoti(Long userId) { return notifications.size() != 0; } - @Transactional - @Scheduled(cron = "0 0 0 * * *", zone = "Asia/Seoul") - @SchedulerLock(name = "SchedulerLock", lockAtMostForString = "PT1M", lockAtLeastForString = "PT1M") - public void checkGoalDateNotification() { - logger.info("목표 달성 체크 스케줄러 작동"); - - TimeZone.setDefault(TimeZone.getTimeZone("Asia/Seoul")); - List allGoals = goalRepository.findLatestGoalsForEachUser(); - LocalDate today = LocalDate.now(); - - logger.info("오늘 날짜: {}", today); - - for (Goal currentGoal : allGoals) { - if (currentGoal.getTargetDate().isEqual(today.minusDays(1))) { - logger.info("알림 생성 goalID: {}", currentGoal.getGoalId()); - - Notification notification = Notification.builder() - .notiType(NotiType.GOALFAILED) - .notiReciver(currentGoal.getUser()) - .notiMessage(NotiType.GOALFAILED.getType()) - .isChecked(false) - .build(); - notification.updateLinkId(null); - notiRepository.save(notification); - } - } - } +// @Transactional +// @Scheduled(cron = "0 0 0 * * *", zone = "Asia/Seoul") +// @SchedulerLock(name = "SchedulerLock", lockAtMostForString = "PT1M", lockAtLeastForString = "PT1M") +// public void checkGoalDateNotification() { +// logger.info("목표 달성 체크 스케줄러 작동"); +// +// TimeZone.setDefault(TimeZone.getTimeZone("Asia/Seoul")); +// List allGoals = goalRepository.findLatestGoalsForEachUser(); +// LocalDate today = LocalDate.now(); +// +// logger.info("오늘 날짜: {}", today); +// +// for (Goal currentGoal : allGoals) { +// if (currentGoal.getTargetDate().isEqual(today.minusDays(1))) { +// logger.info("알림 생성 goalID: {}", currentGoal.getGoalId()); +// +// Notification notification = Notification.builder() +// .notiType(NotiType.GOALFAILED) +// .notiReciver(currentGoal.getUser()) +// .notiMessage(NotiType.GOALFAILED.getType()) +// .isChecked(false) +// .build(); +// notification.updateLinkId(null); +// notiRepository.save(notification); +// } +// } +// } @Scheduled(cron = "0 0 2 * * *", zone = "Asia/Seoul") //혹시 모를 racing에 대비해 새벽 2시에 시작되도록 함. @Transactional diff --git a/src/main/java/org/winey/server/service/UserService.java b/src/main/java/org/winey/server/service/UserService.java index d6d93ce..013f3e7 100644 --- a/src/main/java/org/winey/server/service/UserService.java +++ b/src/main/java/org/winey/server/service/UserService.java @@ -1,77 +1,56 @@ package org.winey.server.service; import lombok.RequiredArgsConstructor; -import org.joda.time.LocalDateTime; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.winey.server.controller.request.UpdateFcmTokenDto; import org.winey.server.controller.request.UpdateUserNicknameDto; import org.winey.server.controller.response.user.UserResponseDto; -import org.winey.server.controller.response.user.UserResponseGoalDto; -import org.winey.server.controller.response.user.UserResponseUserDto; -import org.winey.server.domain.goal.Goal; -import org.winey.server.domain.notification.NotiType; -import org.winey.server.domain.notification.Notification; import org.winey.server.domain.user.User; import org.winey.server.exception.Error; import org.winey.server.exception.model.BadRequestException; import org.winey.server.exception.model.NotFoundException; -import org.winey.server.infrastructure.GoalRepository; -import org.winey.server.infrastructure.NotiRepository; import org.winey.server.infrastructure.UserRepository; -import java.time.Duration; -import java.time.LocalDate; -import java.time.Period; -import java.time.temporal.ChronoUnit; -import java.util.List; - @Service @RequiredArgsConstructor public class UserService { + private final UserRepository userRepository; - private final GoalRepository goalRepository; - @Transactional(readOnly = true) + @Transactional public UserResponseDto getUser(Long userId) { User user = userRepository.findByUserId(userId) - .orElseThrow(() -> new NotFoundException(Error.NOT_FOUND_USER_EXCEPTION, Error.NOT_FOUND_USER_EXCEPTION.getMessage())); - - UserResponseUserDto userDto = UserResponseUserDto.of(user.getUserId(), user.getNickname(), user.getUserLevel().getName(),user.getFcmIsAllowed()); + .orElseThrow(() -> new NotFoundException(Error.NOT_FOUND_USER_EXCEPTION, + Error.NOT_FOUND_USER_EXCEPTION.getMessage())); - List goalList = goalRepository.findByUserOrderByCreatedAtDesc(user); - - if (goalList.size() == 0) { - return UserResponseDto.of(userDto, null); - } - Goal presentGoal = goalList.stream().findFirst() - .orElseThrow(() -> new NotFoundException(Error.NOT_FOUND_GOAL_EXCEPTION, Error.NOT_FOUND_GOAL_EXCEPTION.getMessage())); - - int targetDay = (int) Period.between(presentGoal.getCreatedAt().toLocalDate(), presentGoal.getTargetDate()).getDays(); - int dDay = (int) ChronoUnit.DAYS.between(LocalDate.now(), presentGoal.getTargetDate()); - Boolean isOver = LocalDate.now().isAfter(presentGoal.getTargetDate()); - UserResponseGoalDto goalDto = UserResponseGoalDto.of(presentGoal.getDuringGoalAmount(), presentGoal.getDuringGoalCount(), presentGoal.getTargetMoney(), targetDay, dDay, isOver, presentGoal.isAttained()); - return UserResponseDto.of(userDto, goalDto); + return UserResponseDto.of(user.getUserId(), user.getNickname(), + user.getUserLevel().getName(), user.getFcmIsAllowed(), user.getSavedAmount(), + user.getSavedCount()); } @Transactional public void updateNickname(Long userId, UpdateUserNicknameDto requestDto) { User user = userRepository.findByUserId(userId) - .orElseThrow(() -> new NotFoundException(Error.NOT_FOUND_USER_EXCEPTION, Error.NOT_FOUND_USER_EXCEPTION.getMessage())); + .orElseThrow(() -> new NotFoundException(Error.NOT_FOUND_USER_EXCEPTION, + Error.NOT_FOUND_USER_EXCEPTION.getMessage())); user.updateNickname(requestDto.getNickname()); } + @Transactional - public void updateFcmToken(Long userId, UpdateFcmTokenDto updateFcmTokenDto){ + public void updateFcmToken(Long userId, UpdateFcmTokenDto updateFcmTokenDto) { User user = userRepository.findByUserId(userId) - .orElseThrow(() -> new NotFoundException(Error.NOT_FOUND_USER_EXCEPTION, Error.NOT_FOUND_USER_EXCEPTION.getMessage())); + .orElseThrow(() -> new NotFoundException(Error.NOT_FOUND_USER_EXCEPTION, + Error.NOT_FOUND_USER_EXCEPTION.getMessage())); user.updateFcmToken(updateFcmTokenDto.getToken()); } //푸시알림 동의 여부 수정 api @Transactional - public Boolean allowedPushNotification(Long userId, Boolean fcmIsAllowed){ + public Boolean allowedPushNotification(Long userId, Boolean fcmIsAllowed) { User user = userRepository.findByUserId(userId) - .orElseThrow(()-> new NotFoundException(Error.NOT_FOUND_USER_EXCEPTION, Error.NOT_FOUND_USER_EXCEPTION.getMessage())); + .orElseThrow(() -> new NotFoundException(Error.NOT_FOUND_USER_EXCEPTION, + Error.NOT_FOUND_USER_EXCEPTION.getMessage())); if (fcmIsAllowed == user.getFcmIsAllowed()) { //같은 경우면 에러가 날 수 있으니 에러 띄움. throw new BadRequestException(Error.REQUEST_VALIDATION_EXCEPTION, Error.REQUEST_VALIDATION_EXCEPTION.getMessage()); diff --git a/src/main/java/org/winey/server/service/auth/AuthService.java b/src/main/java/org/winey/server/service/auth/AuthService.java index 4ce32b1..0dd6cdd 100644 --- a/src/main/java/org/winey/server/service/auth/AuthService.java +++ b/src/main/java/org/winey/server/service/auth/AuthService.java @@ -1,7 +1,7 @@ package org.winey.server.service.auth; -import com.sun.net.httpserver.Authenticator; -import feign.FeignException; +import java.util.Random; +import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -9,28 +9,22 @@ import org.winey.server.controller.request.auth.SignInRequestDto; import org.winey.server.controller.response.auth.SignInResponseDto; import org.winey.server.controller.response.auth.TokenResponseDto; -import org.winey.server.domain.feed.Feed; +import org.winey.server.domain.goal.Goal; +import org.winey.server.domain.goal.GoalType; import org.winey.server.domain.notification.NotiType; import org.winey.server.domain.notification.Notification; import org.winey.server.domain.user.SocialType; - import org.winey.server.domain.user.User; import org.winey.server.exception.Error; import org.winey.server.exception.model.NotFoundException; import org.winey.server.exception.model.UnprocessableEntityException; import org.winey.server.infrastructure.BlockUserRepository; -import org.winey.server.infrastructure.FeedRepository; import org.winey.server.infrastructure.GoalRepository; import org.winey.server.infrastructure.NotiRepository; import org.winey.server.infrastructure.UserRepository; import org.winey.server.service.auth.apple.AppleSignInService; import org.winey.server.service.auth.kakao.KakaoSignInService; -import javax.validation.constraints.NotNull; -import java.util.List; -import java.util.Random; -import java.util.stream.Collectors; - @Service @RequiredArgsConstructor public class AuthService { @@ -40,6 +34,7 @@ public class AuthService { private final UserRepository userRepository; private final BlockUserRepository blockUserRepository; + private final GoalRepository goalRepository; private final Long TOKEN_EXPIRATION_TIME_ACCESS = 100 * 24 * 60 * 60 * 1000L; @@ -76,6 +71,12 @@ public SignInResponseDto signIn(String socialAccessToken, SignInRequestDto reque .build(); newNoti.updateLinkId(null); notiRepository.save(newNoti); + + Goal newGoal = Goal.builder() + .goalType(GoalType.COMMONER_GOAL) + .user(newUser) + .build(); + goalRepository.save(newGoal); } User user = userRepository.findBySocialIdAndSocialType(socialId, socialType) From 3b59eba1eff19848fc90e2421dbc4538b88da392 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=80=E1=85=B5=E1=86=B7=E1=84=8B=E1=85=B3=E1=86=BC?= =?UTF-8?q?=E1=84=80=E1=85=AA=E1=86=AB?= Date: Wed, 10 Jan 2024 01:36:26 +0900 Subject: [PATCH 29/78] =?UTF-8?q?.gitignore=EC=97=90=20application.yml=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 162f436..e9d8ac3 100644 --- a/.gitignore +++ b/.gitignore @@ -39,6 +39,7 @@ out/ ### application.yml ### application.yaml +application.yml ### DS_Store .DS_Store From f6a0d17e5139af03017c39fdad21a422daa2b5e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=80=E1=85=B5=E1=86=B7=E1=84=8B=E1=85=B3=E1=86=BC?= =?UTF-8?q?=E1=84=80=E1=85=AA=E1=86=AB?= Date: Wed, 10 Jan 2024 01:40:37 +0900 Subject: [PATCH 30/78] =?UTF-8?q?FeedType=20enum=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../winey/server/domain/feed/FeedType.java | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 src/main/java/org/winey/server/domain/feed/FeedType.java diff --git a/src/main/java/org/winey/server/domain/feed/FeedType.java b/src/main/java/org/winey/server/domain/feed/FeedType.java new file mode 100644 index 0000000..03c5272 --- /dev/null +++ b/src/main/java/org/winey/server/domain/feed/FeedType.java @@ -0,0 +1,23 @@ +package org.winey.server.domain.feed; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Objects; + +@Getter +@AllArgsConstructor +public enum FeedType { + + SAVE("SAVE"), + CONSUME("CONSUME"); + + private final String stringVal; + + public static boolean isValidFeedType(String type) { + for (FeedType feed : FeedType.values()) { + if (Objects.equals(feed.getStringVal(), type)) return true; + } + return false; + } +} From 24217777c54d30af2a69e89f3d12d5f13dcf1e8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=80=E1=85=B5=E1=86=B7=E1=84=8B=E1=85=B3=E1=86=BC?= =?UTF-8?q?=E1=84=80=E1=85=AA=E1=86=AB?= Date: Wed, 10 Jan 2024 01:43:36 +0900 Subject: [PATCH 31/78] =?UTF-8?q?Feed=20=EC=97=94=ED=8B=B0=ED=8B=B0?= =?UTF-8?q?=EC=97=90=20FeedType=20=EC=BB=AC=EB=9F=BC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/winey/server/domain/feed/Feed.java | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/src/main/java/org/winey/server/domain/feed/Feed.java b/src/main/java/org/winey/server/domain/feed/Feed.java index 09a3cc8..3c11ac7 100644 --- a/src/main/java/org/winey/server/domain/feed/Feed.java +++ b/src/main/java/org/winey/server/domain/feed/Feed.java @@ -1,16 +1,8 @@ package org.winey.server.domain.feed; import java.util.List; -import javax.persistence.CascadeType; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.FetchType; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.JoinColumn; -import javax.persistence.ManyToOne; -import javax.persistence.OneToMany; +import javax.persistence.*; + import lombok.AccessLevel; import lombok.Builder; import lombok.Getter; @@ -40,6 +32,10 @@ public class Feed extends AuditingTimeEntity { @Column(nullable = false) private String feedImage; + @Column + @Enumerated(EnumType.STRING) + private FeedType feedType; + @Column(nullable = false) private Long feedMoney; @@ -54,11 +50,12 @@ public class Feed extends AuditingTimeEntity { private List comments; @Builder - public Feed(User user, String feedTitle, String feedImage, Long feedMoney) { + public Feed(User user, String feedTitle, String feedImage, Long feedMoney, FeedType feedType) { this.user = user; this.feedTitle = feedTitle; this.feedImage = feedImage; this.feedMoney = feedMoney; + this.feedType = feedType; this.goal = null; } } From 3db99077638df049381ba15855a415a127e8a2ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=80=E1=85=B5=E1=86=B7=E1=84=8B=E1=85=B3=E1=86=BC?= =?UTF-8?q?=E1=84=80=E1=85=AA=E1=86=AB?= Date: Wed, 10 Jan 2024 01:46:52 +0900 Subject: [PATCH 32/78] =?UTF-8?q?=ED=94=BC=EB=93=9C=EC=83=9D=EC=84=B1?= =?UTF-8?q?=EC=9A=94=EC=B2=AD=20Dto=EC=97=90=20default=EA=B0=92=EC=9D=B4?= =?UTF-8?q?=20null=EC=9D=B8=20feedType=20=EC=B6=94=EA=B0=80=20=EB=B0=8F=20?= =?UTF-8?q?feedType=20setter=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../server/controller/request/CreateFeedRequestDto.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/main/java/org/winey/server/controller/request/CreateFeedRequestDto.java b/src/main/java/org/winey/server/controller/request/CreateFeedRequestDto.java index f8c3421..83be69c 100644 --- a/src/main/java/org/winey/server/controller/request/CreateFeedRequestDto.java +++ b/src/main/java/org/winey/server/controller/request/CreateFeedRequestDto.java @@ -3,6 +3,7 @@ import lombok.*; import org.springframework.web.multipart.MultipartFile; +import javax.annotation.Nullable; import javax.validation.constraints.DecimalMax; import javax.validation.constraints.NotNull; @@ -16,4 +17,10 @@ public class CreateFeedRequestDto { private MultipartFile feedImage; @NotNull @DecimalMax(value = "9999999") private Long feedMoney; + @Nullable + private String feedType = null; + + public void setFeedType(String type) { + this.feedType = type; + } } From 36abf6c05c9a1796228028d4a3183e5c2dd34223 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=80=E1=85=B5=E1=86=B7=E1=84=8B=E1=85=B3=E1=86=BC?= =?UTF-8?q?=E1=84=80=E1=85=AA=E1=86=AB?= Date: Wed, 10 Jan 2024 01:49:17 +0900 Subject: [PATCH 33/78] =?UTF-8?q?=EC=9E=98=EB=AA=BB=EB=90=9C=20feedType=20?= =?UTF-8?q?=EC=9C=A0=ED=98=95=EC=9D=98=20=EA=B0=92=EC=9D=B4=20requestDto?= =?UTF-8?q?=EB=A1=9C=20=EC=A0=84=EB=8B=AC=EB=90=98=EC=97=88=EC=9D=84?= =?UTF-8?q?=EB=95=8C=20=EC=9D=91=EB=8B=B5=ED=95=98=EB=8A=94=20=EC=BB=A4?= =?UTF-8?q?=EC=8A=A4=ED=85=80=20400=EC=97=90=EB=9F=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/org/winey/server/exception/Error.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/org/winey/server/exception/Error.java b/src/main/java/org/winey/server/exception/Error.java index 171f6ac..8d90356 100644 --- a/src/main/java/org/winey/server/exception/Error.java +++ b/src/main/java/org/winey/server/exception/Error.java @@ -17,6 +17,7 @@ public enum Error { LOGIN_SUCCESS(HttpStatus.OK, "로그인에 성공했습니다."), INVALID_PASSWORD_EXCEPTION(HttpStatus.BAD_REQUEST, "잘못된 비밀번호가 입력됐습니다."), NOT_FOUND_IMAGE_EXCEPTION(HttpStatus.BAD_REQUEST, "잘못된 이미지 파일입니다"), + INVALID_FEEDTYPE(HttpStatus.BAD_REQUEST, "잘못된 피드유형 입니다"), VALIDATION_REQUEST_MISSING_EXCEPTION(HttpStatus.BAD_REQUEST, "요청값이 입력되지 않았습니다."), VALIDATION_REQUEST_HEADER_MISSING_EXCEPTION(HttpStatus.BAD_REQUEST, "요청 헤더값이 입력되지 않았습니다."), VALIDATION_REQUEST_PARAMETER_MISSING_EXCEPTION(HttpStatus.BAD_REQUEST, "요청 파라미터값이 입력되지 않았습니다."), From 9bb431b255eb5e1caa778d837534c28dbaa80990 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=80=E1=85=B5=E1=86=B7=E1=84=8B=E1=85=B3=E1=86=BC?= =?UTF-8?q?=E1=84=80=E1=85=AA=E1=86=AB?= Date: Wed, 10 Jan 2024 01:58:38 +0900 Subject: [PATCH 34/78] =?UTF-8?q?=ED=94=BC=EB=93=9C=20=EA=B0=9C=EC=88=98?= =?UTF-8?q?=20=EC=A6=9D=EA=B0=80,=20=EC=A0=88=EC=95=BD=20=EA=B8=88?= =?UTF-8?q?=EC=95=A1=20=EC=A6=9D=EA=B0=80=20=EA=B8=B0=EB=8A=A5=EC=9D=B4=20?= =?UTF-8?q?=EB=B3=84=EB=8F=84=EB=A1=9C=20=EB=8F=99=EC=9E=91=ED=95=98?= =?UTF-8?q?=EA=B2=8C=EB=81=94=20=ED=95=A8=EC=88=98=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/org/winey/server/domain/user/User.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/winey/server/domain/user/User.java b/src/main/java/org/winey/server/domain/user/User.java index adff709..ed205b5 100644 --- a/src/main/java/org/winey/server/domain/user/User.java +++ b/src/main/java/org/winey/server/domain/user/User.java @@ -125,13 +125,16 @@ public void updateNickname(String nickname) { public void updateFcmIsAllowed(Boolean isAllowed){this.fcmIsAllowed = isAllowed;} - public void increaseSavedAmountAndCount(Long money) { - this.savedAmount += money; + public void increaseCount() { this.savedCount += 1; } + public void increaseSavedAmount(Long money) { + this.savedAmount += money; + } + public void decreaseSavedAmountAndCount(Long money) { - this.savedCount -= money; + this.savedAmount -= money; this.savedCount -= 1; } From 08f228f924bd071935e15ec27b4038ceeae4724f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=80=E1=85=B5=E1=86=B7=E1=84=8B=E1=85=B3=E1=86=BC?= =?UTF-8?q?=E1=84=80=E1=85=AA=E1=86=AB?= Date: Wed, 10 Jan 2024 01:59:42 +0900 Subject: [PATCH 35/78] =?UTF-8?q?feedType=20=EA=B0=92=EC=9D=84=20=EA=B3=A0?= =?UTF-8?q?=EB=A0=A4=ED=95=98=EC=97=AC=20=ED=94=BC=EB=93=9C=EA=B0=80=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1=EB=90=98=EA=B3=A0,=20=EC=A0=88=EC=95=BD=20?= =?UTF-8?q?=EC=95=A1=EC=88=98=EA=B0=80=20=EC=A1=B0=EA=B1=B4=EC=A0=81?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EC=A6=9D=EA=B0=80=ED=95=98=EA=B2=8C?= =?UTF-8?q?=EB=81=94=20=ED=94=BC=EB=93=9C=EC=83=9D=EC=84=B1=20=EB=B9=84?= =?UTF-8?q?=EC=A6=88=EB=8B=88=EC=8A=A4=EB=A1=9C=EC=A7=81=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 --- .../org/winey/server/service/FeedService.java | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/winey/server/service/FeedService.java b/src/main/java/org/winey/server/service/FeedService.java index da9d0a1..825c456 100644 --- a/src/main/java/org/winey/server/service/FeedService.java +++ b/src/main/java/org/winey/server/service/FeedService.java @@ -19,11 +19,13 @@ import org.winey.server.controller.response.feed.GetFeedResponseDto; import org.winey.server.domain.block.BlockUser; import org.winey.server.domain.feed.Feed; +import org.winey.server.domain.feed.FeedType; import org.winey.server.domain.notification.NotiType; import org.winey.server.domain.notification.Notification; import org.winey.server.domain.user.User; import org.winey.server.domain.user.UserLevel; import org.winey.server.exception.Error; +import org.winey.server.exception.model.BadRequestException; import org.winey.server.exception.model.NotFoundException; import org.winey.server.exception.model.UnauthorizedException; import org.winey.server.infrastructure.BlockUserRepository; @@ -50,19 +52,29 @@ public CreateFeedResponseDto createFeed(CreateFeedRequestDto request, Long userI User presentUser = userRepository.findByUserId(userId) .orElseThrow(() -> new NotFoundException(Error.NOT_FOUND_USER_EXCEPTION, Error.NOT_FOUND_USER_EXCEPTION.getMessage())); - // 2. 피드를 생성한다. + // 2. 피드 타입 값을 받아와 올바른 피드유형인가 판별 + String feedType = request.getFeedType(); +// if (!FeedType.isValidFeedType(feedType)) +// throw new BadRequestException(Error.INVALID_FEEDTYPE, Error.INVALID_FEEDTYPE.getMessage()); + + // 3. 피드를 생성한다. Feed feed = Feed.builder() .feedImage(imageUrl) + .feedType(FeedType.valueOf(feedType)) .feedMoney(request.getFeedMoney()) .feedTitle(request.getFeedTitle()) .user(presentUser) .build(); + feedRepository.save(feed); - // 3. 유저의 누적 절약 금액, 누적 절약 횟수를 업데이트한다. - presentUser.increaseSavedAmountAndCount(feed.getFeedMoney()); + // 4. 유저의 누적 피드 개수를 업데이트한다. + presentUser.increaseCount(); + + // 5. 유저의 피드 유형이 절약인 경우에는 누적 절약 금액도 업데이트한다. + if (Objects.equals(feedType, "SAVE")) presentUser.increaseSavedAmount(feed.getFeedMoney()); - // 4. 레벨업을 체크한다. + // 6. 레벨업을 체크한다. UserLevel newUserLevel = UserLevel.calculateUserLevel(presentUser.getSavedAmount(), presentUser.getSavedCount()); if (presentUser.getUserLevel() != newUserLevel) { From c8468d76da0173bc87e3dcefe53ab069d6635128 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=80=E1=85=B5=E1=86=B7=E1=84=8B=E1=85=B3=E1=86=BC?= =?UTF-8?q?=E1=84=80=E1=85=AA=E1=86=AB?= Date: Wed, 10 Jan 2024 02:01:37 +0900 Subject: [PATCH 36/78] =?UTF-8?q?=ED=94=BC=EB=93=9C=EC=A1=B0=ED=9A=8C=20re?= =?UTF-8?q?sponse=20dto=EC=97=90=20feedType=EA=B0=92=EB=8F=84=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=EB=90=98=EA=B2=8C=EB=81=94=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../server/controller/response/feed/GetFeedResponseDto.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/winey/server/controller/response/feed/GetFeedResponseDto.java b/src/main/java/org/winey/server/controller/response/feed/GetFeedResponseDto.java index 4071046..8e4b893 100644 --- a/src/main/java/org/winey/server/controller/response/feed/GetFeedResponseDto.java +++ b/src/main/java/org/winey/server/controller/response/feed/GetFeedResponseDto.java @@ -18,6 +18,7 @@ public class GetFeedResponseDto { private String nickName; private int writerLevel; // 여기까쥐 + private String feedType; private String feedTitle; private String feedImage; private Long feedMoney; @@ -28,8 +29,8 @@ public class GetFeedResponseDto { private LocalDateTime createdAt; - public static GetFeedResponseDto of(Long feedId, Long userId, String nickName, int writerLevel, String feedTitle, String feedImage, + public static GetFeedResponseDto of(Long feedId, Long userId, String nickName, int writerLevel, String feedType, String feedTitle, String feedImage, Long feedMoney, Boolean isLiked, Long likes, Long comments, String timeAgo, LocalDateTime createdAt) { - return new GetFeedResponseDto(feedId, userId, nickName, writerLevel, feedTitle, feedImage, feedMoney, isLiked, likes, comments, timeAgo, createdAt); + return new GetFeedResponseDto(feedId, userId, nickName, writerLevel, feedType, feedTitle, feedImage, feedMoney, isLiked, likes, comments, timeAgo, createdAt); } } From 5c5d37a6ff6ab9cfd72b2632aa746f64a23447c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=80=E1=85=B5=E1=86=B7=E1=84=8B=E1=85=B3=E1=86=BC?= =?UTF-8?q?=E1=84=80=E1=85=AA=E1=86=AB?= Date: Wed, 10 Jan 2024 02:05:31 +0900 Subject: [PATCH 37/78] =?UTF-8?q?=ED=94=BC=EB=93=9C=EC=A1=B0=ED=9A=8C=20re?= =?UTF-8?q?sponse=20dto=EC=97=90=20feedType=EA=B0=92=EB=8F=84=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=EB=90=98=EA=B2=8C=EB=81=94=20service=20=EA=B3=84?= =?UTF-8?q?=EC=B8=B5=20=EB=A1=9C=EC=A7=81=20=EC=88=98=EC=A0=95=20=EB=B0=8F?= =?UTF-8?q?=20=ED=94=BC=EB=93=9C=EC=83=9D=EC=84=B1=EC=8B=9C=20feedType=20n?= =?UTF-8?q?ull=EC=9D=BC=20=EA=B2=BD=EC=9A=B0=20"null"=20=EB=8C=80=EC=8B=A0?= =?UTF-8?q?=20null=20=EB=93=A4=EC=96=B4=EA=B0=80=EA=B2=8C=EB=81=94=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 --- src/main/java/org/winey/server/service/FeedService.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/winey/server/service/FeedService.java b/src/main/java/org/winey/server/service/FeedService.java index 825c456..03ccec6 100644 --- a/src/main/java/org/winey/server/service/FeedService.java +++ b/src/main/java/org/winey/server/service/FeedService.java @@ -60,7 +60,7 @@ public CreateFeedResponseDto createFeed(CreateFeedRequestDto request, Long userI // 3. 피드를 생성한다. Feed feed = Feed.builder() .feedImage(imageUrl) - .feedType(FeedType.valueOf(feedType)) + .feedType(feedType == null || feedType.isEmpty() ? null : FeedType.valueOf(feedType)) .feedMoney(request.getFeedMoney()) .feedTitle(request.getFeedTitle()) .user(presentUser) @@ -173,6 +173,7 @@ public GetAllFeedResponseDto getAllFeed(int page, Long userId) { feed.getUser().getUserId(), feed.getUser().getNickname(), feed.getUser().getUserLevel().getLevelNumber(), + feed.getFeedType() == null ? null : feed.getFeedType().getStringVal(), feed.getFeedTitle(), feed.getFeedImage(), feed.getFeedMoney(), @@ -197,6 +198,7 @@ public GetAllFeedResponseDto getMyFeed(int page, Long userId) { myFeed.getUser().getUserId(), myFeed.getUser().getNickname(), myFeed.getUser().getUserLevel().getLevelNumber(), + myFeed.getFeedType() == null ? null : myFeed.getFeedType().getStringVal(), myFeed.getFeedTitle(), myFeed.getFeedImage(), myFeed.getFeedMoney(), @@ -230,6 +232,7 @@ public GetFeedDetailResponseDto getFeedDetail(Long feedId, Long userId) { detailFeed.getUser().getUserId(), detailFeed.getUser().getNickname(), detailFeed.getUser().getUserLevel().getLevelNumber(), + detailFeed.getFeedType() == null ? null : detailFeed.getFeedType().getStringVal(), detailFeed.getFeedTitle(), detailFeed.getFeedImage(), detailFeed.getFeedMoney(), From dd7ae80664ee6fca671f4e50d37d7c867b32c668 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=80=E1=85=B5=E1=86=B7=E1=84=8B=E1=85=B3=E1=86=BC?= =?UTF-8?q?=E1=84=80=E1=85=AA=E1=86=AB?= Date: Wed, 10 Jan 2024 02:49:00 +0900 Subject: [PATCH 38/78] =?UTF-8?q?=ED=94=BC=EB=93=9C=EC=82=AD=EC=A0=9C?= =?UTF-8?q?=EC=8B=9C=EC=97=90=20=EB=88=84=EC=A0=81=ED=94=BC=EB=93=9C?= =?UTF-8?q?=EA=B0=9C=EC=88=98,=20=EB=88=84=EC=A0=81=EC=A0=88=EC=95=BD?= =?UTF-8?q?=EA=B8=88=EC=95=A1=20=EA=B0=90=EC=86=8C=EA=B0=80=20=EB=B3=84?= =?UTF-8?q?=EB=8F=84=EB=A1=9C=20=EB=B0=9C=EC=83=9D=ED=95=98=EA=B2=8C?= =?UTF-8?q?=EB=81=94=20=ED=95=A8=EC=88=98=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/org/winey/server/domain/user/User.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/winey/server/domain/user/User.java b/src/main/java/org/winey/server/domain/user/User.java index ed205b5..1714cac 100644 --- a/src/main/java/org/winey/server/domain/user/User.java +++ b/src/main/java/org/winey/server/domain/user/User.java @@ -133,11 +133,14 @@ public void increaseSavedAmount(Long money) { this.savedAmount += money; } - public void decreaseSavedAmountAndCount(Long money) { - this.savedAmount -= money; + public void decreaseCount() { this.savedCount -= 1; } + public void decreaseSavedAmount(Long money) { + this.savedAmount -= money; + } + public String getFcmToken() { if (Objects.nonNull(this.fcmToken)) { return this.fcmToken; From 1b375baf982e7034b86fde13169e70422047202b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=80=E1=85=B5=E1=86=B7=E1=84=8B=E1=85=B3=E1=86=BC?= =?UTF-8?q?=E1=84=80=E1=85=AA=E1=86=AB?= Date: Wed, 10 Jan 2024 02:49:23 +0900 Subject: [PATCH 39/78] =?UTF-8?q?=EC=A0=88=EC=95=BD=ED=94=BC=EB=93=9C=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C=EC=8B=9C=EC=97=90=EB=A7=8C=20=EB=88=84?= =?UTF-8?q?=EC=A0=81=EC=A0=88=EC=95=BD=EA=B8=88=EC=95=A1=20=EA=B0=90?= =?UTF-8?q?=EC=86=8C=ED=95=98=EB=8F=84=EB=A1=9D=20=EB=A1=9C=EC=A7=81?= =?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/org/winey/server/service/FeedService.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/winey/server/service/FeedService.java b/src/main/java/org/winey/server/service/FeedService.java index 03ccec6..fd562e8 100644 --- a/src/main/java/org/winey/server/service/FeedService.java +++ b/src/main/java/org/winey/server/service/FeedService.java @@ -115,10 +115,15 @@ public String deleteFeed(Long userId, Long feedId) { throw new UnauthorizedException(Error.DELETE_UNAUTHORIZED, Error.DELETE_UNAUTHORIZED.getMessage()); // 삭제하는 사람 아니면 삭제 못함 처리. } - // 4. 유저의 누적 절약 금액, 누적 절약 횟수를 업데이트한다. - presentUser.decreaseSavedAmountAndCount(wantDeleteFeed.getFeedMoney()); + // 4. 누적 피드 개수 감소 + presentUser.decreaseCount(); - // 5. 레벨다운을 체크한다. + // 5. 절약 피드가 삭제되면 유저의 누적 절약 금액 업데이트 + if (wantDeleteFeed.getFeedType() == FeedType.SAVE) { + presentUser.decreaseSavedAmount(wantDeleteFeed.getFeedMoney()); + } + + // 6. 레벨다운을 체크한다. UserLevel newUserLevel = UserLevel.calculateUserLevel(presentUser.getSavedAmount(), presentUser.getSavedCount()); if (presentUser.getUserLevel() != newUserLevel) { From becd795a009d68db47bc3fab88b7123152c10bf9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=80=E1=85=B5=E1=86=B7=E1=84=8B=E1=85=B3=E1=86=BC?= =?UTF-8?q?=E1=84=80=E1=85=AA=E1=86=AB?= Date: Wed, 10 Jan 2024 19:04:39 +0900 Subject: [PATCH 40/78] =?UTF-8?q?=EB=88=84=EC=A0=81=20=ED=94=BC=EB=93=9C?= =?UTF-8?q?=EA=B0=9C=EC=88=98,=20=EC=A0=88=EC=95=BD=EA=B8=88=EC=95=A1=20?= =?UTF-8?q?=ED=95=A8=EA=BB=98=20=EC=A6=9D=EA=B0=90=ED=95=98=EB=8F=84?= =?UTF-8?q?=EB=A1=9D=20=ED=95=A8=EC=88=98=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/org/winey/server/domain/user/User.java | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/main/java/org/winey/server/domain/user/User.java b/src/main/java/org/winey/server/domain/user/User.java index 1714cac..29454ad 100644 --- a/src/main/java/org/winey/server/domain/user/User.java +++ b/src/main/java/org/winey/server/domain/user/User.java @@ -125,19 +125,13 @@ public void updateNickname(String nickname) { public void updateFcmIsAllowed(Boolean isAllowed){this.fcmIsAllowed = isAllowed;} - public void increaseCount() { - this.savedCount += 1; - } - - public void increaseSavedAmount(Long money) { + public void increaseSavedAmountAndCount(Long money) { this.savedAmount += money; + this.savedCount += 1; } - public void decreaseCount() { + public void decreaseSavedAmountAndCount(Long money) { this.savedCount -= 1; - } - - public void decreaseSavedAmount(Long money) { this.savedAmount -= money; } From 728baa7b30b1e76b888d138b07fbbbd138ef83d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=80=E1=85=B5=E1=86=B7=E1=84=8B=E1=85=B3=E1=86=BC?= =?UTF-8?q?=E1=84=80=E1=85=AA=E1=86=AB?= Date: Wed, 10 Jan 2024 19:05:05 +0900 Subject: [PATCH 41/78] =?UTF-8?q?=EC=A0=88=EC=95=BD=ED=94=BC=EB=93=9C=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1,=EC=82=AD=EC=A0=9C=EC=9D=98=20=EA=B2=BD?= =?UTF-8?q?=EC=9A=B0=EC=97=90=EB=A7=8C=20=EC=9D=BC=EA=B4=84=EC=A0=81?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EC=A0=88=EC=95=BD=ED=94=BC=EB=93=9C?= =?UTF-8?q?=EA=B0=9C=EC=88=98,=20=EC=A0=88=EC=95=BD=EB=88=84=EC=A0=81?= =?UTF-8?q?=EA=B8=88=EC=95=A1=20=EC=A6=9D=EA=B0=90=ED=95=98=EB=8F=84?= =?UTF-8?q?=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/winey/server/service/FeedService.java | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/src/main/java/org/winey/server/service/FeedService.java b/src/main/java/org/winey/server/service/FeedService.java index fd562e8..fcebadb 100644 --- a/src/main/java/org/winey/server/service/FeedService.java +++ b/src/main/java/org/winey/server/service/FeedService.java @@ -68,13 +68,10 @@ public CreateFeedResponseDto createFeed(CreateFeedRequestDto request, Long userI feedRepository.save(feed); - // 4. 유저의 누적 피드 개수를 업데이트한다. - presentUser.increaseCount(); - - // 5. 유저의 피드 유형이 절약인 경우에는 누적 절약 금액도 업데이트한다. - if (Objects.equals(feedType, "SAVE")) presentUser.increaseSavedAmount(feed.getFeedMoney()); + // 4. 유저의 피드 유형이 절약인 경우 누적 절약 금액과 누적 절약 피드 개수 업데이트한다. + if (Objects.equals(feedType, "SAVE")) presentUser.increaseSavedAmountAndCount(feed.getFeedMoney()); - // 6. 레벨업을 체크한다. + // 5. 레벨업을 체크한다. UserLevel newUserLevel = UserLevel.calculateUserLevel(presentUser.getSavedAmount(), presentUser.getSavedCount()); if (presentUser.getUserLevel() != newUserLevel) { @@ -115,15 +112,12 @@ public String deleteFeed(Long userId, Long feedId) { throw new UnauthorizedException(Error.DELETE_UNAUTHORIZED, Error.DELETE_UNAUTHORIZED.getMessage()); // 삭제하는 사람 아니면 삭제 못함 처리. } - // 4. 누적 피드 개수 감소 - presentUser.decreaseCount(); - - // 5. 절약 피드가 삭제되면 유저의 누적 절약 금액 업데이트 + // 4. 절약 피드가 삭제되면 유저의 누적 절약 금액과 누적 절약 피드 개수 감소 if (wantDeleteFeed.getFeedType() == FeedType.SAVE) { - presentUser.decreaseSavedAmount(wantDeleteFeed.getFeedMoney()); + presentUser.decreaseSavedAmountAndCount(wantDeleteFeed.getFeedMoney()); } - // 6. 레벨다운을 체크한다. + // 5. 레벨다운을 체크한다. UserLevel newUserLevel = UserLevel.calculateUserLevel(presentUser.getSavedAmount(), presentUser.getSavedCount()); if (presentUser.getUserLevel() != newUserLevel) { From b7a7467c7131e1a5dd1f8ab52aa22f06d4748183 Mon Sep 17 00:00:00 2001 From: Seonheui Jeon <88873302+funnysunny08@users.noreply.github.com> Date: Fri, 12 Jan 2024 16:43:02 +0900 Subject: [PATCH 42/78] =?UTF-8?q?[Feat]=20=EB=A7=88=EC=9D=B4=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20=EA=B0=9C=ED=8E=B8=20(#217)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../response/user/UserResponseDto.java | 12 ++++++--- .../server/infrastructure/FeedRepository.java | 12 ++++++--- .../org/winey/server/service/UserService.java | 11 +++++++- src/main/resources/logback-spring.xml | 27 ------------------- 4 files changed, 27 insertions(+), 35 deletions(-) delete mode 100644 src/main/resources/logback-spring.xml diff --git a/src/main/java/org/winey/server/controller/response/user/UserResponseDto.java b/src/main/java/org/winey/server/controller/response/user/UserResponseDto.java index 932644e..4e875f6 100644 --- a/src/main/java/org/winey/server/controller/response/user/UserResponseDto.java +++ b/src/main/java/org/winey/server/controller/response/user/UserResponseDto.java @@ -10,14 +10,20 @@ @NoArgsConstructor(access = AccessLevel.PRIVATE) @AllArgsConstructor(access = AccessLevel.PRIVATE) public class UserResponseDto { + private Long userId; private String nickname; private String userLevel; private Boolean fcmIsAllowed; private Long savedAmount; - private Long savedCount; + private Long spentAmount; + private Long accumulatedAmount; + private Long accumulatedCount; - public static UserResponseDto of(Long userId, String nickname, String userLevel, Boolean fcmIsAllowed, Long savedAmount, Long savedCount) { - return new UserResponseDto(userId, nickname, userLevel, fcmIsAllowed, savedAmount, savedCount); + public static UserResponseDto of(Long userId, String nickname, String userLevel, + Boolean fcmIsAllowed, Long savedAmount, Long spentAmount, Long accumulatedAmount, + Long accumulatedCount) { + return new UserResponseDto(userId, nickname, userLevel, fcmIsAllowed, savedAmount, + spentAmount, accumulatedAmount, accumulatedCount); } } diff --git a/src/main/java/org/winey/server/infrastructure/FeedRepository.java b/src/main/java/org/winey/server/infrastructure/FeedRepository.java index 6a20c82..99fe1eb 100644 --- a/src/main/java/org/winey/server/infrastructure/FeedRepository.java +++ b/src/main/java/org/winey/server/infrastructure/FeedRepository.java @@ -1,17 +1,16 @@ package org.winey.server.infrastructure; +import java.time.LocalDateTime; import java.util.Collection; +import java.util.Optional; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; -import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.Repository; +import org.springframework.data.repository.query.Param; import org.winey.server.domain.feed.Feed; import org.winey.server.domain.user.User; -import java.util.List; -import java.util.Optional; - public interface FeedRepository extends Repository { void save(Feed feed); Optional findByFeedId(Long feedId); @@ -20,4 +19,9 @@ public interface FeedRepository extends Repository { Page findAllByUserOrderByCreatedAtDesc(User user, Pageable pageable); Page findByUserNotInOrderByCreatedAtDesc(Collection users, Pageable pageable); + @Query("select sum(f.feedMoney) from Feed f where f.user = :user and f.feedType = 'SAVE' and f.createdAt > :date") + Long getSavedAmountForTwoWeeks(@Param("user") User user, @Param("date") LocalDateTime date); + + @Query("select sum(f.feedMoney) from Feed f where f.user = :user and f.feedType = 'CONSUME' and f.createdAt > :date") + Long getSpentAmountForTwoWeeks(@Param("user") User user, @Param("date") LocalDateTime date); } diff --git a/src/main/java/org/winey/server/service/UserService.java b/src/main/java/org/winey/server/service/UserService.java index 013f3e7..e5b506d 100644 --- a/src/main/java/org/winey/server/service/UserService.java +++ b/src/main/java/org/winey/server/service/UserService.java @@ -1,5 +1,6 @@ package org.winey.server.service; +import java.time.LocalDateTime; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -10,6 +11,7 @@ import org.winey.server.exception.Error; import org.winey.server.exception.model.BadRequestException; import org.winey.server.exception.model.NotFoundException; +import org.winey.server.infrastructure.FeedRepository; import org.winey.server.infrastructure.UserRepository; @Service @@ -17,6 +19,7 @@ public class UserService { private final UserRepository userRepository; + private final FeedRepository feedRepository; @Transactional public UserResponseDto getUser(Long userId) { @@ -24,8 +27,14 @@ public UserResponseDto getUser(Long userId) { .orElseThrow(() -> new NotFoundException(Error.NOT_FOUND_USER_EXCEPTION, Error.NOT_FOUND_USER_EXCEPTION.getMessage())); + LocalDateTime twoWeeksAgo = LocalDateTime.now().minusWeeks(2); + Long savedAmount = feedRepository.getSavedAmountForTwoWeeks(user, twoWeeksAgo); + Long spentAmount = feedRepository.getSpentAmountForTwoWeeks(user, twoWeeksAgo); + return UserResponseDto.of(user.getUserId(), user.getNickname(), - user.getUserLevel().getName(), user.getFcmIsAllowed(), user.getSavedAmount(), + user.getUserLevel().getName(), user.getFcmIsAllowed(), + savedAmount == null ? 0L : savedAmount, + spentAmount == null ? 0L : spentAmount, user.getSavedAmount(), user.getSavedCount()); } diff --git a/src/main/resources/logback-spring.xml b/src/main/resources/logback-spring.xml deleted file mode 100644 index 86bca5a..0000000 --- a/src/main/resources/logback-spring.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - - ${LOGS_DIR}/NotiService_INFO_${byDate}.log - true - true - - %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] INFO %logger{36} - %msg%n - - - - - - %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] INFO %logger{36} - %msg%n - - - - - - - - From f3d912a87c2532974a762a54d468fe341cde0165 Mon Sep 17 00:00:00 2001 From: Seonheui Jeon <88873302+funnysunny08@users.noreply.github.com> Date: Wed, 24 Jan 2024 08:34:26 +0900 Subject: [PATCH 43/78] =?UTF-8?q?=20[Feat]=20=EB=A0=88=EB=B2=A8=20?= =?UTF-8?q?=EB=8B=AC=EC=84=B1=20=ED=98=84=ED=99=A9=20API=20(#219)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [Feat] 유저 레벨 달성 현황 API * [Feat] 무의미한 값은 0으로 응답 --- .../server/controller/UserController.java | 8 +++++++ .../user/GetAchievementStatusResponseDto.java | 21 ++++++++++++++++++ .../winey/server/domain/user/UserLevel.java | 9 ++++++++ .../org/winey/server/exception/Success.java | 1 + .../org/winey/server/service/UserService.java | 22 +++++++++++++++++++ 5 files changed, 61 insertions(+) create mode 100644 src/main/java/org/winey/server/controller/response/user/GetAchievementStatusResponseDto.java diff --git a/src/main/java/org/winey/server/controller/UserController.java b/src/main/java/org/winey/server/controller/UserController.java index aa3fc9b..74ae27e 100644 --- a/src/main/java/org/winey/server/controller/UserController.java +++ b/src/main/java/org/winey/server/controller/UserController.java @@ -10,6 +10,7 @@ import org.winey.server.controller.request.UpdateAllowedPushDto; import org.winey.server.controller.request.UpdateFcmTokenDto; import org.winey.server.controller.request.UpdateUserNicknameDto; +import org.winey.server.controller.response.user.GetAchievementStatusResponseDto; import org.winey.server.controller.response.user.UserResponseDto; import org.winey.server.exception.Error; import org.winey.server.exception.Success; @@ -77,4 +78,11 @@ public ApiResponse updateAllowedNotification (@UserId Long userId, @RequestBody }; return ApiResponse.success(Success.UPDATE_PUSH_ALLOWED_SUCCESS, result); } + + @GetMapping("/achievement-status") + @ResponseStatus(HttpStatus.OK) + @Operation(summary = "레벨 달성 현황 조회 API", description = "유저의 레벨 달성 현황을 조회합니다.") + public ApiResponse getAchievementStatus(@UserId Long userId) { + return ApiResponse.success(Success.GET_ACHIEVEMENT_STATUS_SUCCESS, userService.getAchievementStatus(userId)); + } } diff --git a/src/main/java/org/winey/server/controller/response/user/GetAchievementStatusResponseDto.java b/src/main/java/org/winey/server/controller/response/user/GetAchievementStatusResponseDto.java new file mode 100644 index 0000000..cfcbb38 --- /dev/null +++ b/src/main/java/org/winey/server/controller/response/user/GetAchievementStatusResponseDto.java @@ -0,0 +1,21 @@ +package org.winey.server.controller.response.user; + +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import org.winey.server.domain.user.UserLevel; + +@Getter +@NoArgsConstructor(access = AccessLevel.PRIVATE) +@AllArgsConstructor(access = AccessLevel.PRIVATE) +public class GetAchievementStatusResponseDto { + + private String userLevel; + private Long remainingAmount; + private Long remainingCount; + + public static GetAchievementStatusResponseDto of(UserLevel userLevel, Long remainingAmount, Long remainingCount) { + return new GetAchievementStatusResponseDto(userLevel.getName(), remainingAmount, remainingCount); + } +} diff --git a/src/main/java/org/winey/server/domain/user/UserLevel.java b/src/main/java/org/winey/server/domain/user/UserLevel.java index 627fdd9..15558b3 100644 --- a/src/main/java/org/winey/server/domain/user/UserLevel.java +++ b/src/main/java/org/winey/server/domain/user/UserLevel.java @@ -26,4 +26,13 @@ public static UserLevel calculateUserLevel(Long amount, Long count) { } return COMMONER; } + + public static UserLevel getNextUserLevel(UserLevel currentLevel) { + for (UserLevel level : UserLevel.values()) { + if (level.getLevelNumber() == currentLevel.getLevelNumber() + 1) { + return level; + } + } + return null; + } } diff --git a/src/main/java/org/winey/server/exception/Success.java b/src/main/java/org/winey/server/exception/Success.java index 15fdc40..0b27d67 100644 --- a/src/main/java/org/winey/server/exception/Success.java +++ b/src/main/java/org/winey/server/exception/Success.java @@ -27,6 +27,7 @@ public enum Success { CHECK_NICKNAME_DUPLICATE_SUCCESS(HttpStatus.OK, "닉네임 중복 확인 성공"), CHECK_NEW_NOTIFICATION_SUCCESS(HttpStatus.OK, "새 알림 여부 조회 성공"), BLOCK_USER_SUCCESS(HttpStatus.OK, "유저 차단 성공"), + GET_ACHIEVEMENT_STATUS_SUCCESS(HttpStatus.OK, "레벨 달성 현황 조회 성공"), /** * 201 CREATED diff --git a/src/main/java/org/winey/server/service/UserService.java b/src/main/java/org/winey/server/service/UserService.java index e5b506d..6d0b43a 100644 --- a/src/main/java/org/winey/server/service/UserService.java +++ b/src/main/java/org/winey/server/service/UserService.java @@ -6,8 +6,10 @@ import org.springframework.transaction.annotation.Transactional; import org.winey.server.controller.request.UpdateFcmTokenDto; import org.winey.server.controller.request.UpdateUserNicknameDto; +import org.winey.server.controller.response.user.GetAchievementStatusResponseDto; import org.winey.server.controller.response.user.UserResponseDto; import org.winey.server.domain.user.User; +import org.winey.server.domain.user.UserLevel; import org.winey.server.exception.Error; import org.winey.server.exception.model.BadRequestException; import org.winey.server.exception.model.NotFoundException; @@ -68,6 +70,26 @@ public Boolean allowedPushNotification(Long userId, Boolean fcmIsAllowed) { return fcmIsAllowed; } + @Transactional(readOnly = true) + public GetAchievementStatusResponseDto getAchievementStatus(Long userId) { + User user = userRepository.findByUserId(userId) + .orElseThrow(() -> new NotFoundException(Error.NOT_FOUND_USER_EXCEPTION, Error.NOT_FOUND_USER_EXCEPTION.getMessage())); + + UserLevel nextUserLevel = UserLevel.getNextUserLevel(user.getUserLevel()); + + if (nextUserLevel == null) { + return GetAchievementStatusResponseDto.of(user.getUserLevel(), 0L, 0L); + } + + long remainingAmount = nextUserLevel.getMinimumAmount() - user.getSavedAmount(); + long remainingCount = nextUserLevel.getMinimumCount() - user.getSavedCount(); + return GetAchievementStatusResponseDto.of( + user.getUserLevel(), + remainingAmount < 0 ? 0L : remainingAmount, + remainingCount < 0 ? 0L : remainingCount + ); + } + public Boolean checkNicknameDuplicate(String nickname) { return userRepository.existsByNickname(nickname); } From 72d87aadbebdc94da47698112da04c3329d8bb44 Mon Sep 17 00:00:00 2001 From: Seonheui Jeon <88873302+funnysunny08@users.noreply.github.com> Date: Sun, 28 Jan 2024 10:11:52 +0900 Subject: [PATCH 44/78] =?UTF-8?q?[Feat]=20=EB=A7=88=EC=9D=B4=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20=EC=9D=91=EB=8B=B5=EA=B0=92=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20(#221)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../response/user/UserResponseDto.java | 14 ++++++------- .../server/infrastructure/FeedRepository.java | 4 ++-- .../org/winey/server/service/UserService.java | 20 +++++++++++-------- 3 files changed, 21 insertions(+), 17 deletions(-) diff --git a/src/main/java/org/winey/server/controller/response/user/UserResponseDto.java b/src/main/java/org/winey/server/controller/response/user/UserResponseDto.java index 4e875f6..96cdac5 100644 --- a/src/main/java/org/winey/server/controller/response/user/UserResponseDto.java +++ b/src/main/java/org/winey/server/controller/response/user/UserResponseDto.java @@ -15,15 +15,15 @@ public class UserResponseDto { private String nickname; private String userLevel; private Boolean fcmIsAllowed; - private Long savedAmount; - private Long spentAmount; private Long accumulatedAmount; - private Long accumulatedCount; + private Long amountSavedHundredDays; + private Long amountSavedTwoWeeks; + private Long amountSpentTwoWeeks; public static UserResponseDto of(Long userId, String nickname, String userLevel, - Boolean fcmIsAllowed, Long savedAmount, Long spentAmount, Long accumulatedAmount, - Long accumulatedCount) { - return new UserResponseDto(userId, nickname, userLevel, fcmIsAllowed, savedAmount, - spentAmount, accumulatedAmount, accumulatedCount); + Boolean fcmIsAllowed, Long accumulatedAmount, Long amountSavedHundredDays, Long amountSavedTwoWeeks, + Long amountSpentTwoWeeks) { + return new UserResponseDto(userId, nickname, userLevel, fcmIsAllowed, accumulatedAmount, + amountSavedHundredDays, amountSavedTwoWeeks, amountSpentTwoWeeks); } } diff --git a/src/main/java/org/winey/server/infrastructure/FeedRepository.java b/src/main/java/org/winey/server/infrastructure/FeedRepository.java index 99fe1eb..442256c 100644 --- a/src/main/java/org/winey/server/infrastructure/FeedRepository.java +++ b/src/main/java/org/winey/server/infrastructure/FeedRepository.java @@ -20,8 +20,8 @@ public interface FeedRepository extends Repository { Page findByUserNotInOrderByCreatedAtDesc(Collection users, Pageable pageable); @Query("select sum(f.feedMoney) from Feed f where f.user = :user and f.feedType = 'SAVE' and f.createdAt > :date") - Long getSavedAmountForTwoWeeks(@Param("user") User user, @Param("date") LocalDateTime date); + Long getSavedAmountForPeriod(@Param("user") User user, @Param("date") LocalDateTime date); @Query("select sum(f.feedMoney) from Feed f where f.user = :user and f.feedType = 'CONSUME' and f.createdAt > :date") - Long getSpentAmountForTwoWeeks(@Param("user") User user, @Param("date") LocalDateTime date); + Long getSpentAmountForPeriod(@Param("user") User user, @Param("date") LocalDateTime date); } diff --git a/src/main/java/org/winey/server/service/UserService.java b/src/main/java/org/winey/server/service/UserService.java index 6d0b43a..bb2ac5e 100644 --- a/src/main/java/org/winey/server/service/UserService.java +++ b/src/main/java/org/winey/server/service/UserService.java @@ -30,14 +30,17 @@ public UserResponseDto getUser(Long userId) { Error.NOT_FOUND_USER_EXCEPTION.getMessage())); LocalDateTime twoWeeksAgo = LocalDateTime.now().minusWeeks(2); - Long savedAmount = feedRepository.getSavedAmountForTwoWeeks(user, twoWeeksAgo); - Long spentAmount = feedRepository.getSpentAmountForTwoWeeks(user, twoWeeksAgo); + LocalDateTime hundredDaysAgo = LocalDateTime.now().minusDays(100); + Long amountSavedHundredDays = feedRepository.getSavedAmountForPeriod(user, hundredDaysAgo); + Long amountSavedTwoWeeks = feedRepository.getSavedAmountForPeriod(user, twoWeeksAgo); + Long amountSpentTwoWeeks = feedRepository.getSpentAmountForPeriod(user, twoWeeksAgo); return UserResponseDto.of(user.getUserId(), user.getNickname(), - user.getUserLevel().getName(), user.getFcmIsAllowed(), - savedAmount == null ? 0L : savedAmount, - spentAmount == null ? 0L : spentAmount, user.getSavedAmount(), - user.getSavedCount()); + user.getUserLevel().getName(), user.getFcmIsAllowed(), user.getSavedAmount(), + amountSavedHundredDays == null ? 0L : amountSavedHundredDays, + amountSavedTwoWeeks == null ? 0L : amountSavedTwoWeeks, + amountSpentTwoWeeks == null ? 0L : amountSpentTwoWeeks + ); } @Transactional @@ -73,14 +76,15 @@ public Boolean allowedPushNotification(Long userId, Boolean fcmIsAllowed) { @Transactional(readOnly = true) public GetAchievementStatusResponseDto getAchievementStatus(Long userId) { User user = userRepository.findByUserId(userId) - .orElseThrow(() -> new NotFoundException(Error.NOT_FOUND_USER_EXCEPTION, Error.NOT_FOUND_USER_EXCEPTION.getMessage())); + .orElseThrow(() -> new NotFoundException(Error.NOT_FOUND_USER_EXCEPTION, + Error.NOT_FOUND_USER_EXCEPTION.getMessage())); UserLevel nextUserLevel = UserLevel.getNextUserLevel(user.getUserLevel()); if (nextUserLevel == null) { return GetAchievementStatusResponseDto.of(user.getUserLevel(), 0L, 0L); } - + long remainingAmount = nextUserLevel.getMinimumAmount() - user.getSavedAmount(); long remainingCount = nextUserLevel.getMinimumCount() - user.getSavedCount(); return GetAchievementStatusResponseDto.of( From 9bf6fa8e219096807b17f952b7078cd799c781dd Mon Sep 17 00:00:00 2001 From: Seonheui Jeon <88873302+funnysunny08@users.noreply.github.com> Date: Mon, 29 Jan 2024 19:38:01 +0900 Subject: [PATCH 45/78] =?UTF-8?q?[Fix]=20=EB=A0=88=EB=B2=A8=EC=97=85=20?= =?UTF-8?q?=EC=A1=B0=EA=B1=B4=20=EB=B3=80=EA=B2=BD=20(#223)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/org/winey/server/domain/user/UserLevel.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/winey/server/domain/user/UserLevel.java b/src/main/java/org/winey/server/domain/user/UserLevel.java index 15558b3..f7194e7 100644 --- a/src/main/java/org/winey/server/domain/user/UserLevel.java +++ b/src/main/java/org/winey/server/domain/user/UserLevel.java @@ -9,7 +9,7 @@ public enum UserLevel { COMMONER("평민", 1, 0L, 0L), KNIGHT("기사", 2, 30000L, 2L), ARISTOCRAT("귀족", 3, 150000L, 4L), - EMPEROR("황제", 4, 300000L, 6L); + EMPEROR("황제", 4, 300000L, 10L); private final String name; private final int levelNumber; From e69f26a6f9c246cace6f8f7dda5647d85af2eeb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=80=E1=85=B5=E1=86=B7=E1=84=8B=E1=85=B3=E1=86=BC?= =?UTF-8?q?=E1=84=80=E1=85=AA=E1=86=AB?= Date: Sun, 4 Feb 2024 22:41:15 +0900 Subject: [PATCH 46/78] =?UTF-8?q?[Fix]=20=EB=A7=88=EC=9D=B4=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20API=20=EA=B5=AC=EC=A1=B0=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../response/user/UserResponseDto.java | 28 ++++++++++++------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/src/main/java/org/winey/server/controller/response/user/UserResponseDto.java b/src/main/java/org/winey/server/controller/response/user/UserResponseDto.java index 96cdac5..49ef015 100644 --- a/src/main/java/org/winey/server/controller/response/user/UserResponseDto.java +++ b/src/main/java/org/winey/server/controller/response/user/UserResponseDto.java @@ -11,19 +11,27 @@ @AllArgsConstructor(access = AccessLevel.PRIVATE) public class UserResponseDto { - private Long userId; - private String nickname; - private String userLevel; - private Boolean fcmIsAllowed; - private Long accumulatedAmount; - private Long amountSavedHundredDays; - private Long amountSavedTwoWeeks; - private Long amountSpentTwoWeeks; + private UserData userData; + + @Getter + @NoArgsConstructor(access = AccessLevel.PRIVATE) + @AllArgsConstructor(access = AccessLevel.PRIVATE) + public static class UserData { + private Long userId; + private String nickname; + private String userLevel; + private Boolean fcmIsAllowed; + private Long accumulatedAmount; + private Long amountSavedHundredDays; + private Long amountSavedTwoWeeks; + private Long amountSpentTwoWeeks; + } public static UserResponseDto of(Long userId, String nickname, String userLevel, Boolean fcmIsAllowed, Long accumulatedAmount, Long amountSavedHundredDays, Long amountSavedTwoWeeks, Long amountSpentTwoWeeks) { - return new UserResponseDto(userId, nickname, userLevel, fcmIsAllowed, accumulatedAmount, - amountSavedHundredDays, amountSavedTwoWeeks, amountSpentTwoWeeks); + UserData userData = new UserData(userId, nickname, userLevel, fcmIsAllowed, accumulatedAmount, + amountSavedHundredDays, amountSavedTwoWeeks, amountSpentTwoWeeks); + return new UserResponseDto(userData); } } From 8fd3a048cb722a0d5f4315d02056ce8c00413484 Mon Sep 17 00:00:00 2001 From: Eunggwan Kim <40496065+alpha-kwhn@users.noreply.github.com> Date: Mon, 5 Feb 2024 18:00:06 +0900 Subject: [PATCH 47/78] Update dev-cd.yml --- .github/workflows/dev-cd.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/dev-cd.yml b/.github/workflows/dev-cd.yml index 1b454b0..90f6ba6 100644 --- a/.github/workflows/dev-cd.yml +++ b/.github/workflows/dev-cd.yml @@ -32,14 +32,14 @@ jobs: # 3) 환경변수 파일 생성 - name: make application.properties 파일 생성 run: | - ## create application.yml + ## create application.yaml cd ./src/main/resources - # application.yml 파일 생성 - touch ./application.yml + # application.yaml 파일 생성 + touch ./application.yaml # GitHub-Actions 에서 설정한 값을 application.yml 파일에 쓰기 - echo "${{ secrets.WINEY_DEV_APPLICATION }}" >> ./application.yml + echo "${{ secrets.WINEY_DEV_APPLICATION }}" >> ./application.yaml # winey-firebase.json 파일 생성 touch ./winey-firebase.json @@ -47,8 +47,8 @@ jobs: # Firebase secrets 파일 복사 echo "${{ secrets.WINEY_FIREBASE }}" >> ./winey-firebase.json - # application.yml 파일 확인 - cat ./application.yml + # application.yaml 파일 확인 + cat ./application.yaml shell: bash # 이 워크플로우는 gradle build From d5e1779ac6ccd185f6538609e9e3b786688f0b22 Mon Sep 17 00:00:00 2001 From: Eunggwan Kim <40496065+alpha-kwhn@users.noreply.github.com> Date: Mon, 5 Feb 2024 18:27:11 +0900 Subject: [PATCH 48/78] Update dev-cd.yml --- .github/workflows/dev-cd.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/dev-cd.yml b/.github/workflows/dev-cd.yml index 90f6ba6..3fb841e 100644 --- a/.github/workflows/dev-cd.yml +++ b/.github/workflows/dev-cd.yml @@ -33,10 +33,10 @@ jobs: - name: make application.properties 파일 생성 run: | ## create application.yaml - cd ./src/main/resources + cd /src/main/resources # application.yaml 파일 생성 - touch ./application.yaml + touch src/main/resources/application.yaml # GitHub-Actions 에서 설정한 값을 application.yml 파일에 쓰기 echo "${{ secrets.WINEY_DEV_APPLICATION }}" >> ./application.yaml From a7e98f79ca7ee5f8908e9cae49e3765388153404 Mon Sep 17 00:00:00 2001 From: soohyun <49307946+sss4920@users.noreply.github.com> Date: Mon, 5 Feb 2024 18:29:52 +0900 Subject: [PATCH 49/78] Update dev-cd.yml --- .github/workflows/dev-cd.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/dev-cd.yml b/.github/workflows/dev-cd.yml index 3fb841e..707ea6d 100644 --- a/.github/workflows/dev-cd.yml +++ b/.github/workflows/dev-cd.yml @@ -34,6 +34,7 @@ jobs: run: | ## create application.yaml cd /src/main/resources + pwd # application.yaml 파일 생성 touch src/main/resources/application.yaml From fca775b7cc1333fa11af3224c17b9cc6f63a630b Mon Sep 17 00:00:00 2001 From: Eunggwan Kim <40496065+alpha-kwhn@users.noreply.github.com> Date: Mon, 5 Feb 2024 18:32:37 +0900 Subject: [PATCH 50/78] Update dev-cd.yml --- .github/workflows/dev-cd.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/dev-cd.yml b/.github/workflows/dev-cd.yml index 707ea6d..cf68146 100644 --- a/.github/workflows/dev-cd.yml +++ b/.github/workflows/dev-cd.yml @@ -33,23 +33,23 @@ jobs: - name: make application.properties 파일 생성 run: | ## create application.yaml - cd /src/main/resources + ## cd /src/main/resources pwd # application.yaml 파일 생성 touch src/main/resources/application.yaml # GitHub-Actions 에서 설정한 값을 application.yml 파일에 쓰기 - echo "${{ secrets.WINEY_DEV_APPLICATION }}" >> ./application.yaml + echo "${{ secrets.WINEY_DEV_APPLICATION }}" >> src/main/resources/application.yaml # winey-firebase.json 파일 생성 - touch ./winey-firebase.json + touch src/main/resources/winey-firebase.json # Firebase secrets 파일 복사 - echo "${{ secrets.WINEY_FIREBASE }}" >> ./winey-firebase.json + echo "${{ secrets.WINEY_FIREBASE }}" >> src/main/resources/winey-firebase.json # application.yaml 파일 확인 - cat ./application.yaml + cat src/main/resources/application.yaml shell: bash # 이 워크플로우는 gradle build From f11d9b320f698500d894bc7159f6245440b371e4 Mon Sep 17 00:00:00 2001 From: Eunggwan Kim <40496065+alpha-kwhn@users.noreply.github.com> Date: Mon, 5 Feb 2024 18:38:32 +0900 Subject: [PATCH 51/78] Update dev-cd.yml --- .github/workflows/dev-cd.yml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/dev-cd.yml b/.github/workflows/dev-cd.yml index cf68146..0ec8ffe 100644 --- a/.github/workflows/dev-cd.yml +++ b/.github/workflows/dev-cd.yml @@ -32,24 +32,24 @@ jobs: # 3) 환경변수 파일 생성 - name: make application.properties 파일 생성 run: | - ## create application.yaml - ## cd /src/main/resources - pwd + ## create application.yml + mkdir -p ./src/main/resources + ## cd ./src/main/resources - # application.yaml 파일 생성 - touch src/main/resources/application.yaml + # application.yml 파일 생성 + touch ./application.yml # GitHub-Actions 에서 설정한 값을 application.yml 파일에 쓰기 - echo "${{ secrets.WINEY_DEV_APPLICATION }}" >> src/main/resources/application.yaml + echo "${{ secrets.WINEY_DEV_APPLICATION }}" >> ./application.yaml # winey-firebase.json 파일 생성 - touch src/main/resources/winey-firebase.json + touch ./winey-firebase.json # Firebase secrets 파일 복사 - echo "${{ secrets.WINEY_FIREBASE }}" >> src/main/resources/winey-firebase.json + echo "${{ secrets.WINEY_FIREBASE }}" >> ./winey-firebase.json - # application.yaml 파일 확인 - cat src/main/resources/application.yaml + # application.yml 파일 확인 + cat ./application.yml shell: bash # 이 워크플로우는 gradle build From c5e46eadb4f24c0fec723972069901e447cd6ec5 Mon Sep 17 00:00:00 2001 From: Eunggwan Kim <40496065+alpha-kwhn@users.noreply.github.com> Date: Mon, 5 Feb 2024 18:45:59 +0900 Subject: [PATCH 52/78] Update dev-cd.yml --- .github/workflows/dev-cd.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/dev-cd.yml b/.github/workflows/dev-cd.yml index 0ec8ffe..969fb24 100644 --- a/.github/workflows/dev-cd.yml +++ b/.github/workflows/dev-cd.yml @@ -34,7 +34,6 @@ jobs: run: | ## create application.yml mkdir -p ./src/main/resources - ## cd ./src/main/resources # application.yml 파일 생성 touch ./application.yml From 8940c92eabca56fa767ad720675507d84240a976 Mon Sep 17 00:00:00 2001 From: soohyun <49307946+sss4920@users.noreply.github.com> Date: Mon, 5 Feb 2024 18:49:54 +0900 Subject: [PATCH 53/78] Update dev-ci.yml --- .github/workflows/dev-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/dev-ci.yml b/.github/workflows/dev-ci.yml index 0b93fa8..483697d 100644 --- a/.github/workflows/dev-ci.yml +++ b/.github/workflows/dev-ci.yml @@ -30,7 +30,7 @@ jobs: - name: make application.properties 파일 생성 run: | ## create application.yml - cd ./src/main/resources + mkdir -p ./src/main/resources # application.yml 파일 생성 touch ./application.yml From 49ad54469fc444a5aa783aec2dfdb85050dd8545 Mon Sep 17 00:00:00 2001 From: Eunggwan Kim <40496065+alpha-kwhn@users.noreply.github.com> Date: Mon, 5 Feb 2024 18:54:39 +0900 Subject: [PATCH 54/78] Update dev-cd.yml --- .github/workflows/dev-cd.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/dev-cd.yml b/.github/workflows/dev-cd.yml index 969fb24..68c9040 100644 --- a/.github/workflows/dev-cd.yml +++ b/.github/workflows/dev-cd.yml @@ -1,3 +1,4 @@ + # 워크플로우의 이름 지정 name: WINEY-DEV-CD From 8e717e4ca8a2eef4e1f09a5d17f32f3978f93c66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EC=9D=91=EA=B4=80?= Date: Mon, 5 Feb 2024 19:29:26 +0900 Subject: [PATCH 55/78] =?UTF-8?q?[Chore]=20=EC=9E=ACmerge=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../winey/server/controller/response/user/UserResponseDto.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/org/winey/server/controller/response/user/UserResponseDto.java b/src/main/java/org/winey/server/controller/response/user/UserResponseDto.java index 49ef015..c9ad4f6 100644 --- a/src/main/java/org/winey/server/controller/response/user/UserResponseDto.java +++ b/src/main/java/org/winey/server/controller/response/user/UserResponseDto.java @@ -27,6 +27,7 @@ public static class UserData { private Long amountSpentTwoWeeks; } + public static UserResponseDto of(Long userId, String nickname, String userLevel, Boolean fcmIsAllowed, Long accumulatedAmount, Long amountSavedHundredDays, Long amountSavedTwoWeeks, Long amountSpentTwoWeeks) { From 546a328419a682f2373c23125de95745c5e1bebb Mon Sep 17 00:00:00 2001 From: Eunggwan Kim <40496065+alpha-kwhn@users.noreply.github.com> Date: Mon, 5 Feb 2024 19:45:11 +0900 Subject: [PATCH 56/78] Update dev-ci.yml --- .github/workflows/dev-ci.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/dev-ci.yml b/.github/workflows/dev-ci.yml index 483697d..4507541 100644 --- a/.github/workflows/dev-ci.yml +++ b/.github/workflows/dev-ci.yml @@ -50,4 +50,5 @@ jobs: run: chmod +x gradlew - name: Build with Gradle # 실제 application build - run: ./gradlew build -PactiveProfiles=local + run: ./gradlew build -Dspring.profiles.active=local + # run: ./gradlew build -PactiveProfiles=local From cb1c3886e1d686ba7e4c44308fc51932463590c0 Mon Sep 17 00:00:00 2001 From: Eunggwan Kim <40496065+alpha-kwhn@users.noreply.github.com> Date: Mon, 5 Feb 2024 19:47:27 +0900 Subject: [PATCH 57/78] Update dev-ci.yml --- .github/workflows/dev-ci.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/dev-ci.yml b/.github/workflows/dev-ci.yml index 4507541..483697d 100644 --- a/.github/workflows/dev-ci.yml +++ b/.github/workflows/dev-ci.yml @@ -50,5 +50,4 @@ jobs: run: chmod +x gradlew - name: Build with Gradle # 실제 application build - run: ./gradlew build -Dspring.profiles.active=local - # run: ./gradlew build -PactiveProfiles=local + run: ./gradlew build -PactiveProfiles=local From cc0f2c82d9365b25857141f1d00ee446554ebffa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EC=9D=91=EA=B4=80?= Date: Mon, 5 Feb 2024 19:48:38 +0900 Subject: [PATCH 58/78] =?UTF-8?q?[Fix]=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EC=98=A4=EB=A5=98=20=EC=A3=BC=EC=84=9D?= =?UTF-8?q?=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../winey/server/ServerApplicationTests.java | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/src/test/java/org/winey/server/ServerApplicationTests.java b/src/test/java/org/winey/server/ServerApplicationTests.java index 7d98d8e..7f631a4 100644 --- a/src/test/java/org/winey/server/ServerApplicationTests.java +++ b/src/test/java/org/winey/server/ServerApplicationTests.java @@ -1,13 +1,10 @@ package org.winey.server; -import org.junit.jupiter.api.Test; -import org.springframework.boot.test.context.SpringBootTest; - -@SpringBootTest -class ServerApplicationTests { - - @Test - void contextLoads() { - } - -} +//@SpringBootTest +//class ServerApplicationTests { +// +// @Test +// void contextLoads() { +// } +// +//} From 36e924c6801a3803082530f02ebddf206d3c801e Mon Sep 17 00:00:00 2001 From: soohyun <49307946+sss4920@users.noreply.github.com> Date: Mon, 5 Feb 2024 20:09:19 +0900 Subject: [PATCH 59/78] Update deploy.sh --- scripts/deploy.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/deploy.sh b/scripts/deploy.sh index b905050..2b05e89 100644 --- a/scripts/deploy.sh +++ b/scripts/deploy.sh @@ -40,8 +40,8 @@ if [ -z $IDLE_PID ] then echo "> 현재 구동중인 애플리케이션이 없으므로 종료하지 않습니다." else - echo "> kill -15 $IDLE_PID" - kill -15 $IDLE_PID + echo "> kill -9 $IDLE_PID" + kill -9 $IDLE_PID sleep 20 fi From e46cc3c61257d5165aaf30143faf6c882ca181ea Mon Sep 17 00:00:00 2001 From: Seonheui Jeon <88873302+funnysunny08@users.noreply.github.com> Date: Fri, 16 Feb 2024 09:47:12 +0900 Subject: [PATCH 60/78] =?UTF-8?q?[Feat]=20=EB=88=84=EC=A0=81=20=EC=9C=84?= =?UTF-8?q?=EB=8B=88=20null=EC=9D=BC=20=EA=B2=BD=EC=9A=B0=200=20=EB=B0=98?= =?UTF-8?q?=ED=99=98=20(#229)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/org/winey/server/service/UserService.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/winey/server/service/UserService.java b/src/main/java/org/winey/server/service/UserService.java index bb2ac5e..1299e5b 100644 --- a/src/main/java/org/winey/server/service/UserService.java +++ b/src/main/java/org/winey/server/service/UserService.java @@ -36,7 +36,8 @@ public UserResponseDto getUser(Long userId) { Long amountSpentTwoWeeks = feedRepository.getSpentAmountForPeriod(user, twoWeeksAgo); return UserResponseDto.of(user.getUserId(), user.getNickname(), - user.getUserLevel().getName(), user.getFcmIsAllowed(), user.getSavedAmount(), + user.getUserLevel().getName(), user.getFcmIsAllowed(), + user.getSavedAmount() == null ? 0L : user.getSavedAmount(), amountSavedHundredDays == null ? 0L : amountSavedHundredDays, amountSavedTwoWeeks == null ? 0L : amountSavedTwoWeeks, amountSpentTwoWeeks == null ? 0L : amountSpentTwoWeeks From 32b916c0335b1c0d01d834848dea0d13283642d3 Mon Sep 17 00:00:00 2001 From: soohyun Date: Wed, 21 Feb 2024 22:28:01 +0900 Subject: [PATCH 61/78] =?UTF-8?q?[Refactor]=20user=20=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=A7=80=EC=97=90=20=EB=82=A8=EC=9D=80=20=EA=B8=88=EC=95=A1=20?= =?UTF-8?q?=EB=82=A8=EC=9D=80=20=ED=9A=9F=EC=88=98=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/response/user/UserResponseDto.java | 8 ++++++-- .../org/winey/server/service/UserService.java | 15 +++++++++++++-- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/winey/server/controller/response/user/UserResponseDto.java b/src/main/java/org/winey/server/controller/response/user/UserResponseDto.java index c9ad4f6..45281be 100644 --- a/src/main/java/org/winey/server/controller/response/user/UserResponseDto.java +++ b/src/main/java/org/winey/server/controller/response/user/UserResponseDto.java @@ -1,5 +1,7 @@ package org.winey.server.controller.response.user; +import org.winey.server.domain.user.UserLevel; + import lombok.AccessLevel; import lombok.AllArgsConstructor; import lombok.Getter; @@ -25,14 +27,16 @@ public static class UserData { private Long amountSavedHundredDays; private Long amountSavedTwoWeeks; private Long amountSpentTwoWeeks; + private Long remainingAmount; + private Long remainingCount; } public static UserResponseDto of(Long userId, String nickname, String userLevel, Boolean fcmIsAllowed, Long accumulatedAmount, Long amountSavedHundredDays, Long amountSavedTwoWeeks, - Long amountSpentTwoWeeks) { + Long amountSpentTwoWeeks, Long remainingAmount, Long remainingCount) { UserData userData = new UserData(userId, nickname, userLevel, fcmIsAllowed, accumulatedAmount, - amountSavedHundredDays, amountSavedTwoWeeks, amountSpentTwoWeeks); + amountSavedHundredDays, amountSavedTwoWeeks, amountSpentTwoWeeks,remainingAmount,remainingCount); return new UserResponseDto(userData); } } diff --git a/src/main/java/org/winey/server/service/UserService.java b/src/main/java/org/winey/server/service/UserService.java index 1299e5b..41e3f41 100644 --- a/src/main/java/org/winey/server/service/UserService.java +++ b/src/main/java/org/winey/server/service/UserService.java @@ -35,12 +35,23 @@ public UserResponseDto getUser(Long userId) { Long amountSavedTwoWeeks = feedRepository.getSavedAmountForPeriod(user, twoWeeksAgo); Long amountSpentTwoWeeks = feedRepository.getSpentAmountForPeriod(user, twoWeeksAgo); + UserLevel nextUserLevel = UserLevel.getNextUserLevel(user.getUserLevel()); + + long savedAmountOfUser = user.getSavedAmount() == null ? 0L : user.getSavedAmount(); //기존의 getSavedAmount()했을 시 null -> 0L로 처리 + long savedCountOfUser = user.getSavedCount() == null ? 0L : user.getSavedCount(); //위와 이유 같음. + + long remainingAmount = nextUserLevel == null ? 0L : nextUserLevel.getMinimumAmount() - savedAmountOfUser; + long remainingCount = nextUserLevel == null ? 0L : nextUserLevel.getMinimumCount() - savedCountOfUser; + return UserResponseDto.of(user.getUserId(), user.getNickname(), - user.getUserLevel().getName(), user.getFcmIsAllowed(), + user.getUserLevel().getName(), + user.getFcmIsAllowed(), user.getSavedAmount() == null ? 0L : user.getSavedAmount(), amountSavedHundredDays == null ? 0L : amountSavedHundredDays, amountSavedTwoWeeks == null ? 0L : amountSavedTwoWeeks, - amountSpentTwoWeeks == null ? 0L : amountSpentTwoWeeks + amountSpentTwoWeeks == null ? 0L : amountSpentTwoWeeks, + remainingAmount < 0 ? 0L : remainingAmount, + remainingCount < 0 ? 0L : remainingCount ); } From 403d9718b91d4b13fb3174a9d9407db1382bb198 Mon Sep 17 00:00:00 2001 From: soohyun Date: Thu, 22 Feb 2024 17:33:54 +0900 Subject: [PATCH 62/78] =?UTF-8?q?[Refactor]#232=20=EC=A0=88=EC=95=BD=20?= =?UTF-8?q?=EB=88=84=EC=A0=81=ED=9A=9F=EC=88=98=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../server/controller/response/user/UserResponseDto.java | 5 +++-- src/main/java/org/winey/server/service/UserService.java | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/winey/server/controller/response/user/UserResponseDto.java b/src/main/java/org/winey/server/controller/response/user/UserResponseDto.java index 45281be..f226b73 100644 --- a/src/main/java/org/winey/server/controller/response/user/UserResponseDto.java +++ b/src/main/java/org/winey/server/controller/response/user/UserResponseDto.java @@ -24,6 +24,7 @@ public static class UserData { private String userLevel; private Boolean fcmIsAllowed; private Long accumulatedAmount; + private Long accumulatedCount; private Long amountSavedHundredDays; private Long amountSavedTwoWeeks; private Long amountSpentTwoWeeks; @@ -33,9 +34,9 @@ public static class UserData { public static UserResponseDto of(Long userId, String nickname, String userLevel, - Boolean fcmIsAllowed, Long accumulatedAmount, Long amountSavedHundredDays, Long amountSavedTwoWeeks, + Boolean fcmIsAllowed, Long accumulatedAmount,Long accumulatedCount , Long amountSavedHundredDays, Long amountSavedTwoWeeks, Long amountSpentTwoWeeks, Long remainingAmount, Long remainingCount) { - UserData userData = new UserData(userId, nickname, userLevel, fcmIsAllowed, accumulatedAmount, + UserData userData = new UserData(userId, nickname, userLevel, fcmIsAllowed, accumulatedAmount, accumulatedCount, amountSavedHundredDays, amountSavedTwoWeeks, amountSpentTwoWeeks,remainingAmount,remainingCount); return new UserResponseDto(userData); } diff --git a/src/main/java/org/winey/server/service/UserService.java b/src/main/java/org/winey/server/service/UserService.java index 41e3f41..2b9bcdb 100644 --- a/src/main/java/org/winey/server/service/UserService.java +++ b/src/main/java/org/winey/server/service/UserService.java @@ -46,7 +46,8 @@ public UserResponseDto getUser(Long userId) { return UserResponseDto.of(user.getUserId(), user.getNickname(), user.getUserLevel().getName(), user.getFcmIsAllowed(), - user.getSavedAmount() == null ? 0L : user.getSavedAmount(), + savedAmountOfUser, + savedCountOfUser, amountSavedHundredDays == null ? 0L : amountSavedHundredDays, amountSavedTwoWeeks == null ? 0L : amountSavedTwoWeeks, amountSpentTwoWeeks == null ? 0L : amountSpentTwoWeeks, From 0c90e6aa23bc8ea911b054e23e15b225f2960d7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=80=E1=85=B5=E1=86=B7=E1=84=8B=E1=85=B3=E1=86=BC?= =?UTF-8?q?=E1=84=80=E1=85=AA=E1=86=AB?= Date: Sat, 24 Feb 2024 08:21:33 +0900 Subject: [PATCH 63/78] =?UTF-8?q?[Fix]=20=ED=94=BC=EB=93=9C=EC=83=9D?= =?UTF-8?q?=EC=84=B1=EC=8B=9C=20=EB=A0=88=EB=B2=A8=EC=97=85=20=EB=8B=AC?= =?UTF-8?q?=EC=84=B1=EC=97=AC=EB=B6=80=20bool=20=EA=B0=92=20responseBody?= =?UTF-8?q?=EC=97=90=20=EB=8B=B4=EA=B2=8C=EB=81=94=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../response/feed/CreateFeedResponseDto.java | 5 +++-- .../java/org/winey/server/service/FeedService.java | 10 ++++++++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/winey/server/controller/response/feed/CreateFeedResponseDto.java b/src/main/java/org/winey/server/controller/response/feed/CreateFeedResponseDto.java index 7fcb7ea..accccd1 100644 --- a/src/main/java/org/winey/server/controller/response/feed/CreateFeedResponseDto.java +++ b/src/main/java/org/winey/server/controller/response/feed/CreateFeedResponseDto.java @@ -13,9 +13,10 @@ public class CreateFeedResponseDto { private Long feedId; private LocalDateTime createdAt; + private Boolean levelUpgraded; - public static CreateFeedResponseDto of(Long feedId, LocalDateTime createdAt){ - return new CreateFeedResponseDto(feedId, createdAt); + public static CreateFeedResponseDto of(Long feedId, LocalDateTime createdAt, Boolean levelUpgraded){ + return new CreateFeedResponseDto(feedId, createdAt, levelUpgraded); } } diff --git a/src/main/java/org/winey/server/service/FeedService.java b/src/main/java/org/winey/server/service/FeedService.java index fcebadb..b31c734 100644 --- a/src/main/java/org/winey/server/service/FeedService.java +++ b/src/main/java/org/winey/server/service/FeedService.java @@ -74,11 +74,17 @@ public CreateFeedResponseDto createFeed(CreateFeedRequestDto request, Long userI // 5. 레벨업을 체크한다. UserLevel newUserLevel = UserLevel.calculateUserLevel(presentUser.getSavedAmount(), presentUser.getSavedCount()); + // 레벨업 달성 여부 담는 Bool 값 + Boolean levelUpgraded = false; + if (presentUser.getUserLevel() != newUserLevel) { // 4-1. 레벨업한다. presentUser.updateUserLevel(newUserLevel); - // 4-2. 레벨업 알림을 생성한다. + // 4-2. 레벨업 달성 여부를 true로 수정 + levelUpgraded = true; + + // 4-3. 레벨업 알림을 생성한다. switch (newUserLevel) { case KNIGHT: notificationBuilderInFeed(NotiType.RANKUPTO2, presentUser); @@ -94,7 +100,7 @@ public CreateFeedResponseDto createFeed(CreateFeedRequestDto request, Long userI } } - return CreateFeedResponseDto.of(feed.getFeedId(), feed.getCreatedAt()); + return CreateFeedResponseDto.of(feed.getFeedId(), feed.getCreatedAt(), levelUpgraded); } @Transactional From 5671cd1985516dacf6c9abc3028d6813603426aa Mon Sep 17 00:00:00 2001 From: soohyun Date: Sat, 2 Mar 2024 17:06:50 +0900 Subject: [PATCH 64/78] =?UTF-8?q?[Refactor]#236=20=EC=B2=AB=EB=A1=9C?= =?UTF-8?q?=EA=B7=B8=EC=9D=B8=20=EB=A9=94=EC=8B=9C=EC=A7=80=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/org/winey/server/domain/notification/NotiType.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/winey/server/domain/notification/NotiType.java b/src/main/java/org/winey/server/domain/notification/NotiType.java index fd9251c..086059e 100644 --- a/src/main/java/org/winey/server/domain/notification/NotiType.java +++ b/src/main/java/org/winey/server/domain/notification/NotiType.java @@ -26,7 +26,7 @@ public enum NotiType { COMMENTNOTI("님이 회원님의 게시글에 댓글을 남겼어요."), //처음 로그인했을 때 생성하는 알림 - HOWTOLEVELUP("위니의 캐릭터 레벨업 방법을 알아볼까요?"); + HOWTOLEVELUP("절약 콘텐츠 업로드를 통해 레벨업을 해보세요!"); private final String type; From 295a5ab81821d72d0940408f88f4cc8475a9f95f Mon Sep 17 00:00:00 2001 From: soohyun Date: Wed, 6 Mar 2024 13:29:16 +0900 Subject: [PATCH 65/78] =?UTF-8?q?[Feat]#238=20feature:=20=EC=A0=84?= =?UTF-8?q?=EC=B2=B4=20=ED=91=B8=EC=8B=9C=EC=95=8C=EB=A6=BC=EB=A9=94?= =?UTF-8?q?=EC=8B=9C=EC=A7=80=20=EB=B0=9C=EC=86=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/BroadCastController.java | 35 ++++++++++ .../broadcast/BroadCastAllUserDto.java | 15 ++++ .../org/winey/server/exception/Error.java | 1 + .../org/winey/server/exception/Success.java | 2 + .../server/infrastructure/UserRepository.java | 4 ++ .../server/service/BroadCastService.java | 50 +++++++++++++ .../org/winey/server/service/FcmService.java | 70 +++++++++++++++++-- .../server/service/message/SendAllFcmDto.java | 23 ++++++ 8 files changed, 193 insertions(+), 7 deletions(-) create mode 100644 src/main/java/org/winey/server/controller/BroadCastController.java create mode 100644 src/main/java/org/winey/server/controller/request/broadcast/BroadCastAllUserDto.java create mode 100644 src/main/java/org/winey/server/service/BroadCastService.java create mode 100644 src/main/java/org/winey/server/service/message/SendAllFcmDto.java diff --git a/src/main/java/org/winey/server/controller/BroadCastController.java b/src/main/java/org/winey/server/controller/BroadCastController.java new file mode 100644 index 0000000..296f072 --- /dev/null +++ b/src/main/java/org/winey/server/controller/BroadCastController.java @@ -0,0 +1,35 @@ +package org.winey.server.controller; + +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestController; +import org.winey.server.common.dto.ApiResponse; +import org.winey.server.controller.request.broadcast.BroadCastAllUserDto; +import org.winey.server.exception.Success; +import org.winey.server.service.BroadCastService; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.google.firebase.messaging.FirebaseMessagingException; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/broadcast") +@Tag(name = "BroadCast", description = "위니 전체 푸시 API Document") +public class BroadCastController { + private final BroadCastService broadCastService; + + @PostMapping("/send-all") + @ResponseStatus(HttpStatus.OK) + @Operation(summary = "전체 유저에게 메시지 발송 API", description = "전체 유저에게 메시지를 발송합니다.") + public ApiResponse sendMessageToEntireUser(@RequestBody BroadCastAllUserDto broadCastAllUserDto) throws JsonProcessingException, FirebaseMessagingException { + return ApiResponse.success(Success.SEND_ENTIRE_MESSAGE_SUCCESS, broadCastService.broadAllUser(broadCastAllUserDto)); + } + +} diff --git a/src/main/java/org/winey/server/controller/request/broadcast/BroadCastAllUserDto.java b/src/main/java/org/winey/server/controller/request/broadcast/BroadCastAllUserDto.java new file mode 100644 index 0000000..4baa93a --- /dev/null +++ b/src/main/java/org/winey/server/controller/request/broadcast/BroadCastAllUserDto.java @@ -0,0 +1,15 @@ +package org.winey.server.controller.request.broadcast; + +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor(access = AccessLevel.PRIVATE) +@AllArgsConstructor +public class BroadCastAllUserDto { + String title; + + String message; +} diff --git a/src/main/java/org/winey/server/exception/Error.java b/src/main/java/org/winey/server/exception/Error.java index 8d90356..69decce 100644 --- a/src/main/java/org/winey/server/exception/Error.java +++ b/src/main/java/org/winey/server/exception/Error.java @@ -66,6 +66,7 @@ public enum Error { * 422 UNPROCESSABLE ENTITY */ UNPROCESSABLE_ENTITY_DELETE_EXCEPTION(HttpStatus.UNPROCESSABLE_ENTITY, "클라의 요청을 이해했지만 삭제하지 못했습니다."), + UNPROCESSABLE_FIND_USERS(HttpStatus.UNPROCESSABLE_ENTITY, "요청을 이해했지만 유저들을 찾을 수 없었습니다."), /** * 500 INTERNAL SERVER ERROR diff --git a/src/main/java/org/winey/server/exception/Success.java b/src/main/java/org/winey/server/exception/Success.java index 0b27d67..02492a4 100644 --- a/src/main/java/org/winey/server/exception/Success.java +++ b/src/main/java/org/winey/server/exception/Success.java @@ -29,6 +29,8 @@ public enum Success { BLOCK_USER_SUCCESS(HttpStatus.OK, "유저 차단 성공"), GET_ACHIEVEMENT_STATUS_SUCCESS(HttpStatus.OK, "레벨 달성 현황 조회 성공"), + SEND_ENTIRE_MESSAGE_SUCCESS(HttpStatus.OK, "전체 메시지 전송 성공"), + /** * 201 CREATED */ diff --git a/src/main/java/org/winey/server/infrastructure/UserRepository.java b/src/main/java/org/winey/server/infrastructure/UserRepository.java index 9203dde..97fbd8a 100644 --- a/src/main/java/org/winey/server/infrastructure/UserRepository.java +++ b/src/main/java/org/winey/server/infrastructure/UserRepository.java @@ -2,6 +2,7 @@ import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.Repository; +import org.springframework.lang.Nullable; import org.winey.server.domain.feed.Feed; import org.winey.server.domain.goal.Goal; import org.winey.server.domain.recommend.Recommend; @@ -18,6 +19,9 @@ public interface UserRepository extends Repository { // READ Optional findByUserId(Long userId); + List findByFcmTokenNotNull(); + + Boolean existsBySocialIdAndSocialType(String socialId, SocialType socialType); Optional findBySocialIdAndSocialType(String socialId, SocialType socialType); diff --git a/src/main/java/org/winey/server/service/BroadCastService.java b/src/main/java/org/winey/server/service/BroadCastService.java new file mode 100644 index 0000000..b85a1b7 --- /dev/null +++ b/src/main/java/org/winey/server/service/BroadCastService.java @@ -0,0 +1,50 @@ +package org.winey.server.service; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import org.winey.server.common.dto.ApiResponse; +import org.winey.server.controller.request.broadcast.BroadCastAllUserDto; +import org.winey.server.domain.user.User; +import org.winey.server.exception.Error; +import org.winey.server.exception.Success; +import org.winey.server.exception.model.CustomException; +import org.winey.server.infrastructure.UserRepository; +import org.winey.server.service.message.SendAllFcmDto; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.google.firebase.messaging.FirebaseMessagingException; +import com.sun.net.httpserver.Authenticator; + +import lombok.RequiredArgsConstructor; + +@Service +@RequiredArgsConstructor +public class BroadCastService { + + private final FcmService fcmService; + + private final UserRepository userRepository; + + public ApiResponse broadAllUser(BroadCastAllUserDto broadCastAllUserDto) throws + JsonProcessingException, + FirebaseMessagingException { + List allUser = userRepository.findByFcmTokenNotNull(); + List tokenList; + if (!allUser.isEmpty()){ + tokenList = allUser.stream().map( + User::getFcmToken).collect(Collectors.toList()); + System.out.println(tokenList); + fcmService.sendAllByTokenList(SendAllFcmDto.of(tokenList,broadCastAllUserDto.getTitle(), broadCastAllUserDto.getMessage())); + return ApiResponse.success(Success.SEND_ENTIRE_MESSAGE_SUCCESS, Success.SEND_ENTIRE_MESSAGE_SUCCESS.getMessage()); + } + return ApiResponse.error(Error.UNPROCESSABLE_FIND_USERS, Error.UNPROCESSABLE_FIND_USERS.getMessage()); + } + + + + +} diff --git a/src/main/java/org/winey/server/service/FcmService.java b/src/main/java/org/winey/server/service/FcmService.java index 404be38..b58d8c8 100644 --- a/src/main/java/org/winey/server/service/FcmService.java +++ b/src/main/java/org/winey/server/service/FcmService.java @@ -15,16 +15,20 @@ import okhttp3.Response; import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.web.client.RestTemplateBuilder; import org.springframework.core.io.ClassPathResource; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; +import org.springframework.web.client.RestTemplate; import org.winey.server.service.message.FcmMessage; import org.winey.server.service.message.FcmRequestDto; +import org.winey.server.service.message.SendAllFcmDto; import javax.annotation.PostConstruct; import java.io.IOException; import java.time.LocalDateTime; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.stream.Collectors; @@ -104,13 +108,6 @@ public void sendByTokenList(List tokenList) { @Async public CompletableFuture sendByToken(FcmRequestDto wineyNotification) throws JsonProcessingException { // 메시지 만들기 - // Message message = Message.builder() - // .putData("feedId", String.valueOf(wineyNotification.getFeedId())) - // .putData("notiType", String.valueOf(wineyNotification.getType())) - // .putData("title", "위니 제국의 편지가 도착했어요.") - // .putData("message" ,wineyNotification.getMessage()) - // .setToken(wineyNotification.getToken()) <- 만약 여기 destination 부분만 수정해도 될 수도 있음. 가능성 생각하기 - // .build(); String jsonMessage = makeSingleMessage(wineyNotification); // 요청에 대한 응답을 받을 response Response response; @@ -182,5 +179,64 @@ private String makeSingleMessage(FcmRequestDto wineyNotification) throws JsonPro } } + private List makeCustomMessages(SendAllFcmDto wineyNotification) throws JsonProcessingException { + try { + List messages = wineyNotification.getTokenList() + .stream() + .map(token -> FcmMessage.builder() + .message(FcmMessage.Message.builder() + .token(token) // 1:1 전송 시 반드시 필요한 대상 토큰 설정 + .data(FcmMessage.Data.builder() + .title("위니 제국의 편지가 도착했어요.") + .message(wineyNotification.getMessage()) + .feedId(null) + .notiType(null) + .build()) + .notification(FcmMessage.Notification.builder() + .title("위니 제국의 편지가 도착했어요.") + .body(wineyNotification.getMessage()) + .build()) + .build() + ).validateOnly(false) + .build()).collect(Collectors.toList()); + return messages; + } catch (Exception e) { + throw new IllegalArgumentException("JSON 처리 도중에 예외가 발생했습니다."); + } + } + + public CompletableFuture sendAllByTokenList(SendAllFcmDto wineyNotification) throws JsonProcessingException, FirebaseMessagingException { + // These registration tokens come from the client FCM SDKs. + List registrationTokens = wineyNotification.getTokenList(); + try { + MulticastMessage message = MulticastMessage.builder() + .putData("title", wineyNotification.getTitle()) + .putData("message", wineyNotification.getMessage()) + .setNotification(new Notification(wineyNotification.getTitle(), wineyNotification.getMessage())) + .addAllTokens(registrationTokens) + .build(); + BatchResponse response = FirebaseMessaging.getInstance().sendMulticast(message); + if (response.getFailureCount() > 0) { + List responses = response.getResponses(); + List failedTokens = new ArrayList<>(); + for (int i = 0; i < responses.size(); i++) { + if (!responses.get(i).isSuccessful()) { + // The order of responses corresponds to the order of the registration tokens. + failedTokens.add(registrationTokens.get(i)); + } + } + + System.out.println("List of tokens that caused failures: " + failedTokens); + } + return CompletableFuture.completedFuture(response); + } catch (Exception e){ + log.info(e.getMessage()); + } + return null; + } + private Notification convertToFirebaseNotification(FcmMessage.Notification customNotification) { + return new Notification(customNotification.getTitle(), customNotification.getBody()); + } + } diff --git a/src/main/java/org/winey/server/service/message/SendAllFcmDto.java b/src/main/java/org/winey/server/service/message/SendAllFcmDto.java new file mode 100644 index 0000000..091df36 --- /dev/null +++ b/src/main/java/org/winey/server/service/message/SendAllFcmDto.java @@ -0,0 +1,23 @@ +package org.winey.server.service.message; + +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import org.winey.server.domain.notification.NotiType; + +import java.io.Serializable; +import java.util.List; +@Getter +@AllArgsConstructor(access = AccessLevel.PRIVATE) +public class SendAllFcmDto { + private List tokenList; + + private String title; + + private String message; + + public static SendAllFcmDto of(List tokenList, String title, String message){ + return new SendAllFcmDto(tokenList, title, message); + } +} From a7af2f670b8683dba6a660b1afb95ed813b7159e Mon Sep 17 00:00:00 2001 From: soohyun Date: Wed, 13 Mar 2024 17:27:52 +0900 Subject: [PATCH 66/78] =?UTF-8?q?[Feature]#238:=20try=20catch=20=EB=AC=B8?= =?UTF-8?q?=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/BroadCastController.java | 2 +- .../org/winey/server/exception/Error.java | 1 + .../server/service/BroadCastService.java | 20 +++++--- .../org/winey/server/service/FcmService.java | 50 +++++++++---------- 4 files changed, 39 insertions(+), 34 deletions(-) diff --git a/src/main/java/org/winey/server/controller/BroadCastController.java b/src/main/java/org/winey/server/controller/BroadCastController.java index 296f072..b140814 100644 --- a/src/main/java/org/winey/server/controller/BroadCastController.java +++ b/src/main/java/org/winey/server/controller/BroadCastController.java @@ -28,7 +28,7 @@ public class BroadCastController { @PostMapping("/send-all") @ResponseStatus(HttpStatus.OK) @Operation(summary = "전체 유저에게 메시지 발송 API", description = "전체 유저에게 메시지를 발송합니다.") - public ApiResponse sendMessageToEntireUser(@RequestBody BroadCastAllUserDto broadCastAllUserDto) throws JsonProcessingException, FirebaseMessagingException { + public ApiResponse sendMessageToEntireUser(@RequestBody BroadCastAllUserDto broadCastAllUserDto){ return ApiResponse.success(Success.SEND_ENTIRE_MESSAGE_SUCCESS, broadCastService.broadAllUser(broadCastAllUserDto)); } diff --git a/src/main/java/org/winey/server/exception/Error.java b/src/main/java/org/winey/server/exception/Error.java index 69decce..a9baf48 100644 --- a/src/main/java/org/winey/server/exception/Error.java +++ b/src/main/java/org/winey/server/exception/Error.java @@ -67,6 +67,7 @@ public enum Error { */ UNPROCESSABLE_ENTITY_DELETE_EXCEPTION(HttpStatus.UNPROCESSABLE_ENTITY, "클라의 요청을 이해했지만 삭제하지 못했습니다."), UNPROCESSABLE_FIND_USERS(HttpStatus.UNPROCESSABLE_ENTITY, "요청을 이해했지만 유저들을 찾을 수 없었습니다."), + UNPROCESSABLE_SEND_TO_FIREBASE(HttpStatus.UNPROCESSABLE_ENTITY, "파이어베이스로 전송하는 과정에서 에러가 발생했습니다."), /** * 500 INTERNAL SERVER ERROR diff --git a/src/main/java/org/winey/server/service/BroadCastService.java b/src/main/java/org/winey/server/service/BroadCastService.java index b85a1b7..14913fc 100644 --- a/src/main/java/org/winey/server/service/BroadCastService.java +++ b/src/main/java/org/winey/server/service/BroadCastService.java @@ -29,17 +29,21 @@ public class BroadCastService { private final UserRepository userRepository; - public ApiResponse broadAllUser(BroadCastAllUserDto broadCastAllUserDto) throws - JsonProcessingException, - FirebaseMessagingException { + public ApiResponse broadAllUser(BroadCastAllUserDto broadCastAllUserDto){ List allUser = userRepository.findByFcmTokenNotNull(); List tokenList; if (!allUser.isEmpty()){ - tokenList = allUser.stream().map( - User::getFcmToken).collect(Collectors.toList()); - System.out.println(tokenList); - fcmService.sendAllByTokenList(SendAllFcmDto.of(tokenList,broadCastAllUserDto.getTitle(), broadCastAllUserDto.getMessage())); - return ApiResponse.success(Success.SEND_ENTIRE_MESSAGE_SUCCESS, Success.SEND_ENTIRE_MESSAGE_SUCCESS.getMessage()); + try { + tokenList = allUser.stream().map( + User::getFcmToken).collect(Collectors.toList()); + System.out.println(tokenList); + fcmService.sendAllByTokenList( + SendAllFcmDto.of(tokenList, broadCastAllUserDto.getTitle(), broadCastAllUserDto.getMessage())); + return ApiResponse.success(Success.SEND_ENTIRE_MESSAGE_SUCCESS, + Success.SEND_ENTIRE_MESSAGE_SUCCESS.getMessage()); + }catch (FirebaseMessagingException | JsonProcessingException e){ + return ApiResponse.error(Error.UNPROCESSABLE_SEND_TO_FIREBASE, Error.UNPROCESSABLE_SEND_TO_FIREBASE.getMessage()); + } } return ApiResponse.error(Error.UNPROCESSABLE_FIND_USERS, Error.UNPROCESSABLE_FIND_USERS.getMessage()); } diff --git a/src/main/java/org/winey/server/service/FcmService.java b/src/main/java/org/winey/server/service/FcmService.java index b58d8c8..373db71 100644 --- a/src/main/java/org/winey/server/service/FcmService.java +++ b/src/main/java/org/winey/server/service/FcmService.java @@ -179,31 +179,31 @@ private String makeSingleMessage(FcmRequestDto wineyNotification) throws JsonPro } } - private List makeCustomMessages(SendAllFcmDto wineyNotification) throws JsonProcessingException { - try { - List messages = wineyNotification.getTokenList() - .stream() - .map(token -> FcmMessage.builder() - .message(FcmMessage.Message.builder() - .token(token) // 1:1 전송 시 반드시 필요한 대상 토큰 설정 - .data(FcmMessage.Data.builder() - .title("위니 제국의 편지가 도착했어요.") - .message(wineyNotification.getMessage()) - .feedId(null) - .notiType(null) - .build()) - .notification(FcmMessage.Notification.builder() - .title("위니 제국의 편지가 도착했어요.") - .body(wineyNotification.getMessage()) - .build()) - .build() - ).validateOnly(false) - .build()).collect(Collectors.toList()); - return messages; - } catch (Exception e) { - throw new IllegalArgumentException("JSON 처리 도중에 예외가 발생했습니다."); - } - } + // private List makeCustomMessages(SendAllFcmDto wineyNotification) throws JsonProcessingException { + // try { + // List messages = wineyNotification.getTokenList() + // .stream() + // .map(token -> FcmMessage.builder() + // .message(FcmMessage.Message.builder() + // .token(token) // 1:1 전송 시 반드시 필요한 대상 토큰 설정 + // .data(FcmMessage.Data.builder() + // .title("위니 제국의 편지가 도착했어요.") + // .message(wineyNotification.getMessage()) + // .feedId(null) + // .notiType(null) + // .build()) + // .notification(FcmMessage.Notification.builder() + // .title("위니 제국의 편지가 도착했어요.") + // .body(wineyNotification.getMessage()) + // .build()) + // .build() + // ).validateOnly(false) + // .build()).collect(Collectors.toList()); + // return messages; + // } catch (Exception e) { + // throw new IllegalArgumentException("JSON 처리 도중에 예외가 발생했습니다."); + // } + // } public CompletableFuture sendAllByTokenList(SendAllFcmDto wineyNotification) throws JsonProcessingException, FirebaseMessagingException { // These registration tokens come from the client FCM SDKs. From 233eea0c5871420ebc62b6c9348dca0122e6d987 Mon Sep 17 00:00:00 2001 From: Seonheui Jeon <88873302+funnysunny08@users.noreply.github.com> Date: Wed, 8 May 2024 20:41:21 +0900 Subject: [PATCH 67/78] =?UTF-8?q?[Feat]=20=ED=86=A0=ED=81=B0=20=EB=A7=8C?= =?UTF-8?q?=EB=A3=8C=EC=8B=9C=EA=B0=84=20=EB=B3=80=EA=B2=BD=20(#243)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/org/winey/server/service/auth/AuthService.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/winey/server/service/auth/AuthService.java b/src/main/java/org/winey/server/service/auth/AuthService.java index 0dd6cdd..99c5ed5 100644 --- a/src/main/java/org/winey/server/service/auth/AuthService.java +++ b/src/main/java/org/winey/server/service/auth/AuthService.java @@ -37,7 +37,8 @@ public class AuthService { private final GoalRepository goalRepository; - private final Long TOKEN_EXPIRATION_TIME_ACCESS = 100 * 24 * 60 * 60 * 1000L; +// private final Long TOKEN_EXPIRATION_TIME_ACCESS = 100 * 24 * 60 * 60 * 1000L; + private final Long TOKEN_EXPIRATION_TIME_ACCESS = 1 * 60 * 1000L; private final Long TOKEN_EXPIRATION_TIME_REFRESH = 200 * 24 * 60 * 60 * 1000L; private final NotiRepository notiRepository; From 20673955e90110e71107d473f35ffad19f3847e7 Mon Sep 17 00:00:00 2001 From: Seonheui Jeon <88873302+funnysunny08@users.noreply.github.com> Date: Wed, 8 May 2024 21:19:36 +0900 Subject: [PATCH 68/78] =?UTF-8?q?[Feat]=20=ED=86=A0=ED=81=B0=20=EB=A7=8C?= =?UTF-8?q?=EB=A3=8C=EC=8B=9C=EA=B0=84=20=EB=B3=80=EA=B2=BD=20(#245)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/org/winey/server/service/auth/AuthService.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/org/winey/server/service/auth/AuthService.java b/src/main/java/org/winey/server/service/auth/AuthService.java index 99c5ed5..0dd6cdd 100644 --- a/src/main/java/org/winey/server/service/auth/AuthService.java +++ b/src/main/java/org/winey/server/service/auth/AuthService.java @@ -37,8 +37,7 @@ public class AuthService { private final GoalRepository goalRepository; -// private final Long TOKEN_EXPIRATION_TIME_ACCESS = 100 * 24 * 60 * 60 * 1000L; - private final Long TOKEN_EXPIRATION_TIME_ACCESS = 1 * 60 * 1000L; + private final Long TOKEN_EXPIRATION_TIME_ACCESS = 100 * 24 * 60 * 60 * 1000L; private final Long TOKEN_EXPIRATION_TIME_REFRESH = 200 * 24 * 60 * 60 * 1000L; private final NotiRepository notiRepository; From 5c0b1eb747971052e2d6b1c4a064c4c53f45dcb5 Mon Sep 17 00:00:00 2001 From: Seonheui Jeon <88873302+funnysunny08@users.noreply.github.com> Date: Mon, 13 May 2024 13:37:30 +0900 Subject: [PATCH 69/78] =?UTF-8?q?[Feat]=20=EB=A7=88=EC=9D=B4=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20=EC=9D=91=EB=8B=B5=EA=B0=92=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=20(#247)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [Feat] 마이페이지 응답값 변경 (+가입일, -100일 절약 금액) * [Feat] 마이페이지 응답값 가입일 -> 가입 디데이로 변경 --- .../controller/response/user/UserResponseDto.java | 13 +++++-------- .../java/org/winey/server/service/UserService.java | 7 +++---- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/src/main/java/org/winey/server/controller/response/user/UserResponseDto.java b/src/main/java/org/winey/server/controller/response/user/UserResponseDto.java index f226b73..9b98541 100644 --- a/src/main/java/org/winey/server/controller/response/user/UserResponseDto.java +++ b/src/main/java/org/winey/server/controller/response/user/UserResponseDto.java @@ -1,7 +1,5 @@ package org.winey.server.controller.response.user; -import org.winey.server.domain.user.UserLevel; - import lombok.AccessLevel; import lombok.AllArgsConstructor; import lombok.Getter; @@ -20,24 +18,23 @@ public class UserResponseDto { @AllArgsConstructor(access = AccessLevel.PRIVATE) public static class UserData { private Long userId; + private Long createdDday; private String nickname; private String userLevel; private Boolean fcmIsAllowed; private Long accumulatedAmount; private Long accumulatedCount; - private Long amountSavedHundredDays; private Long amountSavedTwoWeeks; private Long amountSpentTwoWeeks; private Long remainingAmount; private Long remainingCount; } - - public static UserResponseDto of(Long userId, String nickname, String userLevel, - Boolean fcmIsAllowed, Long accumulatedAmount,Long accumulatedCount , Long amountSavedHundredDays, Long amountSavedTwoWeeks, + public static UserResponseDto of(Long userId, Long createdDday, String nickname, String userLevel, + Boolean fcmIsAllowed, Long accumulatedAmount,Long accumulatedCount, Long amountSavedTwoWeeks, Long amountSpentTwoWeeks, Long remainingAmount, Long remainingCount) { - UserData userData = new UserData(userId, nickname, userLevel, fcmIsAllowed, accumulatedAmount, accumulatedCount, - amountSavedHundredDays, amountSavedTwoWeeks, amountSpentTwoWeeks,remainingAmount,remainingCount); + UserData userData = new UserData(userId, createdDday, nickname, userLevel, fcmIsAllowed, accumulatedAmount, accumulatedCount, + amountSavedTwoWeeks, amountSpentTwoWeeks,remainingAmount,remainingCount); return new UserResponseDto(userData); } } diff --git a/src/main/java/org/winey/server/service/UserService.java b/src/main/java/org/winey/server/service/UserService.java index 2b9bcdb..05ffbf3 100644 --- a/src/main/java/org/winey/server/service/UserService.java +++ b/src/main/java/org/winey/server/service/UserService.java @@ -1,6 +1,7 @@ package org.winey.server.service; import java.time.LocalDateTime; +import java.time.temporal.ChronoUnit; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -30,8 +31,7 @@ public UserResponseDto getUser(Long userId) { Error.NOT_FOUND_USER_EXCEPTION.getMessage())); LocalDateTime twoWeeksAgo = LocalDateTime.now().minusWeeks(2); - LocalDateTime hundredDaysAgo = LocalDateTime.now().minusDays(100); - Long amountSavedHundredDays = feedRepository.getSavedAmountForPeriod(user, hundredDaysAgo); + Long createdDday = Math.abs(ChronoUnit.DAYS.between(user.getCreatedAt(), LocalDateTime.now())) + 1; Long amountSavedTwoWeeks = feedRepository.getSavedAmountForPeriod(user, twoWeeksAgo); Long amountSpentTwoWeeks = feedRepository.getSpentAmountForPeriod(user, twoWeeksAgo); @@ -43,12 +43,11 @@ public UserResponseDto getUser(Long userId) { long remainingAmount = nextUserLevel == null ? 0L : nextUserLevel.getMinimumAmount() - savedAmountOfUser; long remainingCount = nextUserLevel == null ? 0L : nextUserLevel.getMinimumCount() - savedCountOfUser; - return UserResponseDto.of(user.getUserId(), user.getNickname(), + return UserResponseDto.of(user.getUserId(), createdDday, user.getNickname(), user.getUserLevel().getName(), user.getFcmIsAllowed(), savedAmountOfUser, savedCountOfUser, - amountSavedHundredDays == null ? 0L : amountSavedHundredDays, amountSavedTwoWeeks == null ? 0L : amountSavedTwoWeeks, amountSpentTwoWeeks == null ? 0L : amountSpentTwoWeeks, remainingAmount < 0 ? 0L : remainingAmount, From b1406aa4b351db0acc1a0195174c2e55dd5c1f2a Mon Sep 17 00:00:00 2001 From: Seonheui Jeon <88873302+funnysunny08@users.noreply.github.com> Date: Sat, 18 May 2024 20:20:24 +0900 Subject: [PATCH 70/78] =?UTF-8?q?[Test]=20=EC=86=8C=EC=85=9C=20=EB=A1=9C?= =?UTF-8?q?=EA=B7=B8=EC=9D=B8=20=EB=A1=9C=EA=B7=B8=20(#250)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/org/winey/server/service/auth/AuthService.java | 6 +++++- .../winey/server/service/auth/kakao/KakaoSignInService.java | 4 ++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/winey/server/service/auth/AuthService.java b/src/main/java/org/winey/server/service/auth/AuthService.java index 0dd6cdd..61e1a1a 100644 --- a/src/main/java/org/winey/server/service/auth/AuthService.java +++ b/src/main/java/org/winey/server/service/auth/AuthService.java @@ -3,6 +3,7 @@ import java.util.Random; import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.winey.server.config.jwt.JwtService; @@ -25,6 +26,7 @@ import org.winey.server.service.auth.apple.AppleSignInService; import org.winey.server.service.auth.kakao.KakaoSignInService; +@Slf4j @Service @RequiredArgsConstructor public class AuthService { @@ -45,10 +47,12 @@ public class AuthService { @Transactional public SignInResponseDto signIn(String socialAccessToken, SignInRequestDto requestDto) { SocialType socialType = SocialType.valueOf(requestDto.getSocialType()); + log.info("after get social type"); String socialId = login(socialType, socialAccessToken); + log.info("after get social info"); Boolean isRegistered = userRepository.existsBySocialIdAndSocialType(socialId, socialType); - + log.info("after check isRegistered"); if (!isRegistered) { String randomString= new Random().ints(6, 0, 36).mapToObj(i -> Character.toString("abcdefghijklmnopqrstuvwxyz0123456789".charAt(i))).collect(Collectors.joining()); while (userRepository.existsByNickname("위니"+randomString)) { diff --git a/src/main/java/org/winey/server/service/auth/kakao/KakaoSignInService.java b/src/main/java/org/winey/server/service/auth/kakao/KakaoSignInService.java index 2d0f213..dff4732 100644 --- a/src/main/java/org/winey/server/service/auth/kakao/KakaoSignInService.java +++ b/src/main/java/org/winey/server/service/auth/kakao/KakaoSignInService.java @@ -3,6 +3,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.google.gson.JsonArray; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; @@ -12,6 +13,7 @@ import java.util.Map; +@Slf4j @Service @RequiredArgsConstructor public class KakaoSignInService { @@ -24,7 +26,9 @@ public String getKaKaoId(String accessToken) { headers.add("Authorization","Bearer "+ accessToken); HttpEntity httpEntity = new HttpEntity<>(headers); ResponseEntity responseData; + log.info("before kakao post"); responseData = restTemplate.postForEntity(KAKAO_URL,httpEntity,Object.class); + log.info("after kakao post"); ObjectMapper objectMapper = new ObjectMapper(); return objectMapper.convertValue(responseData.getBody(), Map.class).get("id").toString(); //소셜 id만 가져오는듯. } From 4cad991faf2fc0f6f78eb60cbfa0527f727c535c Mon Sep 17 00:00:00 2001 From: soohyun Date: Sat, 18 May 2024 20:42:04 +0900 Subject: [PATCH 71/78] =?UTF-8?q?[Fix/#248]=20kakao=20=EC=97=B0=EA=B2=B0?= =?UTF-8?q?=EB=81=8A=EA=B8=B0=20=EC=96=B4=EB=93=9C=EB=AF=BC=ED=82=A4=20?= =?UTF-8?q?=EB=B0=A9=EC=8B=9D=EC=9C=BC=EB=A1=9C=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/winey/server/exception/Error.java | 1 + .../server/service/auth/AuthService.java | 4 ++ .../auth/kakao/KakaoSignInService.java | 65 +++++++++++++++---- 3 files changed, 57 insertions(+), 13 deletions(-) diff --git a/src/main/java/org/winey/server/exception/Error.java b/src/main/java/org/winey/server/exception/Error.java index a9baf48..146fccb 100644 --- a/src/main/java/org/winey/server/exception/Error.java +++ b/src/main/java/org/winey/server/exception/Error.java @@ -68,6 +68,7 @@ public enum Error { UNPROCESSABLE_ENTITY_DELETE_EXCEPTION(HttpStatus.UNPROCESSABLE_ENTITY, "클라의 요청을 이해했지만 삭제하지 못했습니다."), UNPROCESSABLE_FIND_USERS(HttpStatus.UNPROCESSABLE_ENTITY, "요청을 이해했지만 유저들을 찾을 수 없었습니다."), UNPROCESSABLE_SEND_TO_FIREBASE(HttpStatus.UNPROCESSABLE_ENTITY, "파이어베이스로 전송하는 과정에서 에러가 발생했습니다."), + UNPROCESSABLE_KAKAO_SERVER_EXCEPTION(HttpStatus.UNPROCESSABLE_ENTITY, "요청을 이해했지만 카카오 서버에러가 발생했습니다."), /** * 500 INTERNAL SERVER ERROR diff --git a/src/main/java/org/winey/server/service/auth/AuthService.java b/src/main/java/org/winey/server/service/auth/AuthService.java index 0dd6cdd..26ba346 100644 --- a/src/main/java/org/winey/server/service/auth/AuthService.java +++ b/src/main/java/org/winey/server/service/auth/AuthService.java @@ -136,6 +136,10 @@ public void withdraw(Long userId){ if (user == null) { throw new NotFoundException(Error.NOT_FOUND_USER_EXCEPTION, Error.NOT_FOUND_USER_EXCEPTION.getMessage()); } + if (user.getSocialType() == SocialType.KAKAO){ + String deleteSocialId = kakaoSignInService.withdrawKakao(user.getSocialId()); + System.out.println(deleteSocialId); + } System.out.println("User: " + user); System.out.println("Goals: " + user.getGoals()); System.out.println("Recommends: " + user.getRecommends()); diff --git a/src/main/java/org/winey/server/service/auth/kakao/KakaoSignInService.java b/src/main/java/org/winey/server/service/auth/kakao/KakaoSignInService.java index 2d0f213..3722894 100644 --- a/src/main/java/org/winey/server/service/auth/kakao/KakaoSignInService.java +++ b/src/main/java/org/winey/server/service/auth/kakao/KakaoSignInService.java @@ -2,30 +2,69 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.google.gson.JsonArray; + import lombok.RequiredArgsConstructor; + import org.springframework.beans.factory.annotation.Value; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; import org.springframework.web.client.RestTemplate; +import org.winey.server.exception.Error; +import org.winey.server.exception.model.CustomException; +import org.winey.server.exception.model.UnprocessableEntityException; +import java.util.HashMap; import java.util.Map; @Service @RequiredArgsConstructor public class KakaoSignInService { - @Value("${jwt.KAKAO_URL}") - private String KAKAO_URL; - - public String getKaKaoId(String accessToken) { - RestTemplate restTemplate = new RestTemplate(); - HttpHeaders headers = new HttpHeaders(); - headers.add("Authorization","Bearer "+ accessToken); - HttpEntity httpEntity = new HttpEntity<>(headers); - ResponseEntity responseData; - responseData = restTemplate.postForEntity(KAKAO_URL,httpEntity,Object.class); - ObjectMapper objectMapper = new ObjectMapper(); - return objectMapper.convertValue(responseData.getBody(), Map.class).get("id").toString(); //소셜 id만 가져오는듯. - } + @Value("${jwt.KAKAO_URL}") + private String KAKAO_URL; + + @Value("${jwt.KAKAO_AK}") + private String KAKAO_AK; + + @Value("${jwt.KAKAO_WITHDRAW_URL}") + private String KAKAO_WITHDRAW; + + public String getKaKaoId(String accessToken) { + RestTemplate restTemplate = new RestTemplate(); + HttpHeaders headers = new HttpHeaders(); + headers.add("Authorization", "Bearer " + accessToken); + HttpEntity httpEntity = new HttpEntity<>(headers); + ResponseEntity responseData; + responseData = restTemplate.postForEntity(KAKAO_URL, httpEntity, Object.class); + ObjectMapper objectMapper = new ObjectMapper(); + return objectMapper.convertValue(responseData.getBody(), Map.class).get("id").toString(); //소셜 id만 가져오는듯. + } + + public String withdrawKakao(String socialId) { + ResponseEntity responseData = requestKakaoServer(socialId); + ObjectMapper objectMapper = new ObjectMapper(); + HashMap profileResponse = (HashMap)objectMapper.convertValue(responseData.getBody(), Map.class); + return profileResponse.get("id").toString(); + } + + private ResponseEntity requestKakaoServer(String socialId) { + RestTemplate restTemplate = new RestTemplate(); + HttpHeaders headers = new HttpHeaders(); + + headers.add("Authorization", "KakaoAK " + KAKAO_AK); + + MultiValueMap param = new LinkedMultiValueMap<>(); + param.set("target_id_type", "user_id"); + param.set("target_id", socialId); + HttpEntity> httpEntity = new HttpEntity<>(param, headers); + try { + return restTemplate.postForEntity(KAKAO_WITHDRAW, httpEntity, Object.class); + } catch (Exception e) { + throw new UnprocessableEntityException(Error.UNPROCESSABLE_KAKAO_SERVER_EXCEPTION, + Error.UNPROCESSABLE_KAKAO_SERVER_EXCEPTION.getMessage()); + } + } } From a42721132a7238407a008c2d27769cbbfa8e0999 Mon Sep 17 00:00:00 2001 From: Seonheui Jeon <88873302+funnysunny08@users.noreply.github.com> Date: Sat, 18 May 2024 21:40:52 +0900 Subject: [PATCH 72/78] =?UTF-8?q?[Feat]=20=EB=AA=A9=ED=91=9C=20=EC=B2=B4?= =?UTF-8?q?=EA=B3=84=20=EC=A0=9C=EA=B1=B0=20(#252)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [Feat] 소셜 로그인 관련 로그 삭제 * [Feat] goal 관련 로직 삭제 --- .../server/controller/GoalController.java | 32 ------- .../request/goal/GoalRequestCreateDto.java | 19 ---- .../response/goal/GoalResponseCreateDto.java | 23 ----- .../org/winey/server/domain/feed/Feed.java | 6 -- .../org/winey/server/domain/goal/Goal.java | 92 ------------------- .../org/winey/server/domain/user/User.java | 5 +- .../server/infrastructure/GoalRepository.java | 23 ----- .../server/infrastructure/UserRepository.java | 1 - .../org/winey/server/service/GoalService.java | 40 -------- .../server/service/auth/AuthService.java | 22 ----- .../auth/kakao/KakaoSignInService.java | 7 +- 11 files changed, 2 insertions(+), 268 deletions(-) delete mode 100644 src/main/java/org/winey/server/controller/GoalController.java delete mode 100644 src/main/java/org/winey/server/controller/request/goal/GoalRequestCreateDto.java delete mode 100644 src/main/java/org/winey/server/controller/response/goal/GoalResponseCreateDto.java delete mode 100644 src/main/java/org/winey/server/domain/goal/Goal.java delete mode 100644 src/main/java/org/winey/server/infrastructure/GoalRepository.java delete mode 100644 src/main/java/org/winey/server/service/GoalService.java diff --git a/src/main/java/org/winey/server/controller/GoalController.java b/src/main/java/org/winey/server/controller/GoalController.java deleted file mode 100644 index efb955d..0000000 --- a/src/main/java/org/winey/server/controller/GoalController.java +++ /dev/null @@ -1,32 +0,0 @@ -package org.winey.server.controller; - -import io.swagger.v3.oas.annotations.Operation; -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.web.bind.annotation.*; -import org.winey.server.common.dto.ApiResponse; -import org.winey.server.config.resolver.UserId; -import org.winey.server.controller.request.goal.GoalRequestCreateDto; -import org.winey.server.controller.response.goal.GoalResponseCreateDto; -import org.winey.server.exception.Success; -import org.winey.server.service.GoalService; - -import javax.validation.Valid; - -@RestController -@RequiredArgsConstructor -@RequestMapping("/goal") -@Tag(name = "Goal", description = "위니 목표 API Document") -public class GoalController { - - private final GoalService goalService; - - @PostMapping("") - @ResponseStatus(HttpStatus.CREATED) - @Operation(summary = "목표 생성 API", description = "위니 목표를 설정합니다.") - public ApiResponse create(@RequestBody @Valid final GoalRequestCreateDto requestDto, @UserId Long userId) { - return ApiResponse.success(Success.CREATE_GOAL_SUCCESS, goalService.createGoal(requestDto, userId)); - } -} diff --git a/src/main/java/org/winey/server/controller/request/goal/GoalRequestCreateDto.java b/src/main/java/org/winey/server/controller/request/goal/GoalRequestCreateDto.java deleted file mode 100644 index 726b103..0000000 --- a/src/main/java/org/winey/server/controller/request/goal/GoalRequestCreateDto.java +++ /dev/null @@ -1,19 +0,0 @@ -package org.winey.server.controller.request.goal; - -import lombok.AccessLevel; -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; - -import javax.validation.constraints.DecimalMax; -import javax.validation.constraints.DecimalMin; - -@Getter -@NoArgsConstructor(access = AccessLevel.PRIVATE) -@AllArgsConstructor(access = AccessLevel.PRIVATE) -public class GoalRequestCreateDto { - @DecimalMin(value = "30000") @DecimalMax(value = "999999999") - private Long targetMoney; - @DecimalMin(value = "5") @DecimalMax(value = "365") - private int targetDay; -} diff --git a/src/main/java/org/winey/server/controller/response/goal/GoalResponseCreateDto.java b/src/main/java/org/winey/server/controller/response/goal/GoalResponseCreateDto.java deleted file mode 100644 index 762c1fc..0000000 --- a/src/main/java/org/winey/server/controller/response/goal/GoalResponseCreateDto.java +++ /dev/null @@ -1,23 +0,0 @@ -package org.winey.server.controller.response.goal; - -import lombok.AccessLevel; -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; - -import java.time.LocalDate; -import java.time.LocalDateTime; - -@Getter -@NoArgsConstructor(access = AccessLevel.PRIVATE) -@AllArgsConstructor(access = AccessLevel.PRIVATE) -public class GoalResponseCreateDto { - private Long userId; - private Long targetMoney; - private LocalDate targetDate; - private LocalDateTime createdAt; - - public static GoalResponseCreateDto of(Long userId, Long targetMoney, LocalDate targetDate, LocalDateTime createdAt) { - return new GoalResponseCreateDto(userId, targetMoney, targetDate, createdAt); - } -} diff --git a/src/main/java/org/winey/server/domain/feed/Feed.java b/src/main/java/org/winey/server/domain/feed/Feed.java index 3c11ac7..96eb6cd 100644 --- a/src/main/java/org/winey/server/domain/feed/Feed.java +++ b/src/main/java/org/winey/server/domain/feed/Feed.java @@ -9,7 +9,6 @@ import lombok.NoArgsConstructor; import org.winey.server.domain.AuditingTimeEntity; import org.winey.server.domain.comment.Comment; -import org.winey.server.domain.goal.Goal; import org.winey.server.domain.user.User; @Getter @@ -39,10 +38,6 @@ public class Feed extends AuditingTimeEntity { @Column(nullable = false) private Long feedMoney; - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "goal_id") - private Goal goal; - @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.REMOVE, mappedBy = "feed", orphanRemoval = true) private List feedLikes; @@ -56,6 +51,5 @@ public Feed(User user, String feedTitle, String feedImage, Long feedMoney, FeedT this.feedImage = feedImage; this.feedMoney = feedMoney; this.feedType = feedType; - this.goal = null; } } diff --git a/src/main/java/org/winey/server/domain/goal/Goal.java b/src/main/java/org/winey/server/domain/goal/Goal.java deleted file mode 100644 index 7ec1d9a..0000000 --- a/src/main/java/org/winey/server/domain/goal/Goal.java +++ /dev/null @@ -1,92 +0,0 @@ -package org.winey.server.domain.goal; - -import java.time.LocalDate; -import javax.persistence.Column; -import javax.persistence.ConstraintMode; -import javax.persistence.Entity; -import javax.persistence.EnumType; -import javax.persistence.Enumerated; -import javax.persistence.FetchType; -import javax.persistence.ForeignKey; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.JoinColumn; -import javax.persistence.ManyToOne; -import javax.persistence.Table; -import javax.persistence.UniqueConstraint; -import lombok.AccessLevel; -import lombok.Builder; -import lombok.Getter; -import lombok.NoArgsConstructor; -import org.hibernate.annotations.DynamicInsert; -import org.winey.server.domain.AuditingTimeEntity; -import org.winey.server.domain.user.User; - -@Entity -@Getter -@DynamicInsert -@Table(uniqueConstraints = { - @UniqueConstraint(columnNames = {"user_id", "goal_type"}) -}) -@NoArgsConstructor(access = AccessLevel.PROTECTED) -public class Goal extends AuditingTimeEntity { - - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long goalId; - - @Column(name = "goal_type") - @Enumerated(EnumType.STRING) - private GoalType goalType; - - @Column - private Long targetMoney; - - @Column - private LocalDate targetDate; - - @Column(nullable = false) - private Long duringGoalAmount; - - @Column(nullable = false) - private boolean isAttained; - - @Column(nullable = false) - private Long duringGoalCount; - - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "user_id", nullable = false, foreignKey = @ForeignKey(ConstraintMode.CONSTRAINT)) - private User user; - -// @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.REMOVE, mappedBy = "goal", orphanRemoval = true) -// private List feeds = new ArrayList<>(); - - @Builder - public Goal(GoalType goalType, User user) { - this.goalType = goalType; - this.user = user; - this.duringGoalCount = 0L; - this.isAttained = false; - this.duringGoalAmount = 0L; - } - - public void updateIsAttained(boolean isAttained) { - this.isAttained = isAttained; - } - - public void updateGoalCountAndAmount(Long feedMoney, boolean createOrDelete) { - if (createOrDelete) { - this.duringGoalCount += 1; - this.duringGoalAmount += feedMoney; - } else { - this.duringGoalCount -= 1; - this.duringGoalAmount -= feedMoney; - } - } - - public void resetGoalCountAndAmount(){ - this.duringGoalAmount = 0L; - this.duringGoalCount = 0L; - } -} diff --git a/src/main/java/org/winey/server/domain/user/User.java b/src/main/java/org/winey/server/domain/user/User.java index 29454ad..1cd95a5 100644 --- a/src/main/java/org/winey/server/domain/user/User.java +++ b/src/main/java/org/winey/server/domain/user/User.java @@ -23,7 +23,6 @@ import org.winey.server.domain.comment.Comment; import org.winey.server.domain.feed.Feed; import org.winey.server.domain.feed.FeedLike; -import org.winey.server.domain.goal.Goal; import org.winey.server.domain.notification.Notification; import org.winey.server.domain.recommend.Recommend; @@ -66,11 +65,9 @@ public class User extends AuditingTimeEntity { @Column private Long savedCount; - @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.REMOVE, mappedBy = "user", orphanRemoval = true) - private List goals; - @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.REMOVE, mappedBy = "user", orphanRemoval = true) private List recommends; + @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.REMOVE, mappedBy = "user", orphanRemoval = true) private List feeds; diff --git a/src/main/java/org/winey/server/infrastructure/GoalRepository.java b/src/main/java/org/winey/server/infrastructure/GoalRepository.java deleted file mode 100644 index 6e82685..0000000 --- a/src/main/java/org/winey/server/infrastructure/GoalRepository.java +++ /dev/null @@ -1,23 +0,0 @@ -package org.winey.server.infrastructure; - -import org.springframework.data.jpa.repository.Query; -import org.springframework.data.repository.Repository; -import org.winey.server.domain.goal.Goal; -import org.winey.server.domain.user.User; - -import java.util.List; - -public interface GoalRepository extends Repository { - // CREATE - Goal save(Goal goal); - - // READ - - int countByUserAndIsAttained(User user, boolean isAttained); - @Query("SELECT g FROM Goal g WHERE g.createdAt IN (SELECT MAX(g2.createdAt) FROM Goal g2 WHERE g2.user = g.user)") - List findLatestGoalsForEachUser(); - - @Query("select g from Goal g where g.user = ?1 order by g.createdAt DESC") - List findByUserOrderByCreatedAtDesc(User user); - -} \ No newline at end of file diff --git a/src/main/java/org/winey/server/infrastructure/UserRepository.java b/src/main/java/org/winey/server/infrastructure/UserRepository.java index 97fbd8a..bcae789 100644 --- a/src/main/java/org/winey/server/infrastructure/UserRepository.java +++ b/src/main/java/org/winey/server/infrastructure/UserRepository.java @@ -4,7 +4,6 @@ import org.springframework.data.repository.Repository; import org.springframework.lang.Nullable; import org.winey.server.domain.feed.Feed; -import org.winey.server.domain.goal.Goal; import org.winey.server.domain.recommend.Recommend; import org.winey.server.domain.user.SocialType; import org.winey.server.domain.user.User; diff --git a/src/main/java/org/winey/server/service/GoalService.java b/src/main/java/org/winey/server/service/GoalService.java deleted file mode 100644 index 4aa1b2f..0000000 --- a/src/main/java/org/winey/server/service/GoalService.java +++ /dev/null @@ -1,40 +0,0 @@ -package org.winey.server.service; - -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; -import org.winey.server.controller.request.goal.GoalRequestCreateDto; -import org.winey.server.controller.response.goal.GoalResponseCreateDto; -import org.winey.server.domain.goal.Goal; -import org.winey.server.domain.goal.GoalType; -import org.winey.server.domain.user.User; -import org.winey.server.exception.Error; -import org.winey.server.exception.model.NotFoundException; -import org.winey.server.infrastructure.GoalRepository; -import org.winey.server.infrastructure.UserRepository; - -import java.time.LocalDate; - -@Service -@RequiredArgsConstructor -public class GoalService { - - private final GoalRepository goalRepository; - private final UserRepository userRepository; - - @Transactional - public GoalResponseCreateDto createGoal(GoalRequestCreateDto requestDto, Long userId) { - User user = userRepository.findByUserId(userId) - .orElseThrow(() -> new NotFoundException(Error.NOT_FOUND_USER_EXCEPTION, - Error.NOT_FOUND_USER_EXCEPTION.getMessage())); - - LocalDate targetDate = LocalDate.now().plusDays(requestDto.getTargetDay()); - Goal createGoal = goalRepository.save(Goal.builder() - .goalType(GoalType.findGoalTypeByUserLevel(user.getUserLevel())) - .user(user) - .build()); - - return GoalResponseCreateDto.of(userId, createGoal.getTargetMoney(), - createGoal.getTargetDate(), createGoal.getCreatedAt()); - } -} diff --git a/src/main/java/org/winey/server/service/auth/AuthService.java b/src/main/java/org/winey/server/service/auth/AuthService.java index 61e1a1a..ae4039a 100644 --- a/src/main/java/org/winey/server/service/auth/AuthService.java +++ b/src/main/java/org/winey/server/service/auth/AuthService.java @@ -3,15 +3,12 @@ import java.util.Random; import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.winey.server.config.jwt.JwtService; import org.winey.server.controller.request.auth.SignInRequestDto; import org.winey.server.controller.response.auth.SignInResponseDto; import org.winey.server.controller.response.auth.TokenResponseDto; -import org.winey.server.domain.goal.Goal; -import org.winey.server.domain.goal.GoalType; import org.winey.server.domain.notification.NotiType; import org.winey.server.domain.notification.Notification; import org.winey.server.domain.user.SocialType; @@ -20,23 +17,19 @@ import org.winey.server.exception.model.NotFoundException; import org.winey.server.exception.model.UnprocessableEntityException; import org.winey.server.infrastructure.BlockUserRepository; -import org.winey.server.infrastructure.GoalRepository; import org.winey.server.infrastructure.NotiRepository; import org.winey.server.infrastructure.UserRepository; import org.winey.server.service.auth.apple.AppleSignInService; import org.winey.server.service.auth.kakao.KakaoSignInService; -@Slf4j @Service @RequiredArgsConstructor public class AuthService { private final AppleSignInService appleSignInService; private final KakaoSignInService kakaoSignInService; private final JwtService jwtService; - private final UserRepository userRepository; private final BlockUserRepository blockUserRepository; - private final GoalRepository goalRepository; private final Long TOKEN_EXPIRATION_TIME_ACCESS = 100 * 24 * 60 * 60 * 1000L; @@ -47,12 +40,9 @@ public class AuthService { @Transactional public SignInResponseDto signIn(String socialAccessToken, SignInRequestDto requestDto) { SocialType socialType = SocialType.valueOf(requestDto.getSocialType()); - log.info("after get social type"); String socialId = login(socialType, socialAccessToken); - log.info("after get social info"); Boolean isRegistered = userRepository.existsBySocialIdAndSocialType(socialId, socialType); - log.info("after check isRegistered"); if (!isRegistered) { String randomString= new Random().ints(6, 0, 36).mapToObj(i -> Character.toString("abcdefghijklmnopqrstuvwxyz0123456789".charAt(i))).collect(Collectors.joining()); while (userRepository.existsByNickname("위니"+randomString)) { @@ -75,12 +65,6 @@ public SignInResponseDto signIn(String socialAccessToken, SignInRequestDto reque .build(); newNoti.updateLinkId(null); notiRepository.save(newNoti); - - Goal newGoal = Goal.builder() - .goalType(GoalType.COMMONER_GOAL) - .user(newUser) - .build(); - goalRepository.save(newGoal); } User user = userRepository.findBySocialIdAndSocialType(socialId, socialType) @@ -140,12 +124,6 @@ public void withdraw(Long userId){ if (user == null) { throw new NotFoundException(Error.NOT_FOUND_USER_EXCEPTION, Error.NOT_FOUND_USER_EXCEPTION.getMessage()); } - System.out.println("User: " + user); - System.out.println("Goals: " + user.getGoals()); - System.out.println("Recommends: " + user.getRecommends()); - System.out.println("Feeds: " + user.getFeeds()); - System.out.println("FeedLikes: " + user.getFeedLikes()); - System.out.println("Comments: "+ user.getComments()); // 유저가 생성한 반응과 관련된 알림 삭제 notiRepository.deleteByRequestUserId(userId); diff --git a/src/main/java/org/winey/server/service/auth/kakao/KakaoSignInService.java b/src/main/java/org/winey/server/service/auth/kakao/KakaoSignInService.java index dff4732..cce616e 100644 --- a/src/main/java/org/winey/server/service/auth/kakao/KakaoSignInService.java +++ b/src/main/java/org/winey/server/service/auth/kakao/KakaoSignInService.java @@ -2,8 +2,8 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.google.gson.JsonArray; +import java.util.Map; import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; @@ -11,9 +11,6 @@ import org.springframework.stereotype.Service; import org.springframework.web.client.RestTemplate; -import java.util.Map; - -@Slf4j @Service @RequiredArgsConstructor public class KakaoSignInService { @@ -26,9 +23,7 @@ public String getKaKaoId(String accessToken) { headers.add("Authorization","Bearer "+ accessToken); HttpEntity httpEntity = new HttpEntity<>(headers); ResponseEntity responseData; - log.info("before kakao post"); responseData = restTemplate.postForEntity(KAKAO_URL,httpEntity,Object.class); - log.info("after kakao post"); ObjectMapper objectMapper = new ObjectMapper(); return objectMapper.convertValue(responseData.getBody(), Map.class).get("id").toString(); //소셜 id만 가져오는듯. } From 02f886401b42a62d019351678d465c6eb27df376 Mon Sep 17 00:00:00 2001 From: soohyun Date: Thu, 6 Feb 2025 16:06:19 +0900 Subject: [PATCH 73/78] =?UTF-8?q?hotfix:=20ssh=ED=84=B0=EB=84=90=EB=A7=81?= =?UTF-8?q?=20=EA=B5=AC=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 8 +- .../config/ssh/SshDataSourceConfig.java | 37 +++++++++ .../config/ssh/SshTunnelingInitializer.java | 83 +++++++++++++++++++ 3 files changed, 126 insertions(+), 2 deletions(-) create mode 100644 src/main/java/org/winey/server/config/ssh/SshDataSourceConfig.java create mode 100644 src/main/java/org/winey/server/config/ssh/SshTunnelingInitializer.java diff --git a/build.gradle b/build.gradle index fe72069..8885425 100644 --- a/build.gradle +++ b/build.gradle @@ -33,8 +33,8 @@ dependencies { // JPA & Database implementation 'org.springframework.boot:spring-boot-starter-data-jpa' - implementation 'mysql:mysql-connector-java:8.0.32' - +// implementation 'mysql:mysql-connector-java:8.0.32' + runtimeOnly 'com.mysql:mysql-connector-j' // S3 AWS implementation group: 'org.springframework.cloud', name: 'spring-cloud-starter-aws', version: '2.2.6.RELEASE' @@ -67,6 +67,10 @@ dependencies { // ShedLock implementation 'net.javacrumbs.shedlock:shedlock-spring:4.14.0' implementation 'net.javacrumbs.shedlock:shedlock-provider-jdbc-template:4.14.0' + + // ssh + implementation 'com.github.mwiede:jsch:0.2.17' + annotationProcessor "org.springframework.boot:spring-boot-configuration-processor" } tasks.named('test') { diff --git a/src/main/java/org/winey/server/config/ssh/SshDataSourceConfig.java b/src/main/java/org/winey/server/config/ssh/SshDataSourceConfig.java new file mode 100644 index 0000000..00e6be7 --- /dev/null +++ b/src/main/java/org/winey/server/config/ssh/SshDataSourceConfig.java @@ -0,0 +1,37 @@ +package org.winey.server.config.ssh; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties; +import org.springframework.boot.jdbc.DataSourceBuilder; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; +import org.springframework.context.annotation.Profile; + +import javax.sql.DataSource; + +@Slf4j +@Profile("local") +@Configuration +@RequiredArgsConstructor +public class SshDataSourceConfig { + + private final SshTunnelingInitializer initializer; + + @Bean("dataSource") + @Primary + public DataSource dataSource(DataSourceProperties properties) { + + Integer forwardedPort = initializer.buildSshConnection(); // ssh 연결 및 터널링 설정 + String url = properties.getUrl().replace("[forwardedPort]", Integer.toString(forwardedPort)); + log.info(url); + return DataSourceBuilder.create() + .url(url) + .username(properties.getUsername()) + .password(properties.getPassword()) + .driverClassName(properties.getDriverClassName()) + .build(); + } + +} diff --git a/src/main/java/org/winey/server/config/ssh/SshTunnelingInitializer.java b/src/main/java/org/winey/server/config/ssh/SshTunnelingInitializer.java new file mode 100644 index 0000000..2427e4a --- /dev/null +++ b/src/main/java/org/winey/server/config/ssh/SshTunnelingInitializer.java @@ -0,0 +1,83 @@ +package org.winey.server.config.ssh; + +import com.jcraft.jsch.JSch; +import com.jcraft.jsch.Session; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Profile; +import org.springframework.stereotype.Component; +import org.springframework.validation.annotation.Validated; + +import javax.annotation.PreDestroy; +import javax.validation.constraints.NotNull; +import java.util.Properties; + +import static java.lang.System.exit; + +@Slf4j +@Profile("local") +@Component +@ConfigurationProperties(prefix = "ssh") +@Validated +@Setter +public class SshTunnelingInitializer { + + @NotNull + private String remoteJumpHost; + @NotNull + private String user; + @NotNull + private int sshPort; + @NotNull + private String privateKey; + @NotNull + private String databaseUrl; + @NotNull + private int databasePort; + + private Session session; + + @PreDestroy + public void closeSSH() { + if (session.isConnected()) + session.disconnect(); + } + + public Integer buildSshConnection() { + + Integer forwardedPort = null; + + try { + log.info("{}@{}:{}:{} with privateKey",user, remoteJumpHost, sshPort, databasePort); + + log.info("start ssh tunneling.."); + JSch jSch = new JSch(); + + log.info("creating ssh session"); + jSch.addIdentity(privateKey); // 개인키 + session = jSch.getSession(user, remoteJumpHost, sshPort); // 세션 설정 + Properties config = new Properties(); + config.put("StrictHostKeyChecking", "no"); + session.setConfig(config); + log.info("complete creating ssh session"); + + log.info("start connecting ssh connection"); + session.connect(); // ssh 연결 + log.info("success connecting ssh connection "); + + // 로컬pc의 남는 포트 하나와 원격 접속한 pc의 db포트 연결 + log.info("start forwarding"); + forwardedPort = session.setPortForwardingL(0, databaseUrl, databasePort); + log.info("successfully connected to database"); + + } catch (Exception e){ + log.error("fail to make ssh tunneling"); + this.closeSSH(); + e.printStackTrace(); + exit(1); + } + + return forwardedPort; + } +} From cf47b9ab6a368d5c826a4b05091ffdaa0aa3dabd Mon Sep 17 00:00:00 2001 From: soohyun Date: Thu, 6 Feb 2025 16:18:10 +0900 Subject: [PATCH 74/78] =?UTF-8?q?hotfix:=20profile=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/org/winey/server/config/ssh/SshDataSourceConfig.java | 2 +- .../org/winey/server/config/ssh/SshTunnelingInitializer.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/winey/server/config/ssh/SshDataSourceConfig.java b/src/main/java/org/winey/server/config/ssh/SshDataSourceConfig.java index 00e6be7..5ede4c5 100644 --- a/src/main/java/org/winey/server/config/ssh/SshDataSourceConfig.java +++ b/src/main/java/org/winey/server/config/ssh/SshDataSourceConfig.java @@ -12,7 +12,7 @@ import javax.sql.DataSource; @Slf4j -@Profile("local") +@Profile({"local","set1","set2"}) @Configuration @RequiredArgsConstructor public class SshDataSourceConfig { diff --git a/src/main/java/org/winey/server/config/ssh/SshTunnelingInitializer.java b/src/main/java/org/winey/server/config/ssh/SshTunnelingInitializer.java index 2427e4a..a739b6b 100644 --- a/src/main/java/org/winey/server/config/ssh/SshTunnelingInitializer.java +++ b/src/main/java/org/winey/server/config/ssh/SshTunnelingInitializer.java @@ -16,7 +16,7 @@ import static java.lang.System.exit; @Slf4j -@Profile("local") +@Profile({"local","set1","set2"}) @Component @ConfigurationProperties(prefix = "ssh") @Validated From e9ecd9fac33ca3c43b986a1b15246fa551f3e9a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=84=EC=84=A0=ED=9D=AC?= <88873302+funnysunny08@users.noreply.github.com> Date: Sun, 9 Feb 2025 18:05:20 +0900 Subject: [PATCH 75/78] Update cd.yml --- .github/workflows/cd.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index 6aee9b5..55f1b0b 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -84,8 +84,8 @@ jobs: - name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@v1 with: - aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY }} - aws-secret-access-key: ${{ secrets.AWS_SECRET_KEY }} + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_2025 }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_KEY_2025 }} aws-region: ap-northeast-2 - name: Upload to S3 From e4ce09745398f04b2c917386a7d834f8b6cc5ac1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=84=EC=84=A0=ED=9D=AC?= <88873302+funnysunny08@users.noreply.github.com> Date: Sun, 9 Feb 2025 19:43:59 +0900 Subject: [PATCH 76/78] Update cd.yml --- .github/workflows/cd.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index 55f1b0b..6aee9b5 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -84,8 +84,8 @@ jobs: - name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@v1 with: - aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_2025 }} - aws-secret-access-key: ${{ secrets.AWS_SECRET_KEY_2025 }} + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_KEY }} aws-region: ap-northeast-2 - name: Upload to S3 From aabaabf074f0e0e786c2405dd4ea08e325fddffc Mon Sep 17 00:00:00 2001 From: soohyun <49307946+sss4920@users.noreply.github.com> Date: Sun, 9 Feb 2025 20:17:04 +0900 Subject: [PATCH 77/78] 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 7fd3784..ebe3024 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -44,7 +44,7 @@ jobs: - name: make application.properties 파일 생성 run: | ## create application.yml - cd ./src/main/resources + mkdir -p ./src/main/resources # application.yml 파일 생성 touch ./application.yml From 80fe170b7ac238795680a447457a00c23e7991f2 Mon Sep 17 00:00:00 2001 From: soohyun <49307946+sss4920@users.noreply.github.com> Date: Sun, 9 Feb 2025 20:51:58 +0900 Subject: [PATCH 78/78] Update deploy.sh --- scripts/deploy.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/deploy.sh b/scripts/deploy.sh index 2b05e89..f2a3a52 100644 --- a/scripts/deploy.sh +++ b/scripts/deploy.sh @@ -48,9 +48,9 @@ fi echo "> $IDLE_PROFILE 배포" nohup java -jar -Duser.timezone=Asia/Seoul -Dspring.profiles.active=$IDLE_PROFILE $IDLE_APPLICATION_PATH >> /home/ubuntu/app/nohup.out 2>&1 & -echo "> $IDLE_PROFILE 10초 후 Health check 시작" +echo "> $IDLE_PROFILE 60초 후 Health check 시작" echo "> curl -s http://localhost:$IDLE_PORT/health " -sleep 10 +sleep 60 for retry_count in {1..10} do