Skip to content

20260303-#264-라운드-종료-후-결석처리#276

Merged
discipline24 merged 1 commit intomainfrom
20260303-#264-라운드-종료-후-결석처리
Mar 4, 2026

Hidden character warning

The head ref may contain hidden characters: "20260303-#264-\ub77c\uc6b4\ub4dc-\uc885\ub8cc-\ud6c4-\uacb0\uc11d\ucc98\ub9ac"
Merged

20260303-#264-라운드-종료-후-결석처리#276
discipline24 merged 1 commit intomainfrom
20260303-#264-라운드-종료-후-결석처리

Conversation

@nayoung04
Copy link
Contributor

@nayoung04 nayoung04 commented Mar 4, 2026

#264

Summary by CodeRabbit

주요 변경사항

  • 새로운 기능
    • 출석 라운드 종료 시 출석 기록이 없는 사용자에 대한 자동 부재 처리 기능 추가
    • 출석 관련 데이터 조회 및 관리 기능 강화

@nayoung04 nayoung04 requested a review from discipline24 as a code owner March 4, 2026 06:42
@nayoung04 nayoung04 changed the title [BE][FEAT] 라운드 종료 시 미출석한 세션 멤버 결석 처리 로직 추가 20260303-#264-라운드-종료-후-결석처리 Mar 4, 2026
@coderabbitai
Copy link

coderabbitai bot commented Mar 4, 2026

Walkthrough

세 개의 repository 및 service 파일을 수정하여 출석 관리 기능을 확장했습니다. AttendanceRepository와 AttendanceRoundRepository에 새로운 쿼리 메서드를 추가하고, AttendanceRoundService에 라운드 종료 시 결석 사용자를 자동으로 표시하는 배치 처리 로직을 구현했습니다.

Changes

Cohort / File(s) Summary
AttendanceRepository 확장
backend/src/main/java/org/sejongisc/backend/attendance/repository/AttendanceRepository.java
6개의 새로운 쿼리 메서드 추가: 사용자 ID와 라운드 ID 기반 존재 여부 확인, 라운드별 조회, 세션-사용자 조합으로 삭제, 사용자별 최신순 조회, 라운드별 사용자별 단일 조회, 라운드별 전체 조회.
AttendanceRoundRepository 확장
backend/src/main/java/org/sejongisc/backend/attendance/repository/AttendanceRoundRepository.java
RoundStatus와 closeAt 시간을 기반으로 종료된 라운드를 조회하는 쿼리 메서드 추가.
AttendanceRoundService 로직 구현
backend/src/main/java/org/sejongisc/backend/attendance/service/AttendanceRoundService.java
AttendanceRepository와 SessionUserRepository 의존성 주입, 라운드 종료 시 결석 사용자를 자동으로 표시하는 processAbsentees 메서드 추가, Quartz 스케줄러의 배치 처리 로직 개선.

Sequence Diagram(s)

sequenceDiagram
    participant Scheduler as Quartz Scheduler
    participant Service as AttendanceRoundService
    participant RoundRepo as AttendanceRoundRepository
    participant UserRepo as SessionUserRepository
    participant AttendanceRepo as AttendanceRepository
    participant DB as Database

    Scheduler->>Service: closeExpiredRounds()
    Service->>RoundRepo: findByRoundStatusAndCloseAtBefore(ACTIVE, now)
    RoundRepo->>DB: Query rounds with ACTIVE status<br/>and closeAt < current time
    DB-->>RoundRepo: List<AttendanceRound>
    RoundRepo-->>Service: roundsToClose

    loop For each round to close
        Service->>Service: Update round status to CLOSED
        Service->>Service: processAbsentees(round)
        
        Service->>UserRepo: Get all users in session
        UserRepo->>DB: Query session users
        DB-->>UserRepo: List<SessionUser>
        UserRepo-->>Service: sessionUsers
        
        Service->>AttendanceRepo: Check attendance existence<br/>for each user in round
        AttendanceRepo->>DB: Query attendance records
        DB-->>AttendanceRepo: Existing attendance
        AttendanceRepo-->>Service: Attendance status
        
        Service->>Service: Identify absent users<br/>(users without records)
        
        Service->>AttendanceRepo: Create ABSENT records<br/>for missing users
        AttendanceRepo->>DB: Persist ABSENT attendance
        DB-->>AttendanceRepo: Success
        AttendanceRepo-->>Service: Batch saved
    end
    
    Service-->>Scheduler: Complete
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related issues

Possibly related PRs

Suggested reviewers

  • discipline24

Poem

