diff --git a/src/main/java/sopt/org/HMH/domain/app/controller/AppController.java b/src/main/java/sopt/org/HMH/domain/app/controller/AppController.java index f7716c8b..093abfc4 100644 --- a/src/main/java/sopt/org/HMH/domain/app/controller/AppController.java +++ b/src/main/java/sopt/org/HMH/domain/app/controller/AppController.java @@ -31,7 +31,7 @@ public ResponseEntity> orderAddApp( @RequestHeader("OS") final String os, @RequestBody final AppArrayGoalTimeRequest request ) { - appService.addAppsByUserId(userId, request.apps(), os); + appService.addAppsAndUpdateRemainingDailyChallenge(userId, request.apps(), os); return ResponseEntity .status(AppSuccess.ADD_APP_SUCCESS.getHttpStatus()) .body(ApiResponse.success(AppSuccess.ADD_APP_SUCCESS, new EmptyJsonResponse())); diff --git a/src/main/java/sopt/org/HMH/domain/app/dto/request/AppGoalTimeRequest.java b/src/main/java/sopt/org/HMH/domain/app/dto/request/AppGoalTimeRequest.java index 598daead..3fcacf01 100644 --- a/src/main/java/sopt/org/HMH/domain/app/dto/request/AppGoalTimeRequest.java +++ b/src/main/java/sopt/org/HMH/domain/app/dto/request/AppGoalTimeRequest.java @@ -4,4 +4,4 @@ public record AppGoalTimeRequest( String appCode, Long goalTime ) { -} \ No newline at end of file +} diff --git a/src/main/java/sopt/org/HMH/domain/app/dto/response/AppGoalTimeResponse.java b/src/main/java/sopt/org/HMH/domain/app/dto/response/AppGoalTimeResponse.java index eb9790b6..9bc6e20f 100644 --- a/src/main/java/sopt/org/HMH/domain/app/dto/response/AppGoalTimeResponse.java +++ b/src/main/java/sopt/org/HMH/domain/app/dto/response/AppGoalTimeResponse.java @@ -1,12 +1,7 @@ package sopt.org.HMH.domain.app.dto.response; -import sopt.org.HMH.domain.app.domain.App; - public record AppGoalTimeResponse( String appCode, Long goalTime ) { - public static AppGoalTimeResponse of(App app) { - return new AppGoalTimeResponse(app.getAppCode(), app.getGoalTime()); - } } \ No newline at end of file diff --git a/src/main/java/sopt/org/HMH/domain/app/service/AppService.java b/src/main/java/sopt/org/HMH/domain/app/service/AppService.java index 11710293..e005c627 100644 --- a/src/main/java/sopt/org/HMH/domain/app/service/AppService.java +++ b/src/main/java/sopt/org/HMH/domain/app/service/AppService.java @@ -7,8 +7,9 @@ import sopt.org.HMH.domain.app.dto.request.AppDeleteRequest; import sopt.org.HMH.domain.app.dto.request.AppGoalTimeRequest; import sopt.org.HMH.domain.app.repository.AppRepository; +import sopt.org.HMH.domain.dailychallenge.domain.DailyChallenge; import sopt.org.HMH.domain.dailychallenge.service.DailyChallengeService; -import java.util.ArrayList; + import java.util.List; @Service @@ -21,22 +22,29 @@ public class AppService { @Transactional public void removeApp(Long userId, AppDeleteRequest request, String os) { - Long todayDailyChallengeId = dailyChallengeService.getTodayDailyChallengeByUserId(userId).getId(); - App app = appRepository.findByDailyChallengeIdAndAppCodeAndOs(todayDailyChallengeId, request.appCode(), os); + App app = appRepository.findByDailyChallengeIdAndAppCodeAndOs( + dailyChallengeService.getTodayDailyChallengeByUserId(userId).getId(), + request.appCode(), + os); appRepository.deleteById(app.getId()); } @Transactional - public void addAppsByUserId(Long userId, List requests, String os) { - List apps = new ArrayList<>(); - for (AppGoalTimeRequest request : requests) { - apps.add(App.builder() - .dailyChallenge(dailyChallengeService.getTodayDailyChallengeByUserId(userId)) - .appCode(request.appCode()) - .goalTime(request.goalTime()) - .os(os).build()); - } - appRepository.saveAll(apps); + public void addAppsAndUpdateRemainingDailyChallenge(Long userId, List requests, String os) { + dailyChallengeService.getRemainingDailyChallengesByUserId(userId).stream() + .map((dailyChallenge -> addApps(dailyChallenge, requests, os))); + } + + @Transactional + public List addApps(DailyChallenge dailyChallenge, List requests, String os) { + List appStream = requests.stream() + .map(request -> App.builder().dailyChallenge(dailyChallenge) + .appCode(request.appCode()) + .goalTime(request.goalTime()) + .os(os).build() + ).toList(); + + return appRepository.saveAll(appStream); } } \ 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 b39de0d4..d405e87f 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 @@ -10,11 +10,11 @@ import org.springframework.web.bind.annotation.RestController; 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.AddChallengeResponse; import sopt.org.HMH.domain.challenge.dto.response.ChallengeResponse; import sopt.org.HMH.domain.challenge.service.ChallengeService; import sopt.org.HMH.global.auth.UserId; import sopt.org.HMH.global.common.response.ApiResponse; +import sopt.org.HMH.global.common.response.EmptyJsonResponse; @RestController @RequiredArgsConstructor @@ -24,12 +24,17 @@ public class ChallengeController { private final ChallengeService challengeService; @PostMapping - public ResponseEntity> orderAddChallenge(@UserId final Long userId, - @RequestBody final ChallengeRequest request) { + public ResponseEntity> orderAddChallenge(@UserId final Long userId, + @RequestHeader("OS") final String os, + @RequestBody final ChallengeRequest request) { + challengeService.updateChallengeForPeriodWithInfo( + challengeService.addChallenge(userId, request.period(), request.goalTime()), + challengeService.getLastApps(userId), + os); + return ResponseEntity .status(ChallengeSuccess.ADD_CHALLENGE_SUCCESS.getHttpStatus()) - .body(ApiResponse.success(ChallengeSuccess.ADD_CHALLENGE_SUCCESS, - challengeService.addChallenge(userId, request.period(), request.goalTime()))); + .body(ApiResponse.success(ChallengeSuccess.ADD_CHALLENGE_SUCCESS, new EmptyJsonResponse())); } @GetMapping 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 3f6c3892..21cb5b33 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,8 +1,5 @@ package sopt.org.HMH.domain.challenge.dto.request; -import sopt.org.HMH.domain.app.dto.request.AppGoalTimeRequest; -import java.util.List; - public record ChallengeRequest( Integer period, Long goalTime diff --git a/src/main/java/sopt/org/HMH/domain/challenge/dto/response/AddChallengeResponse.java b/src/main/java/sopt/org/HMH/domain/challenge/dto/response/AddChallengeResponse.java deleted file mode 100644 index 58385b09..00000000 --- a/src/main/java/sopt/org/HMH/domain/challenge/dto/response/AddChallengeResponse.java +++ /dev/null @@ -1,9 +0,0 @@ -package sopt.org.HMH.domain.challenge.dto.response; - -public record AddChallengeResponse( - Long challengeId -) { - public static AddChallengeResponse of(Long challengeId) { - return new AddChallengeResponse(challengeId); - } -} \ No newline at end of file diff --git a/src/main/java/sopt/org/HMH/domain/challenge/dto/response/ChallengeResponse.java b/src/main/java/sopt/org/HMH/domain/challenge/dto/response/ChallengeResponse.java index 5788ab19..35e32acb 100644 --- a/src/main/java/sopt/org/HMH/domain/challenge/dto/response/ChallengeResponse.java +++ b/src/main/java/sopt/org/HMH/domain/challenge/dto/response/ChallengeResponse.java @@ -1,18 +1,16 @@ package sopt.org.HMH.domain.challenge.dto.response; -import lombok.AccessLevel; import lombok.Builder; -import lombok.val; import sopt.org.HMH.domain.app.dto.response.AppGoalTimeResponse; 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 java.time.LocalDateTime; import java.time.temporal.ChronoUnit; -import java.util.ArrayList; import java.util.List; -@Builder(access = AccessLevel.PRIVATE) +@Builder public record ChallengeResponse( Integer period, List statuses, @@ -21,25 +19,24 @@ public record ChallengeResponse( List apps ) { public static ChallengeResponse of(Challenge challenge, String os) { - val dailyChallenges = challenge.getDailyChallenges(); + List dailyChallenges = challenge.getDailyChallenges(); - val statuses = new ArrayList(); - for (val dailyChallenge : dailyChallenges) { - statuses.add(dailyChallenge.getStatus()); - } - - val todayIndex = calculateDaysSinceToday(challenge.getCreatedAt()); - val todayDailyChallenge = dailyChallenges.get(todayIndex); + int daysSinceToday = (int) ChronoUnit.DAYS.between(LocalDateTime.now().toLocalDate(), + challenge.getDailyChallenges().get(0).getCreatedAt().toLocalDate()); + int todayIndex = daysSinceToday >= challenge.getPeriod() ? -1 : daysSinceToday; + int dailyChallengeIndex = todayIndex == -1 ? dailyChallenges.size()-1 : todayIndex; return ChallengeResponse.builder() .period(challenge.getPeriod()) - .statuses(statuses) + .statuses(dailyChallenges.stream() + .map(dailyChallenge -> { return dailyChallenge.getStatus(); }) + .toList()) .todayIndex(todayIndex) - .goalTime(todayDailyChallenge.getGoalTime()) - .apps(todayDailyChallenge.getApps() + .goalTime(dailyChallenges.get(dailyChallengeIndex).getGoalTime()) + .apps(dailyChallenges.get(dailyChallengeIndex).getApps() .stream() .filter(app -> os.equals(app.getOs())) - .map(AppGoalTimeResponse::of) + .map(app -> new AppGoalTimeResponse(app.getAppCode(), app.getGoalTime())) .toList()) .build(); } 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 be4c71e2..462469cf 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,34 +1,55 @@ package sopt.org.HMH.domain.challenge.service; import lombok.RequiredArgsConstructor; -import lombok.val; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import sopt.org.HMH.domain.app.dto.request.AppGoalTimeRequest; +import sopt.org.HMH.domain.app.service.AppService; import sopt.org.HMH.domain.challenge.domain.Challenge; -import sopt.org.HMH.domain.challenge.dto.response.AddChallengeResponse; import sopt.org.HMH.domain.challenge.dto.response.ChallengeResponse; import sopt.org.HMH.domain.challenge.repository.ChallengeRepository; +import sopt.org.HMH.domain.dailychallenge.domain.DailyChallenge; import sopt.org.HMH.domain.dailychallenge.service.DailyChallengeService; +import java.util.List; + @Service @RequiredArgsConstructor public class ChallengeService { private final ChallengeRepository challengeRepository; private final DailyChallengeService dailyChallengeService; + private final AppService appService; + + @Transactional + public Challenge addChallenge(Long userId, Integer period, Long goalTime) { + return challengeRepository.save(Challenge.builder() + .period(period) + .goalTime(goalTime) + .userId(userId).build()); + } @Transactional - public AddChallengeResponse addChallenge(Long userId, Integer period, Long goalTime) { - Challenge challenge = challengeRepository.save(Challenge.builder() - .period(period) - .goalTime(goalTime) - .userId(userId).build()); - dailyChallengeService.addDailyChallengesForPeriod(challenge, period, goalTime); - return AddChallengeResponse.of(challenge.getId()); + public Challenge updateChallengeForPeriodWithInfo(Challenge challenge, List apps, String os) { + for (int count = 0; count < challenge.getPeriod(); count++) { + DailyChallenge dailyChallenge = dailyChallengeService.addDailyChallenge(challenge); + appService.addApps(dailyChallenge, apps, os); + } + + return challenge; + } + + public List getLastApps(Long userId) { + List lastDailyChallenges = challengeRepository.findFirstByUserIdOrderByCreatedAtDesc(userId).getDailyChallenges(); + List lastApps = lastDailyChallenges.get(lastDailyChallenges.size()-1) + .getApps() + .stream() + .map(app -> new AppGoalTimeRequest(app.getAppCode(), app.getGoalTime())) + .toList(); + return lastApps; } public ChallengeResponse getChallenge(Long userId, String os) { - val challenge = challengeRepository.findFirstByUserIdOrderByCreatedAtDesc(userId); - return ChallengeResponse.of(challenge, os); + return ChallengeResponse.of(challengeRepository.findFirstByUserIdOrderByCreatedAtDesc(userId), os); } } diff --git a/src/main/java/sopt/org/HMH/domain/dailychallenge/dto/response/DailyChallengeResponse.java b/src/main/java/sopt/org/HMH/domain/dailychallenge/dto/response/DailyChallengeResponse.java index 09aaff40..94222a7a 100644 --- a/src/main/java/sopt/org/HMH/domain/dailychallenge/dto/response/DailyChallengeResponse.java +++ b/src/main/java/sopt/org/HMH/domain/dailychallenge/dto/response/DailyChallengeResponse.java @@ -1,13 +1,12 @@ package sopt.org.HMH.domain.dailychallenge.dto.response; -import lombok.AccessLevel; import lombok.Builder; import sopt.org.HMH.domain.app.dto.response.AppGoalTimeResponse; import sopt.org.HMH.domain.dailychallenge.domain.DailyChallenge; import java.util.List; -@Builder(access = AccessLevel.PRIVATE) +@Builder public record DailyChallengeResponse( String status, Long goalTime, @@ -20,7 +19,7 @@ public static DailyChallengeResponse of(DailyChallenge dailyChallenge, String os .apps(dailyChallenge.getApps() .stream() .filter(app -> os.equals(app.getOs())) - .map(AppGoalTimeResponse::of) + .map(app -> new AppGoalTimeResponse(app.getAppCode(), app.getGoalTime())) .toList()) .build(); } 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 eeb6d5d4..4e0bc2a9 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 @@ -1,7 +1,6 @@ package sopt.org.HMH.domain.dailychallenge.service; import lombok.RequiredArgsConstructor; -import lombok.val; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import sopt.org.HMH.domain.app.domain.App; @@ -16,12 +15,11 @@ import java.time.LocalDateTime; import java.time.temporal.ChronoUnit; -import java.util.ArrayList; import java.util.List; @Service @RequiredArgsConstructor -@Transactional(readOnly = true) +@Transactional public class DailyChallengeService { private final DailyChallengeRepository dailyChallengeRepository; @@ -29,23 +27,15 @@ public class DailyChallengeService { private final AppRepository appRepository; @Transactional - public List addDailyChallengesForPeriod(Challenge challenge, Integer period, Long goalTime) { - List dailyChallenges = new ArrayList<>(); - for (int count = 0; count < period; count++) { - dailyChallenges.add(DailyChallenge.builder() - .challenge(challenge) - .goalTime(goalTime) - .build()); - } - dailyChallengeRepository.saveAll(dailyChallenges); - - return dailyChallenges; + public DailyChallenge addDailyChallenge(Challenge challenge) { + return dailyChallengeRepository.save(DailyChallenge.builder() + .challenge(challenge) + .goalTime(challenge.getGoalTime()) + .build()); } public DailyChallengeResponse getDailyChallenge(Long userId, String os) { - DailyChallenge dailyChallenge = getTodayDailyChallengeByUserId(userId); - - return DailyChallengeResponse.of(dailyChallenge, os); + return DailyChallengeResponse.of(getTodayDailyChallengeByUserId(userId), os); } @Transactional @@ -57,24 +47,31 @@ public void modifyDailyChallengeStatusFailure(Long userId) { @Transactional public void modifyDailyChallengeStatus(Long userId, List requests, String os) { DailyChallenge todayDailyChallenge = getTodayDailyChallengeByUserId(userId); - long successCount = requests.stream() - .map(request -> { + long successCount = requests.stream() + .filter(request -> { App app = appRepository.findByDailyChallengeIdAndAppCodeAndOs( todayDailyChallenge.getId(), request.appCode(), os); app.setUsageTime(request.usageTime()); - return request.usageTime() <= app.getGoalTime(); - }) - .filter(Boolean::booleanValue) - .count(); + return (request.usageTime() <= app.getGoalTime()); + }).count(); Status status = (successCount == requests.size()) ? Status.UNEARNED : Status.FAILURE; todayDailyChallenge.setStatus(status); } public DailyChallenge getTodayDailyChallengeByUserId(Long userId) { Challenge challenge = challengeRepository.findFirstByUserIdOrderByCreatedAtDesc(userId); - val startDateOfChallenge = challenge.getCreatedAt().toLocalDate(); - val todayDailyChallengeIndex = (int) ChronoUnit.DAYS.between(startDateOfChallenge, LocalDateTime.now().toLocalDate()); - - return challenge.getDailyChallenges().get(todayDailyChallengeIndex); + + return challenge.getDailyChallenges().get(calculateDaysSinceToday(challenge.getCreatedAt())); + } + + public List getRemainingDailyChallengesByUserId(Long userId) { + Challenge challenge = challengeRepository.findFirstByUserIdOrderByCreatedAtDesc(userId); + + return challenge.getDailyChallenges() + .subList(calculateDaysSinceToday(challenge.getCreatedAt()), challenge.getDailyChallenges().size()); + } + + private Integer calculateDaysSinceToday(LocalDateTime dateToCompare) { + return (int) ChronoUnit.DAYS.between(LocalDateTime.now().toLocalDate(), dateToCompare.toLocalDate()); } } \ No newline at end of file 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 72210d0b..507d4c69 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 @@ -8,6 +8,7 @@ import org.springframework.util.StringUtils; import sopt.org.HMH.domain.app.service.AppService; import sopt.org.HMH.domain.challenge.service.ChallengeService; +import sopt.org.HMH.domain.dailychallenge.service.DailyChallengeService; import sopt.org.HMH.domain.user.domain.OnboardingInfo; import sopt.org.HMH.domain.user.domain.OnboardingProblem; import sopt.org.HMH.domain.user.domain.User; @@ -43,6 +44,7 @@ public class UserService { private final ChallengeService challengeService; private final TokenService tokenService; private final AppleOAuthProvider appleOAuthProvider; + private final DailyChallengeService dailyChallengeService; private final AppService appService; @Transactional @@ -66,8 +68,12 @@ public LoginResponse signup(String socialAccessToken, SocialSignUpRequest reques User user = addUser(socialPlatform, socialId, request.name()); - challengeService.addChallenge(user.getId(), request.challengeSignUpRequest().period(), request.challengeSignUpRequest().goalTime()); - appService.addAppsByUserId(user.getId(), request.challengeSignUpRequest().apps(), os); + challengeService.updateChallengeForPeriodWithInfo( + challengeService.addChallenge(user.getId(), + request.challengeSignUpRequest().period(), + request.challengeSignUpRequest().goalTime()), + request.challengeSignUpRequest().apps(), + os); registerOnboardingInfo(request); return performLogin(socialAccessToken, socialPlatform, user);