Skip to content

Comments

[WTH-148] schedule 도메인 코틀린 마이그레이션#15

Open
soo0711 wants to merge 57 commits intodevfrom
refactor/WTH-148-schedule-도메인-코틀린-마이그레이션

Hidden character warning

The head ref may contain hidden characters: "refactor/WTH-148-schedule-\ub3c4\uba54\uc778-\ucf54\ud2c0\ub9b0-\ub9c8\uc774\uadf8\ub808\uc774\uc158"
Open

[WTH-148] schedule 도메인 코틀린 마이그레이션#15
soo0711 wants to merge 57 commits intodevfrom
refactor/WTH-148-schedule-도메인-코틀린-마이그레이션

Conversation

@soo0711
Copy link
Collaborator

@soo0711 soo0711 commented Feb 20, 2026

📌 Summary

어떤 작업인지 한 줄 요약해 주세요.

Schedule 도메인 Java → Kotlin 마이그레이션 및 아키텍처 구조 변경

📝 Changes

변경사항을 what, why, how로 구분해 작성해 주세요.

What

  • Schedule 도메인 전체 Java → Kotlin 전환
  • UseCase command/query 분리
  • Event/Session API 분리 (/events, /sessions)
  • 비관적 락 적용 (SessionRepository, AttendanceRepository)

Why

  • 기존에는 일반 일정(Event)과 정기모임(Session)이 하나의 Schedule 엔티티로 묶여 있었고 API도 /schedules로 통합되어 있었습니다.
  • 두 개념은 성격이 다르고 (Event는 단순 일정, Session은 출석과 연결된 정기모임) 각각 독립적인 생명주기를 가지므로 분리가 필요했습니다.

How

  • Schedule 엔티티를 Event, Session으로 분리하고 각각 독립 엔티티로 전환
  • Session은 출석(Attendance)과 연관되므로 Attendance 도메인으로 이동
  • API를 /api/v4/events, /api/v4/sessions으로 분리하고 각각 전용 Controller 생성
  • 조회/수정/삭제 UseCase도 ManageEventUseCase, ManageSessionUseCase로 분리
  • 조회 로직은 GetScheduleQueryService, GetSessionQueryService로 분리하여 Command/Query 책임 명확화

📸 Screenshots / Logs

필요시 스크린샷 or 로그를 첨부해주세요.

💡 Reviewer 참고사항

리뷰에 참고할 내용을 작성해주세요.

  • @ScheduleTimeCheck 관련 코드(ScheduleTimeCheck, ScheduleTimeCheckValidator, ScheduleTimeRequest)는 현재 사용되지 않고 있습니다.
  • Validator는 ScheduleTimeRequest 타입 필드를 대상으로 동작하도록 구현되어 있으나, ScheduleSaveRequest / ScheduleUpdateRequest에는 해당 타입 필드가 없어 실제로 검증이 수행되지 않습니다.
  • 현재 시간 유효성 검증은 Event.create() 내부의 require(!end.isBefore(start))에서만 이루어지고 있습니다.
  • DTO 구조를 변경해 어노테이션을 적용할지, 아니면 관련 코드를 제거할지 논의해봐야 할 것 같습니다!
  • 적용한다면 start/end를 ScheduleTimeRequest로 묶어 아래처럼 변경
  // ScheduleSaveRequest.kt
  data class ScheduleSaveRequest(
      val title: String,
      val content: String,
      val location: String,
      val cardinal: Int,
      @field:Valid
      @field:ScheduleTimeCheck
      val time: ScheduleTimeRequest,  // start/end를 중첩 객체로
  )
  // 요청 바디 구조가 아래처럼 변경됨 (Breaking Change)
  {
    "title": "MT",
    "time": {
      "start": "2024-03-01T10:00:00",
      "end": "2024-03-01T12:00:00"
    }
  }
  • 제거한다면 ScheduleTimeCheck, ScheduleTimeCheckValidator, ScheduleTimeRequest 세 파일 삭제. 시간 검증은 Entity 레이어(Event.create())에만 의존.

  • SessionReader / findAllBySessionIn: QR 코드 출석 기능 구현 예정으로 남겨둔 메서드입니다.

  • Schedule 유의미한 UseCase 단위 테스트가 없어 테스트를 추가하지 않았습니다.

✅ Checklist

  • PR 제목 설정 완료 (WTH-123 인증 필터 설정)
  • 테스트 구현 완료
  • 리뷰어 등록 완료
  • 자체 코드 리뷰 완료

Summary by CodeRabbit

  • 새로운 기능

    • 세션 기반 정기모임 기능 추가: 관리자용 세션 CRUD, 세션 상세·목록 조회 및 세션 응답 포맷 제공
    • 일정(이벤트) 상세 조회 및 월별·연도별 일정 조회 API 추가
    • 통합 출석 관리 도입: 체크인, 자동 마감, 출석 상태 일괄 업데이트 기능 제공
  • 개선사항

    • 출석·세션 흐름을 리포지토리/엔티티 중심으로 재구성하여 안정성 향상
    • API 버전 정리(v4) 및 시간 유효성 검증 강화
  • 제거

    • 이전의 레거시 일정/정기모임 API 및 매퍼/서비스/컨트롤러 제거 (대체된 기능은 위에 포함)

@soo0711 soo0711 self-assigned this Feb 20, 2026
@soo0711 soo0711 added the 🔨 Refactor 코드 구조 개선 및 리팩토링 label Feb 20, 2026
@soo0711 soo0711 requested a review from hyxklee February 20, 2026 10:53
@coderabbitai
Copy link

coderabbitai bot commented Feb 20, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉


📝 Walkthrough

Walkthrough

스케줄·출석 도메인을 Java에서 Kotlin으로 대규모 마이그레이션 및 리팩터링했습니다. Meeting→Session, Status→AttendanceStatus로 모델 변경; Java 기반 엔티티/레포/서비스/컨트롤러/DTO 대거 삭제 및 Kotlin 기반 구현(ManageAttendance/ManageSession/ManageEvent, Session/Attendance 엔티티/레포/매퍼/컨트롤러 등)과 API 버전 v1→v4 전환이 포함됩니다.

Changes

