diff --git a/backend/pirocheck/src/main/java/backend/pirocheck/Attendance/controller/AdminAttendanceController.java b/backend/pirocheck/src/main/java/backend/pirocheck/Attendance/controller/AdminAttendanceController.java index f811f53..5b75a14 100644 --- a/backend/pirocheck/src/main/java/backend/pirocheck/Attendance/controller/AdminAttendanceController.java +++ b/backend/pirocheck/src/main/java/backend/pirocheck/Attendance/controller/AdminAttendanceController.java @@ -1,7 +1,9 @@ package backend.pirocheck.Attendance.controller; +import backend.pirocheck.Attendance.dto.request.UpdateAttendanceStatusReq; import backend.pirocheck.Attendance.dto.response.ApiResponse; import backend.pirocheck.Attendance.dto.response.AttendanceCodeResponse; +import backend.pirocheck.Attendance.dto.response.UserAttendanceStatusRes; import backend.pirocheck.Attendance.entity.AttendanceCode; import backend.pirocheck.Attendance.service.AttendanceService; import io.swagger.v3.oas.annotations.Operation; @@ -12,8 +14,11 @@ import io.swagger.v3.oas.annotations.security.SecurityRequirement; import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; +import org.springframework.format.annotation.DateTimeFormat; import org.springframework.web.bind.annotation.*; +import java.time.LocalDate; +import java.util.List; import java.util.Optional; @RestController @@ -79,7 +84,7 @@ public ApiResponse getActiveCode() { public ApiResponse expireAttendance( @Parameter(description = "만료할 출석 코드", required = true) @RequestParam String code) { - String result = attendanceService.exprireAttendanceCode(code); + String result = attendanceService.expireAttendanceCode(code); if (result.equals("출석 코드가 성공적으로 만료되었습니다")) { return ApiResponse.success(result, null); @@ -104,4 +109,49 @@ public ApiResponse expireLatestAttendance() { return ApiResponse.error(result); } } + + // 출석 상태 변경 (관리자 전용) + @Operation(summary = "출석 상태 변경", description = "관리자가 특정 사용자의 출석 상태를 변경합니다.") + @ApiResponses({ + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "출석 상태 변경 성공"), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "400", description = "잘못된 요청"), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "404", description = "출석 기록을 찾을 수 없음") + }) + @PutMapping("/status") + public ApiResponse updateAttendanceStatus( + @io.swagger.v3.oas.annotations.parameters.RequestBody( + description = "출석 상태 변경 요청", + required = true, + content = @Content(schema = @Schema(implementation = UpdateAttendanceStatusReq.class)) + ) + @RequestBody UpdateAttendanceStatusReq req) { + + boolean result = attendanceService.updateAttendanceStatus( + req.getAttendanceId(), + req.isStatus() + ); + + if (result) { + return ApiResponse.success("출석 상태가 성공적으로 변경되었습니다", null); + } else { + return ApiResponse.error("출석 상태 변경에 실패했습니다. 출석 기록을 찾을 수 없습니다."); + } + } + + // 특정 날짜와 차수에 대한 모든 학생의 출석 현황 조회 + @Operation(summary = "특정 날짜와 차수의 출석 현황 조회", description = "특정 날짜와 차수에 대한 모든 학생의 출석 현황을 조회합니다.") + @ApiResponses({ + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "조회 성공"), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "400", description = "잘못된 요청") + }) + @GetMapping("/list") + public ApiResponse> getAllAttendanceByDateAndOrder( + @Parameter(description = "조회할 날짜 (YYYY-MM-DD)", required = true) + @RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate date, + @Parameter(description = "조회할 차수", required = true) + @RequestParam int order) { + + List attendances = attendanceService.findAllByDateAndOrder(date, order); + return ApiResponse.success(attendances); + } } \ No newline at end of file diff --git a/backend/pirocheck/src/main/java/backend/pirocheck/Attendance/dto/request/UpdateAttendanceStatusReq.java b/backend/pirocheck/src/main/java/backend/pirocheck/Attendance/dto/request/UpdateAttendanceStatusReq.java new file mode 100644 index 0000000..2c6973c --- /dev/null +++ b/backend/pirocheck/src/main/java/backend/pirocheck/Attendance/dto/request/UpdateAttendanceStatusReq.java @@ -0,0 +1,20 @@ +package backend.pirocheck.Attendance.dto.request; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@Schema(description = "출석 상태 수정 요청") +public class UpdateAttendanceStatusReq { + @Schema(description = "출석 기록 ID", example = "1") + private Long attendanceId; + + @Schema(description = "변경할 출석 상태", example = "true") + private boolean status; +} \ No newline at end of file diff --git a/backend/pirocheck/src/main/java/backend/pirocheck/Attendance/dto/response/UserAttendanceStatusRes.java b/backend/pirocheck/src/main/java/backend/pirocheck/Attendance/dto/response/UserAttendanceStatusRes.java new file mode 100644 index 0000000..fcb624c --- /dev/null +++ b/backend/pirocheck/src/main/java/backend/pirocheck/Attendance/dto/response/UserAttendanceStatusRes.java @@ -0,0 +1,34 @@ +package backend.pirocheck.Attendance.dto.response; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import java.time.LocalDate; + +@Getter +@Builder +@NoArgsConstructor +@AllArgsConstructor +@Schema(description = "사용자 출석 상태 응답") +public class UserAttendanceStatusRes { + @Schema(description = "출석 기록 ID", example = "1") + private Long attendanceId; + + @Schema(description = "사용자 ID", example = "1") + private Long userId; + + @Schema(description = "사용자 이름", example = "홍길동") + private String username; + + @Schema(description = "출석 날짜", example = "2023-10-20") + private LocalDate date; + + @Schema(description = "출석 차수", example = "1") + private int order; + + @Schema(description = "출석 상태", example = "true") + private boolean status; +} \ No newline at end of file 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 e06f6e9..783b90b 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 @@ -17,4 +17,7 @@ public interface AttendanceRepository extends JpaRepository { // 출석 실패 int countByUserAndStatusFalse(User user); + + // 특정 날짜와 차수에 대한 모든 출석 기록 조회 + List findByDateAndOrder(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 ae2d5a8..4a1adb8 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 @@ -6,6 +6,7 @@ import backend.pirocheck.Attendance.dto.response.AttendanceMarkResponse; import backend.pirocheck.Attendance.dto.response.AttendanceSlotRes; import backend.pirocheck.Attendance.dto.response.AttendanceStatusRes; +import backend.pirocheck.Attendance.dto.response.UserAttendanceStatusRes; import backend.pirocheck.Attendance.entity.Attendance; import backend.pirocheck.Attendance.entity.AttendanceCode; import backend.pirocheck.Attendance.repository.AttendanceCodeRepository; @@ -104,7 +105,7 @@ public String expireLatestAttendanceCode() { // 출석코드 만료처리 함수 @Transactional - public String exprireAttendanceCode(String code) { + public String expireAttendanceCode(String code) { Optional codeOpt = attendanceCodeRepository.findByCodeAndDate(code, LocalDate.now()); if (codeOpt.isEmpty()) { @@ -208,4 +209,42 @@ public List findByUserIdAndDate(Long userId, LocalDate date) .sorted(Comparator.comparingInt(AttendanceSlotRes::getOrder)) .toList(); } + + // 관리자가 유저의 출석 상태를 변경하는 함수 + @Transactional + public boolean updateAttendanceStatus(Long attendanceId, boolean status) { + Optional attendanceOpt = attendanceRepository.findById(attendanceId); + + if (attendanceOpt.isEmpty()) { + return false; + } + + // 출석 상태 변경 + Attendance attendance = attendanceOpt.get(); + attendance.setStatus(status); + attendanceRepository.save(attendance); + return true; + } + + // 특정 날짜와 차수의 모든 학생 출석 현황 조회 + public List findAllByDateAndOrder(LocalDate date, int order) { + // 해당 날짜와 차수에 대한 모든 출석 기록 조회 + List attendances = attendanceRepository.findByDateAndOrder(date, order); + + // 사용자별로 DTO 변환 + return attendances.stream() + .map(attendance -> { + User user = attendance.getUser(); + return UserAttendanceStatusRes.builder() + .userId(user.getId()) + .username(user.getName()) + .date(attendance.getDate()) + .order(attendance.getOrder()) + .status(attendance.isStatus()) + .attendanceId(attendance.getId()) // 출석 기록 ID 추가 + .build(); + }) + .sorted(Comparator.comparing(UserAttendanceStatusRes::getUsername)) + .toList(); + } }