-
Notifications
You must be signed in to change notification settings - Fork 1
[FIX] 출석체크 시 벌점 가중치 부여 로직 추가 #291
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
WalkthroughAttendModel에 Lombok @Setter가 도입되어 rank와 penaltyScore의 세터가 대체되었고, AttendService는 AttendWeightCalculator 의존성을 추가하고 상태 변경 로직을 updateAttendStatus로 분리해 패널티 적용 및 랭크 부여를 캡슐화했다. 저장 호출은 유지된다. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant U as User
participant S as AttendService
participant C as AttendWeightCalculator
participant M as AttendModel
participant P as ProgramModel
participant R as Repository
U->>S: changeStatus(targetId, programId, request)
S->>S: updateAttendStatus(M, P)
S->>M: 현재 모드 판정 및 상태 매핑
alt 상태 == ABSENT or LATE
S->>C: calculatePenalty(M, P)
C-->>S: penaltyScore
S->>M: setPenaltyScore
else 상태 == ATTEND
S->>S: getNextRank(P)
S->>M: setRank
end
S->>R: save(M)
R-->>S: saved(M)
S-->>U: 응답
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests
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. Comment |
There was a problem hiding this 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 (2)
eeos/src/main/java/com/blackcompany/eeos/target/application/model/AttendModel.java (2)
12-12: 도메인 모델의 Setter 공개 범위 축소 권장Setter 노출은 도메인 불변성을 약화시킬 수 있습니다. 패키지 범위로 제한하는 것을 권장합니다.
적용 예시(diff):
import lombok.NoArgsConstructor; +import lombok.AccessLevel; import lombok.Setter; import lombok.ToString;
26-27: rank/penaltyScore Setter 범위 축소 및 값 검증 고려
- Setter를 패키지 범위로 제한해 외부 임의 변경을 방지하세요.
- 음수/비정상 값 방지(예: rank > 0, penaltyScore >= 0) 검증이 필요합니다.
적용 예시(diff):
- @Setter private Long rank; - @Setter private Integer penaltyScore; + @Setter(AccessLevel.PACKAGE) private Long rank; + @Setter(AccessLevel.PACKAGE) private Integer penaltyScore;추가로, 도메인 메서드(ex. assignRank, applyPenalty)를 통해 유효성 검사를 내재화하는 접근도 고려해 주세요.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
eeos/src/main/java/com/blackcompany/eeos/target/application/model/AttendModel.java(2 hunks)eeos/src/main/java/com/blackcompany/eeos/target/application/service/AttendService.java(3 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: test
🔇 Additional comments (3)
eeos/src/main/java/com/blackcompany/eeos/target/application/service/AttendService.java (3)
149-149: 상태 변경 로직 분리(LGTM)변경 계산을 updateAttendStatus로 캡슐화한 것은 응집도 향상에 유익합니다.
89-89: AttendWeightCalculator 빈 등록 확인 — 생성자 주입 가능AttendWeightCalculator에 @service와 @requiredargsconstructor가 선언되어 있어 생성자 주입 가능함. calculateTotalScore는 public int 반환(AttendWeightCalculator.java)이며 PenaltyPointRepository와 관련 조회 메서드가 존재함.
409-412: 벌점 타입·영속성 검증 완료 — 수정 불필요calculateTotalScore는 int를 반환하고 AttendModel/AttendEntity의 penaltyScore는 Integer라 primitive→박싱으로 타입 불일치 없음. setPenaltyScore로 설정한 뒤 attendRepository.save(attendEntityConverter.toEntity(changedModel))가 호출되어 AttendEntity.penalty_score에 값이 저장되며, PenaltyPointRepository도 AttendEntity를 대상으로 집계 쿼리를 수행(별도 penalty_point 테이블 없음).
eeos/src/main/java/com/blackcompany/eeos/target/application/service/AttendService.java
Show resolved
Hide resolved
| ProgramAttendMode attendMode = program.getAttendMode(); | ||
| AttendStatus attendStatus = AttendStatus.find(attendMode.getMode()); | ||
|
|
||
| // 상태 바꾸기 | ||
| AttendModel changedModel = model.changeStatus(attendMode.getMode()); | ||
| // 벌점 반영하기 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
ProgramAttendMode → AttendStatus 매핑/케이스 일치 여부 확인
attendMode.getMode()를 String으로 받아 AttendStatus.find로 변환합니다. 모드 문자열과 AttendStatus의 내부 문자열 표현(대소문자/별칭)이 1:1로 일치하는지 검증이 필요합니다.
점검 스크립트:
🏁 Script executed:
#!/bin/bash
# ProgramAttendMode 정의와 getMode() 반환값 확인
rg -nC3 -P 'enum\s+ProgramAttendMode\b|getMode\s*\('
# AttendStatus.find 구현과 허용 문자열(케이스/별칭) 확인
rg -nC5 -P 'enum\s+AttendStatus\b|static\s+\w+\s*find\s*\('Length of output: 18723
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== AttendStatus.java ==="
sed -n '1,240p' eeos/src/main/java/com/blackcompany/eeos/target/application/model/AttendStatus.java || true
echo
echo "=== ProgramAttendMode.java ==="
sed -n '1,240p' eeos/src/main/java/com/blackcompany/eeos/program/application/model/ProgramAttendMode.java || trueLength of output: 1261
'END' 모드가 AttendStatus에 없음 — AttendStatus.find 호출 시 예외 발생
ProgramAttendMode.getMode()가 "attend"|"late"|"end"을 반환하는데 "end"는 AttendStatus(허용값: "attend","absent","late","nonResponse","nonRelated")에 없어 AttendStatus.find(attendMode.getMode()) 호출이 NotFoundAttendStatusException을 던집니다.
위치: eeos/src/main/java/com/blackcompany/eeos/target/application/service/AttendService.java (updateAttendStatus 호출부, 약 401–411행); 관련 파일: ProgramAttendMode.java, AttendStatus.java.
해결안(중 택1):
- AttendStatus.find 호출 전에 ProgramAttendMode.END를 분기 처리(무시/기본값 변환 등)하거나
- 명시적 매핑 함수를 만들어 "end"를 적절한 AttendStatus로 변환하거나 AttendStatus에 대응값 추가
🤖 Prompt for AI Agents
In
eeos/src/main/java/com/blackcompany/eeos/target/application/service/AttendService.java
around lines 403–408, ProgramAttendMode.getMode() can return "end" which is not
present in AttendStatus and causes AttendStatus.find(...) to throw
NotFoundAttendStatusException; fix by adding a branch before calling
AttendStatus.find to handle ProgramAttendMode.END (either skip the find and
treat as a no-op or map it to a sensible default AttendStatus), or implement an
explicit mapping function that converts ProgramAttendMode values (including END)
to corresponding AttendStatus values (or update AttendStatus to include a
matching value) and use that mapping instead of calling AttendStatus.find
directly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
음 저 함수를 호출하기 전에, AttendStatus 가 END인 경우는 이미 validate 함수를 호출해서 미리 검증하고 있어
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
@rlajm1203 맞습니다! 코드를 다시 확인해보니 changeStatus 메서드에서 validateAttend(program, model)를 먼저 호출하고, 이 함수가 programModel.getAttendMode().equals(ProgramAttendMode.END)인 경우 NotStartAttendException을 던져서 updateAttendStatus가 호출되기 전에 이미 차단하고 있네요.
따라서 updateAttendStatus 함수는 실제로 ProgramAttendMode.END 케이스를 만나지 않게 되어 있어서 제가 지적한 문제는 발생하지 않을 것 같습니다. 검증 로직이 잘 되어 있군요! 😊
(\ /)
( ._.)
o_(")(")
thinking rabbit
🏁 Script executed:
#!/bin/bash
# Check the validateAttend function implementation and how it handles END mode
rg -nC5 -P 'validateAttend\s*\(' eeos/src/main/java/com/blackcompany/eeos/target/application/service/AttendService.java
# Check the changeStatus method flow to see the validation order
rg -nC10 -P 'changeStatus\s*\(' eeos/src/main/java/com/blackcompany/eeos/target/application/service/AttendService.java
Length of output: 2530
@rlajm1203 완전히 맞습니다! 스크립트 결과를 보니 확실히 검증되었네요.
changeStatus 메서드에서:
- Line 147:
validateAttend(program, model)먼저 호출 - Line 149:
updateAttendStatus(model, program)나중에 호출
그리고 validateAttend 함수 (Line 432)에서:
if (programModel.getAttendMode().equals(ProgramAttendMode.END))
throw new NotStartAttendException();이렇게 END 모드일 때 예외를 던져서 updateAttendStatus가 호출되기 전에 이미 차단하고 있군요. 제가 놓친 부분이었습니다. 검증 로직이 잘 설계되어 있어서 문제없이 동작할 것 같습니다!
감사합니다. 🙂
(\ /)
( ^.^)
o_(")(")
happy rabbit
✏ Learnings added
Learnt from: rlajm1203
PR: JNU-econovation/EEOS-BE#291
File: eeos/src/main/java/com/blackcompany/eeos/target/application/service/AttendService.java:403-408
Timestamp: 2025-09-24T15:04:52.518Z
Learning: In AttendService.changeStatus(), the validateAttend() method is called before updateAttendStatus() and throws NotStartAttendException when ProgramAttendMode is END, preventing updateAttendStatus from encountering the END case that would cause AttendStatus.find() to fail.
Learnt from: rlajm1203
PR: JNU-econovation/EEOS-BE#291
File: eeos/src/main/java/com/blackcompany/eeos/target/application/service/AttendService.java:403-408
Timestamp: 2025-09-24T15:04:52.518Z
Learning: In AttendService.changeStatus(), the validateAttend() method is called before updateAttendStatus() and throws NotStartAttendException when ProgramAttendMode is END (line 432-433), preventing updateAttendStatus from encountering the END case that would cause AttendStatus.find() to fail.
Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
eeos/src/main/java/com/blackcompany/eeos/target/application/service/AttendService.java(3 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-09-24T15:04:52.518Z
Learnt from: rlajm1203
PR: JNU-econovation/EEOS-BE#291
File: eeos/src/main/java/com/blackcompany/eeos/target/application/service/AttendService.java:403-408
Timestamp: 2025-09-24T15:04:52.518Z
Learning: In AttendService.changeStatus(), the validateAttend() method is called before updateAttendStatus() and throws NotStartAttendException when ProgramAttendMode is END (line 432-433), preventing updateAttendStatus from encountering the END case that would cause AttendStatus.find() to fail.
Applied to files:
eeos/src/main/java/com/blackcompany/eeos/target/application/service/AttendService.java
📚 Learning: 2025-09-24T15:04:52.518Z
Learnt from: rlajm1203
PR: JNU-econovation/EEOS-BE#291
File: eeos/src/main/java/com/blackcompany/eeos/target/application/service/AttendService.java:403-408
Timestamp: 2025-09-24T15:04:52.518Z
Learning: In AttendService.changeStatus(), the validateAttend() method is called before updateAttendStatus() and throws NotStartAttendException when ProgramAttendMode is END, preventing updateAttendStatus from encountering the END case that would cause AttendStatus.find() to fail.
Applied to files:
eeos/src/main/java/com/blackcompany/eeos/target/application/service/AttendService.java
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: test
🔇 Additional comments (4)
eeos/src/main/java/com/blackcompany/eeos/target/application/service/AttendService.java (4)
147-154: 상태 변경 로직 분리로 응집도/가독성 개선changeStatus에서 updateAttendStatus로 위임한 방향성 좋습니다. 퍼시스턴스 흐름도 유지됩니다.
403-405: END 모드 매핑 이슈는 validate에서 선제 차단됨 — OKProgramAttendMode.END → AttendStatus 미존재 문제는 changeStatus()에서 validateAttend()가 먼저 예외를 던져 실제로는 도달하지 않습니다. 이 부분은 이전 논의와 일치합니다.
413-416: 랭크 부여 조건의 enum 비교로 안정화 — 좋습니다문자열 비교 대신 enum 비교 사용으로 회귀 위험 감소했습니다.
89-90: AttendWeightCalculator 빈 등록/스캔 확인 — 조치 불필요AttendWeightCalculator(eeos/src/main/java/com/blackcompany/eeos/target/application/service/AttendWeightCalculator.java)는 @service로 빈 등록되어 있고, AttendPenaltyResponseConverter(eeos/src/main/java/com/blackcompany/eeos/target/application/dto/converter/AttendPenaltyResponseConverter.java)는 @component로 등록되어 있습니다. @requiredargsconstructor 기반 생성자 주입으로 문제가 없으며, 검색 결과 명시적 순환 참조는 발견되지 않습니다.
| if (attendStatus == AttendStatus.ABSENT || attendStatus == AttendStatus.LATE) | ||
| changedModel.setPenaltyScore( | ||
| attendWeightCalculator.calculateTotalScore(List.of(attendStatus))); | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
ABSENT 벌점 분기 도달 불가 가능성 + 벌점 덮어쓰기 의도 확인
- 현재 흐름(updateAttendStatus)은 ProgramAttendMode 기반이라 LATE는 적용되지만 ABSENT는 호출 경로상 도달하지 않습니다(END는 validate에서 차단). “불참(ABSENT)” 벌점은 어디서 반영되는지 확인 필요합니다(예: 배치/일괄 마감 로직). 요구사항이 “지각/불참 모두 가중치 부여”라면, 불참 경로의 반영 위치가 이 메서드가 아니라면 그 근거를 남겨 주세요.
- setPenaltyScore는 ‘대입’입니다. 기존 값 누적이 의도라면 += 또는 계산기에서 누계 반환이 필요합니다. 단건 대입이 의도면 현 구현으로 충분합니다.
명시적 분기로 의도를 드러내는 것을 권장합니다.
적용 예시(diff):
- // 벌점 반영하기
- if (attendStatus == AttendStatus.ABSENT || attendStatus == AttendStatus.LATE)
- changedModel.setPenaltyScore(
- attendWeightCalculator.calculateTotalScore(List.of(attendStatus)));
+ // 벌점 반영하기
+ switch (attendStatus) {
+ case LATE:
+ // 단건 가중치라면 List.of(AttendStatus.LATE) 그대로 사용
+ changedModel.setPenaltyScore(
+ attendWeightCalculator.calculateTotalScore(List.of(AttendStatus.LATE)));
+ break;
+ case ATTEND:
+ // 참석 시 벌점 초기화가 요구사항이면 유지, 아니라면 제거
+ changedModel.setPenaltyScore(0L);
+ break;
+ default:
+ // ABSENT는 별도 마감/배치 로직에서 처리된다면 여기서는 no-op
+ break;
+ }추가 확인 스크립트(ABSENT 벌점 반영 경로 탐색):
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== ABSENT 벌점 관련 코드 경로 탐색 =="
rg -nC3 -P 'ABSENT|setPenaltyScore|calculateTotalScore|AttendWeightCalculator' --type=javaLength of output: 12094
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== Show AttendService.java (first 800 lines with numbers) =="
nl -ba eeos/src/main/java/com/blackcompany/eeos/target/application/service/AttendService.java | sed -n '1,800p'Length of output: 101
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== Locate files =="
attend_service_file=$(rg -n --hidden -S "^public class AttendService\b" --type java | head -n1 | cut -d: -f1 || true)
attend_model_file=$(rg -n --hidden -S "^public class AttendModel\b" --type java | head -n1 | cut -d: -f1 || true)
attend_calc_file=$(rg -n --hidden -S "^public class AttendWeightCalculator\b" --type java | head -n1 | cut -d: -f1 || true)
program_quit_file=$(rg -n --hidden -S "^public class ProgramQuitService\b" --type java | head -n1 | cut -d: -f1 || true)
attend_repo_file=$(rg -n --hidden -S "interface .*AttendRepository|interface AttendRepository" --type java | head -n1 | cut -d: -f1 || true)
attend_count_calc_file=$(rg -n --hidden -S "^public class AttendCountCalculate\b" --type java | head -n1 | cut -d: -f1 || true)
echo "AttendService: $attend_service_file"
echo "AttendModel: $attend_model_file"
echo "AttendWeightCalculator: $attend_calc_file"
echo "ProgramQuitService: $program_quit_file"
echo "AttendRepository: $attend_repo_file"
echo "AttendCountCalculate: $attend_count_calc_file"
echo
echo "== Show AttendService (around lines 350-440) =="
if [ -n "$attend_service_file" ] && [ -f "$attend_service_file" ]; then
sed -n '340,440p' "$attend_service_file"
else
echo "AttendService file not found"
fi
echo
echo "== Show AttendModel (full) =="
if [ -n "$attend_model_file" ] && [ -f "$attend_model_file" ]; then
sed -n '1,240p' "$attend_model_file"
else
echo "AttendModel file not found"
fi
echo
echo "== Show AttendWeightCalculator (full) =="
if [ -n "$attend_calc_file" ] && [ -f "$attend_calc_file" ]; then
sed -n '1,240p' "$attend_calc_file"
else
echo "AttendWeightCalculator file not found"
fi
echo
echo "== Show ProgramQuitService (around lines 1-160) =="
if [ -n "$program_quit_file" ] && [ -f "$program_quit_file" ]; then
sed -n '1,240p' "$program_quit_file"
else
echo "ProgramQuitService file not found"
fi
echo
echo "== Show AttendRepository (any match) =="
if [ -n "$attend_repo_file" ] && [ -f "$attend_repo_file" ]; then
sed -n '1,240p' "$attend_repo_file"
else
rg -n --hidden -S "updateAttendStatusByProgramId|updateAttendStatus" --type java || true
fi
echo
echo "== Show AttendCountCalculate (full) =="
if [ -n "$attend_count_calc_file" ] && [ -f "$attend_count_calc_file" ]; then
sed -n '1,240p' "$attend_count_calc_file"
else
echo "AttendCountCalculate file not found"
fiLength of output: 13525
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== ProgramAttendMode 정의 위치 검색 =="
rg -n --hidden -S "enum ProgramAttendMode|public enum ProgramAttendMode" --type java || true
echo
echo "== Show ProgramAttendMode file =="
rg -n --hidden -S "enum ProgramAttendMode|public enum ProgramAttendMode" --type java | head -n1 | cut -d: -f1 | xargs -r sed -n '1,240p'
echo
echo "== Show AttendEntity =="
rg -n --hidden -S "class AttendEntity" --type java | head -n1 | cut -d: -f1 | xargs -r sed -n '1,240p'
echo
echo "== Search for calculateTotalScore / setPenaltyScore usages =="
rg -n --hidden -S "calculateTotalScore|setPenaltyScore" --type java || true
echo
echo "== Search for code handling ABSENT penalty elsewhere =="
rg -n --hidden -S "ABSENT.*penal|penal.*ABSENT|penaltyScore|penalty point|penaltyPoint|updateAttendStatusByProgramId" --type java || true
echo
echo "== Search for places that create/update AttendEntity penaltyScore field =="
rg -n --hidden -S "penaltyScore" --type java || true
echo
echo "== Show AttendRepository.updateAttendStatusByProgramId implementation (persistence layer) =="
rg -n --hidden -S "updateAttendStatusByProgramId" --type java -n || true
rg -n --hidden -S "updateAttendStatusByProgramId" --type sql || trueLength of output: 11722
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== Show persistence AttendRepository =="
sed -n '1,240p' eeos/src/main/java/com/blackcompany/eeos/target/persistence/AttendRepository.javaLength of output: 4577
ABSENT 분기 불필요 — 배치로 상태 전환 시 벌점이 반영되지 않음(수정 필요)
- 검증 결과: updateAttendStatus(...)는 ProgramAttendMode(ATTEND/LATE/END)를 기반으로 호출되며 validateAttend()에서 END는 차단됨 → 이 경로에서 AttendStatus.ABSENT에 도달하지 않음. (eeos/src/main/java/com/blackcompany/eeos/target/application/service/AttendService.java — updateAttendStatus)
- 현재 배치(ProgramQuitService.doQuit)에서 attendRepository.updateAttendStatusByProgramId(..., NONRESPONSE, ABSENT)로 상태만 변경하고 penaltyScore는 업데이트하지 않음(eeos/src/main/java/com/blackcompany/eeos/program/application/service/ProgramQuitService.java, eeos/src/main/java/com/blackcompany/eeos/target/persistence/AttendRepository.java, eeos/src/main/java/com/blackcompany/eeos/target/persistence/AttendEntity.java — penaltyScore 필드). setPenaltyScore 사용처는 updateAttendStatus 하나뿐임.
- setPenaltyScore는 대입(assign)임. 벌점 누적이 의도라면 현재 구현(대입)은 잘못이고, 단건 대입이 의도라면 유지하면 됨.
권장 조치:
- AttendService의 ABSENT 분기는 제거하거나 명시적 주석/분기로 의도 표시.
- 배치에서 NONRESPONSE→ABSENT 전환 시 벌점도 함께 반영하도록 수정(예: 배치에서 attendWeightCalculator.calculateTotalScore(List.of(AttendStatus.ABSENT))로 penaltyScore 일괄 업데이트 또는 엔티티 로드 후 setPenaltyScore 적용).
- 벌점 대입 vs 누적 요구사항을 명확히 하고 그에 따라 setPenaltyScore 동작을 조정.
🤖 Prompt for AI Agents
In
eeos/src/main/java/com/blackcompany/eeos/target/application/service/AttendService.java
around lines 409-412, remove the unreachable AttendStatus.ABSENT branch (or
replace it with an explicit comment clarifying it is unreachable in
updateAttendStatus) to avoid dead code; then ensure batch-driven status
transitions (ProgramQuitService.doQuit) also update penaltyScore: modify
ProgramQuitService to compute penaltyScore for ABSENT (e.g., use
attendWeightCalculator.calculateTotalScore(List.of(AttendStatus.ABSENT))) and
persist it via AttendRepository update or by loading entities and calling
setPenaltyScore, and finally decide whether penaltyScore should be assigned or
accumulated and adjust setPenaltyScore implementation and all callers
accordingly.
📌 관련 이슈
✒️ 작업 내용
스크린샷 🏞️ (선택)
💬 REVIEWER에게 요구사항 💬
Summary by CodeRabbit