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
@@ -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<AttendanceCodeResponse> 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<AttendanceCodeResponse> getActiveCode() {
Optional<AttendanceCode> 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<Void> 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<Void> expireLatestAttendance() {
String result = attendanceService.expireLatestAttendanceCode();

if (result.equals("출석 코드가 성공적으로 만료되었습니다")) {
return ApiResponse.success(result, null);
} else {
return ApiResponse.error(result);
}
}
}
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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();
}
}
}
Original file line number Diff line number Diff line change
@@ -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"));
}
}