package com.kamco.cd.kamcoback.label; import com.kamco.cd.kamcoback.code.dto.CommonCodeDto; import com.kamco.cd.kamcoback.config.api.ApiResponseDto; import com.kamco.cd.kamcoback.label.dto.LabelAllocateDto; import com.kamco.cd.kamcoback.label.dto.LabelAllocateDto.TargetInspector; import com.kamco.cd.kamcoback.label.dto.LabelAllocateDto.TargetUser; import com.kamco.cd.kamcoback.label.dto.WorkerStatsDto.WorkerListResponse; import com.kamco.cd.kamcoback.label.service.LabelAllocateService; 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.ExampleObject; import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.swagger.v3.oas.annotations.tags.Tag; import java.util.List; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @Slf4j @Tag(name = "라벨링 작업 관리", description = "라벨링 작업 배정 및 통계 조회 API") @RequestMapping({"/api/label"}) @RequiredArgsConstructor @RestController public class LabelAllocateApiController { private final LabelAllocateService labelAllocateService; @Operation(summary = "배정 가능한 사용자 목록 조회", description = "라벨링 작업 배정을 위한 활성 상태의 사용자 목록을 조회합니다.") @ApiResponses( value = { @ApiResponse( responseCode = "200", description = "조회 성공", content = @Content( mediaType = "application/json", schema = @Schema(implementation = CommonCodeDto.Basic.class), examples = @ExampleObject( name = "사용자 목록 응답", value = """ { "data": [ { "userRole": "LABELER", "employeeNo": "1234567", "name": "김라벨" }, { "userRole": "LABELER", "employeeNo": "2345678", "name": "이작업" } ] } """))), @ApiResponse(responseCode = "404", description = "코드를 찾을 수 없음", content = @Content), @ApiResponse(responseCode = "500", description = "서버 오류", content = @Content) }) @GetMapping("/avail-user") public ApiResponseDto> availUserList( @Parameter(description = "사용자 역할 (LABELER: 라벨러, INSPECTOR: 검수자)", example = "LABELER") @RequestParam @Schema() String role) { return ApiResponseDto.ok(labelAllocateService.availUserList(role)); } @Operation( summary = "작업자 목록 및 3일치 통계 조회", description = """ 학습데이터 제작 현황 조회 API입니다. """) @ApiResponses( value = { @ApiResponse( responseCode = "200", description = "조회 성공", content = @Content( mediaType = "application/json", schema = @Schema(implementation = WorkerListResponse.class), examples = { @ExampleObject( name = "라벨러 조회 예시", description = "라벨러 작업자들의 통계 정보", value = """ { "data": { "progressInfo": { "labelingProgressRate": 79.34, "workStatus": "진행중", "completedCount": 6554, "totalAssignedCount": 8258, "labelerCount": 5, "remainingLabelCount": 1704, "inspectorCount": 3, "remainingInspectCount": 890 }, "workers": [ { "workerId": "1234567", "workerName": "김라벨", "workerType": "LABELER", "totalAssigned": 1500, "completed": 1100, "skipped": 50, "remaining": 350, "history": { "day1Ago": 281, "day2Ago": 302, "day3Ago": 294, "average": 292 }, "isStagnated": false }, { "workerId": "2345678", "workerName": "이작업", "workerType": "LABELER", "totalAssigned": 2000, "completed": 1850, "skipped": 100, "remaining": 50, "history": { "day1Ago": 5, "day2Ago": 3, "day3Ago": 8, "average": 5 }, "isStagnated": true } ] } } """), @ExampleObject( name = "검수자 조회 예시", description = "검수자 작업자들의 통계 정보", value = """ { "data": { "progressInfo": { "labelingProgressRate": 79.34, "workStatus": "진행중", "completedCount": 6554, "totalAssignedCount": 8258, "labelerCount": 5, "remainingLabelCount": 1704, "inspectorCount": 3, "remainingInspectCount": 890 }, "workers": [ { "workerId": "9876543", "workerName": "박검수", "workerType": "INSPECTOR", "totalAssigned": 1200, "completed": 980, "skipped": 20, "remaining": 200, "history": { "day1Ago": 150, "day2Ago": 145, "day3Ago": 155, "average": 150 }, "isStagnated": false } ] } } """) })), @ApiResponse( responseCode = "404", description = "데이터를 찾을 수 없음", content = @Content( examples = @ExampleObject( value = """ { "error": { "code": "NOT_FOUND", "message": "해당 분석 ID의 데이터를 찾을 수 없습니다." } } """))), @ApiResponse( responseCode = "500", description = "서버 오류", content = @Content( examples = @ExampleObject( value = """ { "error": { "code": "INTERNAL_SERVER_ERROR", "message": "서버에 문제가 발생 하였습니다." } } """))) }) @GetMapping("/admin/workers") public ApiResponseDto getWorkerStatistics( @Parameter(description = "분석 ID (필수)", required = true, example = "3") @RequestParam Long analUid, @Parameter( description = "작업자 유형 (선택) - 미입력 시 LABELER로 조회", example = "LABELER", schema = @Schema( allowableValues = {"LABELER", "INSPECTOR"}, defaultValue = "LABELER")) @RequestParam(required = false) String type, @Parameter(description = "작업자 이름 검색 (부분 일치)", example = "김라벨") @RequestParam(required = false) String searchName, @Parameter(description = "작업자 사번 검색 (부분 일치)", example = "1234567") @RequestParam(required = false) String searchEmployeeNo, @Parameter( description = "정렬 조건 (선택) - 미입력 시 이름 오름차순", example = "REMAINING_DESC", schema = @Schema( allowableValues = { "REMAINING_DESC", "REMAINING_ASC", "NAME_ASC", "NAME_DESC" }, defaultValue = "NAME_ASC")) @RequestParam(required = false) String sort) { // type이 null이면 전체 조회 (일단 LABELER로 기본 설정) String workerType = (type == null || type.isEmpty()) ? "LABELER" : type; return ApiResponseDto.ok( labelAllocateService.getWorkerStatistics( analUid, workerType, searchName, searchEmployeeNo, sort)); } // 라벨링 수량 할당하는 로직 테스트 @PostMapping("/allocate") public ApiResponseDto labelAllocate(@RequestBody LabelAllocateDto dto) { List targets = List.of( new TargetUser("1234567", 1000), new TargetUser("2345678", 400), new TargetUser("3456789", 440)); List inspectors = List.of( new TargetInspector("9876543", 1000), new TargetInspector("8765432", 340), new TargetInspector("98765432", 500)); labelAllocateService.allocateAsc(targets, inspectors); return ApiResponseDto.ok(null); } }