diff --git a/src/main/java/sopt/org/hmh/domain/app/domain/App.java b/src/main/java/sopt/org/hmh/domain/app/domain/App.java index 87212392..a4731b54 100644 --- a/src/main/java/sopt/org/hmh/domain/app/domain/App.java +++ b/src/main/java/sopt/org/hmh/domain/app/domain/App.java @@ -4,6 +4,7 @@ import jakarta.persistence.GenerationType; import jakarta.persistence.Id; import jakarta.persistence.MappedSuperclass; +import jakarta.validation.constraints.NotNull; import lombok.Getter; @Getter @@ -14,7 +15,9 @@ public abstract class App { @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; + @NotNull(message = "os는 null일 수 없습니다.") protected String os; + @NotNull(message = "appCode는 null일 수 없습니다.") protected String appCode; } \ No newline at end of file diff --git a/src/main/java/sopt/org/hmh/domain/app/domain/ChallengeApp.java b/src/main/java/sopt/org/hmh/domain/app/domain/ChallengeApp.java index 4e10f79e..3534a27c 100644 --- a/src/main/java/sopt/org/hmh/domain/app/domain/ChallengeApp.java +++ b/src/main/java/sopt/org/hmh/domain/app/domain/ChallengeApp.java @@ -1,6 +1,7 @@ package sopt.org.hmh.domain.app.domain; import jakarta.persistence.*; +import jakarta.validation.constraints.NotNull; import lombok.AccessLevel; import lombok.Builder; import lombok.Getter; @@ -16,6 +17,7 @@ public class ChallengeApp extends App { @JoinColumn(name = "challenge_id") private Challenge challenge; + @NotNull(message = "목표 시간은 null일 수 없습니다.") private Long goalTime; @Builder diff --git a/src/main/java/sopt/org/hmh/domain/app/domain/HistoryApp.java b/src/main/java/sopt/org/hmh/domain/app/domain/HistoryApp.java index 8425641d..addda272 100644 --- a/src/main/java/sopt/org/hmh/domain/app/domain/HistoryApp.java +++ b/src/main/java/sopt/org/hmh/domain/app/domain/HistoryApp.java @@ -1,6 +1,7 @@ package sopt.org.hmh.domain.app.domain; import jakarta.persistence.*; +import jakarta.validation.constraints.NotNull; import lombok.AccessLevel; import lombok.Builder; import lombok.Getter; @@ -16,7 +17,10 @@ public class HistoryApp extends App { @JoinColumn(name = "daily_challenge_id") private DailyChallenge dailyChallenge; + @NotNull(message = "사용 시간은 null일 수 없습니다.") private Long usageTime; + + @NotNull(message = "목표 시간은 null일 수 없습니다.") private Long goalTime; @Builder diff --git a/src/main/java/sopt/org/hmh/domain/app/dto/request/ChallengeAppRequest.java b/src/main/java/sopt/org/hmh/domain/app/dto/request/ChallengeAppRequest.java index 3d8d5f3f..20732ecd 100644 --- a/src/main/java/sopt/org/hmh/domain/app/dto/request/ChallengeAppRequest.java +++ b/src/main/java/sopt/org/hmh/domain/app/dto/request/ChallengeAppRequest.java @@ -2,8 +2,10 @@ import jakarta.validation.constraints.NotNull; import sopt.org.hmh.domain.app.domain.AppConstants; +import sopt.org.hmh.domain.app.domain.ChallengeApp; import sopt.org.hmh.domain.app.domain.exception.AppError; import sopt.org.hmh.domain.app.domain.exception.AppException; +import sopt.org.hmh.domain.challenge.domain.Challenge; public record ChallengeAppRequest( @NotNull(message = "앱 코드는 null일 수 없습니다.") @@ -16,4 +18,13 @@ public record ChallengeAppRequest( throw new AppException(AppError.INVALID_GOAL_TIME); } } + + public ChallengeApp toEntity(Challenge challenge , String os) { + return ChallengeApp.builder() + .challenge(challenge) + .appCode(appCode) + .goalTime(goalTime) + .os(os) + .build(); + } } diff --git a/src/main/java/sopt/org/hmh/domain/app/service/ChallengeAppService.java b/src/main/java/sopt/org/hmh/domain/app/service/ChallengeAppService.java new file mode 100644 index 00000000..366272f4 --- /dev/null +++ b/src/main/java/sopt/org/hmh/domain/app/service/ChallengeAppService.java @@ -0,0 +1,40 @@ +package sopt.org.hmh.domain.app.service; + +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import sopt.org.hmh.domain.app.domain.ChallengeApp; +import sopt.org.hmh.domain.app.domain.exception.AppError; +import sopt.org.hmh.domain.app.domain.exception.AppException; +import sopt.org.hmh.domain.app.dto.request.ChallengeAppRequest; +import sopt.org.hmh.domain.app.repository.ChallengeAppRepository; +import sopt.org.hmh.domain.challenge.domain.Challenge; + +@Service +@RequiredArgsConstructor +public class ChallengeAppService { + + private final ChallengeAppRepository challengeAppRepository; + + public void removeApp(Challenge challenge, String appcode, String os) { + ChallengeApp appToRemove = + challengeAppRepository.findFirstByChallengeIdAndAppCodeAndOsOrElseThrow(challenge.getId(), appcode, os); + challengeAppRepository.delete(appToRemove); + } + + public void addApps(Challenge challenge, List requests, String os) { + challengeAppRepository.saveAll( + requests.stream().map( + request -> { + validateAppExist(challenge.getId(), request.appCode(), os); + return request.toEntity(challenge, os); + }).toList()); + } + + private void validateAppExist(Long challengeId, String appCode, String os) { + if (challengeAppRepository.existsByChallengeIdAndAppCodeAndOs(challengeId, appCode, os)) { + throw new AppException(AppError.APP_EXIST_ALREADY); + } + } + +} diff --git a/src/main/java/sopt/org/hmh/domain/app/service/AppService.java b/src/main/java/sopt/org/hmh/domain/app/service/HistoryAppService.java similarity index 85% rename from src/main/java/sopt/org/hmh/domain/app/service/AppService.java rename to src/main/java/sopt/org/hmh/domain/app/service/HistoryAppService.java index e6e24b93..c4a79d9a 100644 --- a/src/main/java/sopt/org/hmh/domain/app/service/AppService.java +++ b/src/main/java/sopt/org/hmh/domain/app/service/HistoryAppService.java @@ -3,7 +3,6 @@ import java.util.List; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; import sopt.org.hmh.domain.app.domain.ChallengeApp; import sopt.org.hmh.domain.app.domain.HistoryApp; import sopt.org.hmh.domain.app.domain.exception.AppError; @@ -14,13 +13,13 @@ @Service @RequiredArgsConstructor -@Transactional -public class AppService { +public class HistoryAppService { private final HistoryAppRepository historyAppRepository; - public void addAppForHistory(List currentChallengeApps, List apps, - DailyChallenge dailyChallenge, String os) { + public void addHistoryApp( + List currentChallengeApps, List apps, + DailyChallenge dailyChallenge, String os) { historyAppRepository.saveAll(supplementAdditionalInfo(currentChallengeApps, apps, dailyChallenge, os)); } diff --git a/src/main/java/sopt/org/hmh/domain/auth/controller/AuthController.java b/src/main/java/sopt/org/hmh/domain/auth/controller/AuthController.java index 792bdbc9..29eacd95 100644 --- a/src/main/java/sopt/org/hmh/domain/auth/controller/AuthController.java +++ b/src/main/java/sopt/org/hmh/domain/auth/controller/AuthController.java @@ -13,7 +13,7 @@ import sopt.org.hmh.domain.auth.exception.AuthSuccess; import sopt.org.hmh.domain.auth.dto.request.SocialPlatformRequest; import sopt.org.hmh.domain.auth.dto.request.SocialSignUpRequest; -import sopt.org.hmh.domain.auth.service.AuthService; +import sopt.org.hmh.domain.auth.service.AuthFacade; import sopt.org.hmh.global.auth.social.SocialAccessTokenResponse; import sopt.org.hmh.global.common.response.BaseResponse; @@ -22,7 +22,7 @@ @RequestMapping("/api/v1/user") public class AuthController implements AuthApi { - private final AuthService authService; + private final AuthFacade authFacade; @PostMapping("/login") @Override @@ -32,7 +32,7 @@ public ResponseEntity> orderLogin( ) { return ResponseEntity .status(AuthSuccess.LOGIN_SUCCESS.getHttpStatus()) - .body(BaseResponse.success(AuthSuccess.LOGIN_SUCCESS, authService.login(socialAccessToken, request))); + .body(BaseResponse.success(AuthSuccess.LOGIN_SUCCESS, authFacade.login(socialAccessToken, request))); } @PostMapping("/signup") @@ -44,7 +44,7 @@ public ResponseEntity> orderSignup( ) { return ResponseEntity .status(AuthSuccess.SIGNUP_SUCCESS.getHttpStatus()) - .body(BaseResponse.success(AuthSuccess.SIGNUP_SUCCESS, authService.signup(socialAccessToken, request, os))); + .body(BaseResponse.success(AuthSuccess.SIGNUP_SUCCESS, authFacade.signup(socialAccessToken, request, os))); } @PostMapping("/reissue") @@ -54,7 +54,7 @@ public ResponseEntity> orderReissue( ) { return ResponseEntity .status(AuthSuccess.REISSUE_SUCCESS.getHttpStatus()) - .body(BaseResponse.success(AuthSuccess.REISSUE_SUCCESS, authService.reissueToken(refreshToken))); + .body(BaseResponse.success(AuthSuccess.REISSUE_SUCCESS, authFacade.reissueToken(refreshToken))); } @GetMapping("/social/token/kakao") @@ -63,6 +63,6 @@ public ResponseEntity> orderGetKakaoAcce ) { return ResponseEntity .status(AuthSuccess.GET_SOCIAL_ACCESS_TOKEN_SUCCESS.getHttpStatus()) - .body(BaseResponse.success(AuthSuccess.GET_SOCIAL_ACCESS_TOKEN_SUCCESS, authService.getSocialAccessTokenByAuthorizationCode(code))); + .body(BaseResponse.success(AuthSuccess.GET_SOCIAL_ACCESS_TOKEN_SUCCESS, authFacade.getSocialAccessTokenByAuthorizationCode(code))); } } \ No newline at end of file diff --git a/src/main/java/sopt/org/hmh/domain/auth/dto/request/SocialSignUpRequest.java b/src/main/java/sopt/org/hmh/domain/auth/dto/request/SocialSignUpRequest.java index 166c194d..dc399a22 100644 --- a/src/main/java/sopt/org/hmh/domain/auth/dto/request/SocialSignUpRequest.java +++ b/src/main/java/sopt/org/hmh/domain/auth/dto/request/SocialSignUpRequest.java @@ -3,8 +3,11 @@ import com.fasterxml.jackson.annotation.JsonProperty; import jakarta.validation.Valid; import jakarta.validation.constraints.NotNull; +import java.util.List; import sopt.org.hmh.domain.challenge.dto.request.ChallengeRequest; import sopt.org.hmh.domain.challenge.dto.request.ChallengeSignUpRequest; +import sopt.org.hmh.domain.user.domain.OnboardingInfo; +import sopt.org.hmh.domain.user.domain.OnboardingProblem; import sopt.org.hmh.global.auth.social.SocialPlatform; public record SocialSignUpRequest( @@ -17,7 +20,24 @@ public record SocialSignUpRequest( @JsonProperty(value = "challenge") ChallengeSignUpRequest challengeSignUpRequest ) { - public ChallengeRequest toChallengeRequest() { - return new ChallengeRequest(challengeSignUpRequest.period(), challengeSignUpRequest.goalTime()); - } + + public ChallengeRequest toChallengeRequest() { + return new ChallengeRequest(challengeSignUpRequest.period(), challengeSignUpRequest.goalTime()); + } + + public OnboardingInfo toOnboardingInfo(Long userId) { + return OnboardingInfo.builder() + .averageUseTime(onboardingRequest.averageUseTime()) + .userId(userId) + .build(); + } + + public List toProblemList(Long onboardingInfoId) { + return onboardingRequest.problemList().stream() + .map(problem -> OnboardingProblem.builder() + .onboardingInfoId(onboardingInfoId) + .problem(problem) + .build() + ).toList(); + } } \ No newline at end of file diff --git a/src/main/java/sopt/org/hmh/domain/auth/dto/response/LoginResponse.java b/src/main/java/sopt/org/hmh/domain/auth/dto/response/LoginResponse.java index e3049c57..e5c16e3c 100644 --- a/src/main/java/sopt/org/hmh/domain/auth/dto/response/LoginResponse.java +++ b/src/main/java/sopt/org/hmh/domain/auth/dto/response/LoginResponse.java @@ -1,7 +1,6 @@ package sopt.org.hmh.domain.auth.dto.response; import com.fasterxml.jackson.annotation.JsonProperty; -import sopt.org.hmh.domain.user.domain.User; import sopt.org.hmh.global.auth.jwt.TokenResponse; public record LoginResponse( @@ -9,12 +8,4 @@ public record LoginResponse( @JsonProperty(value = "token") TokenResponse tokenResponse ){ - - public static LoginResponse of(User loginUser, TokenResponse tokenResponse) { - return new LoginResponse( - loginUser.getId(), - tokenResponse - ); - } - } \ No newline at end of file diff --git a/src/main/java/sopt/org/hmh/domain/auth/service/AuthService.java b/src/main/java/sopt/org/hmh/domain/auth/service/AuthFacade.java similarity index 76% rename from src/main/java/sopt/org/hmh/domain/auth/service/AuthService.java rename to src/main/java/sopt/org/hmh/domain/auth/service/AuthFacade.java index a1bc731f..fbc2725a 100644 --- a/src/main/java/sopt/org/hmh/domain/auth/service/AuthService.java +++ b/src/main/java/sopt/org/hmh/domain/auth/service/AuthFacade.java @@ -3,9 +3,10 @@ import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import sopt.org.hmh.domain.app.service.ChallengeAppService; import sopt.org.hmh.domain.auth.dto.response.ReissueResponse; import sopt.org.hmh.domain.challenge.domain.Challenge; -import sopt.org.hmh.domain.challenge.service.ChallengeService; +import sopt.org.hmh.domain.challenge.service.ChallengeFacade; import sopt.org.hmh.domain.user.domain.User; import sopt.org.hmh.domain.auth.dto.request.SocialPlatformRequest; import sopt.org.hmh.domain.auth.dto.request.SocialSignUpRequest; @@ -21,22 +22,19 @@ @Service @RequiredArgsConstructor -@Transactional(readOnly = true) -public class AuthService { +public class AuthFacade { private final KakaoLoginService kakaoLoginService; private final AppleOAuthProvider appleOAuthProvider; - - private final ChallengeService challengeService; + private final ChallengeFacade challengeFacade; + private final ChallengeAppService challengeAppService; private final TokenService tokenService; private final UserService userService; - @Transactional + @Transactional(readOnly = true) public LoginResponse login(String socialAccessToken, SocialPlatformRequest request) { - SocialPlatform socialPlatform = request.socialPlatform(); - String socialId = getSocialIdBySocialAccessToken(socialPlatform, socialAccessToken); - + String socialId = this.getSocialIdBySocialAccessToken(socialPlatform, socialAccessToken); User loginUser = userService.getUserBySocialPlatformAndSocialId(socialPlatform, socialId); return performLogin(socialAccessToken, socialPlatform, loginUser); @@ -44,18 +42,16 @@ public LoginResponse login(String socialAccessToken, SocialPlatformRequest reque @Transactional public LoginResponse signup(String socialAccessToken, SocialSignUpRequest request, String os) { - SocialPlatform socialPlatform = request.socialPlatform(); - String socialId = getSocialIdBySocialAccessToken(socialPlatform, socialAccessToken); + String socialId = this.getSocialIdBySocialAccessToken(socialPlatform, socialAccessToken); userService.validateDuplicateUser(socialId, socialPlatform); - User user = userService.addUser(socialPlatform, socialId, request.name()); + Long userId = user.getId(); + userService.registerOnboardingInfo(request, userId); - Challenge challenge = challengeService.addChallenge(user.getId(), request.toChallengeRequest() , os); - challengeService.addApps(challenge, request.challengeSignUpRequest().apps(), os); - - userService.registerOnboardingInfo(request); + Challenge challenge = challengeFacade.addChallenge(userId, request.toChallengeRequest() , os); + challengeAppService.addApps(challenge, request.challengeSignUpRequest().apps(), os); return performLogin(socialAccessToken, socialPlatform, user); } @@ -72,16 +68,15 @@ public ReissueResponse reissueToken(String refreshToken) { return tokenService.reissueToken(refreshToken); } - private LoginResponse performLogin(String socialAccessToken, SocialPlatform socialPlatform, User loginUser) { if (socialPlatform == SocialPlatform.KAKAO) { kakaoLoginService.updateUserInfoByKakao(loginUser, socialAccessToken); } - return LoginResponse.of(loginUser, tokenService.issueToken(loginUser.getId())); + Long userId = loginUser.getId(); + return new LoginResponse(userId, tokenService.issueToken(userId)); } public SocialAccessTokenResponse getSocialAccessTokenByAuthorizationCode(String code) { return kakaoLoginService.getKakaoAccessToken(code); } - } \ No newline at end of file diff --git a/src/main/java/sopt/org/hmh/domain/challenge/controller/ChallengeController.java b/src/main/java/sopt/org/hmh/domain/challenge/controller/ChallengeController.java index 1c8775c9..6dd9f363 100644 --- a/src/main/java/sopt/org/hmh/domain/challenge/controller/ChallengeController.java +++ b/src/main/java/sopt/org/hmh/domain/challenge/controller/ChallengeController.java @@ -7,12 +7,11 @@ import sopt.org.hmh.domain.app.domain.exception.AppSuccess; import sopt.org.hmh.domain.app.dto.request.ChallengeAppArrayRequest; import sopt.org.hmh.domain.app.dto.request.AppRemoveRequest; -import sopt.org.hmh.domain.challenge.domain.Challenge; import sopt.org.hmh.domain.challenge.domain.exception.ChallengeSuccess; import sopt.org.hmh.domain.challenge.dto.request.ChallengeRequest; import sopt.org.hmh.domain.challenge.dto.response.ChallengeResponse; import sopt.org.hmh.domain.challenge.dto.response.DailyChallengeResponse; -import sopt.org.hmh.domain.challenge.service.ChallengeService; +import sopt.org.hmh.domain.challenge.service.ChallengeFacade; import sopt.org.hmh.global.auth.UserId; import sopt.org.hmh.global.common.response.BaseResponse; import sopt.org.hmh.global.common.response.EmptyJsonResponse; @@ -22,14 +21,14 @@ @RequestMapping("/api/v1/challenge") public class ChallengeController implements ChallengeApi { - private final ChallengeService challengeService; + private final ChallengeFacade challengeFacade; @PostMapping @Override public ResponseEntity> orderAddChallenge(@UserId final Long userId, @RequestHeader("OS") final String os, @RequestBody @Valid final ChallengeRequest request) { - challengeService.addChallenge(userId, request, os); + challengeFacade.addChallenge(userId, request, os); return ResponseEntity .status(ChallengeSuccess.ADD_CHALLENGE_SUCCESS.getHttpStatus()) @@ -43,7 +42,7 @@ public ResponseEntity> orderGetChallenge(@UserId return ResponseEntity .status(ChallengeSuccess.GET_CHALLENGE_SUCCESS.getHttpStatus()) .body(BaseResponse.success(ChallengeSuccess.GET_CHALLENGE_SUCCESS, - challengeService.getChallenge(userId))); + challengeFacade.getCurrentChallengeInfo(userId))); } @GetMapping("/home") @@ -53,7 +52,7 @@ public ResponseEntity> orderGetDailyChallen return ResponseEntity .status(ChallengeSuccess.GET_DAILY_CHALLENGE_SUCCESS.getHttpStatus()) .body(BaseResponse.success(ChallengeSuccess.GET_DAILY_CHALLENGE_SUCCESS, - challengeService.getDailyChallenge(userId))); + challengeFacade.getDailyChallengeInfo(userId))); } @PostMapping("/app") @@ -61,8 +60,7 @@ public ResponseEntity> orderGetDailyChallen public ResponseEntity> orderAddApps(@UserId final Long userId, @RequestHeader("OS") final String os, @RequestBody @Valid final ChallengeAppArrayRequest requests) { - Challenge challenge = challengeService.findCurrentChallengeByUserId(userId); - challengeService.addApps(challenge, requests.apps(), os); + challengeFacade.addAppsToCurrentChallenge(userId, requests.apps(), os); return ResponseEntity .status(AppSuccess.ADD_APP_SUCCESS.getHttpStatus()) @@ -75,8 +73,7 @@ public ResponseEntity> orderAddApps(@UserId final Long userId, public ResponseEntity> orderRemoveApp(@UserId final Long userId, @RequestHeader("OS") final String os, @RequestBody @Valid final AppRemoveRequest request) { - Challenge challenge = challengeService.findCurrentChallengeByUserId(userId); - challengeService.removeApp(challenge, request, os); + challengeFacade.removeAppFromCurrentChallenge(userId, request.appCode(), os); return ResponseEntity .status(AppSuccess.REMOVE_APP_SUCCESS.getHttpStatus()) diff --git a/src/main/java/sopt/org/hmh/domain/challenge/domain/Challenge.java b/src/main/java/sopt/org/hmh/domain/challenge/domain/Challenge.java index a4ab2e9e..a33a1f61 100644 --- a/src/main/java/sopt/org/hmh/domain/challenge/domain/Challenge.java +++ b/src/main/java/sopt/org/hmh/domain/challenge/domain/Challenge.java @@ -3,6 +3,7 @@ import static jakarta.persistence.GenerationType.*; import jakarta.persistence.*; +import jakarta.validation.constraints.NotNull; import lombok.AccessLevel; import lombok.Builder; import lombok.Getter; @@ -22,15 +23,20 @@ public class Challenge extends BaseTimeEntity { @GeneratedValue(strategy = IDENTITY) private Long id; + @NotNull(message = "유저 아이디는 null일 수 없습니다.") private Long userId; + + @NotNull(message = "챌린지 기간은 null일 수 없습니다.") private Integer period; + + @NotNull(message = "목표 시간은 null일 수 없습니다.") private Long goalTime; @OneToMany(mappedBy = "challenge", cascade = CascadeType.REMOVE, orphanRemoval = true) - private List apps = new ArrayList<>(); + private List apps; @OneToMany(mappedBy = "challenge", cascade = CascadeType.REMOVE, orphanRemoval = true) - private final List historyDailyChallenges = new ArrayList<>(); + private List historyDailyChallenges; @Builder private Challenge(Integer period, Long userId, Long goalTime, List apps) { diff --git a/src/main/java/sopt/org/hmh/domain/challenge/dto/request/ChallengeRequest.java b/src/main/java/sopt/org/hmh/domain/challenge/dto/request/ChallengeRequest.java index 7f32031f..c2d60485 100644 --- a/src/main/java/sopt/org/hmh/domain/challenge/dto/request/ChallengeRequest.java +++ b/src/main/java/sopt/org/hmh/domain/challenge/dto/request/ChallengeRequest.java @@ -1,9 +1,7 @@ package sopt.org.hmh.domain.challenge.dto.request; import jakarta.validation.constraints.NotNull; -import sopt.org.hmh.domain.app.domain.AppConstants; -import sopt.org.hmh.domain.app.domain.exception.AppError; -import sopt.org.hmh.domain.app.domain.exception.AppException; +import sopt.org.hmh.domain.challenge.domain.Challenge; import sopt.org.hmh.domain.challenge.domain.ChallengeConstants; import sopt.org.hmh.domain.challenge.domain.exception.ChallengeError; import sopt.org.hmh.domain.challenge.domain.exception.ChallengeException; @@ -23,4 +21,12 @@ public record ChallengeRequest( throw new ChallengeException(ChallengeError.INVALID_GOAL_TIME); } } + + public Challenge toEntity(Long userId) { + return Challenge.builder() + .userId(userId) + .period(period) + .goalTime(goalTime) + .build(); + } } diff --git a/src/main/java/sopt/org/hmh/domain/challenge/service/ChallengeFacade.java b/src/main/java/sopt/org/hmh/domain/challenge/service/ChallengeFacade.java new file mode 100644 index 00000000..f49bbbc7 --- /dev/null +++ b/src/main/java/sopt/org/hmh/domain/challenge/service/ChallengeFacade.java @@ -0,0 +1,108 @@ +package sopt.org.hmh.domain.challenge.service; + +import java.util.Optional; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import sopt.org.hmh.domain.app.dto.request.ChallengeAppRequest; +import sopt.org.hmh.domain.app.dto.response.ChallengeAppResponse; +import sopt.org.hmh.domain.app.service.ChallengeAppService; +import sopt.org.hmh.domain.challenge.domain.Challenge; +import sopt.org.hmh.domain.challenge.dto.request.ChallengeRequest; +import sopt.org.hmh.domain.challenge.dto.response.ChallengeResponse; +import sopt.org.hmh.domain.challenge.dto.response.DailyChallengeResponse; +import sopt.org.hmh.domain.dailychallenge.domain.DailyChallenge; +import sopt.org.hmh.domain.dailychallenge.service.DailyChallengeService; +import sopt.org.hmh.domain.user.domain.User; +import sopt.org.hmh.domain.user.service.UserService; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.temporal.ChronoUnit; +import java.util.List; + +@Service +@RequiredArgsConstructor +public class ChallengeFacade { + + private final ChallengeService challengeService; + private final DailyChallengeService dailyChallengeService; + private final UserService userService; + private final ChallengeAppService challengeAppService; + + @Transactional + public Challenge addChallenge(Long userId, ChallengeRequest challengeRequest, String os) { + User user = userService.findByIdOrThrowException(userId); + Optional previousChallengeId = Optional.of(user.getCurrentChallengeId()); + + Challenge challenge = challengeService.save(challengeRequest.toEntity(userId)); + user.changeCurrentChallengeId(challenge.getId()); + + LocalDate startDate = challenge.getCreatedAt().toLocalDate(); + dailyChallengeService.addDailyChallenge(userId, startDate, challenge); + this.addAppsIfPreviousChallengeExist(os, previousChallengeId, challenge); + + return challenge; + } + + private void addAppsIfPreviousChallengeExist(String os, Optional previousChallengeId, Challenge challenge) { + if (previousChallengeId.isPresent()) { + Challenge previousChallenge = challengeService.findByIdOrElseThrow(previousChallengeId.get()); + List previousApps = previousChallenge.getApps().stream() + .map(app -> new ChallengeAppRequest(app.getAppCode(), app.getGoalTime())) + .toList(); + challengeAppService.addApps(challenge, previousApps, os); + } + } + + @Transactional(readOnly = true) + public ChallengeResponse getCurrentChallengeInfo(Long userId) { + Challenge challenge = this.findCurrentChallengeByUserId(userId); + + return ChallengeResponse.builder() + .period(challenge.getPeriod()) + .statuses(challenge.getHistoryDailyChallenges() + .stream() + .map(DailyChallenge::getStatus) + .toList()) + .todayIndex(this.calculateTodayIndex(challenge.getCreatedAt(), challenge.getPeriod())) + .startDate(challenge.getCreatedAt().toLocalDate().toString()) + .goalTime(challenge.getGoalTime()) + .apps(challenge.getApps().stream() + .map(app -> new ChallengeAppResponse(app.getAppCode(), app.getGoalTime())).toList()) + .build(); + } + + @Transactional(readOnly = true) + public DailyChallengeResponse getDailyChallengeInfo(Long userId) { + Challenge challenge = this.findCurrentChallengeByUserId(userId); + + return DailyChallengeResponse.builder() + .goalTime(challenge.getGoalTime()) + .apps(challenge.getApps().stream() + .map(app -> new ChallengeAppResponse(app.getAppCode(), app.getGoalTime())).toList()) + .build(); + } + + public Challenge findCurrentChallengeByUserId(Long userId) { + User user = userService.findByIdOrThrowException(userId); + return challengeService.findByIdOrElseThrow(user.getCurrentChallengeId()); + } + + private Integer calculateTodayIndex(LocalDateTime challengeCreateAt, int period) { + int daysBetween = (int) ChronoUnit.DAYS.between(challengeCreateAt.toLocalDate(), LocalDate.now()); + return (daysBetween >= period) ? -1 : daysBetween; + } + + @Transactional + public void addAppsToCurrentChallenge(Long userId, List requests, String os) { + Challenge challenge = this.findCurrentChallengeByUserId(userId); + challengeAppService.addApps(challenge, requests, os); + } + + @Transactional + public void removeAppFromCurrentChallenge(Long userId, String appCode, String os) { + Challenge challenge = this.findCurrentChallengeByUserId(userId); + challengeAppService.removeApp(challenge, appCode, os); + } +} \ No newline at end of file diff --git a/src/main/java/sopt/org/hmh/domain/challenge/service/ChallengeService.java b/src/main/java/sopt/org/hmh/domain/challenge/service/ChallengeService.java index a745bfd4..569a4684 100644 --- a/src/main/java/sopt/org/hmh/domain/challenge/service/ChallengeService.java +++ b/src/main/java/sopt/org/hmh/domain/challenge/service/ChallengeService.java @@ -1,157 +1,34 @@ package sopt.org.hmh.domain.challenge.service; +import java.util.List; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; import sopt.org.hmh.domain.app.domain.ChallengeApp; -import sopt.org.hmh.domain.app.domain.exception.AppError; -import sopt.org.hmh.domain.app.domain.exception.AppException; -import sopt.org.hmh.domain.app.dto.request.AppRemoveRequest; -import sopt.org.hmh.domain.app.dto.request.ChallengeAppRequest; -import sopt.org.hmh.domain.app.dto.response.ChallengeAppResponse; -import sopt.org.hmh.domain.app.repository.ChallengeAppRepository; import sopt.org.hmh.domain.challenge.domain.Challenge; import sopt.org.hmh.domain.challenge.domain.exception.ChallengeError; import sopt.org.hmh.domain.challenge.domain.exception.ChallengeException; -import sopt.org.hmh.domain.challenge.dto.request.ChallengeRequest; -import sopt.org.hmh.domain.challenge.dto.response.ChallengeResponse; -import sopt.org.hmh.domain.challenge.dto.response.DailyChallengeResponse; import sopt.org.hmh.domain.challenge.repository.ChallengeRepository; -import sopt.org.hmh.domain.dailychallenge.domain.DailyChallenge; -import sopt.org.hmh.domain.dailychallenge.repository.DailyChallengeRepository; -import sopt.org.hmh.domain.user.domain.User; -import sopt.org.hmh.domain.user.service.UserService; - -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.time.temporal.ChronoUnit; -import java.util.ArrayList; -import java.util.List; @Service @RequiredArgsConstructor public class ChallengeService { private final ChallengeRepository challengeRepository; - private final ChallengeAppRepository challengeAppRepository; - private final DailyChallengeRepository dailyChallengeRepository; - private final UserService userService; - - @Transactional - public Challenge addChallenge(Long userId, ChallengeRequest challengeRequest, String os) { - Integer period = challengeRequest.period(); - Long goalTime = challengeRequest.goalTime(); - - Challenge challenge = challengeRepository.save(Challenge.builder() - .userId(userId) - .period(period) - .goalTime(goalTime) - .build()); - - User user = userService.findByIdOrThrowException(userId); - Long previousChallengeId = user.getCurrentChallengeId(); - if (previousChallengeId != null) { - Challenge previousChallenge = findByIdOrElseThrow(previousChallengeId); - List previousApps = previousChallenge.getApps().stream() - .map(app -> new ChallengeAppRequest(app.getAppCode(), app.getGoalTime())) - .toList(); - addApps(challenge, previousApps, os); - } - - List dailyChallenges = new ArrayList<>(); - LocalDate startDate = challenge.getCreatedAt().toLocalDate(); - for (int dayCount = 0; dayCount < period; dayCount++) { - DailyChallenge dailyChallenge = DailyChallenge.builder() - .challengeDate(startDate.plusDays(dayCount)) - .challenge(challenge) - .userId(userId) - .goalTime(goalTime).build(); - dailyChallenges.add(dailyChallenge); - } - dailyChallengeRepository.saveAll(dailyChallenges); - - user.changeCurrentChallengeId(challenge.getId()); - - return challenge; - } - - public ChallengeResponse getChallenge(Long userId) { - Challenge challenge = findCurrentChallengeByUserId(userId); - Integer todayIndex = calculateTodayIndex(challenge.getCreatedAt(), challenge.getPeriod()); - - return ChallengeResponse.builder() - .period(challenge.getPeriod()) - .statuses(challenge.getHistoryDailyChallenges() - .stream() - .map(DailyChallenge::getStatus) - .toList()) - .todayIndex(todayIndex) - .startDate(challenge.getCreatedAt().toLocalDate().toString()) - .goalTime(challenge.getGoalTime()) - .apps(challenge.getApps().stream() - .map(app -> new ChallengeAppResponse(app.getAppCode(), app.getGoalTime())).toList()) - .build(); - } - - public DailyChallengeResponse getDailyChallenge(Long userId) { - Challenge challenge = findCurrentChallengeByUserId(userId); - return DailyChallengeResponse.builder() - .goalTime(challenge.getGoalTime()) - .apps(challenge.getApps().stream() - .map(app -> new ChallengeAppResponse(app.getAppCode(), app.getGoalTime())).toList()) - .build(); - } - - @Transactional - public void removeApp(Challenge challenge, AppRemoveRequest request, String os) { - ChallengeApp appToRemove = challengeAppRepository - .findFirstByChallengeIdAndAppCodeAndOsOrElseThrow(challenge.getId(), request.appCode(), os); - challengeAppRepository.delete(appToRemove); - } - - @Transactional - public void addApps(Challenge challenge, List requests, String os) { - List appsToUpdate = requests.stream() - .map(request -> { - validateAppExist(challenge.getId(), request.appCode(), os); - return ChallengeApp.builder() - .challenge(challenge) - .appCode(request.appCode()) - .goalTime(request.goalTime()) - .os(os) - .build(); - }).toList(); - challengeAppRepository.saveAll(appsToUpdate); - } - - @Transactional public void deleteChallengeRelatedByUserId(List expiredUserIdList) { challengeRepository.deleteByUserIdIn(expiredUserIdList); } - private Integer calculateTodayIndex(LocalDateTime challengeCreateAt, int period) { - int daysBetween = (int) ChronoUnit.DAYS.between(challengeCreateAt.toLocalDate(), LocalDate.now()); - return (daysBetween >= period) ? -1 : daysBetween; - } - - private void validateAppExist(Long challengeId, String appCode, String os) { - if (challengeAppRepository.existsByChallengeIdAndAppCodeAndOs(challengeId, appCode, os)) { - throw new AppException(AppError.APP_EXIST_ALREADY); - } - } - public Challenge findByIdOrElseThrow(Long challengeId) { return challengeRepository.findById(challengeId).orElseThrow( () -> new ChallengeException(ChallengeError.CHALLENGE_NOT_FOUND)); } - public Challenge findCurrentChallengeByUserId(Long userId) { - User user = userService.findByIdOrThrowException(userId); - return findByIdOrElseThrow(user.getCurrentChallengeId()); - } - public List getCurrentChallengeAppByChallengeId(Long challengeId) { return this.findByIdOrElseThrow(challengeId).getApps(); } -} \ No newline at end of file + + public Challenge save(Challenge challenge) { + return challengeRepository.save(challenge); + } +} diff --git a/src/main/java/sopt/org/hmh/domain/dailychallenge/controller/DailyChallengeController.java b/src/main/java/sopt/org/hmh/domain/dailychallenge/controller/DailyChallengeController.java index 46a1c42e..63c045c5 100644 --- a/src/main/java/sopt/org/hmh/domain/dailychallenge/controller/DailyChallengeController.java +++ b/src/main/java/sopt/org/hmh/domain/dailychallenge/controller/DailyChallengeController.java @@ -8,7 +8,6 @@ import sopt.org.hmh.domain.dailychallenge.dto.request.FinishedDailyChallengeListRequest; import sopt.org.hmh.domain.dailychallenge.dto.request.FinishedDailyChallengeStatusListRequest; import sopt.org.hmh.domain.dailychallenge.service.DailyChallengeFacade; -import sopt.org.hmh.domain.dailychallenge.service.DailyChallengeService; import sopt.org.hmh.global.auth.UserId; import sopt.org.hmh.global.common.response.BaseResponse; import sopt.org.hmh.global.common.response.EmptyJsonResponse; @@ -19,7 +18,6 @@ public class DailyChallengeController implements DailyChallengeApi { private final DailyChallengeFacade dailyChallengeFacade; - private final DailyChallengeService dailyChallengeService; @Override @PostMapping("/finish") @@ -41,7 +39,7 @@ public ResponseEntity> orderChangeStatusDailyCha @RequestHeader("OS") final String os, @RequestBody final FinishedDailyChallengeStatusListRequest request ) { - dailyChallengeService.changeDailyChallengeStatusByIsSuccess(userId, request); + dailyChallengeFacade.changeDailyChallengeStatusByIsSuccess(userId, request); return ResponseEntity .status(DailyChallengeSuccess.SEND_FINISHED_DAILY_CHALLENGE_SUCCESS.getHttpStatus()) .body(BaseResponse.success(DailyChallengeSuccess.SEND_FINISHED_DAILY_CHALLENGE_SUCCESS, new EmptyJsonResponse())); diff --git a/src/main/java/sopt/org/hmh/domain/dailychallenge/domain/DailyChallenge.java b/src/main/java/sopt/org/hmh/domain/dailychallenge/domain/DailyChallenge.java index 440c92ac..d62ac340 100644 --- a/src/main/java/sopt/org/hmh/domain/dailychallenge/domain/DailyChallenge.java +++ b/src/main/java/sopt/org/hmh/domain/dailychallenge/domain/DailyChallenge.java @@ -3,6 +3,7 @@ import static jakarta.persistence.GenerationType.*; import jakarta.persistence.*; +import jakarta.validation.constraints.NotNull; import java.time.LocalDate; import lombok.AccessLevel; import lombok.Builder; @@ -25,27 +26,27 @@ public class DailyChallenge extends BaseTimeEntity { @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "challenge_id") + @NotNull(message = "챌린지는 null일 수 없습니다.") private Challenge challenge; @OneToMany(mappedBy = "dailyChallenge", cascade = CascadeType.REMOVE, orphanRemoval = true) private List apps; @Enumerated(EnumType.STRING) + @NotNull(message = "status값은 null일 수 없습니다.") private Status status; + @NotNull(message = "유저 아이디는 null일 수 없습니다.") private Long userId; + @NotNull(message = "목표 시간은 null일 수 없습니다.") private Long goalTime; + @NotNull(message = "챌린지 날짜는 null일 수 없습니다.") private LocalDate challengeDate; @Builder DailyChallenge(Challenge challenge, Long userId, Long goalTime, LocalDate challengeDate) { - Assert.notNull(challenge, "Challenge must not be null"); - Assert.notNull(userId, "UserId must not be null"); - Assert.notNull(goalTime, "GoalTime must not be null"); - Assert.notNull(challengeDate, "ChallengeDate must not be null"); - this.challenge = challenge; this.userId = userId; this.goalTime = goalTime; diff --git a/src/main/java/sopt/org/hmh/domain/dailychallenge/service/DailyChallengeFacade.java b/src/main/java/sopt/org/hmh/domain/dailychallenge/service/DailyChallengeFacade.java index be3ba3f1..122b52fc 100644 --- a/src/main/java/sopt/org/hmh/domain/dailychallenge/service/DailyChallengeFacade.java +++ b/src/main/java/sopt/org/hmh/domain/dailychallenge/service/DailyChallengeFacade.java @@ -5,29 +5,46 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import sopt.org.hmh.domain.app.domain.ChallengeApp; -import sopt.org.hmh.domain.app.service.AppService; +import sopt.org.hmh.domain.app.service.HistoryAppService; import sopt.org.hmh.domain.challenge.service.ChallengeService; import sopt.org.hmh.domain.dailychallenge.domain.DailyChallenge; +import sopt.org.hmh.domain.dailychallenge.domain.Status; import sopt.org.hmh.domain.dailychallenge.dto.request.FinishedDailyChallengeListRequest; +import sopt.org.hmh.domain.dailychallenge.dto.request.FinishedDailyChallengeStatusListRequest; import sopt.org.hmh.domain.user.service.UserService; @Service @RequiredArgsConstructor -@Transactional public class DailyChallengeFacade { private final DailyChallengeService dailyChallengeService; - private final AppService appService; + private final HistoryAppService historyAppService; private final ChallengeService challengeService; private final UserService userService; + @Transactional public void addFinishedDailyChallengeHistory(Long userId, FinishedDailyChallengeListRequest requests, String os) { + Long currentChallengeId = userService.getCurrentChallengeIdByUserId(userId); + List currentChallengeApps = challengeService.getCurrentChallengeAppByChallengeId(currentChallengeId); + requests.finishedDailyChallenges().forEach(request -> { DailyChallenge dailyChallenge = dailyChallengeService.findByChallengeDateAndUserIdOrThrowException(request.challengeDate(), userId); dailyChallengeService.changeStatusByCurrentStatus(dailyChallenge); - Long currentChallengeId = userService.getCurrentChallengeIdByUserId(userId); - List currentChallengeApps = challengeService.getCurrentChallengeAppByChallengeId(currentChallengeId); - appService.addAppForHistory(currentChallengeApps, request.apps(), dailyChallenge, os); + historyAppService.addHistoryApp(currentChallengeApps, request.apps(), dailyChallenge, os); + }); + } + + @Transactional + public void changeDailyChallengeStatusByIsSuccess(Long userId, FinishedDailyChallengeStatusListRequest requests) { + requests.finishedDailyChallenges().forEach(request -> { + DailyChallenge dailyChallenge = dailyChallengeService.findByChallengeDateAndUserIdOrThrowException(request.challengeDate(), userId); + if (request.isSuccess()) { + dailyChallengeService.validateDailyChallengeStatus(dailyChallenge.getStatus(), List.of(Status.NONE)); + dailyChallenge.changeStatus(Status.UNEARNED); + } else { + dailyChallengeService.validateDailyChallengeStatus(dailyChallenge.getStatus(), List.of(Status.NONE, Status.FAILURE)); + dailyChallenge.changeStatus(Status.FAILURE); + } }); } } diff --git a/src/main/java/sopt/org/hmh/domain/dailychallenge/service/DailyChallengeService.java b/src/main/java/sopt/org/hmh/domain/dailychallenge/service/DailyChallengeService.java index bf8a48ca..7062cae1 100644 --- a/src/main/java/sopt/org/hmh/domain/dailychallenge/service/DailyChallengeService.java +++ b/src/main/java/sopt/org/hmh/domain/dailychallenge/service/DailyChallengeService.java @@ -2,19 +2,18 @@ import java.time.LocalDate; import java.util.List; +import java.util.stream.IntStream; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; +import sopt.org.hmh.domain.challenge.domain.Challenge; import sopt.org.hmh.domain.dailychallenge.domain.DailyChallenge; import sopt.org.hmh.domain.dailychallenge.domain.Status; import sopt.org.hmh.domain.dailychallenge.domain.exception.DailyChallengeError; import sopt.org.hmh.domain.dailychallenge.domain.exception.DailyChallengeException; -import sopt.org.hmh.domain.dailychallenge.dto.request.FinishedDailyChallengeStatusListRequest; import sopt.org.hmh.domain.dailychallenge.repository.DailyChallengeRepository; @Service @RequiredArgsConstructor -@Transactional public class DailyChallengeService { private final DailyChallengeRepository dailyChallengeRepository; @@ -25,15 +24,7 @@ public DailyChallenge findByChallengeDateAndUserIdOrThrowException(LocalDate cha } public void validateDailyChallengeStatus(Status dailyChallengeStatus, List expectedStatuses) { - boolean isAlreadyProcessed = true; - for (Status expected : expectedStatuses) { - if (dailyChallengeStatus == expected) { - isAlreadyProcessed = false; - break; - } - } - - if (isAlreadyProcessed) { + if (!expectedStatuses.contains(dailyChallengeStatus)) { throw new DailyChallengeException(DailyChallengeError.DAILY_CHALLENGE_ALREADY_PROCESSED); } } @@ -53,16 +44,13 @@ private void handleAlreadyProcessedDailyChallenge(DailyChallenge dailyChallenge) throw new DailyChallengeException(DailyChallengeError.DAILY_CHALLENGE_ALREADY_PROCESSED); } - public void changeDailyChallengeStatusByIsSuccess(Long userId, FinishedDailyChallengeStatusListRequest requests) { - requests.finishedDailyChallenges().forEach(request -> { - DailyChallenge dailyChallenge = this.findByChallengeDateAndUserIdOrThrowException(request.challengeDate(), userId); - if (request.isSuccess()) { - this.validateDailyChallengeStatus(dailyChallenge.getStatus(), List.of(Status.NONE)); - dailyChallenge.changeStatus(Status.UNEARNED); - } else { - this.validateDailyChallengeStatus(dailyChallenge.getStatus(), List.of(Status.NONE, Status.FAILURE)); - dailyChallenge.changeStatus(Status.FAILURE); - } - }); + public void addDailyChallenge(Long userId, LocalDate startDate, Challenge challenge) { + dailyChallengeRepository.saveAll(IntStream.range(0, challenge.getPeriod()) + .mapToObj(i -> DailyChallenge.builder() + .challengeDate(startDate.plusDays(i)) + .challenge(challenge) + .userId(userId) + .goalTime(challenge.getGoalTime()).build()) + .toList()); } } \ No newline at end of file diff --git a/src/main/java/sopt/org/hmh/domain/point/service/PointFacade.java b/src/main/java/sopt/org/hmh/domain/point/service/PointFacade.java index 606db230..295caa80 100644 --- a/src/main/java/sopt/org/hmh/domain/point/service/PointFacade.java +++ b/src/main/java/sopt/org/hmh/domain/point/service/PointFacade.java @@ -18,15 +18,16 @@ @Service @RequiredArgsConstructor -@Transactional public class PointFacade { private final UserService userService; private final DailyChallengeService dailyChallengeService; private final ChallengeService challengeService; + @Transactional public UsePointResponse usePointAndChallengeFailed(Long userId, LocalDate challengeDate) { - DailyChallenge dailyChallenge = dailyChallengeService.findByChallengeDateAndUserIdOrThrowException(challengeDate, userId); + DailyChallenge dailyChallenge = + dailyChallengeService.findByChallengeDateAndUserIdOrThrowException(challengeDate, userId); User user = userService.findByIdOrThrowException(userId); dailyChallengeService.validateDailyChallengeStatus(dailyChallenge.getStatus(), List.of(Status.NONE)); @@ -38,20 +39,22 @@ public UsePointResponse usePointAndChallengeFailed(Long userId, LocalDate challe ); } + @Transactional public EarnPointResponse earnPointAndChallengeEarned(Long userId, LocalDate challengeDate) { - DailyChallenge dailyChallenge = dailyChallengeService.findByChallengeDateAndUserIdOrThrowException(challengeDate, userId); + DailyChallenge dailyChallenge = + dailyChallengeService.findByChallengeDateAndUserIdOrThrowException(challengeDate, userId); User user = userService.findByIdOrThrowException(userId); dailyChallengeService.validateDailyChallengeStatus(dailyChallenge.getStatus(), List.of(Status.UNEARNED)); dailyChallenge.changeStatus(Status.EARNED); - return new EarnPointResponse( - user.increasePoint(ChallengeConstants.EARNED_POINT) - ); + return new EarnPointResponse(user.increasePoint(ChallengeConstants.EARNED_POINT)); } + @Transactional(readOnly = true) public ChallengePointStatusListResponse getChallengePointStatusList(Long userId) { - Challenge challenge = challengeService.findCurrentChallengeByUserId(userId); + User user = userService.findByIdOrThrowException(userId); + Challenge challenge = challengeService.findByIdOrElseThrow(user.getCurrentChallengeId()); List challengePointStatusResponseList = challenge.getHistoryDailyChallenges().stream() .map(dailyChallenge -> new ChallengePointStatusResponse( @@ -59,7 +62,7 @@ public ChallengePointStatusListResponse getChallengePointStatusList(Long userId) dailyChallenge.getStatus())).toList(); return new ChallengePointStatusListResponse( - userService.getUserInfo(userId).point(), + user.getPoint(), challenge.getPeriod(), challengePointStatusResponseList ); diff --git a/src/main/java/sopt/org/hmh/domain/user/domain/OnboardingInfo.java b/src/main/java/sopt/org/hmh/domain/user/domain/OnboardingInfo.java index 08db01bb..4fbb39a9 100644 --- a/src/main/java/sopt/org/hmh/domain/user/domain/OnboardingInfo.java +++ b/src/main/java/sopt/org/hmh/domain/user/domain/OnboardingInfo.java @@ -4,6 +4,7 @@ import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; +import jakarta.validation.constraints.NotNull; import lombok.AccessLevel; import lombok.Builder; import lombok.Getter; @@ -18,7 +19,10 @@ public class OnboardingInfo { @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; + @NotNull(message = "평균 사용 시간은 null일 수 없습니다.") private String averageUseTime; + + @NotNull(message = "유저 아이디는 null일 수 없습니다.") private Long userId; @Builder diff --git a/src/main/java/sopt/org/hmh/domain/user/domain/OnboardingProblem.java b/src/main/java/sopt/org/hmh/domain/user/domain/OnboardingProblem.java index 5220bd5b..81844d1c 100644 --- a/src/main/java/sopt/org/hmh/domain/user/domain/OnboardingProblem.java +++ b/src/main/java/sopt/org/hmh/domain/user/domain/OnboardingProblem.java @@ -5,6 +5,7 @@ import jakarta.persistence.GenerationType; import jakarta.persistence.Id; import jakarta.persistence.Table; +import jakarta.validation.constraints.NotNull; import lombok.AccessLevel; import lombok.Builder; import lombok.Getter; @@ -20,7 +21,9 @@ public class OnboardingProblem { @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; + @NotNull(message = "온보딩 정보 아이디는 null일 수 없습니다.") private Long onboardingInfoId; + @NotNull(message = "문제 항목은 null일 수 없습니다.") private String problem; @Builder diff --git a/src/main/java/sopt/org/hmh/domain/user/domain/User.java b/src/main/java/sopt/org/hmh/domain/user/domain/User.java index d67594a8..0ed66780 100644 --- a/src/main/java/sopt/org/hmh/domain/user/domain/User.java +++ b/src/main/java/sopt/org/hmh/domain/user/domain/User.java @@ -11,6 +11,7 @@ import jakarta.persistence.Id; import jakarta.persistence.Table; import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotNull; import java.time.LocalDate; import java.time.LocalDateTime; import lombok.AccessLevel; @@ -31,21 +32,22 @@ public class User extends BaseTimeEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; + private String name; + private Long currentChallengeId; @Enumerated(EnumType.STRING) + @NotNull(message = "소셜 플랫폼은 null일 수 없습니다.") private SocialPlatform socialPlatform; @Column(unique = true) private String socialId; @Min(value = 0) + @NotNull(message = "포인트는 기본 값이 설정되어야 합니다.") private Integer point; - @Column(columnDefinition = "TEXT") - private String profileImageUrl; - private LocalDate recentLockDate; private boolean isDeleted = false; @@ -59,9 +61,8 @@ public User(SocialPlatform socialPlatform, String socialId, String name) { this.point = UserConstants.INITIAL_POINT; } - public void updateSocialInfo(String nickname, String profileImageUrl) { + public void updateNickname(String nickname) { this.name = nickname; - this.profileImageUrl = profileImageUrl; } public void softDelete() { diff --git a/src/main/java/sopt/org/hmh/domain/user/service/ExpiredUserDeleteScheduler.java b/src/main/java/sopt/org/hmh/domain/user/service/ExpiredUserDeleteScheduler.java index 578445b3..055fa30f 100644 --- a/src/main/java/sopt/org/hmh/domain/user/service/ExpiredUserDeleteScheduler.java +++ b/src/main/java/sopt/org/hmh/domain/user/service/ExpiredUserDeleteScheduler.java @@ -11,13 +11,13 @@ @Component @RequiredArgsConstructor -@Transactional public class ExpiredUserDeleteScheduler { private final UserRepository userRepository; private final ChallengeService challengeService; @Scheduled(cron = "0 0 4 * * ?") + @Transactional public void deleteExpiredUser() { deleteExpiredUser(LocalDateTime.now()); } diff --git a/src/main/java/sopt/org/hmh/domain/user/service/UserService.java b/src/main/java/sopt/org/hmh/domain/user/service/UserService.java index 54466c4d..12e2bfa9 100644 --- a/src/main/java/sopt/org/hmh/domain/user/service/UserService.java +++ b/src/main/java/sopt/org/hmh/domain/user/service/UserService.java @@ -1,7 +1,6 @@ package sopt.org.hmh.domain.user.service; import java.time.LocalDate; -import java.util.List; import java.util.Optional; import lombok.RequiredArgsConstructor; @@ -13,8 +12,6 @@ import sopt.org.hmh.domain.auth.exception.AuthException; import sopt.org.hmh.domain.auth.repository.OnboardingInfoRepository; import sopt.org.hmh.domain.auth.repository.ProblemRepository; -import sopt.org.hmh.domain.user.domain.OnboardingInfo; -import sopt.org.hmh.domain.user.domain.OnboardingProblem; import sopt.org.hmh.domain.user.domain.User; import sopt.org.hmh.domain.user.domain.UserConstants; import sopt.org.hmh.domain.user.domain.exception.UserError; @@ -26,19 +23,18 @@ @Service @RequiredArgsConstructor -@Transactional(readOnly = true) public class UserService { private final UserRepository userRepository; private final OnboardingInfoRepository onboardingInfoRepository; private final ProblemRepository problemRepository; - @Transactional public void withdraw(Long userId) { this.findByIdOrThrowException(userId).softDelete(); } + @Transactional(readOnly = true) public UserInfoResponse getUserInfo(Long userId) { return UserInfoResponse.of(this.findByIdOrThrowException(userId)); } @@ -74,21 +70,12 @@ private String validateName(String name) { return name; } - public void registerOnboardingInfo(SocialSignUpRequest request) { - OnboardingInfo onboardingInfo = OnboardingInfo.builder() - .averageUseTime(request.onboardingRequest().averageUseTime()) - .build(); - Long onboardingInfoId = onboardingInfoRepository.save(onboardingInfo).getId(); - - List problemList = request.onboardingRequest().problemList().stream() - .map(problem -> OnboardingProblem.builder() - .onboardingInfoId(onboardingInfoId) - .problem(problem).build()) - .toList(); - problemRepository.saveAll(problemList); + public void registerOnboardingInfo(SocialSignUpRequest request, Long userId) { + Long onboardingInfoId = onboardingInfoRepository.save(request.toOnboardingInfo(userId)).getId(); + problemRepository.saveAll(request.toProblemList(onboardingInfoId)); } - public User findBySocialPlatformAndSocialIdOrThrowException(SocialPlatform socialPlatform, String socialId) { + private User findBySocialPlatformAndSocialIdOrThrowException(SocialPlatform socialPlatform, String socialId) { return userRepository.findBySocialPlatformAndSocialId(socialPlatform, socialId).orElseThrow( () -> new AuthException(AuthError.NOT_SIGNUP_USER)); } @@ -104,17 +91,13 @@ public Long getCurrentChallengeIdByUserId(Long userId) { } @Transactional - public void changeRecentLockDate(Long userId, LocalDate localDate) { - this.findByIdOrThrowException(userId).changeRecentLockDate(localDate); + public void changeRecentLockDate(Long userId, LocalDate lockDate) { + this.findByIdOrThrowException(userId).changeRecentLockDate(lockDate); } + @Transactional(readOnly = true) public IsLockTodayResponse checkIsTodayLock(Long userId, LocalDate lockCheckDate) { LocalDate userRecentLockDate = this.findByIdOrThrowException(userId).getRecentLockDate(); - - if (userRecentLockDate == null) { - return new IsLockTodayResponse(false); - } - - return new IsLockTodayResponse(userRecentLockDate.equals(lockCheckDate)); + return new IsLockTodayResponse(lockCheckDate.equals(userRecentLockDate)); } } \ No newline at end of file diff --git a/src/main/java/sopt/org/hmh/global/auth/jwt/JwtProvider.java b/src/main/java/sopt/org/hmh/global/auth/jwt/JwtProvider.java index dceb8aba..a1933e0f 100644 --- a/src/main/java/sopt/org/hmh/global/auth/jwt/JwtProvider.java +++ b/src/main/java/sopt/org/hmh/global/auth/jwt/JwtProvider.java @@ -11,7 +11,7 @@ public class JwtProvider { private final JwtGenerator jwtGenerator; public TokenResponse issueToken(Long userId) { - return TokenResponse.of(jwtGenerator.generateToken(userId, false), + return new TokenResponse(jwtGenerator.generateToken(userId, false), jwtGenerator.generateToken(userId, true)); } diff --git a/src/main/java/sopt/org/hmh/global/auth/jwt/TokenResponse.java b/src/main/java/sopt/org/hmh/global/auth/jwt/TokenResponse.java index d23e0086..a0751843 100644 --- a/src/main/java/sopt/org/hmh/global/auth/jwt/TokenResponse.java +++ b/src/main/java/sopt/org/hmh/global/auth/jwt/TokenResponse.java @@ -4,7 +4,4 @@ public record TokenResponse( String accessToken, String refreshToken ) { - public static TokenResponse of(String accessToken, String refreshToken) { - return new TokenResponse(accessToken, refreshToken); - } } \ No newline at end of file diff --git a/src/main/java/sopt/org/hmh/global/auth/social/kakao/fegin/KakaoLoginService.java b/src/main/java/sopt/org/hmh/global/auth/social/kakao/fegin/KakaoLoginService.java index c1aff229..8311b6e8 100644 --- a/src/main/java/sopt/org/hmh/global/auth/social/kakao/fegin/KakaoLoginService.java +++ b/src/main/java/sopt/org/hmh/global/auth/social/kakao/fegin/KakaoLoginService.java @@ -5,7 +5,6 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import org.springframework.util.StringUtils; import sopt.org.hmh.domain.user.domain.User; import sopt.org.hmh.global.auth.jwt.exception.JwtError; import sopt.org.hmh.global.auth.jwt.exception.JwtException; @@ -36,15 +35,7 @@ public void updateUserInfoByKakao(User loginUser, final String socialAccessToken KakaoUserResponse userRequest = getKakaoUserRequest(socialAccessToken); try { - String nickname = userRequest.kakaoAccount().profile().nickname(); - String profileImageUrl = userRequest.kakaoAccount().profile().profileImageUrl(); - - if (!StringUtils.hasText(profileImageUrl)) { - profileImageUrl = ""; - } - - loginUser.updateSocialInfo(nickname, profileImageUrl); - + loginUser.updateNickname(userRequest.kakaoAccount().profile().nickname()); } catch (NullPointerException exception) { throw new JwtException(JwtError.INVALID_SOCIAL_ACCESS_TOKEN_FORMAT); } diff --git a/src/main/java/sopt/org/hmh/global/auth/social/kakao/response/KakaoUserProfile.java b/src/main/java/sopt/org/hmh/global/auth/social/kakao/response/KakaoUserProfile.java index 5809a820..e903cf2a 100644 --- a/src/main/java/sopt/org/hmh/global/auth/social/kakao/response/KakaoUserProfile.java +++ b/src/main/java/sopt/org/hmh/global/auth/social/kakao/response/KakaoUserProfile.java @@ -5,7 +5,6 @@ @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) public record KakaoUserProfile( - String nickname, - String profileImageUrl + String nickname ) { } \ No newline at end of file