Cohort / File(s) Summary
Java 스케줄/출석 삭제
src/main/java/com/weeth/domain/schedule/..., src/main/java/com/weeth/domain/attendance/..., src/main/java/com/weeth/domain/attendance/...
기존 Java 기반 스케줄·출석 도메인 파일(엔티티, DTO, 매퍼, 서비스, usecase, 컨트롤러, 예외, 레포지토리, validator 등) 대부분 삭제.
Kotlin 스케줄 추가/변경
src/main/kotlin/com/weeth/domain/schedule/... (annotation, dto/request, dto/response, exception, mapper, usecase, validator, domain/entity, repository, presentation)
Kotlin으로 재구현된 스케줄 도메인 추가(예: Event, Session, Type enum, mappers, DTOs, ManageEventUseCase, GetScheduleQueryService, ScheduleMapper 등), API 경로을 v4로 노출.
Kotlin 출석(세션) 도입·리포지터리 변경
src/main/kotlin/com/weeth/domain/attendance/... (domain/entity/Attendance.kt, Session.kt, enums, repository/SessionRepository.kt, AttendanceRepository.kt, usecase, presentation)
Meeting→Session, Status→AttendanceStatus 전환. Attendance/Session 엔티티 행위 추가(생성팩토리, attend/absent/close 등), 세션 기반 쿼리·락 메서드 및 레포지토리(락/쿼리 힌트 포함) 추가.
UseCase 통합·신규
src/main/kotlin/.../ManageAttendanceUseCase.kt, ManageSessionUseCase.kt, ManageEventUseCase.kt, GetScheduleQueryService.kt
체크인/마감/상태업데이트 로직 통합 및 세션 관리·일정 조회용 신규 usecase/서비스 추가.
컨트롤러·엔드포인트 변경
src/main/kotlin/.../attendance/presentation/*, src/main/kotlin/.../schedule/presentation/*
관리자·사용자용 컨트롤러가 Session/Manage* usecase로 전환, 엔드포인트 베이스 일부를 /api/v4로 변경 및 신규 SessionController/Admin 추가.
테스트·픽스처 업데이트
src/test/kotlin/...
테스트와 픽스처를 Meeting→Session, Status→AttendanceStatus로 변경; 일부 테스트 삭제·리팩터링 및 fixture 함수명 변경.
User 도메인 변경
src/main/java/com/weeth/domain/user/...
UserManageUseCaseImpl에서 AttendanceSaveService/MeetingGetService 대신 AttendanceRepository/SessionRepository 직접 사용; User 엔티티에서 attendances 컬렉션 제거.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant Client as "Client"
    participant Controller as "AttendanceController"
    participant UseCase as "ManageAttendanceUseCase"
    participant SessionRepo as "SessionRepository"
    participant AttendanceRepo as "AttendanceRepository"
    participant DB as "Database"

    Client->>Controller: POST /api/v4/attendances/check-in (code)
    Controller->>UseCase: checkIn(userId, code)
    UseCase->>SessionRepo: find current session by time (now..now+10m)
    SessionRepo-->>UseCase: session?
    alt session found
      UseCase->>AttendanceRepo: findBySessionAndUserWithLock(session, user)
      AttendanceRepo-->>UseCase: attendance
      UseCase->>AttendanceRepo: save/update attendance
      AttendanceRepo-->>DB: persist
      UseCase-->>Controller: success
      Controller-->>Client: 200 SESSION_FIND_SUCCESS
    else not found
      UseCase-->>Controller: throw AttendanceNotFoundException
      Controller-->>Client: 404 error
    end
Loading
sequenceDiagram
    autonumber
    participant Admin as "Admin Client"
    participant Ctrl as "SessionAdminController"
    participant UseCase as "ManageSessionUseCase"
    participant UserSvc as "UserGetService"
    participant SessionRepo as "SessionRepository"
    participant AttendanceRepo as "AttendanceRepository"
    participant DB as "Database"

    Admin->>Ctrl: POST /api/v4/admin/sessions (ScheduleSaveRequest)
    Ctrl->>UseCase: create(request, adminId)
    UseCase->>UserSvc: find users by cardinal
    UserSvc-->>UseCase: List<User>
    UseCase->>SessionRepo: save(session)
    SessionRepo-->>DB: persist
    UseCase->>AttendanceRepo: saveAll(attendances)
    AttendanceRepo-->>DB: persist
    UseCase-->>Ctrl: Void
    Ctrl-->>Admin: 200 SESSION_SAVE_SUCCESS
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Possibly related PRs

Suggested reviewers

  • hyxklee
  • dalzzy

Poem

🐇 깡충깡충 코드밭을 뛰어가며,
난 토끼, Kotlin 씨앗을 뿌렸네.
Meeting은 Session으로 깔끔히 변하고,
v4 길 따라 API들이 춤추네.
당근 한 입에 테스트가 웃고, 새벽 빛이 든다.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 2.20% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed PR 제목은 'WTH-148 schedule 도메인 코틀린 마이그레이션'으로 변경사항의 주요 내용(Schedule 도메인의 Java→Kotlin 마이그레이션)을 명확하게 요약하고 있습니다.
Description check ✅ Passed PR 설명은 Summary, Changes(What/Why/How), Reviewer 참고사항, Checklist를 포함한 요구되는 구조를 따르고 있으며, 마이그레이션 목적과 아키텍처 변경 사항을 상세히 설명하고 있습니다.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch refactor/WTH-148-schedule-도메인-코틀린-마이그레이션

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 12

🧹 Nitpick comments (11)
src/main/kotlin/com/weeth/domain/schedule/application/dto/response/EventResponse.kt (1)

7-30: SessionResponse와의 일관성 확인 필요

SessionResponse@JsonInclude(JsonInclude.Include.NON_NULL)을 사용하여 null 필드를 응답에서 제외하지만, EventResponse에는 해당 어노테이션이 없습니다. name, createdAt, modifiedAt 등 nullable 필드가 있으므로 동일한 직렬화 동작을 원한다면 일관성을 맞추는 것이 좋습니다.

💡 선택적 수정 제안
 package com.weeth.domain.schedule.application.dto.response
 
+import com.fasterxml.jackson.annotation.JsonInclude
 import com.weeth.domain.schedule.domain.entity.enums.Type
 import io.swagger.v3.oas.annotations.media.Schema
 import java.time.LocalDateTime
 
+@JsonInclude(JsonInclude.Include.NON_NULL)
 data class EventResponse(
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/main/kotlin/com/weeth/domain/schedule/application/dto/response/EventResponse.kt`
around lines 7 - 30, EventResponse lacks the JsonInclude setting used by
SessionResponse, causing nullable fields like name, createdAt, and modifiedAt to
be serialized as null; update the EventResponse data class to use
`@JsonInclude`(JsonInclude.Include.NON_NULL) (and add the corresponding import for
com.fasterxml.jackson.annotation.JsonInclude) so that null properties are
omitted from JSON responses, matching SessionResponse behavior.
src/main/kotlin/com/weeth/domain/attendance/application/usecase/command/ManageAttendanceUseCase.kt (2)

52-59: 예외 이름 불일치 및 메모리 내 필터링 비효율

  1. MeetingNotFoundException을 Session 관련 로직에서 사용하고 있습니다. PR 목적에 따르면 Meeting을 Session으로 통일하고 있으므로, SessionNotFoundException으로 변경하는 것이 적절합니다.

  2. findAllByCardinalOrderByStartAsc로 모든 Session을 조회한 후 메모리에서 날짜 필터링을 수행합니다. 쿼리 레벨에서 필터링하면 더 효율적입니다.

♻️ 개선 제안
  1. 예외 타입 변경:
-import com.weeth.domain.schedule.application.exception.MeetingNotFoundException
+import com.weeth.domain.attendance.application.exception.SessionNotFoundException
  1. Repository에 날짜 필터링 쿼리 추가 고려:
// SessionRepository에 추가
fun findByCardinalAndStartDate(cardinal: Int, date: LocalDate): Session?
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/main/kotlin/com/weeth/domain/attendance/application/usecase/command/ManageAttendanceUseCase.kt`
around lines 52 - 59, The code uses MeetingNotFoundException and loads all
sessions then filters in memory via
sessionRepository.findAllByCardinalOrderByStartAsc; change the exception to
SessionNotFoundException (replace MeetingNotFoundException with
SessionNotFoundException in ManageAttendanceUseCase) and move the date filtering
into the repository by adding a method such as
SessionRepository.findByCardinalAndStartDate(cardinal: Int, date: LocalDate):
Session?; then call that new repository method to get targetSession and keep the
subsequent attendanceRepository.findAllBySessionAndUserStatus(targetSession,
Status.ACTIVE) and closePendingAttendances(attendances) logic unchanged.

26-45: checkIn 메서드의 LocalDateTime.now() 사용

메서드 내부에서 LocalDateTime.now()를 직접 호출하면 테스트가 어렵습니다. Clock을 주입하거나 파라미터로 현재 시간을 받는 것이 테스트 용이성을 높입니다.

💡 테스트 용이성 개선 제안
 `@Service`
 class ManageAttendanceUseCase(
     private val userGetService: UserGetService,
     private val sessionRepository: SessionRepository,
     private val attendanceRepository: AttendanceRepository,
+    private val clock: Clock = Clock.systemDefaultZone(),
 ) {
     `@Transactional`
     fun checkIn(
         userId: Long,
         code: Int,
     ) {
         val user = userGetService.find(userId)
-        val now = LocalDateTime.now()
+        val now = LocalDateTime.now(clock)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/main/kotlin/com/weeth/domain/attendance/application/usecase/command/ManageAttendanceUseCase.kt`
around lines 26 - 45, The checkIn method currently calls LocalDateTime.now()
directly which hampers testability; modify ManageAttendanceUseCase.checkIn to
accept the "now" as a dependency (either inject a java.time.Clock into the class
and use LocalDateTime.now(clock) or add a LocalDateTime parameter to checkIn)
and update all callers/tests accordingly, then use that injected clock/parameter
when calling attendanceRepository.findCurrentByUserId(userId, now,
now.plusMinutes(10)) so the time is controllable in tests; keep all existing
logic (userGetService.find, todayAttendance handling, lockedAttendance retrieval
and attend() calls) unchanged aside from replacing direct LocalDateTime.now()
usage.
src/main/kotlin/com/weeth/domain/schedule/domain/repository/EventRepository.kt (1)

8-11: 파라미터 이름이 메서드 정의와 불일치

메서드 이름 findByStartLessThanEqualAndEndGreaterThanEqualStart <= ? AND End >= ? 조건을 의미하지만, 파라미터 이름이 (end, start) 순서로 되어 있어 혼란스럽습니다.

실제 동작: entity.start <= end AND entity.end >= start (기간 겹침 쿼리)

파라미터 이름을 명확하게 수정하는 것이 좋습니다.

♻️ 파라미터 이름 명확화 제안
 interface EventRepository : JpaRepository<Event, Long> {
     fun findByStartLessThanEqualAndEndGreaterThanEqualOrderByStartAsc(
-        end: LocalDateTime,
-        start: LocalDateTime,
+        rangeEnd: LocalDateTime,
+        rangeStart: LocalDateTime,
     ): List<Event>

또는 @Query를 사용하여 의도를 명확히 할 수 있습니다:

`@Query`("SELECT e FROM Event e WHERE e.start <= :rangeEnd AND e.end >= :rangeStart ORDER BY e.start ASC")
fun findOverlappingEvents(rangeStart: LocalDateTime, rangeEnd: LocalDateTime): List<Event>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/main/kotlin/com/weeth/domain/schedule/domain/repository/EventRepository.kt`
around lines 8 - 11, The repository method
findByStartLessThanEqualAndEndGreaterThanEqualOrderByStartAsc has its parameters
declared as (end: LocalDateTime, start: LocalDateTime) which is confusing and
mismatches the method semantics (entity.start <= rangeEnd AND entity.end >=
rangeStart); rename the parameters to meaningful names (e.g., rangeStart:
LocalDateTime, rangeEnd: LocalDateTime) and/or reorder them to (rangeStart,
rangeEnd) so callers are clear, or replace the derived query with an explicit
`@Query` and rename the method to findOverlappingEvents(rangeStart, rangeEnd) to
make intent explicit—update all call sites to use the new names/signature.
src/test/kotlin/com/weeth/domain/schedule/fixture/ScheduleTestFixture.kt (1)

43-57: createSessionSession.create() 팩토리 메서드를 우회합니다.

createEventEvent.create() 팩토리 메서드를 사용하지만, createSession은 생성자를 직접 호출합니다. 의도적인 설계라면(예: 테스트에서 다양한 status를 설정하기 위해) 괜찮지만, 팩토리 메서드의 유효성 검증(제목 필수, 종료 시간 체크)을 우회하게 됩니다.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/test/kotlin/com/weeth/domain/schedule/fixture/ScheduleTestFixture.kt`
around lines 43 - 57, createSession currently instantiates Session via the
constructor, bypassing the Session.create() factory validation (title required,
end time checks); change createSession in ScheduleTestFixture to call
Session.create(...) with the same parameters so factory validations run, then if
an id must be injected for tests keep the ReflectionTestUtils.setField(session,
"id", id) after creation; ensure you adapt parameter order/names to match
Session.create and preserve any status/start/end values passed into
createSession.
src/main/kotlin/com/weeth/domain/attendance/application/usecase/command/ManageSessionUseCase.kt (1)

9-9: 예외 클래스명과 도메인 용어가 불일치합니다.

Session 엔티티를 사용하지만 MeetingNotFoundException을 던지고 있습니다. 도메인 용어 통일을 위해 SessionNotFoundException으로 리네이밍하는 것을 고려해주세요.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/main/kotlin/com/weeth/domain/attendance/application/usecase/command/ManageSessionUseCase.kt`
at line 9, The code imports and throws MeetingNotFoundException while operating
on Session domain — rename and align the exception to SessionNotFoundException:
create/rename the exception class from MeetingNotFoundException to
SessionNotFoundException, update the import in ManageSessionUseCase (and any
other callers) to
com.weeth.domain.attendance.application.exception.SessionNotFoundException, and
change any throw sites that reference MeetingNotFoundException (e.g., in
ManageSessionUseCase methods) to throw SessionNotFoundException so domain
terminology is consistent.
src/main/kotlin/com/weeth/domain/attendance/domain/repository/SessionRepository.kt (1)

21-24: 파라미터 이름이 메서드 시그니처와 일치하지 않아 혼란을 줍니다.

메서드명은 findByStartLessThanEqualAndEndGreaterThanEqual인데, 파라미터가 (end, start) 순서입니다. Spring Data JPA는 메서드명의 순서대로 파라미터를 바인딩하므로:

  • s.start <= end (첫 번째 파라미터)
  • s.end >= start (두 번째 파라미터)

의도한 동작이라면 파라미터명을 명확하게 수정하거나, @Query로 명시적 JPQL을 작성하는 것을 권장합니다.

♻️ 명확한 네이밍 제안
-    fun findByStartLessThanEqualAndEndGreaterThanEqualOrderByStartAsc(
-        end: LocalDateTime,
-        start: LocalDateTime,
-    ): List<Session>
+    `@Query`("SELECT s FROM Session s WHERE s.start <= :targetEnd AND s.end >= :targetStart ORDER BY s.start ASC")
+    fun findOverlappingSessions(
+        targetStart: LocalDateTime,
+        targetEnd: LocalDateTime,
+    ): List<Session>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/main/kotlin/com/weeth/domain/attendance/domain/repository/SessionRepository.kt`
around lines 21 - 24, The method in SessionRepository named
findByStartLessThanEqualAndEndGreaterThanEqualOrderByStartAsc has its parameters
in the wrong order (currently end, start), which will bind incorrectly in Spring
Data JPA; fix by reordering the parameters to (start: LocalDateTime, end:
LocalDateTime) so the first argument maps to "start <= end" and the second to
"end >= start", or alternatively add an explicit `@Query` on the SessionRepository
method with named parameters to guarantee correct binding if you prefer keeping
the current parameter order.
src/main/kotlin/com/weeth/domain/schedule/application/mapper/SessionMapper.kt (1)

14-44: toResponsetoAdminResponse 간 코드 중복 개선 권장

두 메서드는 code 필드 값만 다르고 나머지는 동일합니다. 중복을 줄이기 위해 private 헬퍼 함수로 추출하는 것을 권장합니다.

♻️ 리팩토링 제안
+    private fun buildResponse(session: Session, includeCode: Boolean): SessionResponse =
+        SessionResponse(
+            id = session.id,
+            title = session.title,
+            content = session.content,
+            location = session.location,
+            name = session.user?.name,
+            cardinal = session.cardinal,
+            type = Type.MEETING,
+            code = if (includeCode) session.code else null,
+            start = session.start,
+            end = session.end,
+            createdAt = session.createdAt,
+            modifiedAt = session.modifiedAt,
+        )
+
-    fun toResponse(session: Session): SessionResponse =
-        SessionResponse(
-            id = session.id,
-            title = session.title,
-            content = session.content,
-            location = session.location,
-            name = session.user?.name,
-            cardinal = session.cardinal,
-            type = Type.MEETING,
-            code = null,
-            start = session.start,
-            end = session.end,
-            createdAt = session.createdAt,
-            modifiedAt = session.modifiedAt,
-        )
-
-    fun toAdminResponse(session: Session): SessionResponse =
-        SessionResponse(
-            id = session.id,
-            title = session.title,
-            content = session.content,
-            location = session.location,
-            name = session.user?.name,
-            cardinal = session.cardinal,
-            type = Type.MEETING,
-            code = session.code,
-            start = session.start,
-            end = session.end,
-            createdAt = session.createdAt,
-            modifiedAt = session.modifiedAt,
-        )
+    fun toResponse(session: Session): SessionResponse = buildResponse(session, includeCode = false)
+
+    fun toAdminResponse(session: Session): SessionResponse = buildResponse(session, includeCode = true)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/main/kotlin/com/weeth/domain/schedule/application/mapper/SessionMapper.kt`
around lines 14 - 44, The two methods toResponse and toAdminResponse in
SessionMapper duplicate all fields except for code; extract a private helper
(e.g., private fun buildResponse(session: Session, code: String?)) that
constructs and returns a SessionResponse using session fields and the provided
code, then update toResponse to call buildResponse(session, null) and
toAdminResponse to call buildResponse(session, session.code) to remove
duplication.
src/main/kotlin/com/weeth/domain/attendance/application/usecase/query/GetAttendanceQueryService.kt (1)

54-54: Kotlin 관용구 사용 고려.

findById(...).orElseThrow { ... } 대신 findByIdOrNull과 Elvis 연산자를 사용하면 더 Kotlin스럽습니다.

♻️ Kotlin 관용구 적용
-    fun findAllAttendanceBySession(sessionId: Long): List<AttendanceInfoResponse> {
-        val session = sessionRepository.findById(sessionId).orElseThrow { MeetingNotFoundException() }
+    fun findAllAttendanceBySession(sessionId: Long): List<AttendanceInfoResponse> {
+        val session = sessionRepository.findByIdOrNull(sessionId)
+            ?: throw MeetingNotFoundException()
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/main/kotlin/com/weeth/domain/attendance/application/usecase/query/GetAttendanceQueryService.kt`
at line 54, Replace the Java-style optional handling for session lookup with
Kotlin idioms: in the code using
sessionRepository.findById(sessionId).orElseThrow { MeetingNotFoundException()
}, call sessionRepository.findByIdOrNull(sessionId) and use the Elvis operator
(?:) to throw MeetingNotFoundException() when null; update any imports if needed
to access findByIdOrNull and ensure the variable name session (and sessionId)
remain unchanged.
src/test/kotlin/com/weeth/domain/attendance/fixture/AttendanceTestFixture.kt (1)

50-62: 테스트 안정성을 위한 시간 파라미터 추가 고려.

LocalDateTime.now()를 직접 사용하면 테스트 실행 시점에 따라 결과가 달라질 수 있습니다. 필요시 기준 시간을 파라미터로 받는 오버로드 메서드 추가를 고려해 보세요.

♻️ 제안 코드
 fun createInProgressSession(
     cardinal: Int,
     code: Int,
     title: String,
+    baseTime: LocalDateTime = LocalDateTime.now(),
 ): Session =
     Session(
         title = title,
         location = "Test Location",
-        start = LocalDateTime.now().minusMinutes(5),
-        end = LocalDateTime.now().plusMinutes(5),
+        start = baseTime.minusMinutes(5),
+        end = baseTime.plusMinutes(5),
         code = code,
         cardinal = cardinal,
     )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/test/kotlin/com/weeth/domain/attendance/fixture/AttendanceTestFixture.kt`
around lines 50 - 62, The createInProgressSession fixture uses
LocalDateTime.now() which makes tests time-dependent; change it to accept a base
time parameter (e.g., baseTime: LocalDateTime = LocalDateTime.now()) or add an
overloaded createInProgressSession(baseTime: LocalDateTime, ...), then compute
start = baseTime.minusMinutes(5) and end = baseTime.plusMinutes(5); update
usages of createInProgressSession to pass a fixed baseTime in tests to stabilize
results (refer to the createInProgressSession function and Session constructor).
src/main/kotlin/com/weeth/domain/attendance/presentation/SessionAdminController.kt (1)

68-69: 응답 코드 불일치: SESSION_FIND_SUCCESS 사용 권장

getSessionInfos() 메서드에서 MEETING_FIND_SUCCESS를 사용 중이나, 같은 컨트롤러의 다른 메서드들은 모두 SESSION_* 패턴(SESSION_SAVE_SUCCESS, SESSION_UPDATE_SUCCESS, SESSION_DELETE_SUCCESS)을 따릅니다. 코드 일관성을 위해 SESSION_FIND_SUCCESS로 변경하세요.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/main/kotlin/com/weeth/domain/attendance/presentation/SessionAdminController.kt`
around lines 68 - 69, The response code used in SessionAdminController for
getSessionQueryService.findSessionInfos is inconsistent: it's calling
CommonResponse.success with AttendanceResponseCode.MEETING_FIND_SUCCESS; change
this to AttendanceResponseCode.SESSION_FIND_SUCCESS to match the SESSION_*
pattern used by other methods (e.g., SESSION_SAVE_SUCCESS,
SESSION_UPDATE_SUCCESS, SESSION_DELETE_SUCCESS) so the controller is consistent.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@src/main/kotlin/com/weeth/domain/attendance/application/usecase/command/ManageAttendanceUseCase.kt`:
- Around line 71-89: The updateStatus method is changing Attendance without
verifying its current state which causes attendance.attend()/attendance.close()
to throw when the record is not PENDING; before calling attendance.attend() or
attendance.close() (and before calling
user.removeAttend()/removeAbsent()/attend()/absent()), fetch the attendance
current status (e.g., attendance.status or attendance.getStatus()) and compare
it to the requested AttendanceStatus (constructed with
AttendanceStatus.valueOf(update.status)); if the statuses are identical either
skip the transition (no-op) or throw a clear domain exception (e.g.,
IllegalAttendanceTransitionException) based on business rules, and only invoke
attendance.attend()/attendance.close() and the corresponding user methods when
the status actually changes.

In
`@src/main/kotlin/com/weeth/domain/attendance/application/usecase/query/GetAttendanceQueryService.kt`:
- Around line 53-56: In findAllAttendanceBySession, the code throws
MeetingNotFoundException when sessionRepository.findById(sessionId) returns
empty, causing inconsistent naming across the attendance domain; update the code
to throw a SessionNotFoundException from the attendance domain (or, if you
intentionally reuse the schedule-domain exception, add a clear comment and
import to justify it) by replacing MeetingNotFoundException with
SessionNotFoundException (or creating SessionNotFoundException in the
attendance.exception package) so sessionRepository.findById(...) and
higher-level attendance flows consistently use SessionNotFoundException.

In
`@src/main/kotlin/com/weeth/domain/attendance/application/usecase/query/GetSessionQueryService.kt`:
- Around line 38-47: The method findSessionInfos performs redundant in-memory
sorting: sessionRepository.findAllByOrderByStartDesc and
findAllByCardinalOrderByStartDesc already return sessions ordered by start desc,
so remove the sortedByDescending { it.start } call and the local variable
sorted; pass the original sessions collection to sessionMapper.toInfos (and to
findThisWeek if appropriate) to avoid duplicate sorting and unnecessary memory
work in findSessionInfos.

In `@src/main/kotlin/com/weeth/domain/attendance/domain/entity/Session.kt`:
- Around line 45-59: The updateInfo function lacks the same end-after-start
validation present in the create() factory; add the same check (e.g.
require(!end.isBefore(start))) at the start of updateInfo and only assign
this.start, this.end and other fields after the validation passes so sessions
cannot be updated to an invalid time range; reference the updateInfo(...) method
and mirror the validation used in create().

In
`@src/main/kotlin/com/weeth/domain/attendance/domain/repository/AttendanceRepository.kt`:
- Around line 80-84: The method findAllBySessionIn in AttendanceRepository is
unused; either delete this unused query method or update the TODO to indicate a
clear plan and owner for future QR-code attendance work. Locate the
findAllBySessionIn declaration in AttendanceRepository (the method returning
List<Attendance> and accepting List<Session>) and either remove the method and
its JPQL `@Query`, or replace the TODO comment above it with a concise
implementation plan (timeline, ticket ID, and owner) so it's clear why the
method is retained.

In
`@src/main/kotlin/com/weeth/domain/schedule/application/usecase/command/ManageEventUseCase.kt`:
- Around line 15-29: ManageEventUseCase currently depends on thin wrapper
services userGetService.find(...) and cardinalGetService.findByUserSide(...),
which violates the guideline to avoid GetService/SaveService wrappers; replace
those calls by injecting the underlying repositories or a domain Reader
interface into the ManageEventUseCase constructor (e.g., UserRepository or
UserReader and CardinalRepository or CardinalReader), remove UserGetService and
CardinalGetService from the constructor, and update create(...) to call the
repository/reader methods (preserving existing semantics such as throwing when
missing) before calling eventRepository.save(eventMapper.toEntity(...)); also
adjust imports and any tests to use the new dependencies.

In
`@src/main/kotlin/com/weeth/domain/schedule/application/validator/ScheduleTimeCheckValidator.kt`:
- Around line 9-12: The current validation in ScheduleTimeCheckValidator.isValid
uses time.start.isBefore(time.end.plusMinutes(1)) which incorrectly allows start
== end; update the logic in isValid (in class ScheduleTimeCheckValidator,
operating on ScheduleTimeRequest.start/end) to reflect the intended rule: for
strict start < end use time.start.isBefore(time.end); or if you require at least
a 1-minute duration use time.start.plusMinutes(1).isBefore(time.end) (adjust to
<= if inclusive end is desired); replace the existing condition accordingly and
keep null handling (time == null) unchanged.

In `@src/main/kotlin/com/weeth/domain/schedule/domain/entity/Event.kt`:
- Around line 32-46: Event.update currently assigns fields without validating
input; add the same checks used in create (verify title.isNotBlank() and that
end >= start) before mutating state. Implement the validation inside
Event.update (or extract a private validateDatesAndTitle(...) helper used by
both Event.create and Event.update) and throw the same domain exception (e.g.,
IllegalArgumentException or the existing validation exception type) when
validation fails, then proceed to set this.title, this.content, this.location,
this.start, this.end, this.user.

In
`@src/main/kotlin/com/weeth/domain/schedule/presentation/EventAdminController.kt`:
- Line 36: The code references ScheduleResponseCode.EVENT_SAVE_SUCCESS,
EVENT_UPDATE_SUCCESS and EVENT_DELETE_SUCCESS but the ScheduleResponseCode type
is not imported, causing a compile error; add an import for the
ScheduleResponseCode class at the top of EventAdminController.kt (so the
references in the return statements like
CommonResponse.success(ScheduleResponseCode.EVENT_SAVE_SUCCESS) resolve) and
ensure the same import is available for other methods in EventAdminController
that use EVENT_UPDATE_SUCCESS and EVENT_DELETE_SUCCESS.

In `@src/main/kotlin/com/weeth/domain/schedule/presentation/EventController.kt`:
- Line 27: The compilation error is caused by a missing import for
ScheduleResponseCode used in the CommonResponse.success(...) call; add an import
for the ScheduleResponseCode class/enum at the top of EventController.kt so that
ScheduleResponseCode.EVENT_FIND_SUCCESS resolves (the reference occurs in the
method that returns
CommonResponse.success(ScheduleResponseCode.EVENT_FIND_SUCCESS,
getScheduleQueryService.findEvent(eventId))).

In
`@src/main/kotlin/com/weeth/domain/schedule/presentation/ScheduleController.kt`:
- Around line 18-39: Update the `@ApiErrorCodeExample` annotation on the
ScheduleController class to remove the unused MeetingErrorCode entry: change
ApiErrorCodeExample(EventErrorCode::class, MeetingErrorCode::class) to
ApiErrorCodeExample(EventErrorCode::class); also remove any now-unused import or
reference to MeetingErrorCode to keep the file clean; target the
ScheduleController class declaration and the annotation above it (the
findByMonthly and findByYearly methods remain unchanged).

In
`@src/test/kotlin/com/weeth/domain/attendance/application/usecase/query/GetAttendanceQueryServiceTest.kt`:
- Line 107: The test's describe block string is outdated
("findAllAttendanceByMeeting") while the actual tested method is
findAllAttendanceBySession; update the describe(...) label to
"findAllAttendanceBySession" (or another accurate session-based name) so the
describe block matches the tested method name findAllAttendanceBySession and
reflects the Meeting→Session migration.

---

Nitpick comments:
In
`@src/main/kotlin/com/weeth/domain/attendance/application/usecase/command/ManageAttendanceUseCase.kt`:
- Around line 52-59: The code uses MeetingNotFoundException and loads all
sessions then filters in memory via
sessionRepository.findAllByCardinalOrderByStartAsc; change the exception to
SessionNotFoundException (replace MeetingNotFoundException with
SessionNotFoundException in ManageAttendanceUseCase) and move the date filtering
into the repository by adding a method such as
SessionRepository.findByCardinalAndStartDate(cardinal: Int, date: LocalDate):
Session?; then call that new repository method to get targetSession and keep the
subsequent attendanceRepository.findAllBySessionAndUserStatus(targetSession,
Status.ACTIVE) and closePendingAttendances(attendances) logic unchanged.
- Around line 26-45: The checkIn method currently calls LocalDateTime.now()
directly which hampers testability; modify ManageAttendanceUseCase.checkIn to
accept the "now" as a dependency (either inject a java.time.Clock into the class
and use LocalDateTime.now(clock) or add a LocalDateTime parameter to checkIn)
and update all callers/tests accordingly, then use that injected clock/parameter
when calling attendanceRepository.findCurrentByUserId(userId, now,
now.plusMinutes(10)) so the time is controllable in tests; keep all existing
logic (userGetService.find, todayAttendance handling, lockedAttendance retrieval
and attend() calls) unchanged aside from replacing direct LocalDateTime.now()
usage.

In
`@src/main/kotlin/com/weeth/domain/attendance/application/usecase/command/ManageSessionUseCase.kt`:
- Line 9: The code imports and throws MeetingNotFoundException while operating
on Session domain — rename and align the exception to SessionNotFoundException:
create/rename the exception class from MeetingNotFoundException to
SessionNotFoundException, update the import in ManageSessionUseCase (and any
other callers) to
com.weeth.domain.attendance.application.exception.SessionNotFoundException, and
change any throw sites that reference MeetingNotFoundException (e.g., in
ManageSessionUseCase methods) to throw SessionNotFoundException so domain
terminology is consistent.

In
`@src/main/kotlin/com/weeth/domain/attendance/application/usecase/query/GetAttendanceQueryService.kt`:
- Line 54: Replace the Java-style optional handling for session lookup with
Kotlin idioms: in the code using
sessionRepository.findById(sessionId).orElseThrow { MeetingNotFoundException()
}, call sessionRepository.findByIdOrNull(sessionId) and use the Elvis operator
(?:) to throw MeetingNotFoundException() when null; update any imports if needed
to access findByIdOrNull and ensure the variable name session (and sessionId)
remain unchanged.

In
`@src/main/kotlin/com/weeth/domain/attendance/domain/repository/SessionRepository.kt`:
- Around line 21-24: The method in SessionRepository named
findByStartLessThanEqualAndEndGreaterThanEqualOrderByStartAsc has its parameters
in the wrong order (currently end, start), which will bind incorrectly in Spring
Data JPA; fix by reordering the parameters to (start: LocalDateTime, end:
LocalDateTime) so the first argument maps to "start <= end" and the second to
"end >= start", or alternatively add an explicit `@Query` on the SessionRepository
method with named parameters to guarantee correct binding if you prefer keeping
the current parameter order.

In
`@src/main/kotlin/com/weeth/domain/attendance/presentation/SessionAdminController.kt`:
- Around line 68-69: The response code used in SessionAdminController for
getSessionQueryService.findSessionInfos is inconsistent: it's calling
CommonResponse.success with AttendanceResponseCode.MEETING_FIND_SUCCESS; change
this to AttendanceResponseCode.SESSION_FIND_SUCCESS to match the SESSION_*
pattern used by other methods (e.g., SESSION_SAVE_SUCCESS,
SESSION_UPDATE_SUCCESS, SESSION_DELETE_SUCCESS) so the controller is consistent.

In
`@src/main/kotlin/com/weeth/domain/schedule/application/dto/response/EventResponse.kt`:
- Around line 7-30: EventResponse lacks the JsonInclude setting used by
SessionResponse, causing nullable fields like name, createdAt, and modifiedAt to
be serialized as null; update the EventResponse data class to use
`@JsonInclude`(JsonInclude.Include.NON_NULL) (and add the corresponding import for
com.fasterxml.jackson.annotation.JsonInclude) so that null properties are
omitted from JSON responses, matching SessionResponse behavior.

In
`@src/main/kotlin/com/weeth/domain/schedule/application/mapper/SessionMapper.kt`:
- Around line 14-44: The two methods toResponse and toAdminResponse in
SessionMapper duplicate all fields except for code; extract a private helper
(e.g., private fun buildResponse(session: Session, code: String?)) that
constructs and returns a SessionResponse using session fields and the provided
code, then update toResponse to call buildResponse(session, null) and
toAdminResponse to call buildResponse(session, session.code) to remove
duplication.

In
`@src/main/kotlin/com/weeth/domain/schedule/domain/repository/EventRepository.kt`:
- Around line 8-11: The repository method
findByStartLessThanEqualAndEndGreaterThanEqualOrderByStartAsc has its parameters
declared as (end: LocalDateTime, start: LocalDateTime) which is confusing and
mismatches the method semantics (entity.start <= rangeEnd AND entity.end >=
rangeStart); rename the parameters to meaningful names (e.g., rangeStart:
LocalDateTime, rangeEnd: LocalDateTime) and/or reorder them to (rangeStart,
rangeEnd) so callers are clear, or replace the derived query with an explicit
`@Query` and rename the method to findOverlappingEvents(rangeStart, rangeEnd) to
make intent explicit—update all call sites to use the new names/signature.

In
`@src/test/kotlin/com/weeth/domain/attendance/fixture/AttendanceTestFixture.kt`:
- Around line 50-62: The createInProgressSession fixture uses
LocalDateTime.now() which makes tests time-dependent; change it to accept a base
time parameter (e.g., baseTime: LocalDateTime = LocalDateTime.now()) or add an
overloaded createInProgressSession(baseTime: LocalDateTime, ...), then compute
start = baseTime.minusMinutes(5) and end = baseTime.plusMinutes(5); update
usages of createInProgressSession to pass a fixed baseTime in tests to stabilize
results (refer to the createInProgressSession function and Session constructor).

In `@src/test/kotlin/com/weeth/domain/schedule/fixture/ScheduleTestFixture.kt`:
- Around line 43-57: createSession currently instantiates Session via the
constructor, bypassing the Session.create() factory validation (title required,
end time checks); change createSession in ScheduleTestFixture to call
Session.create(...) with the same parameters so factory validations run, then if
an id must be injected for tests keep the ReflectionTestUtils.setField(session,
"id", id) after creation; ensure you adapt parameter order/names to match
Session.create and preserve any status/start/end values passed into
createSession.

Comment on lines 71 to 89
@Transactional
fun updateStatus(attendanceUpdates: List<UpdateAttendanceStatusRequest>) {
attendanceUpdates.forEach { update ->
val attendance =
attendanceRepository.findByIdWithUser(update.attendanceId)
?: throw AttendanceNotFoundException()
val user = attendance.user
val newStatus = AttendanceStatus.valueOf(update.status)
if (newStatus == AttendanceStatus.ABSENT) {
attendance.close()
user.removeAttend()
user.absent()
} else {
attendance.attend()
user.removeAbsent()
user.attend()
}
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

find . -type f -name "*.kt" | xargs grep -l "class Attendance" | head -5

Repository: Team-Weeth/weeth-server

Length of output: 558


🏁 Script executed:

find . -type f -name "*.kt" | xargs grep -l "class User" | head -5

Repository: Team-Weeth/weeth-server

Length of output: 481


🏁 Script executed:

rg -n "fun (attend|absent|removeAbsent|removeAttend)\(" --type kt -B 2 -A 8

Repository: Team-Weeth/weeth-server

Length of output: 93


🏁 Script executed:

fd -e kt "Attendance" --type f | grep -E "domain.*Attendance\.kt$" | head -10

Repository: Team-Weeth/weeth-server

Length of output: 138


🏁 Script executed:

fd -e kt "User" --type f | grep -E "domain.*User\.kt$" | head -10

Repository: Team-Weeth/weeth-server

Length of output: 49


🏁 Script executed:

rg "fun (attend|absent|removeAbsent|removeAttend)" -A 8 --max-count 20

Repository: Team-Weeth/weeth-server

Length of output: 1506


🏁 Script executed:

fd -e kt "User" --type f --path "*/domain/*/domain/entity/*" | head -10

Repository: Team-Weeth/weeth-server

Length of output: 318


🏁 Script executed:

rg "fun (removeAttend|removeAbsent|attend|absent)" src/main/kotlin/com/weeth/domain -A 6 --max-count 30

Repository: Team-Weeth/weeth-server

Length of output: 1295


🏁 Script executed:

cat -n src/main/kotlin/com/weeth/domain/attendance/domain/entity/Attendance.kt | head -50

Repository: Team-Weeth/weeth-server

Length of output: 2031


🏁 Script executed:

find src/main/kotlin/com/weeth/domain -name "User.kt" -type f

Repository: Team-Weeth/weeth-server

Length of output: 49


🏁 Script executed:

rg "class User" src/main/kotlin/com/weeth/domain -B 2 -A 15

Repository: Team-Weeth/weeth-server

Length of output: 49


🏁 Script executed:

find src/main/kotlin -name "User.kt" -type f

Repository: Team-Weeth/weeth-server

Length of output: 49


🏁 Script executed:

rg "fun (removeAttend|removeAbsent|attend|absent)" src/main/kotlin/com/weeth/domain/user -A 5

Repository: Team-Weeth/weeth-server

Length of output: 144


🏁 Script executed:

rg "class User" src/main/kotlin --type kt -A 10 | head -50

Repository: Team-Weeth/weeth-server

Length of output: 93


🏁 Script executed:

rg "fun (removeAttend|removeAbsent|attend|absent)" src/main/kotlin --type kt -A 4 | head -100

Repository: Team-Weeth/weeth-server

Length of output: 93


🏁 Script executed:

rg "class User" src/main/kotlin -A 10 | head -80

Repository: Team-Weeth/weeth-server

Length of output: 49


🏁 Script executed:

rg "fun (removeAttend|removeAbsent|attend|absent)" src/main/kotlin -A 4 | head -150

Repository: Team-Weeth/weeth-server

Length of output: 1058


🏁 Script executed:

cat -n src/main/kotlin/com/weeth/domain/attendance/application/usecase/command/ManageAttendanceUseCase.kt | head -100

Repository: Team-Weeth/weeth-server

Length of output: 4716


🏁 Script executed:

find src -name "*.kt" | xargs grep -l "fun removeAttend"

Repository: Team-Weeth/weeth-server

Length of output: 51


🏁 Script executed:

find src -name "*.kt" | xargs grep "class User"

Repository: Team-Weeth/weeth-server

Length of output: 623


🏁 Script executed:

find src/main -name "*.kt" -type f | xargs grep -l "^class User"

Repository: Team-Weeth/weeth-server

Length of output: 51


🏁 Script executed:

ls -la src/main/kotlin/com/weeth/domain/

Repository: Team-Weeth/weeth-server

Length of output: 510


🏁 Script executed:

rg "user\." src/main/kotlin/com/weeth/domain/attendance/application/usecase/command/ManageAttendanceUseCase.kt -B 1 -A 1

Repository: Team-Weeth/weeth-server

Length of output: 718


🏁 Script executed:

find src/main -type d -name "user"

Repository: Team-Weeth/weeth-server

Length of output: 102


🏁 Script executed:

ls -la src/main/kotlin/com/weeth/

Repository: Team-Weeth/weeth-server

Length of output: 285


🏁 Script executed:

find src/main/kotlin/com/weeth -name "*.kt" | xargs grep -l "^class User "

Repository: Team-Weeth/weeth-server

Length of output: 51


🏁 Script executed:

find src/main/java/com/weeth/domain/user -name "*.java" | xargs grep -l "class User"

Repository: Team-Weeth/weeth-server

Length of output: 1625


🏁 Script executed:

find src/main/java/com/weeth/domain/user -name "User.java"

Repository: Team-Weeth/weeth-server

Length of output: 126


🏁 Script executed:

cat src/main/java/com/weeth/domain/user/domain/entity/User.java | head -150

Repository: Team-Weeth/weeth-server

Length of output: 3539


🏁 Script executed:

cat src/main/java/com/weeth/domain/user/domain/entity/User.java | sed -n '120,180p'

Repository: Team-Weeth/weeth-server

Length of output: 1405


상태 전환 시 기존 상태 검증 필요

현재 updateStatus() 메서드는 Attendance 상태를 변경하기 전에 현재 상태를 확인하지 않습니다. Attendance 엔티티의 attend()absent() 메서드는 반드시 PENDING 상태에서만 호출 가능하므로, 이미 ATTEND 상태인 출석 기록을 다시 ATTEND 상태로 변경하려 하면 "이미 처리된 출석입니다" 예외가 발생합니다.

상태 변경 전에 현재 상태와 요청된 상태를 비교하여 불필요한 상태 전환을 방지하거나, 의도적인 상태 재전환인 경우를 위해 명확한 비즈니스 로직을 정의해야 합니다.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/main/kotlin/com/weeth/domain/attendance/application/usecase/command/ManageAttendanceUseCase.kt`
around lines 71 - 89, The updateStatus method is changing Attendance without
verifying its current state which causes attendance.attend()/attendance.close()
to throw when the record is not PENDING; before calling attendance.attend() or
attendance.close() (and before calling
user.removeAttend()/removeAbsent()/attend()/absent()), fetch the attendance
current status (e.g., attendance.status or attendance.getStatus()) and compare
it to the requested AttendanceStatus (constructed with
AttendanceStatus.valueOf(update.status)); if the statuses are identical either
skip the transition (no-op) or throw a clear domain exception (e.g.,
IllegalAttendanceTransitionException) based on business rules, and only invoke
attendance.attend()/attendance.close() and the corresponding user methods when
the status actually changes.

Comment on lines 45 to 59
fun updateInfo(
title: String,
content: String?,
location: String?,
start: LocalDateTime,
end: LocalDateTime,
user: User?,
) {
this.title = title
this.content = content
this.location = location
this.start = start
this.end = end
this.user = user
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

updateInfo에서 시간 유효성 검증이 누락되었습니다.

create() 팩토리 메서드에서는 require(!end.isBefore(start))로 종료 시간이 시작 시간 이후인지 검증하지만, updateInfo()에서는 동일한 검증이 없습니다. 잘못된 시간 범위로 세션이 업데이트될 수 있습니다.

🛡️ 시간 검증 추가 제안
 fun updateInfo(
     title: String,
     content: String?,
     location: String?,
     start: LocalDateTime,
     end: LocalDateTime,
     user: User?,
 ) {
+    require(!end.isBefore(start)) { "종료 시간은 시작 시간 이후여야 합니다" }
     this.title = title
     this.content = content
     this.location = location
     this.start = start
     this.end = end
     this.user = user
 }
📝 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.

Suggested change
fun updateInfo(
title: String,
content: String?,
location: String?,
start: LocalDateTime,
end: LocalDateTime,
user: User?,
) {
this.title = title
this.content = content
this.location = location
this.start = start
this.end = end
this.user = user
}
fun updateInfo(
title: String,
content: String?,
location: String?,
start: LocalDateTime,
end: LocalDateTime,
user: User?,
) {
require(!end.isBefore(start)) { "종료 시간은 시작 시간 이후여야 합니다" }
this.title = title
this.content = content
this.location = location
this.start = start
this.end = end
this.user = user
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/kotlin/com/weeth/domain/attendance/domain/entity/Session.kt` around
lines 45 - 59, The updateInfo function lacks the same end-after-start validation
present in the create() factory; add the same check (e.g.
require(!end.isBefore(start))) at the start of updateInfo and only assign
this.start, this.end and other fields after the validation passes so sessions
cannot be updated to an invalid time range; reference the updateInfo(...) method
and mirror the validation used in create().

Comment on lines +80 to +84
// TODO: QR 코드 출석 기능 구현 시 사용 예정 (여러 세션의 출석자 배치 조회)
@Query("SELECT a FROM Attendance a JOIN FETCH a.user WHERE a.session IN :sessions")
fun findAllBySessionIn(
@Param("sessions") sessions: List<Session>,
): List<Attendance>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: findAllBySessionIn 메서드의 사용처 확인
rg -n "findAllBySessionIn" --type=kt

Repository: Team-Weeth/weeth-server

Length of output: 93


🏁 Script executed:

#!/bin/bash
# findAllBySessionIn 메서드 사용처 확인 (Kotlin 파일)
rg "findAllBySessionIn" --type=kotlin

Repository: Team-Weeth/weeth-server

Length of output: 180


🏁 Script executed:

#!/bin/bash
# 모든 파일 타입에서 findAllBySessionIn 검색 (타입 지정 안 함)
rg "findAllBySessionIn" -n

echo "---"
# 테스트 파일에서 특별히 확인
echo "Test files:"
rg "findAllBySessionIn" --type=test -n 2>/dev/null || echo "No matches in test files"

Repository: Team-Weeth/weeth-server

Length of output: 224


findAllBySessionIn 메서드는 미사용 코드로 확인됨 — 제거하거나 TODO 정보 명확화 필요.

메서드가 코드베이스 전체에서 호출되지 않고 있습니다. QR 코드 출석 기능을 구현할 계획이 있다면 TODO 주석에 구현 일정이나 담당자 정보를 추가하거나, 계획이 없다면 메서드를 제거하는 것을 권장합니다.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/main/kotlin/com/weeth/domain/attendance/domain/repository/AttendanceRepository.kt`
around lines 80 - 84, The method findAllBySessionIn in AttendanceRepository is
unused; either delete this unused query method or update the TODO to indicate a
clear plan and owner for future QR-code attendance work. Locate the
findAllBySessionIn declaration in AttendanceRepository (the method returning
List<Attendance> and accepting List<Session>) and either remove the method and
its JPQL `@Query`, or replace the TODO comment above it with a concise
implementation plan (timeline, ticket ID, and owner) so it's clear why the
method is retained.

Comment on lines 32 to 46
fun update(
title: String,
content: String,
location: String,
start: LocalDateTime,
end: LocalDateTime,
user: User?,
) {
this.title = title
this.content = content
this.location = location
this.start = start
this.end = end
this.user = user
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

update() 메서드에 검증 로직 누락

create() 메서드에서는 title.isNotBlank()end >= start 검증을 수행하지만, update() 메서드에는 동일한 검증이 없습니다. 이로 인해 업데이트 시 유효하지 않은 상태가 발생할 수 있습니다.

🔧 검증 로직 추가 제안
     fun update(
         title: String,
         content: String,
         location: String,
         start: LocalDateTime,
         end: LocalDateTime,
         user: User?,
     ) {
+        require(title.isNotBlank()) { "제목은 필수입니다" }
+        require(!end.isBefore(start)) { "종료 시간은 시작 시간 이후여야 합니다" }
         this.title = title
         this.content = content
         this.location = location
         this.start = start
         this.end = end
         this.user = user
     }
📝 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.

Suggested change
fun update(
title: String,
content: String,
location: String,
start: LocalDateTime,
end: LocalDateTime,
user: User?,
) {
this.title = title
this.content = content
this.location = location
this.start = start
this.end = end
this.user = user
}
fun update(
title: String,
content: String,
location: String,
start: LocalDateTime,
end: LocalDateTime,
user: User?,
) {
require(title.isNotBlank()) { "제목은 필수입니다" }
require(!end.isBefore(start)) { "종료 시간은 시작 시간 이후여야 합니다" }
this.title = title
this.content = content
this.location = location
this.start = start
this.end = end
this.user = user
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/kotlin/com/weeth/domain/schedule/domain/entity/Event.kt` around
lines 32 - 46, Event.update currently assigns fields without validating input;
add the same checks used in create (verify title.isNotBlank() and that end >=
start) before mutating state. Implement the validation inside Event.update (or
extract a private validateDatesAndTitle(...) helper used by both Event.create
and Event.update) and throw the same domain exception (e.g.,
IllegalArgumentException or the existing validation exception type) when
validation fails, then proceed to set this.title, this.content, this.location,
this.start, this.end, this.user.

@Parameter(hidden = true) @CurrentUser userId: Long,
): CommonResponse<Void?> {
manageEventUseCase.create(dto, userId)
return CommonResponse.success(ScheduleResponseCode.EVENT_SAVE_SUCCESS)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

ScheduleResponseCode import 누락으로 컴파일 오류 발생

ScheduleResponseCode.EVENT_SAVE_SUCCESS, EVENT_UPDATE_SUCCESS, EVENT_DELETE_SUCCESS를 참조하고 있지만 해당 클래스가 import 되어 있지 않습니다.

🐛 import 추가 제안
 import org.springframework.web.bind.annotation.RestController
+import com.weeth.domain.schedule.presentation.ScheduleResponseCode
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/main/kotlin/com/weeth/domain/schedule/presentation/EventAdminController.kt`
at line 36, The code references ScheduleResponseCode.EVENT_SAVE_SUCCESS,
EVENT_UPDATE_SUCCESS and EVENT_DELETE_SUCCESS but the ScheduleResponseCode type
is not imported, causing a compile error; add an import for the
ScheduleResponseCode class at the top of EventAdminController.kt (so the
references in the return statements like
CommonResponse.success(ScheduleResponseCode.EVENT_SAVE_SUCCESS) resolve) and
ensure the same import is available for other methods in EventAdminController
that use EVENT_UPDATE_SUCCESS and EVENT_DELETE_SUCCESS.

fun getEvent(
@PathVariable eventId: Long,
): CommonResponse<EventResponse> =
CommonResponse.success(ScheduleResponseCode.EVENT_FIND_SUCCESS, getScheduleQueryService.findEvent(eventId))
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

ScheduleResponseCode import 누락으로 컴파일 오류 발생

ScheduleResponseCode.EVENT_FIND_SUCCESS를 참조하고 있지만 해당 클래스가 import 되어 있지 않습니다.

🐛 import 추가 제안
 import com.weeth.domain.schedule.application.usecase.query.GetScheduleQueryService
+import com.weeth.domain.schedule.presentation.ScheduleResponseCode
 import com.weeth.global.common.exception.ApiErrorCodeExample
📝 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.

Suggested change
CommonResponse.success(ScheduleResponseCode.EVENT_FIND_SUCCESS, getScheduleQueryService.findEvent(eventId))
import com.weeth.domain.schedule.application.usecase.query.GetScheduleQueryService
import com.weeth.domain.schedule.presentation.ScheduleResponseCode
import com.weeth.global.common.exception.ApiErrorCodeExample
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/kotlin/com/weeth/domain/schedule/presentation/EventController.kt` at
line 27, The compilation error is caused by a missing import for
ScheduleResponseCode used in the CommonResponse.success(...) call; add an import
for the ScheduleResponseCode class/enum at the top of EventController.kt so that
ScheduleResponseCode.EVENT_FIND_SUCCESS resolves (the reference occurs in the
method that returns
CommonResponse.success(ScheduleResponseCode.EVENT_FIND_SUCCESS,
getScheduleQueryService.findEvent(eventId))).

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/main/java/com/weeth/domain/user/application/usecase/UserManageUseCaseImpl.java (1)

37-38: ⚠️ Potential issue | 🔴 Critical

컴파일 오류: 필드 선언 불일치

파이프라인 실패 로그에서 확인되듯이, AttendanceSaveServiceMeetingGetService는 더 이상 존재하지 않습니다. Lines 99-103과 147-151에서 사용하는 sessionRepositoryattendanceRepository 필드 선언이 누락되어 있습니다.

🐛 필드 선언 수정 제안
     private final UserGetService userGetService;
     private final UserUpdateService userUpdateService;
     private final UserDeleteService userDeleteService;

-    private final AttendanceSaveService attendanceSaveService;
-    private final MeetingGetService meetingGetService;
+    private final AttendanceRepository attendanceRepository;
+    private final SessionRepository sessionRepository;
     private final RefreshTokenStorePort refreshTokenStorePort;

As per coding guidelines: "Do not create thin wrapper services — UseCases call Repositories directly"

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/main/java/com/weeth/domain/user/application/usecase/UserManageUseCaseImpl.java`
around lines 37 - 38, In UserManageUseCaseImpl, replace the now-invalid service
fields (AttendanceSaveService and MeetingGetService) with repository fields used
later: declare private final SessionRepository sessionRepository and private
final AttendanceRepository attendanceRepository, and update the constructor to
accept and assign these repository dependencies; remove the unused
AttendanceSaveService/MeetingGetService declarations so methods referencing
sessionRepository and attendanceRepository (e.g., where they are used on lines
~99-103 and ~147-151) compile against the repositories directly.
src/test/kotlin/com/weeth/domain/user/application/usecase/UserManageUseCaseTest.kt (1)

41-63: ⚠️ Potential issue | 🔴 Critical

테스트 파일 마이그레이션 미완료: 컴파일 오류 발생

sessionRepositoryattendanceRepository가 선언되지 않았지만 Lines 148, 153, 213, 217에서 사용되고 있습니다. 또한 더 이상 필요하지 않은 attendanceSaveServicemeetingGetService가 여전히 선언되어 있고 UseCase 생성자에 전달되고 있습니다.

🐛 테스트 mock 선언 및 생성자 수정 제안
         val userGetService = mockk<UserGetService>()
         val userUpdateService = mockk<UserUpdateService>(relaxUnitFun = true)
         val userDeleteService = mockk<UserDeleteService>(relaxUnitFun = true)
-        val attendanceSaveService = mockk<AttendanceSaveService>(relaxUnitFun = true)
-        val meetingGetService = mockk<MeetingGetService>()
+        val attendanceRepository = mockk<AttendanceRepository>(relaxUnitFun = true)
+        val sessionRepository = mockk<SessionRepository>()
         val refreshTokenStorePort = mockk<RefreshTokenStorePort>(relaxUnitFun = true)
         val cardinalGetService = mockk<CardinalGetService>()
         val userCardinalSaveService = mockk<UserCardinalSaveService>(relaxUnitFun = true)
         val userCardinalGetService = mockk<UserCardinalGetService>()
         val userMapper = mockk<UserMapper>()
         val passwordEncoder = mockk<PasswordEncoder>()

         val useCase =
             UserManageUseCaseImpl(
                 userGetService,
                 userUpdateService,
                 userDeleteService,
-                attendanceSaveService,
-                meetingGetService,
+                attendanceRepository,
+                sessionRepository,
                 refreshTokenStorePort,
                 cardinalGetService,
                 userCardinalSaveService,
                 userCardinalGetService,
                 userMapper,
                 passwordEncoder,
             )

As per coding guidelines: "Test architecture mirrors source: mock Repository/Reader/Port in UseCase tests"

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/test/kotlin/com/weeth/domain/user/application/usecase/UserManageUseCaseTest.kt`
around lines 41 - 63, The test fails to compile because sessionRepository and
attendanceRepository are not declared but are referenced, and unused mocks
attendanceSaveService and meetingGetService are still passed into the
UserManageUseCaseImpl constructor; update UserManageUseCaseTest by removing the
obsolete mocks (attendanceSaveService, meetingGetService) from the mock
declarations and from the UserManageUseCaseImpl(...) constructor call, and
instead declare and mock the required sessionRepository and attendanceRepository
(matching the repository/port types used by UserManageUseCaseImpl) so the test
mirrors the current UseCase constructor and compiles.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In
`@src/main/java/com/weeth/domain/user/application/usecase/UserManageUseCaseImpl.java`:
- Around line 37-38: In UserManageUseCaseImpl, replace the now-invalid service
fields (AttendanceSaveService and MeetingGetService) with repository fields used
later: declare private final SessionRepository sessionRepository and private
final AttendanceRepository attendanceRepository, and update the constructor to
accept and assign these repository dependencies; remove the unused
AttendanceSaveService/MeetingGetService declarations so methods referencing
sessionRepository and attendanceRepository (e.g., where they are used on lines
~99-103 and ~147-151) compile against the repositories directly.

In
`@src/test/kotlin/com/weeth/domain/user/application/usecase/UserManageUseCaseTest.kt`:
- Around line 41-63: The test fails to compile because sessionRepository and
attendanceRepository are not declared but are referenced, and unused mocks
attendanceSaveService and meetingGetService are still passed into the
UserManageUseCaseImpl constructor; update UserManageUseCaseTest by removing the
obsolete mocks (attendanceSaveService, meetingGetService) from the mock
declarations and from the UserManageUseCaseImpl(...) constructor call, and
instead declare and mock the required sessionRepository and attendanceRepository
(matching the repository/port types used by UserManageUseCaseImpl) so the test
mirrors the current UseCase constructor and compiles.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
src/main/java/com/weeth/domain/user/application/usecase/UserManageUseCaseImpl.java (2)

99-103: 동일 cardinal에 대한 세션 조회가 반복됩니다.

accept()/applyOB() 루프 안에서 findAllByCardinalOrderByStartAsc가 매번 호출되어, 동일 cardinal가 많을 경우 불필요한 DB 반복 조회가 발생할 수 있습니다. cardinal 기준 캐싱 또는 선조회로 중복 호출을 줄이는 편이 좋습니다.

♻️ 중복 조회 캐싱 예시
 public void accept(UserId userIds) {
     List<User> users = userGetService.findAll(userIds.userId());
+    Map<Integer, List<Session>> sessionCache = new HashMap<>();

     users.forEach(user -> {
         Integer cardinal = userCardinalGetService.getCurrentCardinal(user).getCardinalNumber();

         if (user.isInactive()) {
             userUpdateService.accept(user);
-            List<Session> sessions = sessionRepository.findAllByCardinalOrderByStartAsc(cardinal);
+            List<Session> sessions =
+                sessionCache.computeIfAbsent(cardinal, sessionRepository::findAllByCardinalOrderByStartAsc);
             List<Attendance> attendances = sessions.stream()
                     .map(s -> Attendance.Companion.create(s, user))
                     .collect(java.util.stream.Collectors.toList());
             attendanceRepository.saveAll(attendances);
         }
     });
 }
 public void applyOB(List<UserApplyOB> requests) {
+    Map<Integer, List<Session>> sessionCache = new HashMap<>();
     requests.forEach(request -> {
         User user = userGetService.find(request.userId());
         Cardinal nextCardinal = cardinalGetService.findByAdminSide(request.cardinal());

         if (userCardinalGetService.notContains(user, nextCardinal)) {
             if (userCardinalGetService.isCurrent(user, nextCardinal)) {
                 user.initAttendance();
-                List<Session> sessionList = sessionRepository.findAllByCardinalOrderByStartAsc(request.cardinal());
+                List<Session> sessionList =
+                    sessionCache.computeIfAbsent(request.cardinal(), sessionRepository::findAllByCardinalOrderByStartAsc);
                 List<Attendance> attendances = sessionList.stream()
                         .map(s -> Attendance.Companion.create(s, user))
                         .collect(java.util.stream.Collectors.toList());
                 attendanceRepository.saveAll(attendances);
             }
             UserCardinal userCardinal = new UserCardinal(user, nextCardinal);

Also applies to: 147-151

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/main/java/com/weeth/domain/user/application/usecase/UserManageUseCaseImpl.java`
around lines 99 - 103, The code repeatedly calls
sessionRepository.findAllByCardinalOrderByStartAsc inside the accept()/applyOB()
loops causing redundant DB queries; modify UserManageUseCaseImpl to prefetch
sessions per cardinal once (e.g., build a Map<Cardinal, List<Session>> by
iterating unique cardinals from the inputs and calling
sessionRepository.findAllByCardinalOrderByStartAsc only once per cardinal) and
then use Attendance.Companion.create(s, user) over the cached lists and saveAll
from the combined attendances; apply the same deduped-prefetch approach to the
other loop that currently invokes findAllByCardinalOrderByStartAsc repeatedly.

4-38: Kotlin 전환을 고려해 주세요.

이번 변경으로 Java 파일에 신규 로직이 추가되었습니다. PR의 마이그레이션 방향과 가이드라인을 고려하면, 이 UseCase도 Kotlin으로 이전하는 편이 일관됩니다(여유가 없다면 후속 작업으로라도 계획을 남겨 주세요).
As per coding guidelines: src/main/**/*.{java,kt}: New code should be written in Kotlin; ~271 existing Java files remain and should be migrated incrementally.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/main/java/com/weeth/domain/user/application/usecase/UserManageUseCaseImpl.java`
around lines 4 - 38, The new UseCase implementation (UserManageUseCaseImpl) was
added in Java but project guideline requires new code under src/main to be
Kotlin; convert this class to Kotlin preserving its behavior: create a Kotlin
file/class named UserManageUseCaseImpl (or corresponding Kotlin implementation
of UserManageUseCase) with the same constructor-injected dependencies
(UserGetService, UserUpdateService, UserDeleteService, AttendanceRepository,
SessionRepository, etc.), keep Spring annotations (`@Service`) and required
imports, translate all methods and DTO usages to Kotlin idioms, and ensure
static imports (UsersOrderBy constants) and mapper/service calls match Kotlin
syntax; if immediate migration is not feasible, add a short TODO in this PR and
open a migration task/issue referencing UserManageUseCaseImpl to ensure it’s
migrated to Kotlin later.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In
`@src/main/java/com/weeth/domain/user/application/usecase/UserManageUseCaseImpl.java`:
- Around line 99-103: The code repeatedly calls
sessionRepository.findAllByCardinalOrderByStartAsc inside the accept()/applyOB()
loops causing redundant DB queries; modify UserManageUseCaseImpl to prefetch
sessions per cardinal once (e.g., build a Map<Cardinal, List<Session>> by
iterating unique cardinals from the inputs and calling
sessionRepository.findAllByCardinalOrderByStartAsc only once per cardinal) and
then use Attendance.Companion.create(s, user) over the cached lists and saveAll
from the combined attendances; apply the same deduped-prefetch approach to the
other loop that currently invokes findAllByCardinalOrderByStartAsc repeatedly.
- Around line 4-38: The new UseCase implementation (UserManageUseCaseImpl) was
added in Java but project guideline requires new code under src/main to be
Kotlin; convert this class to Kotlin preserving its behavior: create a Kotlin
file/class named UserManageUseCaseImpl (or corresponding Kotlin implementation
of UserManageUseCase) with the same constructor-injected dependencies
(UserGetService, UserUpdateService, UserDeleteService, AttendanceRepository,
SessionRepository, etc.), keep Spring annotations (`@Service`) and required
imports, translate all methods and DTO usages to Kotlin idioms, and ensure
static imports (UsersOrderBy constants) and mapper/service calls match Kotlin
syntax; if immediate migration is not feasible, add a short TODO in this PR and
open a migration task/issue referencing UserManageUseCaseImpl to ensure it’s
migrated to Kotlin later.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In
`@src/main/kotlin/com/weeth/domain/attendance/application/usecase/command/ManageAttendanceUseCase.kt`:
- Around line 52-56: In ManageAttendanceUseCase.kt replace the thrown domain
exception from MeetingNotFoundException to the attendance domain
SessionNotFoundException: locate the session lookup using
sessionRepository.findAllByCardinalOrderByStartAsc(cardinal).firstOrNull { ... }
and change the fallback throw to throw SessionNotFoundException(); also update
imports and any references in this class to the SessionNotFoundException type,
and run a project-wide search for
MeetingNotFoundException/SessionNotFoundException to ensure attendance flows
consistently use SessionNotFoundException.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🔨 Refactor 코드 구조 개선 및 리팩토링

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant