diff --git a/.gitignore b/.gitignore index 894ecfdb..024e067d 100644 --- a/.gitignore +++ b/.gitignore @@ -173,4 +173,7 @@ application-prod.yml ### Test ### data.sql +### Querydsl ### +**/generated/ + # End of https://www.gitignore.io/api/java,macos,gradle,intellij diff --git a/build.gradle b/build.gradle index 041e3297..e50a71d7 100644 --- a/build.gradle +++ b/build.gradle @@ -41,8 +41,11 @@ dependencies { runtimeOnly group: 'io.jsonwebtoken', name: 'jjwt-impl', version: '0.11.5' runtimeOnly group: 'io.jsonwebtoken', name: 'jjwt-jackson', version: '0.11.5' - // Redis - implementation 'org.springframework.boot:spring-boot-starter-data-redis' + // Querydsl + implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta' + annotationProcessor "com.querydsl:querydsl-apt:${dependencyManagement.importedProperties['querydsl.version']}:jakarta" + annotationProcessor "jakarta.annotation:jakarta.annotation-api" + annotationProcessor "jakarta.persistence:jakarta.persistence-api" // Open Feign implementation 'org.springframework.cloud:spring-cloud-starter-openfeign:4.1.0' @@ -59,4 +62,8 @@ tasks.named('test') { useJUnitPlatform() } +clean { + delete file('src/main/generated') +} + jar.enabled = false \ No newline at end of file diff --git a/src/main/java/sopt/org/hmh/domain/app/repository/ChallengeAppRepository.java b/src/main/java/sopt/org/hmh/domain/app/repository/ChallengeAppRepository.java deleted file mode 100644 index 3663e832..00000000 --- a/src/main/java/sopt/org/hmh/domain/app/repository/ChallengeAppRepository.java +++ /dev/null @@ -1,20 +0,0 @@ -package sopt.org.hmh.domain.app.repository; - -import org.springframework.data.jpa.repository.JpaRepository; -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 java.util.Optional; - -public interface ChallengeAppRepository extends JpaRepository { - - Optional findFirstByChallengeIdAndAppCodeAndOs(Long challengeId, String appCode, String os); - - default ChallengeApp findFirstByChallengeIdAndAppCodeAndOsOrElseThrow(Long challengeId, String appCode, String os) { - return findFirstByChallengeIdAndAppCodeAndOs(challengeId, appCode, os).orElseThrow(() -> new AppException( - AppError.APP_NOT_FOUND)); - } - - boolean existsByChallengeIdAndAppCodeAndOs(Long challengeId, String appCode, String os); -} diff --git a/src/main/java/sopt/org/hmh/domain/app/repository/HistoryAppRepository.java b/src/main/java/sopt/org/hmh/domain/app/repository/HistoryAppRepository.java index e650e923..97d496e7 100644 --- a/src/main/java/sopt/org/hmh/domain/app/repository/HistoryAppRepository.java +++ b/src/main/java/sopt/org/hmh/domain/app/repository/HistoryAppRepository.java @@ -2,17 +2,7 @@ import org.springframework.data.jpa.repository.JpaRepository; import sopt.org.hmh.domain.app.domain.HistoryApp; -import sopt.org.hmh.domain.app.domain.exception.AppError; -import sopt.org.hmh.domain.app.domain.exception.AppException; - -import java.util.Optional; public interface HistoryAppRepository extends JpaRepository { - Optional findFirstByDailyChallengeIdAndAppCodeAndOs(Long dailyChallengeId, String appCode, String os); - - default HistoryApp findFirstByDailyChallengeIdAndAppCodeAndOsOrElseThrow(Long dailyChallengeId, String appCode, String os) { - return findFirstByDailyChallengeIdAndAppCodeAndOs(dailyChallengeId, appCode, os).orElseThrow(() -> new AppException( - AppError.APP_NOT_FOUND)); - } } diff --git a/src/main/java/sopt/org/hmh/domain/app/repository/challenge_app/ChallengeAppJpaRepository.java b/src/main/java/sopt/org/hmh/domain/app/repository/challenge_app/ChallengeAppJpaRepository.java new file mode 100644 index 00000000..bad15c17 --- /dev/null +++ b/src/main/java/sopt/org/hmh/domain/app/repository/challenge_app/ChallengeAppJpaRepository.java @@ -0,0 +1,15 @@ +package sopt.org.hmh.domain.app.repository.challenge_app; + +import java.util.List; +import java.util.Optional; +import org.springframework.data.jpa.repository.JpaRepository; +import sopt.org.hmh.domain.app.domain.ChallengeApp; + +public interface ChallengeAppJpaRepository extends JpaRepository { + + Optional findFirstByChallengeIdAndAppCodeAndOs(Long challengeId, String appCode, String os); + + boolean existsByChallengeIdAndAppCodeAndOs(Long challengeId, String appCode, String os); + + List findAllByChallengeId(Long previousChallengeId); +} diff --git a/src/main/java/sopt/org/hmh/domain/app/repository/challenge_app/ChallengeAppRepository.java b/src/main/java/sopt/org/hmh/domain/app/repository/challenge_app/ChallengeAppRepository.java new file mode 100644 index 00000000..131fe47e --- /dev/null +++ b/src/main/java/sopt/org/hmh/domain/app/repository/challenge_app/ChallengeAppRepository.java @@ -0,0 +1,18 @@ +package sopt.org.hmh.domain.app.repository.challenge_app; + +import java.util.List; +import sopt.org.hmh.domain.app.domain.ChallengeApp; +import java.util.Optional; + +public interface ChallengeAppRepository { + + void saveAll(List challengeApps); + + void delete(ChallengeApp appToRemove); + + Optional findFirstByChallengeIdAndAppCodeAndOs(Long challengeId, String appCode, String os); + + boolean existsByChallengeIdAndAppCodeAndOs(Long challengeId, String appCode, String os); + + List findAllByChallengeId(Long previousChallengeId); +} diff --git a/src/main/java/sopt/org/hmh/domain/app/repository/challenge_app/ChallengeAppRepositoryImpl.java b/src/main/java/sopt/org/hmh/domain/app/repository/challenge_app/ChallengeAppRepositoryImpl.java new file mode 100644 index 00000000..bdc1fe7e --- /dev/null +++ b/src/main/java/sopt/org/hmh/domain/app/repository/challenge_app/ChallengeAppRepositoryImpl.java @@ -0,0 +1,39 @@ +package sopt.org.hmh.domain.app.repository.challenge_app; + +import java.util.List; +import java.util.Optional; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Repository; +import sopt.org.hmh.domain.app.domain.ChallengeApp; + +@Repository +@RequiredArgsConstructor +public class ChallengeAppRepositoryImpl implements ChallengeAppRepository{ + + private final ChallengeAppJpaRepository challengeAppJpaRepository; + + @Override + public void saveAll(List challengeApps) { + challengeAppJpaRepository.saveAll(challengeApps); + } + + @Override + public void delete(ChallengeApp appToRemove) { + challengeAppJpaRepository.delete(appToRemove); + } + + @Override + public Optional findFirstByChallengeIdAndAppCodeAndOs(Long challengeId, String appCode, String os) { + return challengeAppJpaRepository.findFirstByChallengeIdAndAppCodeAndOs(challengeId, appCode, os); + } + + @Override + public boolean existsByChallengeIdAndAppCodeAndOs(Long challengeId, String appCode, String os) { + return challengeAppJpaRepository.existsByChallengeIdAndAppCodeAndOs(challengeId, appCode, os); + } + + @Override + public List findAllByChallengeId(Long previousChallengeId) { + return challengeAppJpaRepository.findAllByChallengeId(previousChallengeId); + } +} 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 index 366272f4..3a4be1ff 100644 --- a/src/main/java/sopt/org/hmh/domain/app/service/ChallengeAppService.java +++ b/src/main/java/sopt/org/hmh/domain/app/service/ChallengeAppService.java @@ -7,7 +7,7 @@ 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.app.repository.challenge_app.ChallengeAppRepository; import sopt.org.hmh.domain.challenge.domain.Challenge; @Service @@ -18,23 +18,37 @@ public class ChallengeAppService { public void removeApp(Challenge challenge, String appcode, String os) { ChallengeApp appToRemove = - challengeAppRepository.findFirstByChallengeIdAndAppCodeAndOsOrElseThrow(challenge.getId(), appcode, os); + this.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()); + public void addAppsByPreviousChallengeApp(String os, Long previousChallengeId, Challenge challenge) { + this.addApps(challengeAppRepository.findAllByChallengeId(previousChallengeId) + .stream().map(previousApp -> + new ChallengeAppRequest(previousApp.getAppCode(), previousApp.getGoalTime()) + .toEntity(challenge, os)) + .toList() + ); } - private void validateAppExist(Long challengeId, String appCode, String os) { - if (challengeAppRepository.existsByChallengeIdAndAppCodeAndOs(challengeId, appCode, os)) { + public void addApps(List challengeApps) { + this.validateAppsExist(challengeApps); + challengeAppRepository.saveAll(challengeApps); + } + + private void validateAppsExist(List challengeApps) { + challengeApps.forEach(this::validateAppExist); + } + + private void validateAppExist(ChallengeApp challengeApp) { + if (challengeAppRepository.existsByChallengeIdAndAppCodeAndOs( + challengeApp.getChallenge().getId(), challengeApp.getAppCode(), challengeApp.getOs())) { throw new AppException(AppError.APP_EXIST_ALREADY); } } + private ChallengeApp findFirstByChallengeIdAndAppCodeAndOsOrElseThrow(Long challengeId, String appCode, String os) { + return challengeAppRepository.findFirstByChallengeIdAndAppCodeAndOs(challengeId, appCode, os) + .orElseThrow(() -> new AppException(AppError.APP_NOT_FOUND)); + } } 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 29eacd95..39a86dcd 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 @@ -32,7 +32,10 @@ public ResponseEntity> orderLogin( ) { return ResponseEntity .status(AuthSuccess.LOGIN_SUCCESS.getHttpStatus()) - .body(BaseResponse.success(AuthSuccess.LOGIN_SUCCESS, authFacade.login(socialAccessToken, request))); + .body(BaseResponse.success( + AuthSuccess.LOGIN_SUCCESS, + authFacade.login(socialAccessToken, request.socialPlatform()) + )); } @PostMapping("/signup") @@ -44,7 +47,10 @@ public ResponseEntity> orderSignup( ) { return ResponseEntity .status(AuthSuccess.SIGNUP_SUCCESS.getHttpStatus()) - .body(BaseResponse.success(AuthSuccess.SIGNUP_SUCCESS, authFacade.signup(socialAccessToken, request, os))); + .body(BaseResponse.success( + AuthSuccess.SIGNUP_SUCCESS, + authFacade.signup(request, socialAccessToken, os) + )); } @PostMapping("/reissue") @@ -54,7 +60,10 @@ public ResponseEntity> orderReissue( ) { return ResponseEntity .status(AuthSuccess.REISSUE_SUCCESS.getHttpStatus()) - .body(BaseResponse.success(AuthSuccess.REISSUE_SUCCESS, authFacade.reissueToken(refreshToken))); + .body(BaseResponse.success( + AuthSuccess.REISSUE_SUCCESS, + authFacade.reissueToken(refreshToken) + )); } @GetMapping("/social/token/kakao") @@ -63,6 +72,9 @@ public ResponseEntity> orderGetKakaoAcce ) { return ResponseEntity .status(AuthSuccess.GET_SOCIAL_ACCESS_TOKEN_SUCCESS.getHttpStatus()) - .body(BaseResponse.success(AuthSuccess.GET_SOCIAL_ACCESS_TOKEN_SUCCESS, authFacade.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 dc399a22..ef3d4511 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 @@ -1,6 +1,5 @@ package sopt.org.hmh.domain.auth.dto.request; -import com.fasterxml.jackson.annotation.JsonProperty; import jakarta.validation.Valid; import jakarta.validation.constraints.NotNull; import java.util.List; @@ -14,26 +13,20 @@ public record SocialSignUpRequest( @NotNull(message = "소셜 플랫폼은 null일 수 없습니다.") SocialPlatform socialPlatform, String name, - @JsonProperty(value = "onboarding") - OnboardingRequest onboardingRequest, + OnboardingRequest onboarding, @Valid - @JsonProperty(value = "challenge") - ChallengeSignUpRequest challengeSignUpRequest + ChallengeSignUpRequest challenge ) { - public ChallengeRequest toChallengeRequest() { - return new ChallengeRequest(challengeSignUpRequest.period(), challengeSignUpRequest.goalTime()); - } - public OnboardingInfo toOnboardingInfo(Long userId) { return OnboardingInfo.builder() - .averageUseTime(onboardingRequest.averageUseTime()) + .averageUseTime(onboarding.averageUseTime()) .userId(userId) .build(); } public List toProblemList(Long onboardingInfoId) { - return onboardingRequest.problemList().stream() + return onboarding.problemList().stream() .map(problem -> OnboardingProblem.builder() .onboardingInfoId(onboardingInfoId) .problem(problem) diff --git a/src/main/java/sopt/org/hmh/domain/auth/service/AuthFacade.java b/src/main/java/sopt/org/hmh/domain/auth/service/AuthFacade.java index 5e314308..d52f691d 100644 --- a/src/main/java/sopt/org/hmh/domain/auth/service/AuthFacade.java +++ b/src/main/java/sopt/org/hmh/domain/auth/service/AuthFacade.java @@ -3,12 +3,9 @@ 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.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; import sopt.org.hmh.domain.auth.dto.response.LoginResponse; import sopt.org.hmh.domain.user.service.UserService; @@ -27,53 +24,60 @@ public class AuthFacade { private final KakaoLoginService kakaoLoginService; private final AppleOAuthProvider appleOAuthProvider; private final ChallengeFacade challengeFacade; - private final ChallengeAppService challengeAppService; private final TokenService tokenService; private final UserService userService; @Transactional(readOnly = true) - public LoginResponse login(String socialAccessToken, SocialPlatformRequest request) { - SocialPlatform socialPlatform = request.socialPlatform(); + public LoginResponse login(String socialAccessToken, SocialPlatform socialPlatform) { String socialId = this.getSocialIdBySocialAccessToken(socialPlatform, socialAccessToken); User loginUser = userService.getUserBySocialPlatformAndSocialId(socialPlatform, socialId); - return performLogin(socialAccessToken, socialPlatform, loginUser); + return performLogin(loginUser, socialAccessToken, socialPlatform); } @Transactional - public LoginResponse signup(String socialAccessToken, SocialSignUpRequest request, String os) { + public LoginResponse signup(SocialSignUpRequest request, String socialAccessToken, String os) { SocialPlatform socialPlatform = request.socialPlatform(); 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); + User newUser = userService.addUser(socialPlatform, socialId, request.name()); + Long newUserId = newUser.getId(); + + userService.registerOnboardingInfo(request, newUserId); - Challenge challenge = challengeFacade.addChallenge(userId, request.toChallengeRequest() , os); - challengeAppService.addApps(challenge, request.challengeSignUpRequest().apps(), os); + challengeFacade.startFirstChallengeWithChallengeSignUpRequest(request.challenge(), newUser , os); - return performLogin(socialAccessToken, socialPlatform, user); + return performLogin(newUser, socialAccessToken, socialPlatform); } private String getSocialIdBySocialAccessToken(SocialPlatform socialPlatform, String socialAccessToken) { - return switch (socialPlatform.toString()) { - case "KAKAO" -> kakaoLoginService.getSocialIdByKakao(socialAccessToken); - case "APPLE" -> appleOAuthProvider.getApplePlatformId(socialAccessToken); - default -> throw new JwtException(JwtError.INVALID_SOCIAL_ACCESS_TOKEN); - }; + if (socialPlatform == SocialPlatform.APPLE) { + return appleOAuthProvider.getApplePlatformId(socialAccessToken); + } + if (socialPlatform == SocialPlatform.KAKAO) { + return kakaoLoginService.getSocialIdByKakao(socialAccessToken); + } + throw new JwtException(JwtError.INVALID_SOCIAL_ACCESS_TOKEN); } - public ReissueResponse reissueToken(String refreshToken) { - return tokenService.reissueToken(refreshToken); + private LoginResponse performLogin(User loginUser, String socialAccessToken, SocialPlatform socialPlatform) { + this.updateAdditionalUserLoginInfo(loginUser, socialAccessToken, socialPlatform); + + Long userId = loginUser.getId(); + return new LoginResponse(userId, tokenService.issueToken(userId.toString())); } - private LoginResponse performLogin(String socialAccessToken, SocialPlatform socialPlatform, User loginUser) { + private void updateAdditionalUserLoginInfo(User loginUser, String socialAccessToken, SocialPlatform socialPlatform) { + userService.recoverIfIsDeletedUser(loginUser); + if (socialPlatform == SocialPlatform.KAKAO) { kakaoLoginService.updateUserInfoByKakao(loginUser, socialAccessToken); } - Long userId = loginUser.getId(); - return new LoginResponse(userId, tokenService.issueToken(userId.toString())); + } + + + public ReissueResponse reissueToken(String refreshToken) { + return tokenService.reissueToken(refreshToken); } public SocialAccessTokenResponse getSocialAccessTokenByAuthorizationCode(String code) { 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 3c503397..5c44f385 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 @@ -29,7 +29,7 @@ public ResponseEntity> orderAddChallenge( @UserId final Long userId, @RequestHeader("OS") final String os, @RequestBody @Valid final ChallengeRequest request) { - challengeFacade.addChallenge(userId, request, os); + challengeFacade.startNewChallengeByPreviousChallenge(userId, request, os); return ResponseEntity .status(ChallengeSuccess.ADD_CHALLENGE_SUCCESS.getHttpStatus()) diff --git a/src/main/java/sopt/org/hmh/domain/challenge/domain/exception/ChallengeError.java b/src/main/java/sopt/org/hmh/domain/challenge/domain/exception/ChallengeError.java index 96aa7f16..8c8a84df 100644 --- a/src/main/java/sopt/org/hmh/domain/challenge/domain/exception/ChallengeError.java +++ b/src/main/java/sopt/org/hmh/domain/challenge/domain/exception/ChallengeError.java @@ -7,7 +7,7 @@ @AllArgsConstructor public enum ChallengeError implements ErrorBase { - CHALLENGE_NOT_FOUND(HttpStatus.NOT_FOUND, "챌린지를 찾을 수 없습니다."), + CHALLENGE_NOT_FOUND(HttpStatus.NOT_FOUND, "챌린지가 존재하지 않습니다."), INVALID_PERIOD_NUMERIC(HttpStatus.BAD_REQUEST, "유효한 챌린지 기간이 아닙니다."), INVALID_GOAL_TIME(HttpStatus.BAD_REQUEST, "챌린지 목표 시간이 유효하지 않습니다."), ; diff --git a/src/main/java/sopt/org/hmh/domain/challenge/dto/request/ChallengeSignUpRequest.java b/src/main/java/sopt/org/hmh/domain/challenge/dto/request/ChallengeSignUpRequest.java index 527ca5d3..27f7d474 100644 --- a/src/main/java/sopt/org/hmh/domain/challenge/dto/request/ChallengeSignUpRequest.java +++ b/src/main/java/sopt/org/hmh/domain/challenge/dto/request/ChallengeSignUpRequest.java @@ -21,4 +21,8 @@ public record ChallengeSignUpRequest( throw new ChallengeException(ChallengeError.INVALID_GOAL_TIME); } } + + public ChallengeRequest toChallengeRequest() { + return new ChallengeRequest(this.period, this.goalTime); + } } \ No newline at end of file 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 index a8e984e6..64fd461a 100644 --- a/src/main/java/sopt/org/hmh/domain/challenge/service/ChallengeFacade.java +++ b/src/main/java/sopt/org/hmh/domain/challenge/service/ChallengeFacade.java @@ -1,6 +1,5 @@ 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; @@ -9,6 +8,7 @@ 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.request.ChallengeSignUpRequest; 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; @@ -31,29 +31,33 @@ public class ChallengeFacade { private final ChallengeAppService challengeAppService; @Transactional - public Challenge addChallenge(Long userId, ChallengeRequest challengeRequest, String os) { + public void startNewChallengeByPreviousChallenge(Long userId, ChallengeRequest challengeRequest, String os) { User user = userService.findByIdOrThrowException(userId); + Long previousChallengeId = userService.getCurrentChallengeIdByUser(user); - Optional previousChallengeId = Optional.ofNullable(user.getCurrentChallengeId()); + Challenge newChallenge = challengeService.addChallengeAndUpdateUserCurrentChallenge( + challengeRequest.toEntity(userId), user); - Challenge challenge = challengeService.save(challengeRequest.toEntity(userId)); - user.changeCurrentChallengeId(challenge.getId()); + dailyChallengeService.addDailyChallenge(userId, newChallenge); - LocalDate startDate = challenge.getCreatedAt().toLocalDate(); - dailyChallengeService.addDailyChallenge(userId, startDate, challenge); - this.addAppsIfPreviousChallengeExist(os, previousChallengeId, challenge); - - return challenge; + challengeAppService.addAppsByPreviousChallengeApp(os, previousChallengeId, newChallenge); } - 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 + public void startFirstChallengeWithChallengeSignUpRequest( + ChallengeSignUpRequest challengeSignUpRequest, User user, String os) { + Long userId = user.getId(); + + Challenge newChallenge = challengeService.addChallengeAndUpdateUserCurrentChallenge( + challengeSignUpRequest.toChallengeRequest().toEntity(userId), user); + + dailyChallengeService.addDailyChallenge(userId, newChallenge); + + challengeAppService.addApps( + challengeSignUpRequest.apps().stream() + .map(challengeAppRequest -> challengeAppRequest.toEntity(newChallenge, os)) + .toList() + ); } @Transactional(readOnly = true) @@ -98,7 +102,11 @@ private Integer calculateTodayIndex(LocalDateTime challengeCreateAt, int period) @Transactional public void addAppsToCurrentChallenge(Long userId, List requests, String os) { Challenge challenge = this.findCurrentChallengeByUserId(userId); - challengeAppService.addApps(challenge, requests, os); + challengeAppService.addApps( + requests.stream() + .map(request -> request.toEntity(challenge, os)) + .toList() + ); } @Transactional 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 ccf0cf02..2576fea1 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 @@ -8,6 +8,7 @@ import sopt.org.hmh.domain.challenge.domain.exception.ChallengeError; import sopt.org.hmh.domain.challenge.domain.exception.ChallengeException; import sopt.org.hmh.domain.challenge.repository.ChallengeRepository; +import sopt.org.hmh.domain.user.domain.User; @Service @RequiredArgsConstructor @@ -32,8 +33,10 @@ public List getCurrentChallengeAppByChallengeId(Long challengeId) return this.findByIdOrElseThrow(challengeId).getApps(); } - public Challenge save(Challenge challenge) { - return challengeRepository.save(challenge); + public Challenge addChallengeAndUpdateUserCurrentChallenge(Challenge challenge, User user) { + Challenge newChallenge = challengeRepository.save(challenge); + user.changeCurrentChallengeId(newChallenge.getId()); + return newChallenge; } public Integer getChallengePeriod(Long challengeId) { 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 c5ee346e..1932deeb 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 @@ -44,7 +44,8 @@ private void handleAlreadyProcessedDailyChallenge(DailyChallenge dailyChallenge) throw new DailyChallengeException(DailyChallengeError.DAILY_CHALLENGE_ALREADY_PROCESSED); } - public void addDailyChallenge(Long userId, LocalDate startDate, Challenge challenge) { + public void addDailyChallenge(Long userId, Challenge challenge) { + LocalDate startDate = challenge.getCreatedAt().toLocalDate(); // TODO: startDate CreatedAt에서 가져오지 않고 새로 만들기 dailyChallengeRepository.saveAll(IntStream.range(0, challenge.getPeriod()) .mapToObj(i -> DailyChallenge.builder() .challengeDate(startDate.plusDays(i)) 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 397c5252..3c9bf910 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 @@ -12,6 +12,8 @@ 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.challenge.domain.exception.ChallengeError; +import sopt.org.hmh.domain.challenge.domain.exception.ChallengeException; 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; @@ -40,11 +42,13 @@ public UserInfoResponse getUserInfo(Long userId) { } public User getUserBySocialPlatformAndSocialId(SocialPlatform socialPlatform, String socialId) { - User user = this.findBySocialPlatformAndSocialIdOrThrowException(socialPlatform, socialId); + return this.findBySocialPlatformAndSocialIdOrThrowException(socialPlatform, socialId); + } + + public void recoverIfIsDeletedUser(User user) { if (user.isDeleted()) { user.recover(); } - return user; } public void validateDuplicateUser(String socialId, SocialPlatform socialPlatform) { @@ -54,6 +58,8 @@ public void validateDuplicateUser(String socialId, SocialPlatform socialPlatform } public User addUser(SocialPlatform socialPlatform, String socialId, String name) { + this.validateDuplicateUser(socialId, socialPlatform); + return userRepository.save( User.builder() .socialPlatform(socialPlatform) @@ -100,6 +106,11 @@ public Long getCurrentChallengeIdByUserId(Long userId) { .orElseThrow(() -> new UserException(UserError.NOT_FOUND_CURRENT_CHALLENGE_ID)); } + public Long getCurrentChallengeIdByUser(User user) { + return Optional.ofNullable(user.getCurrentChallengeId()) + .orElseThrow(() -> new ChallengeException(ChallengeError.CHALLENGE_NOT_FOUND)); + } + @Transactional public void changeRecentLockDate(Long userId, LocalDate lockDate) { this.findByIdOrThrowException(userId).changeRecentLockDate(lockDate);