Feat/detox-progress - #20
Conversation
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthrough디톡스 진행 조회 API와 응답 DTO를 추가하고, 미션 상태·디톡스 기간·남은 시간·겹치는 팀원 정보를 계산하는 서비스를 구현했다. 미션 인증 트랜잭션과 목표 날짜 계산을 분리·통합했으며, S3 리전 기본값과 이미지 콘텐츠 타입 검증을 보강했다. Changes디톡스 진행 조회
미션 인증 처리
S3 설정 및 이미지 검증
Estimated code review effort: 4 (Complex) | ~60 minutes Sequence Diagram(s)sequenceDiagram
participant Client
participant DetoxProgressController
participant DetoxProgressService
participant UserRepository
participant UserMissionLogRepository
participant TeamMemberRepository
Client->>DetoxProgressController: GET /api/detox/progress with X-Device-Id
DetoxProgressController->>DetoxProgressService: getProgress(deviceId)
DetoxProgressService->>UserRepository: find user by deviceId
DetoxProgressService->>UserMissionLogRepository: find current or previous mission log
DetoxProgressService->>TeamMemberRepository: findDistinctTeammatesByUserId(userId)
DetoxProgressService-->>DetoxProgressController: DetoxProgressResponse
DetoxProgressController-->>Client: ApiResponse.ok(response)
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (3 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (2)
src/main/java/com/example/hackathon/domain/detox/service/DetoxProgressService.java (1)
81-81: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win자정교차 판단 로직 중복.
!user.getDetoxEndTime().isAfter(user.getDetoxStartTime())(Line 81)와DetoxPeriod.of의endTime.isAfter(startTime)(Line 146) 조건이 동일한 "자정을 넘기는지" 여부를 각각 별도로 계산합니다. 두 곳 중 한 곳만 수정되면 로그 선택과 기간 계산이 어긋날 수 있으니 공용 헬퍼로 추출하는 것을 권장합니다.♻️ 제안 리팩터
+ private static boolean isOvernight(LocalTime startTime, LocalTime endTime) { + return !endTime.isAfter(startTime); + } + private UserMissionLog findCurrentMissionLog(User user, LocalDate today) { return userMissionLogRepository.findByUserIdAndTargetDate(user.getId(), today) .orElseGet(() -> { - if (!user.getDetoxEndTime().isAfter(user.getDetoxStartTime())) { + if (isOvernight(user.getDetoxStartTime(), user.getDetoxEndTime())) { return userMissionLogRepository .findByUserIdAndTargetDate(user.getId(), today.minusDays(1)) .orElseThrow(() -> new BusinessException(ErrorCode.MISSION_ERROR_404_NOT_FOUND)); } throw new BusinessException(ErrorCode.MISSION_ERROR_404_NOT_FOUND); }); }private static DetoxPeriod of(LocalDate targetDate, LocalTime startTime, LocalTime endTime) { - LocalDate endDate = endTime.isAfter(startTime) ? targetDate : targetDate.plusDays(1); + LocalDate endDate = isOvernight(startTime, endTime) ? targetDate.plusDays(1) : targetDate; return new DetoxPeriod(Also applies to: 146-146
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/main/java/com/example/hackathon/domain/detox/service/DetoxProgressService.java` at line 81, 중복된 자정 교차 판단 로직을 공용 헬퍼로 통합하세요. DetoxProgressService의 조건문과 DetoxPeriod.of 내부의 endTime.isAfter(startTime) 판단이 동일한 헬퍼를 사용하도록 수정해 로그 선택과 기간 계산이 항상 일치하게 하세요.src/test/java/com/example/hackathon/domain/detox/service/DetoxProgressServiceTest.java (1)
35-46: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick windetoxStartTime/detoxEndTime 미설정 케이스 테스트 부재.
DetoxProgressService.findUser(Line 72-74)의MISSION_ERROR_400_DETOX_TIME_NOT_SET분기에 대한 테스트가 없습니다. 다른 예외 분기들과 동일한 패턴으로 쉽게 추가할 수 있는 케이스입니다.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/test/java/com/example/hackathon/domain/detox/service/DetoxProgressServiceTest.java` around lines 35 - 46, DetoxProgressService.findUser의 detoxStartTime/detoxEndTime 미설정 분기를 검증하는 테스트가 없습니다. 기존 예외 분기 테스트 패턴을 따라 해당 시간 값이 설정되지 않은 사용자를 구성하고, MISSION_ERROR_400_DETOX_TIME_NOT_SET 예외와 관련 메시지가 반환되는지 검증하는 테스트를 DetoxProgressServiceTest에 추가하세요.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In
`@src/main/java/com/example/hackathon/domain/detox/service/DetoxProgressService.java`:
- Line 81: 중복된 자정 교차 판단 로직을 공용 헬퍼로 통합하세요. DetoxProgressService의 조건문과
DetoxPeriod.of 내부의 endTime.isAfter(startTime) 판단이 동일한 헬퍼를 사용하도록 수정해 로그 선택과 기간
계산이 항상 일치하게 하세요.
In
`@src/test/java/com/example/hackathon/domain/detox/service/DetoxProgressServiceTest.java`:
- Around line 35-46: DetoxProgressService.findUser의 detoxStartTime/detoxEndTime
미설정 분기를 검증하는 테스트가 없습니다. 기존 예외 분기 테스트 패턴을 따라 해당 시간 값이 설정되지 않은 사용자를 구성하고,
MISSION_ERROR_400_DETOX_TIME_NOT_SET 예외와 관련 메시지가 반환되는지 검증하는 테스트를
DetoxProgressServiceTest에 추가하세요.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 74cb60f6-4e57-464c-a23c-863fb7755953
📒 Files selected for processing (7)
src/main/java/com/example/hackathon/domain/detox/controller/DetoxProgressController.javasrc/main/java/com/example/hackathon/domain/detox/dto/DetoxProgressResponse.javasrc/main/java/com/example/hackathon/domain/detox/dto/OverlappingMemberResponse.javasrc/main/java/com/example/hackathon/domain/detox/service/DetoxProgressService.javasrc/main/java/com/example/hackathon/domain/team/repository/TeamMemberRepository.javasrc/main/java/com/example/hackathon/global/exception/ErrorCode.javasrc/test/java/com/example/hackathon/domain/detox/service/DetoxProgressServiceTest.java
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
`@src/main/java/com/example/hackathon/domain/mission/service/MissionTargetDateResolver.java`:
- Line 20: MissionTargetDateResolver의 날짜 판정이 마감 시각과 같은 순간에 오늘 날짜로 전환됩니다. 해당
resolver 메서드의 조건을 isAfter 기준으로 변경해 now가 yesterdayDeadline을 초과한 경우에만 오늘 날짜를 반환하고,
동일한 시각에는 전날 날짜를 반환하도록 수정하세요. 마감 시각과 정확히 일치하는 경계 조건 테스트도 추가하세요.
In
`@src/test/java/com/example/hackathon/domain/image/service/S3StorageServiceTest.java`:
- Around line 70-78: Update uploadReturnsUrlWithConfiguredRegion to configure a
region different from the default, such as us-west-2, and assert the generated
URL contains that configured region. Ensure the test setup exercises
resolvedRegion() using the non-default configuration so it fails if the
configured value is ignored.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: e6720590-e000-4b8f-a386-183729a8246e
📒 Files selected for processing (10)
src/main/java/com/example/hackathon/domain/image/config/S3Config.javasrc/main/java/com/example/hackathon/domain/image/config/S3Properties.javasrc/main/java/com/example/hackathon/domain/image/service/S3StorageService.javasrc/main/java/com/example/hackathon/domain/mission/service/MissionCertificationService.javasrc/main/java/com/example/hackathon/domain/mission/service/MissionCertificationTransactionService.javasrc/main/java/com/example/hackathon/domain/mission/service/MissionService.javasrc/main/java/com/example/hackathon/domain/mission/service/MissionTargetDateResolver.javasrc/main/java/com/example/hackathon/global/exception/BusinessException.javasrc/test/java/com/example/hackathon/domain/image/service/S3StorageServiceTest.javasrc/test/java/com/example/hackathon/domain/mission/service/MissionCertificationServiceTest.java
| LocalDateTime yesterdayDeadline = LocalDateTime | ||
| .of(today.minusDays(1), user.getDetoxStartTime()) | ||
| .plusMinutes(DEADLINE_MINUTES); | ||
| return now.isBefore(yesterdayDeadline) ? today.minusDays(1) : today; |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
마감 시각을 전날 미션에 포함하세요.
다른 검증은 deadlineAt 이후에만 실패하지만, 여기서는 정확히 마감 시각에 오늘 날짜를 반환합니다. 예를 들어 23:55 시작이면 다음 날 00:05에 오늘 로그로 전환되어 BEFORE_DETOX_START가 발생합니다. isAfter 기준으로 맞추고 해당 경계 테스트를 추가하세요.
수정 예시
- return now.isBefore(yesterdayDeadline) ? today.minusDays(1) : today;
+ return now.isAfter(yesterdayDeadline) ? today : today.minusDays(1);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| return now.isBefore(yesterdayDeadline) ? today.minusDays(1) : today; | |
| return now.isAfter(yesterdayDeadline) ? today : today.minusDays(1); |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@src/main/java/com/example/hackathon/domain/mission/service/MissionTargetDateResolver.java`
at line 20, MissionTargetDateResolver의 날짜 판정이 마감 시각과 같은 순간에 오늘 날짜로 전환됩니다. 해당
resolver 메서드의 조건을 isAfter 기준으로 변경해 now가 yesterdayDeadline을 초과한 경우에만 오늘 날짜를 반환하고,
동일한 시각에는 전날 날짜를 반환하도록 수정하세요. 마감 시각과 정확히 일치하는 경계 조건 테스트도 추가하세요.
| void uploadReturnsUrlWithConfiguredRegion() { | ||
| when(s3Client.putObject(any(PutObjectRequest.class), any(software.amazon.awssdk.core.sync.RequestBody.class))) | ||
| .thenReturn(PutObjectResponse.builder().build()); | ||
| MockMultipartFile image = new MockMultipartFile("image", "proof.jpg", "image/jpeg", new byte[]{1}); | ||
|
|
||
| String imageUrl = storageService.uploadMissionImage(15L, image); | ||
|
|
||
| assertThat(imageUrl).startsWith("https://test-bucket.s3.ap-northeast-2.amazonaws.com/mission/15/") | ||
| .endsWith(".jpg"); |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
구성 리전을 기본값과 다른 값으로 검증하세요.
현재 ap-northeast-2는 기본값과 같아서 resolvedRegion()이 구성값을 무시해도 테스트가 통과합니다.
수정 예시
- S3Properties properties = new S3Properties(
- "ap-northeast-2",
+ S3Properties properties = new S3Properties(
+ "us-west-2",
new S3Properties.S3("test-bucket", "access", "secret", 300)
);
...
- assertThat(imageUrl).startsWith("https://test-bucket.s3.ap-northeast-2.amazonaws.com/mission/15/")
+ assertThat(imageUrl).startsWith("https://test-bucket.s3.us-west-2.amazonaws.com/mission/15/")🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@src/test/java/com/example/hackathon/domain/image/service/S3StorageServiceTest.java`
around lines 70 - 78, Update uploadReturnsUrlWithConfiguredRegion to configure a
region different from the default, such as us-west-2, and assert the generated
URL contains that configured region. Ensure the test setup exercises
resolvedRegion() using the non-default configuration so it fails if the
configured value is ignored.
📌 개요
🛠️ 주요 변경 사항
디톡스 진행 상태 조회 API 추가
GET /api/detox/progressX-Device-Id헤더를 통해 사용자 식별Clock을 사용해 서버 현재 시각 계산디톡스 진행 시간 계산
targetDate와 사용자 디톡스 시간을 이용해 실제 시작·종료 시각 계산inProgress = true반환startDateTime <= now < endDateTimeremainingSeconds = 0반환디톡스 시간이 겹치는 팀원 조회
시간 구간 중첩 처리
다음 조건을 모두 만족하면 두 디톡스 시간이 겹치는 것으로 처리
사용자 시작 시각 < 팀원 종료 시각팀원 시작 시각 < 사용자 종료 시각경계 시각만 맞닿는 경우에는 겹치지 않는 것으로 처리
ex.
22:00 ~ 23:0023:00 ~ 24:00자정을 넘기는 디톡스 시간 지원
23:00 ~ 01:00과 같은 디톡스 구간 지원targetDate에 결합팀원 조회 성능 개선
distinct를 사용해 DB 조회 단계에서 중복 제거userId를 기준으로 중복 제거📦 응답 정보
진행 상태
missionLogIdstatusinProgressstartDateTimeendDateTimeendTimeremainingSecondstitleMessageunlockMessageoverlappingMemberCountoverlappingMembers겹치는 팀원 정보
userIdnicknamedetoxStartTimedetoxEndTimeoverlapStartDateTimeoverlapEndDateTimeSummary by CodeRabbit