Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;

@Getter
Expand All @@ -22,9 +23,8 @@ public class AttendModel implements AbstractModel, MemberIdModel {
private Long programId;
private AttendStatus status;

private Long rank;

private Integer penaltyScore;
@Setter private Long rank;
@Setter private Integer penaltyScore;

public AttendModel changeStatus(String afterStatus) {
validateChange(afterStatus);
Expand Down Expand Up @@ -76,8 +76,4 @@ private void isSameBeforeStatus(String status) {
}
throw new NotSameBeforeAttendStatusException(memberId);
}

public void setRank(Long rank) {
this.rank = rank;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ public class AttendService
private final ProgramRepository programRepository;
private final ProgramEntityConverter programEntityConverter;
private final AttendCountCalculate attendCountCalculate;
private final AttendWeightCalculator attendWeightCalculator;
private final AttendPenaltyResponseConverter attendPenaltyResponseConverter;
private final ProgramRankCounterRepository programRankCounterRepository;
private final SemesterPeriodProvider semesterPeriodProvider;
Expand Down Expand Up @@ -145,13 +146,7 @@ public ChangeAttendStatusResponse changeStatus(final Long memberId, final Long p

validateAttend(program, model);

AttendModel changedModel = model.changeStatus(program.getAttendMode().getMode());

if (changedModel.getStatus().equals("attend")) {
Long rank = getNextRank(programId);
changedModel.setRank(rank);
}

AttendModel changedModel = updateAttendStatus(model, program);
AttendEntity updated = attendRepository.save(attendEntityConverter.toEntity(changedModel));

String name = queryMemberService.getName(memberId);
Expand Down Expand Up @@ -403,6 +398,26 @@ public PageResponse<MemberStatistics> getStatistics(
return new PageResponse<>(Page.empty(PageRequest.of(page - 1, size)));
}

private AttendModel updateAttendStatus(AttendModel model, ProgramModel program) {
// 현재 출석 모드 가져오기
ProgramAttendMode attendMode = program.getAttendMode();
AttendStatus attendStatus = AttendStatus.find(attendMode.getMode());

// 상태 바꾸기
AttendModel changedModel = model.changeStatus(attendMode.getMode());
// 벌점 반영하기
Comment on lines +403 to +408
Copy link

@coderabbitai coderabbitai bot Sep 24, 2025

Choose a reason for hiding this comment

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

⚠️ Potential issue

🧩 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 || true

Length 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.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

음 저 함수를 호출하기 전에, AttendStatus 가 END인 경우는 이미 validate 함수를 호출해서 미리 검증하고 있어

Copy link

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 메서드에서:

  1. Line 147: validateAttend(program, model) 먼저 호출
  2. 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.

if (attendStatus == AttendStatus.ABSENT || attendStatus == AttendStatus.LATE)
changedModel.setPenaltyScore(
attendWeightCalculator.calculateTotalScore(List.of(attendStatus)));

Comment on lines +409 to +412
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

🧩 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=java

Length 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"
fi

Length 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 || true

Length 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.java

Length 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.

if (attendStatus == AttendStatus.ATTEND) {
Long rank = getNextRank(program.getId());
changedModel.setRank(rank);
}

return changedModel;
}

private List<AttendModel> findMyAttends(List<ProgramModel> programs) {
Long memberId = RequestScope.getMemberId();
return attendRepository
Expand Down