diff --git a/backend/pirocheck/src/main/java/backend/pirocheck/Assignment/service/AssignmentService.java b/backend/pirocheck/src/main/java/backend/pirocheck/Assignment/service/AssignmentService.java index 489d3fa..13c0bfe 100644 --- a/backend/pirocheck/src/main/java/backend/pirocheck/Assignment/service/AssignmentService.java +++ b/backend/pirocheck/src/main/java/backend/pirocheck/Assignment/service/AssignmentService.java @@ -13,6 +13,9 @@ import backend.pirocheck.Assignment.entity.AssignmentStatus; import backend.pirocheck.Assignment.repository.AssignmentItemRepository; import backend.pirocheck.Assignment.repository.AssignmentRepository; +import backend.pirocheck.Deposit.entity.Deposit; +import backend.pirocheck.Deposit.repository.DepositRepository; +import backend.pirocheck.Deposit.service.DepositService; import backend.pirocheck.User.entity.Role; import backend.pirocheck.User.entity.User; import backend.pirocheck.User.repository.UserRepository; @@ -33,6 +36,7 @@ public class AssignmentService { private final AssignmentItemRepository assignmentItemRepository; private final AssignmentRepository assignmentRepository; private final UserRepository userRepository; + private final DepositService depositService; public List search(Long userId) { @@ -95,7 +99,7 @@ public String createAssignment(AssignmentCreateReq assignmentCreateReq) { for (User user : users) { - AssignmentItem item = AssignmentItem.create(user, assignment, AssignmentStatus.INSUFFICIENT); + AssignmentItem item = AssignmentItem.create(user, assignment, AssignmentStatus.SUCCESS); assignment.addAssignmentItem(item); user.addAssignmentItem(item); @@ -103,6 +107,10 @@ public String createAssignment(AssignmentCreateReq assignmentCreateReq) { // assignmentItemRepository.save(item); // Cascade 설정이 되어있으므로 assignment = assignmentRepository.save(assignment); 이 코드를 실행할 때 연관된 AssignmentItem도 함께 저장 됨 } + // assignment 저장 후 모든 유저 보증금 재계산 + for (User user : users) { + depositService.recalculateDeposit(user.getId()); + } return assignment.getAssignmentName(); } @@ -132,6 +140,13 @@ public List searchAssignment(AssignmentRes assignmentRes) { // 과제 삭제 public String deleteAssignment(Long assignmentId) { assignmentRepository.deleteById(assignmentId); + + // 모든 MEMBER 유저 보증금 재계산 + List members = userRepository.findByRole(Role.MEMBER); + for (User user : members) { + depositService.recalculateDeposit(user.getId()); + } + return "과제가 성공적으로 삭제되었습니다."; } @@ -163,6 +178,9 @@ public AssignmentStatus createAssignmentItem(Long userId, Long assignmentId, Ass assignmentItemRepository.save(assignmentItem); + // 보증금 즉시 재계산 + depositService.recalculateDeposit(userId); + return assignmentItem.getSubmitted(); } @@ -177,10 +195,12 @@ public AssignmentStatus updateAssignmentItem(Long userId, Long assignmentId, Ass AssignmentItem assignmentItem = assignmentItemRepository.findByUserAndAssignment(user, assignment) .orElseThrow(() -> new IllegalArgumentException("해당 유저의 과제 채점 결과가 없습니다.")); - assignmentItem.update(req.getStatus()); // 상태 업데이트 + assignmentItem.update(req.getStatus()); // 상태 업데이트 assignmentItemRepository.save(assignmentItem); // 상태 저장 + // 보증금 즉시 재계산 + depositService.recalculateDeposit(userId); return assignmentItem.getSubmitted(); } } diff --git a/backend/pirocheck/src/main/java/backend/pirocheck/Attendance/repository/AttendanceRepository.java b/backend/pirocheck/src/main/java/backend/pirocheck/Attendance/repository/AttendanceRepository.java index 783b90b..5de470f 100644 --- a/backend/pirocheck/src/main/java/backend/pirocheck/Attendance/repository/AttendanceRepository.java +++ b/backend/pirocheck/src/main/java/backend/pirocheck/Attendance/repository/AttendanceRepository.java @@ -20,4 +20,7 @@ public interface AttendanceRepository extends JpaRepository { // 특정 날짜와 차수에 대한 모든 출석 기록 조회 List findByDateAndOrder(LocalDate date, int order); + + // 보증금 + List findByDateAndOrderAndStatusFalse(LocalDate date, int order); } diff --git a/backend/pirocheck/src/main/java/backend/pirocheck/Attendance/service/AttendanceService.java b/backend/pirocheck/src/main/java/backend/pirocheck/Attendance/service/AttendanceService.java index ae0dec4..c936c41 100644 --- a/backend/pirocheck/src/main/java/backend/pirocheck/Attendance/service/AttendanceService.java +++ b/backend/pirocheck/src/main/java/backend/pirocheck/Attendance/service/AttendanceService.java @@ -1,5 +1,8 @@ package backend.pirocheck.Attendance.service; +import backend.pirocheck.Deposit.entity.Deposit; +import backend.pirocheck.Deposit.repository.DepositRepository; +import backend.pirocheck.Deposit.service.DepositService; import backend.pirocheck.User.entity.Role; import backend.pirocheck.User.entity.User; import backend.pirocheck.User.repository.UserRepository; @@ -30,6 +33,7 @@ public class AttendanceService { private final AttendanceRepository attendanceRepository; private final AttendanceCodeRepository attendanceCodeRepository; private final UserRepository userRepository; + private final DepositService depositService; // 출석코드 생성 함수 @Transactional @@ -121,6 +125,13 @@ public String expireAttendanceCode(String code) { attendanceCode.setExpired(true); attendanceCodeRepository.save(attendanceCode); + // 보증금 + List absents = attendanceRepository.findByDateAndOrderAndStatusFalse( + attendanceCode.getDate(), attendanceCode.getOrder()); + + for (Attendance attendance : absents) { + depositService.recalculateDeposit(attendance.getUser().getId()); + } return "출석 코드가 성공적으로 만료되었습니다"; } @@ -171,6 +182,9 @@ public AttendanceMarkResponse markAttendance(Long userId, String inputCode) { attendance.setStatus(true); attendanceRepository.save(attendance); + //보증금 재계산 + depositService.recalculateDeposit(userId); + return AttendanceMarkResponse.success(); } @@ -223,6 +237,10 @@ public boolean updateAttendanceStatus(Long attendanceId, boolean status) { Attendance attendance = attendanceOpt.get(); attendance.setStatus(status); attendanceRepository.save(attendance); + + // 출석 변경 → 보증금 재계산 + depositService.recalculateDeposit(attendance.getUser().getId()); + return true; } @@ -279,8 +297,14 @@ public boolean deleteAttendance(Long attendanceId) { if (attendanceOpt.isEmpty()) { return false; } - - attendanceRepository.delete(attendanceOpt.get()); + + Attendance attendance = attendanceOpt.get(); // 변수로 저장 + Long userId = attendance.getUser().getId(); + + attendanceRepository.delete(attendance); + + // 출석 삭제 후 보증금 재계산 + depositService.recalculateDeposit(userId); return true; } diff --git a/backend/pirocheck/src/main/java/backend/pirocheck/Deposit/service/DepositService.java b/backend/pirocheck/src/main/java/backend/pirocheck/Deposit/service/DepositService.java index 66eafb1..194dbaf 100644 --- a/backend/pirocheck/src/main/java/backend/pirocheck/Deposit/service/DepositService.java +++ b/backend/pirocheck/src/main/java/backend/pirocheck/Deposit/service/DepositService.java @@ -28,29 +28,40 @@ public DepositResDto getDeposit(Long userId) { .orElseThrow(() -> new IllegalArgumentException("존재하지 않는 유저입니다.")); Deposit deposit = depositRepository.findByUser(user); + calculateAndSave(user, deposit); - // 출석 실패 + return DepositResDto.builder() + .amount(deposit.getAmount()) + .descentAssignment(deposit.getDescentAssignment()) + .descentAttendance(deposit.getDescentAttendance()) + .ascentDefence(deposit.getAscentDefence()) + .build(); + } + + // 보증금 재계산 (내부 로직만 수행) + @Transactional + public void recalculateDeposit(Long userId) { + User user = userRepository.findById(userId) + .orElseThrow(() -> new IllegalArgumentException("존재하지 않는 유저입니다.")); + + Deposit deposit = depositRepository.findByUser(user); + calculateAndSave(user, deposit); + } + + // 공통 계산 로직 + private void calculateAndSave(User user, Deposit deposit) { + //출석 int failAttendanceCount = attendanceRepository.countByUserAndStatusFalse(user); int descentAttendance = failAttendanceCount * 10_000; - // 과제 실패 + //과제 int failAssignmentCount = assignmentItemRepository.countByUserAndSubmitted(user, AssignmentStatus.FAILURE); int weakAssignmentCount = assignmentItemRepository.countByUserAndSubmitted(user, AssignmentStatus.INSUFFICIENT); int descentAssignment = failAssignmentCount * 20_000 + weakAssignmentCount * 10_000; - // 방어권 int ascentDefence = deposit.getAscentDefence(); - // 보증금 업데이트 deposit.updateAmounts(descentAssignment, descentAttendance, ascentDefence); depositRepository.save(deposit); - - return DepositResDto.builder() - .amount(deposit.getAmount()) - .descentAssignment(deposit.getDescentAssignment()) - .descentAttendance(deposit.getDescentAttendance()) - .ascentDefence(deposit.getAscentDefence()) - .build(); - } } \ No newline at end of file diff --git a/frontend/src/components/AdminDailyAttendanceCard.jsx b/frontend/src/components/AdminDailyAttendanceCard.jsx index f4f0f25..4333504 100644 --- a/frontend/src/components/AdminDailyAttendanceCard.jsx +++ b/frontend/src/components/AdminDailyAttendanceCard.jsx @@ -3,7 +3,7 @@ import "./componentsCss/AdminDailyAttendanceCard.css"; import api from "../api/api"; import { getStudentAttendance,updateAttendanceStatus } from "../api/adminattendance"; -const AdminDailyAttendanceCard = ({ date, order,studentId, onClose, onRefresh }) => { +const AdminDailyAttendanceCard = ({ date, studentId, onClose, onRefresh }) => { const [slots, setSlots] = useState([]); const [modified, setModified] = useState([]); @@ -34,11 +34,11 @@ const AdminDailyAttendanceCard = ({ date, order,studentId, onClose, onRefresh } */ const rawSlots = rawData .filter((d) => d.date === date) // 해당 날짜의 출석만 필터 - .sort((a, b) => a.order - b.order) // order 순으로 정렬 + //.sort((a, b) => a.order - b.order) // order 순으로 정렬 .map((d) => ({ - date: d.date, + //date: d.date, id: d.attendanceId, // 출석 ID - order: d.order, // 회차 표시용 + //order: d.order, // 회차 표시용 status: d.status ? "SUCCESS" : "FAILURE", // 드롭다운에 맞게 변환 })); @@ -46,10 +46,10 @@ const AdminDailyAttendanceCard = ({ date, order,studentId, onClose, onRefresh } rawSlots.length > 0 ? rawSlots : [1, 2, 3].map((order) => ({ - date, + //date, id: null, // 새 출석이므로 아직 id 없음 - order, - status: "EMPTY",//기본값 + //order, + status: "FAILURE",//기본값 })); setSlots(filledSlots); @@ -63,9 +63,9 @@ const AdminDailyAttendanceCard = ({ date, order,studentId, onClose, onRefresh } } }; - fetchSlots(); + if (date) fetchSlots(); }, [date, studentId]); - + const handleChange = (idx, newValue) => { const newSlots = [...slots]; newSlots[idx].status = newValue; @@ -91,8 +91,8 @@ const AdminDailyAttendanceCard = ({ date, order,studentId, onClose, onRefresh } console.log("📝 저장 요청", { id: slot.id, - order: slot.order, - date: slot.date, + //order: slot.order, + //date: slot.date, status: slot.status, }); @@ -126,7 +126,7 @@ const AdminDailyAttendanceCard = ({ date, order,studentId, onClose, onRefresh }
{slots.map((slot, idx) => ( -
+
{idx + 1}차 출석