[WTH-148] schedule 도메인 코틀린 마이그레이션#15
Conversation
|
Note Reviews pausedIt 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 Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
No actionable comments were generated in the recent review. 🎉 📝 WalkthroughWalkthrough스케줄·출석 도메인을 Java에서 Kotlin으로 대규모 마이그레이션 및 리팩터링했습니다. Meeting→Session, Status→AttendanceStatus로 모델 변경; Java 기반 엔티티/레포/서비스/컨트롤러/DTO 대거 삭제 및 Kotlin 기반 구현(ManageAttendance/ManageSession/ManageEvent, Session/Attendance 엔티티/레포/매퍼/컨트롤러 등)과 API 버전 v1→v4 전환이 포함됩니다. Changes
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
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
Estimated code review effort🎯 4 (Complex) | ⏱️ ~75 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
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: 예외 이름 불일치 및 메모리 내 필터링 비효율
MeetingNotFoundException을 Session 관련 로직에서 사용하고 있습니다. PR 목적에 따르면 Meeting을 Session으로 통일하고 있으므로,SessionNotFoundException으로 변경하는 것이 적절합니다.
findAllByCardinalOrderByStartAsc로 모든 Session을 조회한 후 메모리에서 날짜 필터링을 수행합니다. 쿼리 레벨에서 필터링하면 더 효율적입니다.♻️ 개선 제안
- 예외 타입 변경:
-import com.weeth.domain.schedule.application.exception.MeetingNotFoundException +import com.weeth.domain.attendance.application.exception.SessionNotFoundException
- 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: 파라미터 이름이 메서드 정의와 불일치메서드 이름
findByStartLessThanEqualAndEndGreaterThanEqual은Start <= ? 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:createSession이Session.create()팩토리 메서드를 우회합니다.
createEvent는Event.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:toResponse와toAdminResponse간 코드 중복 개선 권장두 메서드는
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.
| @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() | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
find . -type f -name "*.kt" | xargs grep -l "class Attendance" | head -5Repository: Team-Weeth/weeth-server
Length of output: 558
🏁 Script executed:
find . -type f -name "*.kt" | xargs grep -l "class User" | head -5Repository: Team-Weeth/weeth-server
Length of output: 481
🏁 Script executed:
rg -n "fun (attend|absent|removeAbsent|removeAttend)\(" --type kt -B 2 -A 8Repository: Team-Weeth/weeth-server
Length of output: 93
🏁 Script executed:
fd -e kt "Attendance" --type f | grep -E "domain.*Attendance\.kt$" | head -10Repository: Team-Weeth/weeth-server
Length of output: 138
🏁 Script executed:
fd -e kt "User" --type f | grep -E "domain.*User\.kt$" | head -10Repository: Team-Weeth/weeth-server
Length of output: 49
🏁 Script executed:
rg "fun (attend|absent|removeAbsent|removeAttend)" -A 8 --max-count 20Repository: Team-Weeth/weeth-server
Length of output: 1506
🏁 Script executed:
fd -e kt "User" --type f --path "*/domain/*/domain/entity/*" | head -10Repository: 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 30Repository: Team-Weeth/weeth-server
Length of output: 1295
🏁 Script executed:
cat -n src/main/kotlin/com/weeth/domain/attendance/domain/entity/Attendance.kt | head -50Repository: Team-Weeth/weeth-server
Length of output: 2031
🏁 Script executed:
find src/main/kotlin/com/weeth/domain -name "User.kt" -type fRepository: Team-Weeth/weeth-server
Length of output: 49
🏁 Script executed:
rg "class User" src/main/kotlin/com/weeth/domain -B 2 -A 15Repository: Team-Weeth/weeth-server
Length of output: 49
🏁 Script executed:
find src/main/kotlin -name "User.kt" -type fRepository: Team-Weeth/weeth-server
Length of output: 49
🏁 Script executed:
rg "fun (removeAttend|removeAbsent|attend|absent)" src/main/kotlin/com/weeth/domain/user -A 5Repository: Team-Weeth/weeth-server
Length of output: 144
🏁 Script executed:
rg "class User" src/main/kotlin --type kt -A 10 | head -50Repository: Team-Weeth/weeth-server
Length of output: 93
🏁 Script executed:
rg "fun (removeAttend|removeAbsent|attend|absent)" src/main/kotlin --type kt -A 4 | head -100Repository: Team-Weeth/weeth-server
Length of output: 93
🏁 Script executed:
rg "class User" src/main/kotlin -A 10 | head -80Repository: Team-Weeth/weeth-server
Length of output: 49
🏁 Script executed:
rg "fun (removeAttend|removeAbsent|attend|absent)" src/main/kotlin -A 4 | head -150Repository: 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 -100Repository: 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 1Repository: 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 -150Repository: 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.
...in/kotlin/com/weeth/domain/attendance/application/usecase/query/GetAttendanceQueryService.kt
Show resolved
Hide resolved
src/main/kotlin/com/weeth/domain/attendance/application/usecase/query/GetSessionQueryService.kt
Outdated
Show resolved
Hide resolved
| 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 | ||
| } |
There was a problem hiding this comment.
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.
| 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().
| // 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> |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: findAllBySessionIn 메서드의 사용처 확인
rg -n "findAllBySessionIn" --type=ktRepository: Team-Weeth/weeth-server
Length of output: 93
🏁 Script executed:
#!/bin/bash
# findAllBySessionIn 메서드 사용처 확인 (Kotlin 파일)
rg "findAllBySessionIn" --type=kotlinRepository: 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.
| 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 | ||
| } |
There was a problem hiding this comment.
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.
| 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) |
There was a problem hiding this comment.
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)) |
There was a problem hiding this comment.
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.
| 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))).
src/main/kotlin/com/weeth/domain/schedule/presentation/ScheduleController.kt
Show resolved
Hide resolved
...otlin/com/weeth/domain/attendance/application/usecase/query/GetAttendanceQueryServiceTest.kt
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
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컴파일 오류: 필드 선언 불일치
파이프라인 실패 로그에서 확인되듯이,
AttendanceSaveService와MeetingGetService는 더 이상 존재하지 않습니다. Lines 99-103과 147-151에서 사용하는sessionRepository와attendanceRepository필드 선언이 누락되어 있습니다.🐛 필드 선언 수정 제안
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테스트 파일 마이그레이션 미완료: 컴파일 오류 발생
sessionRepository와attendanceRepository가 선언되지 않았지만 Lines 148, 153, 213, 217에서 사용되고 있습니다. 또한 더 이상 필요하지 않은attendanceSaveService와meetingGetService가 여전히 선언되어 있고 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.
There was a problem hiding this comment.
🧹 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.
There was a problem hiding this comment.
🤖 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.
📌 Summary
Schedule 도메인 Java → Kotlin 마이그레이션 및 아키텍처 구조 변경
📝 Changes
What
Why
How
📸 Screenshots / Logs
💡 Reviewer 참고사항
제거한다면 ScheduleTimeCheck, ScheduleTimeCheckValidator, ScheduleTimeRequest 세 파일 삭제. 시간 검증은 Entity 레이어(Event.create())에만 의존.
SessionReader / findAllBySessionIn: QR 코드 출석 기능 구현 예정으로 남겨둔 메서드입니다.
Schedule 유의미한 UseCase 단위 테스트가 없어 테스트를 추가하지 않았습니다.
✅ Checklist
Summary by CodeRabbit
새로운 기능
개선사항
제거