diff --git a/src/main/java/com/process/clash/adapter/persistence/user/userexphistory/UserExpHistoryJpaRepository.java b/src/main/java/com/process/clash/adapter/persistence/user/userexphistory/UserExpHistoryJpaRepository.java index dd0634805..428aeda72 100644 --- a/src/main/java/com/process/clash/adapter/persistence/user/userexphistory/UserExpHistoryJpaRepository.java +++ b/src/main/java/com/process/clash/adapter/persistence/user/userexphistory/UserExpHistoryJpaRepository.java @@ -174,7 +174,7 @@ SELECT COALESCE(SUM(earn_exp), 0) JOIN battles b ON b.id IN (:battleIds) AND ueh.fk_user_id = :userId AND ueh.date >= (b.started_at AT TIME ZONE 'Asia/Seoul')::date - AND ueh.date < (b.end_at AT TIME ZONE 'Asia/Seoul')::date + AND ueh.date <= ((b.end_at - interval '1 microsecond') AT TIME ZONE 'Asia/Seoul')::date GROUP BY b.id """, nativeQuery = true) List findAverageExpForBattles( diff --git a/src/main/java/com/process/clash/application/compete/rival/battle/service/AcceptBattleService.java b/src/main/java/com/process/clash/application/compete/rival/battle/service/AcceptBattleService.java index 8919e7b5a..b4995226d 100644 --- a/src/main/java/com/process/clash/application/compete/rival/battle/service/AcceptBattleService.java +++ b/src/main/java/com/process/clash/application/compete/rival/battle/service/AcceptBattleService.java @@ -16,7 +16,6 @@ import org.springframework.transaction.annotation.Transactional; import java.time.Instant; -import java.time.ZoneId; import java.util.List; @Service @@ -29,7 +28,6 @@ public class AcceptBattleService implements AcceptBattleUseCase { private final RivalRepositoryPort rivalRepositoryPort; private final UserNoticeRepositoryPort userNoticeRepositoryPort; private final CompeteRefetchNotifier competeRefetchNotifier; - private final ZoneId battleZoneId; @Override public void execute(ModifyBattleData.Command command) { @@ -39,8 +37,7 @@ public void execute(ModifyBattleData.Command command) { Battle battle = battleRepositoryPort.findById(command.id()) .orElseThrow(BattleNotFoundException::new); - // 승인 시각을 배틀 시작 시각으로 사용, 종료 시각은 수락일(KST) + duration일의 자정 KST - Battle updatedBattle = battle.accept(Instant.now(), battleZoneId); + Battle updatedBattle = battle.accept(Instant.now()); battleRepositoryPort.save(updatedBattle); diff --git a/src/main/java/com/process/clash/application/compete/rival/battle/service/FindAllBattleInfoService.java b/src/main/java/com/process/clash/application/compete/rival/battle/service/FindAllBattleInfoService.java index 1e1428b43..04e70834c 100644 --- a/src/main/java/com/process/clash/application/compete/rival/battle/service/FindAllBattleInfoService.java +++ b/src/main/java/com/process/clash/application/compete/rival/battle/service/FindAllBattleInfoService.java @@ -172,7 +172,7 @@ private BattleInfo toBattleInfo( ); LocalDate expireDate = battle.endAt() != null - ? battle.endAt().atZone(battleZoneId).toLocalDate().minusDays(1) + ? battle.endAt().minusNanos(1).atZone(battleZoneId).toLocalDate() : null; return BattleInfo.of( diff --git a/src/main/java/com/process/clash/application/compete/rival/battle/service/FindDetailedBattleInfoService.java b/src/main/java/com/process/clash/application/compete/rival/battle/service/FindDetailedBattleInfoService.java index c10188a4d..22a105b43 100644 --- a/src/main/java/com/process/clash/application/compete/rival/battle/service/FindDetailedBattleInfoService.java +++ b/src/main/java/com/process/clash/application/compete/rival/battle/service/FindDetailedBattleInfoService.java @@ -39,14 +39,14 @@ public FindDetailedBattleInfoData.Result execute(FindDetailedBattleInfoData.Comm ? battle.startedAt().atZone(battleZoneId).toLocalDate() : LocalDate.now(battleZoneId); - // exp 계산 기간 종료일: DONE이면 마지막 활동일(endAt 자정의 전날), 진행 중이면 오늘까지 + // exp 계산 기간 종료일: DONE이면 endAt 기준 날짜, 진행 중이면 오늘까지 LocalDate endDate = battle.battleStatus().equals(BattleStatus.DONE) && battle.endAt() != null - ? battle.endAt().atZone(battleZoneId).toLocalDate().minusDays(1) + ? battle.endAt().minusNanos(1).atZone(battleZoneId).toLocalDate() : LocalDate.now(battleZoneId); - // 클라이언트 표시용 종료 예정일: endAt 자정의 전날 = 마지막 활동일 + // 클라이언트 표시용 종료 예정일: endAt 기준 날짜 (마지막 활동일) LocalDate expireDate = battle.endAt() != null - ? battle.endAt().atZone(battleZoneId).toLocalDate().minusDays(1) + ? battle.endAt().minusNanos(1).atZone(battleZoneId).toLocalDate() : null; // 상대방이 탈퇴해 라이벌이 삭제된 경우 상대 정보 없이 반환 diff --git a/src/main/java/com/process/clash/domain/rival/battle/entity/Battle.java b/src/main/java/com/process/clash/domain/rival/battle/entity/Battle.java index 7d9cdb84f..08f7aa121 100644 --- a/src/main/java/com/process/clash/domain/rival/battle/entity/Battle.java +++ b/src/main/java/com/process/clash/domain/rival/battle/entity/Battle.java @@ -3,8 +3,7 @@ import com.process.clash.domain.rival.battle.enums.BattleStatus; import java.time.Instant; -import java.time.LocalDate; -import java.time.ZoneId; +import java.time.temporal.ChronoUnit; public record Battle( Long id, @@ -35,9 +34,8 @@ public static Battle createDefault(int duration, Long rivalId, Long applicantId) ); } - public Battle accept(Instant now, ZoneId zoneId) { - LocalDate acceptanceDateKST = now.atZone(zoneId).toLocalDate(); - Instant endAt = acceptanceDateKST.plusDays(this.duration).atStartOfDay(zoneId).toInstant(); + public Battle accept(Instant now) { + Instant endAt = now.plus(this.duration, ChronoUnit.DAYS); return new Battle( this.id, diff --git a/src/test/java/com/process/clash/application/compete/rival/battle/service/AcceptBattleServiceTest.java b/src/test/java/com/process/clash/application/compete/rival/battle/service/AcceptBattleServiceTest.java index 884e3392f..4224ad3e5 100644 --- a/src/test/java/com/process/clash/application/compete/rival/battle/service/AcceptBattleServiceTest.java +++ b/src/test/java/com/process/clash/application/compete/rival/battle/service/AcceptBattleServiceTest.java @@ -19,7 +19,6 @@ import org.mockito.junit.jupiter.MockitoExtension; import java.time.Instant; -import java.time.ZoneId; import java.util.Collection; import java.util.Optional; @@ -47,8 +46,6 @@ class AcceptBattleServiceTest { private AcceptBattleService acceptBattleService; - private static final ZoneId BATTLE_ZONE_ID = ZoneId.of("Asia/Seoul"); - @BeforeEach void setUp() { acceptBattleService = new AcceptBattleService( @@ -56,8 +53,7 @@ void setUp() { modifyBattlePolicy, rivalRepositoryPort, userNoticeRepositoryPort, - competeRefetchNotifier, - BATTLE_ZONE_ID + competeRefetchNotifier ); } @@ -71,7 +67,7 @@ void execute_notifiesBothUsersOnAccept() { Battle battle = pendingBattle(battleId, rivalId, opponentId); when(battleRepositoryPort.findById(battleId)).thenReturn(Optional.of(battle)); - when(battleRepositoryPort.save(any(Battle.class))).thenReturn(battle.accept(Instant.now(), BATTLE_ZONE_ID)); + when(battleRepositoryPort.save(any(Battle.class))).thenReturn(battle.accept(Instant.now())); when(rivalRepositoryPort.findOpponentIdByIdAndUserId(rivalId, actor.id())).thenReturn(opponentId); when(userNoticeRepositoryPort.save(any(UserNotice.class))).thenAnswer(invocation -> invocation.getArgument(0)); @@ -96,7 +92,7 @@ void execute_savesOneNoticeOnAccept() { Battle battle = pendingBattle(battleId, rivalId, opponentId); when(battleRepositoryPort.findById(battleId)).thenReturn(Optional.of(battle)); - when(battleRepositoryPort.save(any(Battle.class))).thenReturn(battle.accept(Instant.now(), BATTLE_ZONE_ID)); + when(battleRepositoryPort.save(any(Battle.class))).thenReturn(battle.accept(Instant.now())); when(rivalRepositoryPort.findOpponentIdByIdAndUserId(rivalId, actor.id())).thenReturn(opponentId); when(userNoticeRepositoryPort.save(any(UserNotice.class))).thenAnswer(invocation -> invocation.getArgument(0)); @@ -115,7 +111,7 @@ void execute_softDeletesApplyBattleNoticeOnAccept() { Battle battle = pendingBattle(battleId, rivalId, opponentId); when(battleRepositoryPort.findById(battleId)).thenReturn(Optional.of(battle)); - when(battleRepositoryPort.save(any(Battle.class))).thenReturn(battle.accept(Instant.now(), BATTLE_ZONE_ID)); + when(battleRepositoryPort.save(any(Battle.class))).thenReturn(battle.accept(Instant.now())); when(rivalRepositoryPort.findOpponentIdByIdAndUserId(rivalId, actor.id())).thenReturn(opponentId); when(userNoticeRepositoryPort.save(any(UserNotice.class))).thenAnswer(invocation -> invocation.getArgument(0)); diff --git a/src/test/java/com/process/clash/application/compete/rival/battle/service/FindAllBattleInfoServiceTest.java b/src/test/java/com/process/clash/application/compete/rival/battle/service/FindAllBattleInfoServiceTest.java index 68e30c5c3..e8016a8b7 100644 --- a/src/test/java/com/process/clash/application/compete/rival/battle/service/FindAllBattleInfoServiceTest.java +++ b/src/test/java/com/process/clash/application/compete/rival/battle/service/FindAllBattleInfoServiceTest.java @@ -190,7 +190,7 @@ void execute_returnsEnemyTierAsRankTier_whenRankTierIsNotNone() { } @Test - @DisplayName("IN_PROGRESS 배틀의 expireDate는 endAt을 KST 기준 LocalDate로 변환한 값이다") + @DisplayName("IN_PROGRESS 배틀의 expireDate는 endAt의 KST 날짜(마지막 활동일)이다") void execute_returnsExpireDateFromEndAt_whenInProgress() { Instant endAt = Instant.now().plus(6, ChronoUnit.DAYS); Battle battle = new Battle(BATTLE_ID, Instant.now(), Instant.now(), @@ -205,7 +205,7 @@ void execute_returnsExpireDateFromEndAt_whenInProgress() { FindAllBattleInfoData.Result result = findAllBattleInfoService.execute(command()); - LocalDate expectedDate = endAt.atZone(ZoneId.of("Asia/Seoul")).toLocalDate(); + LocalDate expectedDate = endAt.minusNanos(1).atZone(ZoneId.of("Asia/Seoul")).toLocalDate(); assertThat(result.battles().get(0).expireDate()).isEqualTo(expectedDate); } diff --git a/src/test/java/com/process/clash/domain/rival/battle/entity/BattleTest.java b/src/test/java/com/process/clash/domain/rival/battle/entity/BattleTest.java index 875976a48..f65e5f13a 100644 --- a/src/test/java/com/process/clash/domain/rival/battle/entity/BattleTest.java +++ b/src/test/java/com/process/clash/domain/rival/battle/entity/BattleTest.java @@ -5,8 +5,6 @@ import org.junit.jupiter.api.Test; import java.time.Instant; -import java.time.LocalDate; -import java.time.ZoneId; import java.time.temporal.ChronoUnit; import static org.assertj.core.api.Assertions.assertThat; @@ -14,16 +12,14 @@ class BattleTest { @Test - @DisplayName("승인 즉시 startedAt이 설정되고, endAt은 수락일(KST) + duration일의 자정 KST로 설정된다") + @DisplayName("승인 즉시 startedAt이 설정되고, endAt은 수락 시각으로부터 duration일(72시간 단위) 후로 설정된다") void accept_setsStartedAtAndReturnsInProgress() { Battle battle = pendingBattle(7); Instant now = Instant.now(); - ZoneId zoneId = ZoneId.of("Asia/Seoul"); - Battle result = battle.accept(now, zoneId); + Battle result = battle.accept(now); - LocalDate acceptanceDateKST = now.atZone(zoneId).toLocalDate(); - Instant expectedEndAt = acceptanceDateKST.plusDays(7).atStartOfDay(zoneId).toInstant(); + Instant expectedEndAt = now.plus(7, ChronoUnit.DAYS); assertThat(result.battleStatus()).isEqualTo(BattleStatus.IN_PROGRESS); assertThat(result.startedAt()).isEqualTo(now);