🐰 라운드가 종료되면,
결석자를 자동으로 찾아내고,
배치 처리로 기록을 남기네!
Repository 메서드들이 늘어나고,
Service는 더욱 똑똑해졌다네. ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 31.25% 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
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 제목이 변경 사항의 주요 내용과 일치합니다. 라운드 종료 후 결석 처리 로직 추가라는 주제가 코드 변경사항(AttendanceRoundService의 processAbsentees 메서드 추가, 배치 결석 처리 로직 등)과 정확히 부합합니다.

✏️ 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 20260303-#264-라운드-종료-후-결석처리

Tip

Try Coding Plans. Let us write the prompt for your AI agent so you can ship faster (with fewer bugs).
Share your feedback on Discord.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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: 2

🧹 Nitpick comments (3)
backend/src/main/java/org/sejongisc/backend/attendance/repository/AttendanceRoundRepository.java (1)

41-41: 들여쓰기 불일치

새로 추가된 메서드가 2칸 들여쓰기를 사용하고 있으나, 파일 내 다른 메서드들은 4칸 들여쓰기를 사용합니다.

🔧 들여쓰기 수정 제안
-  List<AttendanceRound> findByRoundStatusAndCloseAtBefore(RoundStatus status, LocalDateTime dateTime);
+    List<AttendanceRound> findByRoundStatusAndCloseAtBefore(RoundStatus status, LocalDateTime dateTime);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@backend/src/main/java/org/sejongisc/backend/attendance/repository/AttendanceRoundRepository.java`
at line 41, AttendanceRoundRepository contains the newly added method
findByRoundStatusAndCloseAtBefore(RoundStatus status, LocalDateTime dateTime)
with 2-space indentation while the rest of the file uses 4-space indentation;
update the method's leading indentation to match the file's 4-space style so
formatting is consistent (edit the AttendanceRoundRepository declaration block
and align the method signature with other repository methods).
backend/src/main/java/org/sejongisc/backend/attendance/service/AttendanceRoundService.java (1)

242-246: 주석 명확화 필요

주석에 "PRESENT, LATE 등"이라고 명시되어 있으나, findAllByAttendanceRoundABSENT 상태를 포함한 모든 출석 기록을 반환합니다. 이는 멱등성을 위해 올바른 동작이지만, 주석이 오해를 유발할 수 있습니다.

📝 주석 수정 제안
-    // 현재 라운드에 이미 출석 기록(PRESENT, LATE 등)이 있는 SessionUser의 ID 추출
+    // 현재 라운드에 이미 출석 기록이 있는 SessionUser의 ID 추출 (PRESENT, LATE, ABSENT 등 모든 상태 포함)
     Set<UUID> attendedUserIds = attendanceRepository.findAllByAttendanceRound(round)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@backend/src/main/java/org/sejongisc/backend/attendance/service/AttendanceRoundService.java`
around lines 242 - 246, Update the misleading comment in AttendanceRoundService
where attendedUserIds is populated from
attendanceRepository.findAllByAttendanceRound(round): clarify that
findAllByAttendanceRound returns all attendance records (including ABSENT) and
that we intentionally collect all SessionUser IDs (not just PRESENT/LATE) to
ensure idempotent handling; reference the attendedUserIds variable and the call
to attendanceRepository.findAllByAttendanceRound(round) so readers understand
the behavior and intent.
backend/src/main/java/org/sejongisc/backend/attendance/repository/AttendanceRepository.java (1)

32-33: @query를 파생 쿼리 메서드로 단순화 가능

해당 @Query는 Spring Data JPA 파생 쿼리 메서드 명명 규칙으로 동일하게 구현할 수 있습니다.

