281 lines
11 KiB
Java
281 lines
11 KiB
Java
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<List<LabelAllocateDto.UserList>> 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<WorkerListResponse> 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<Void> labelAllocate(@RequestBody LabelAllocateDto dto) {
|
|
|
|
List<TargetUser> targets =
|
|
List.of(
|
|
new TargetUser("1234567", 1000),
|
|
new TargetUser("2345678", 400),
|
|
new TargetUser("3456789", 440));
|
|
List<TargetInspector> inspectors =
|
|
List.of(
|
|
new TargetInspector("9876543", 1000),
|
|
new TargetInspector("8765432", 340),
|
|
new TargetInspector("98765432", 500));
|
|
labelAllocateService.allocateAsc(targets, inspectors);
|
|
|
|
return ApiResponseDto.ok(null);
|
|
}
|
|
}
|