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)