♻️ 파생 쿼리 메서드로 변경 제안
-  // 라운드별 특정 사용자 출석 확인
-  `@Query`("SELECT a FROM Attendance a WHERE a.attendanceRound.roundId = :roundId AND a.user = :user")
-  Optional<Attendance> findByAttendanceRound_RoundIdAndUser(`@Param`("roundId") UUID roundId, `@Param`("user") User user);
+  // 라운드별 특정 사용자 출석 확인
+  Optional<Attendance> findByAttendanceRound_RoundIdAndUser(UUID roundId, User user);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@backend/src/main/java/org/sejongisc/backend/attendance/repository/AttendanceRepository.java`
around lines 32 - 33, 현재 정의한 `@Query`("SELECT a FROM Attendance a WHERE
a.attendanceRound.roundId = :roundId AND a.user = :user")을 직접 작성할 필요가 없으니 `@Query`
어노테이션과 `@Param` 어노테이션을 제거하고 파생 쿼리 메서드로 대체하세요: 유지할 메서드 이름은
findByAttendanceRound_RoundIdAndUser(UUID roundId, User user) 또는
findByAttendanceRoundRoundIdAndUser(UUID roundId, User user)처럼 두 인자를 직접 받아
Spring Data JPA가 자동으로 쿼리를 생성하도록 변경하면 됩니다; 관련된 메서드 서명(Attendance,
attendanceRound.roundId, user)을 그대로 참조해 구현을 단순화하세요.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@backend/src/main/java/org/sejongisc/backend/attendance/service/AttendanceRoundService.java`:
- Around line 236-265: processAbsentees can violate the Attendance
(round_id,user_id) unique constraint (uk_attendance_round_user) when run twice;
fix by ensuring idempotency: before creating Attendance records, exclude users
who already have an Attendance for that round (use
attendanceRepository.findAllByAttendanceRound(round) results or
attendanceRepository.existsByAttendanceRoundAndUser(user, round) to filter), and
additionally wrap the saveAll call (attendanceRepository.saveAll) in a try/catch
that handles DataIntegrityViolationException/ConstraintViolationException to
ignore duplicate-insert race conditions and log/debug instead of failing the
job; update processAbsentees to perform the pre-insert existence check and add
the exception handling fallback.
- Around line 206-215: The loop in AttendanceRoundService that calls
attendanceRoundRepository.findByRoundStatusAndCloseAtBefore and then
round.changeStatus/processAbsentees is racy in a clustered environment; add a
concurrency-safe guard: add an optimistic lock field (e.g., private `@Version`
Long version) to the AttendanceRound entity, persist schema change, and wrap the
status change + processAbsentees save in a transactional block that catches
OptimisticLockingFailureException and skips already-updated rounds;
alternatively implement an atomic repository update method (e.g.,
updateStatusByIdAndRoundStatus(id, RoundStatus.ACTIVE, RoundStatus.CLOSED) that
returns affected row count and only call processAbsentees when the update
returns 1, or enable Quartz clustering via
spring.quartz.properties.org.quartz.jobStore.isClustered=true — pick one
approach and apply it to AttendanceRoundService (where round.changeStatus and
processAbsentees are invoked).

---

Nitpick comments:
In
`@backend/src/main/java/org/sejongisc/backend/attendance/repository/AttendanceRepository.java`:
- Around line 32-33: 현재 정의한 `@Query`("SELECT a FROM Attendance a WHERE
a.attendanceRound.roundId = :roundId AND a.user = :user")을 직접 작성할 필요가 없으니 `@Query`
어노테이션과 `@Param` 어노테이션을 제거하고 파생 쿼리 메서드로 대체하세요: 유지할 메서드 이름은
findByAttendanceRound_RoundIdAndUser(UUID roundId, User user) 또는
findByAttendanceRoundRoundIdAndUser(UUID roundId, User user)처럼 두 인자를 직접 받아
Spring Data JPA가 자동으로 쿼리를 생성하도록 변경하면 됩니다; 관련된 메서드 서명(Attendance,
attendanceRound.roundId, user)을 그대로 참조해 구현을 단순화하세요.

In
`@backend/src/main/java/org/sejongisc/backend/attendance/repository/AttendanceRoundRepository.java`:
- Line 41: AttendanceRoundRepository contains the newly added method
findByRoundStatusAndCloseAtBefore(RoundStatus status, LocalDateTime dateTime)
with 2-space indentation while the rest of the file uses 4-space indentation;
update the method's leading indentation to match the file's 4-space style so
formatting is consistent (edit the AttendanceRoundRepository declaration block
and align the method signature with other repository methods).

In
`@backend/src/main/java/org/sejongisc/backend/attendance/service/AttendanceRoundService.java`:
- Around line 242-246: Update the misleading comment in AttendanceRoundService
where attendedUserIds is populated from
attendanceRepository.findAllByAttendanceRound(round): clarify that
findAllByAttendanceRound returns all attendance records (including ABSENT) and
that we intentionally collect all SessionUser IDs (not just PRESENT/LATE) to
ensure idempotent handling; reference the attendedUserIds variable and the call
to attendanceRepository.findAllByAttendanceRound(round) so readers understand
the behavior and intent.

ℹ️ Review info
Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 4a65cd9b-fa6d-4d88-8585-e61f4dcc310e

📥 Commits

Reviewing files that changed from the base of the PR and between 218ce6a and caf58c3.

📒 Files selected for processing (3)
  • backend/src/main/java/org/sejongisc/backend/attendance/repository/AttendanceRepository.java
  • backend/src/main/java/org/sejongisc/backend/attendance/repository/AttendanceRoundRepository.java
  • backend/src/main/java/org/sejongisc/backend/attendance/service/AttendanceRoundService.java

Copy link
Contributor

@discipline24 discipline24 left a comment

Choose a reason for hiding this comment

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

고생하셨습니다~

@discipline24 discipline24 merged commit adf1b29 into main Mar 4, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants