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 new file mode 100644 index 0000000..f811f53 --- /dev/null +++ b/backend/pirocheck/src/main/java/backend/pirocheck/Attendance/controller/AdminAttendanceController.java @@ -0,0 +1,107 @@ +package backend.pirocheck.Attendance.controller; + +import backend.pirocheck.Attendance.dto.response.ApiResponse; +import backend.pirocheck.Attendance.dto.response.AttendanceCodeResponse; +import backend.pirocheck.Attendance.entity.AttendanceCode; +import backend.pirocheck.Attendance.service.AttendanceService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.*; + +import java.util.Optional; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/api/admin/attendance") +@Tag(name = "관리자 출석관리", description = "관리자용 출석 관리 API") +public class AdminAttendanceController { + + private final AttendanceService attendanceService; + + // 출석체크 시작 + @Operation(summary = "출석 체크 시작", description = "새로운 출석 코드를 생성하고 출석 체크를 시작합니다.") + @ApiResponses({ + @io.swagger.v3.oas.annotations.responses.ApiResponse( + responseCode = "200", + description = "출석 코드 생성 성공", + content = @Content(schema = @Schema(implementation = AttendanceCodeResponse.class)) + ), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "400", description = "잘못된 요청") + }) + @PostMapping("/start") + public ApiResponse startAttendance() { + try { + AttendanceCode code = attendanceService.generateCodeAndCreateAttendances(); + return ApiResponse.success(AttendanceCodeResponse.from(code)); + } catch (IllegalStateException e) { + // 하루 최대 출석 체크 횟수를 초과한 경우 + return ApiResponse.error(e.getMessage()); + } catch (Exception e) { + return ApiResponse.error("출석 코드 생성 중 오류가 발생했습니다: " + e.getMessage()); + } + } + + // 현재 활성화된 출석코드 조회 + @Operation(summary = "현재 활성화된 출석 코드 조회", description = "현재 활성화된 출석 코드 정보를 조회합니다.") + @ApiResponses({ + @io.swagger.v3.oas.annotations.responses.ApiResponse( + responseCode = "200", + description = "조회 성공", + content = @Content(schema = @Schema(implementation = AttendanceCodeResponse.class)) + ), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "404", description = "활성화된 출석 코드 없음") + }) + @GetMapping("/active-code") + public ApiResponse getActiveCode() { + Optional codeOpt = attendanceService.getActiveAttendanceCode(); + + if (codeOpt.isEmpty()) { + return ApiResponse.error("현재 활성화된 출석코드가 없습니다"); + } + + return ApiResponse.success(AttendanceCodeResponse.from(codeOpt.get())); + } + + // 출석체크 종료 (코드 직접 전달) + @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("/expire") + public ApiResponse expireAttendance( + @Parameter(description = "만료할 출석 코드", required = true) + @RequestParam String code) { + String result = attendanceService.exprireAttendanceCode(code); + + if (result.equals("출석 코드가 성공적으로 만료되었습니다")) { + return ApiResponse.success(result, null); + } else { + 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 = "404", description = "활성화된 출석 코드가 없음") + }) + @PutMapping("/expire-latest") + public ApiResponse expireLatestAttendance() { + String result = attendanceService.expireLatestAttendanceCode(); + + if (result.equals("출석 코드가 성공적으로 만료되었습니다")) { + return ApiResponse.success(result, null); + } else { + return ApiResponse.error(result); + } + } +} \ No newline at end of file diff --git a/backend/pirocheck/src/main/java/backend/pirocheck/Attendance/dto/response/AttendanceMarkResponse.java b/backend/pirocheck/src/main/java/backend/pirocheck/Attendance/dto/response/AttendanceMarkResponse.java index fc38565..8f3551c 100644 --- a/backend/pirocheck/src/main/java/backend/pirocheck/Attendance/dto/response/AttendanceMarkResponse.java +++ b/backend/pirocheck/src/main/java/backend/pirocheck/Attendance/dto/response/AttendanceMarkResponse.java @@ -1,5 +1,6 @@ package backend.pirocheck.Attendance.dto.response; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; @@ -9,55 +10,59 @@ @Builder @NoArgsConstructor @AllArgsConstructor +@Schema(description = "출석 체크 응답") public class AttendanceMarkResponse { - private String statusCode; // SUCCESS, INVALID_CODE, CODE_EXPIRED, ALREADY_MARKED, NO_ACTIVE_SESSION, ERROR + @Schema(description = "응답 메시지", example = "출석이 성공적으로 처리되었습니다") private String message; - - // 성공 응답 + + @Schema(description = "상태 코드 (SUCCESS, ALREADY_MARKED, INVALID_CODE, NO_ACTIVE_SESSION, CODE_EXPIRED, ERROR)", example = "SUCCESS") + private String statusCode; + + // 출석 성공 public static AttendanceMarkResponse success() { return AttendanceMarkResponse.builder() .statusCode("SUCCESS") - .message("출석이 완료되었습니다") + .message("출석이 성공적으로 처리되었습니다") .build(); } - - // 유효하지 않은 코드 - public static AttendanceMarkResponse invalidCode() { + + // 이미 출석 완료 + public static AttendanceMarkResponse alreadyMarked() { return AttendanceMarkResponse.builder() - .statusCode("INVALID_CODE") - .message("유효하지 않은 출석 코드입니다") + .statusCode("ALREADY_MARKED") + .message("이미 출석처리가 완료되었습니다") .build(); } - - // 만료된 코드 - public static AttendanceMarkResponse codeExpired() { + + // 출석체크 진행중 아님 + public static AttendanceMarkResponse noActiveSession() { return AttendanceMarkResponse.builder() - .statusCode("CODE_EXPIRED") - .message("만료된 출석 코드입니다") + .statusCode("NO_ACTIVE_SESSION") + .message("출석 코드가 존재하지 않습니다. 현재 출석 체크가 진행중이 아닙니다") .build(); } - - // 이미 출석 완료 - public static AttendanceMarkResponse alreadyMarked() { + + // 잘못된 출석 코드 입력 + public static AttendanceMarkResponse invalidCode() { return AttendanceMarkResponse.builder() - .statusCode("ALREADY_MARKED") - .message("이미 출석 처리되었습니다") + .statusCode("INVALID_CODE") + .message("잘못된 출석 코드입니다. 다시 확인해주세요") .build(); } - - // 활성화된 출석 세션 없음 - public static AttendanceMarkResponse noActiveSession() { + + // 출석 코드 만료 + public static AttendanceMarkResponse codeExpired() { return AttendanceMarkResponse.builder() - .statusCode("NO_ACTIVE_SESSION") - .message("현재 활성화된 출석 세션이 없습니다") + .statusCode("CODE_EXPIRED") + .message("출석 코드가 만료되었습니다") .build(); } - - // 일반 에러 + + // 기타 오류 public static AttendanceMarkResponse error(String message) { return AttendanceMarkResponse.builder() .statusCode("ERROR") .message(message) .build(); } -} \ No newline at end of file +} diff --git a/backend/pirocheck/src/main/java/backend/pirocheck/config/SwaggerConfig.java b/backend/pirocheck/src/main/java/backend/pirocheck/config/SwaggerConfig.java new file mode 100644 index 0000000..aa87446 --- /dev/null +++ b/backend/pirocheck/src/main/java/backend/pirocheck/config/SwaggerConfig.java @@ -0,0 +1,24 @@ +package backend.pirocheck.config; + +import io.swagger.v3.oas.models.Components; +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.info.Info; +import io.swagger.v3.oas.models.info.License; +import io.swagger.v3.oas.models.servers.Server; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class SwaggerConfig { + + @Bean + public OpenAPI openAPI() { + return new OpenAPI() + .info(new Info() + .title("PiroCheck API") + .description("피로그래밍 출석체크 시스템 API 문서") + .version("v1.0.0") + .license(new License().name("MIT").url("https://opensource.org/licenses/MIT"))) + .addServersItem(new Server().url("/").description("Default Server URL")); + } +} \ No newline at end of file