diff --git a/src/main/java/com/kamco/cd/kamcoback/label/LabelAllocateApiController.java b/src/main/java/com/kamco/cd/kamcoback/label/LabelAllocateApiController.java index 8f463bdd..51520875 100644 --- a/src/main/java/com/kamco/cd/kamcoback/label/LabelAllocateApiController.java +++ b/src/main/java/com/kamco/cd/kamcoback/label/LabelAllocateApiController.java @@ -36,85 +36,86 @@ public class LabelAllocateApiController { @Operation(summary = "배정 가능한 사용자 목록 조회", description = "라벨링 작업 배정을 위한 활성 상태의 사용자 목록을 조회합니다.") @ApiResponses( - value = { - @ApiResponse(responseCode = "200", description = "조회 성공"), - @ApiResponse(responseCode = "404", description = "코드를 찾을 수 없음"), - @ApiResponse(responseCode = "500", description = "서버 오류") - }) + value = { + @ApiResponse(responseCode = "200", description = "조회 성공"), + @ApiResponse(responseCode = "404", description = "코드를 찾을 수 없음"), + @ApiResponse(responseCode = "500", description = "서버 오류") + }) @GetMapping("/avail-user") public ApiResponseDto> availUserList( - @Parameter( - description = "사용자 역할", - example = "LABELER", - schema = @Schema(allowableValues = {"LABELER", "REVIEWER"})) - @RequestParam - String role) { + @Parameter( + description = "사용자 역할", + example = "LABELER", + schema = @Schema(allowableValues = {"LABELER", "REVIEWER"})) + @RequestParam + String role) { return ApiResponseDto.ok(labelAllocateService.availUserList(role)); } @Operation(summary = "작업현황관리(작업자 목록 및 3일치 통계 조회)", description = "학습데이터 제작 현황 조회 API입니다.") @ApiResponses( - value = { - @ApiResponse(responseCode = "200", description = "조회 성공"), - @ApiResponse(responseCode = "404", description = "데이터를 찾을 수 없음"), - @ApiResponse(responseCode = "500", description = "서버 오류") - }) + value = { + @ApiResponse(responseCode = "200", description = "조회 성공"), + @ApiResponse(responseCode = "404", description = "데이터를 찾을 수 없음"), + @ApiResponse(responseCode = "500", description = "서버 오류") + }) @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", "REVIEWER"}, - 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) { + @Parameter(description = "분석 ID (필수)", required = true, example = "3") @RequestParam + Long analUid, + @Parameter( + description = "작업자 유형 (선택) - 미입력 시 LABELER로 조회", + example = "LABELER", + schema = + @Schema( + allowableValues = {"LABELER", "REVIEWER"}, + 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()) ? RoleType.LABELER.name() : type; return ApiResponseDto.ok( - labelAllocateService.getWorkerStatistics( - analUid, workerType, searchName, searchEmployeeNo, sort)); + labelAllocateService.getWorkerStatistics( + analUid, workerType, searchName, searchEmployeeNo, sort)); } @Operation(summary = "작업 배정", description = "작업 배정") @ApiResponses( - value = { - @ApiResponse( - responseCode = "201", - description = "등록 성공", - content = - @Content( - mediaType = "application/json", - schema = @Schema(implementation = Long.class), - examples = {@ExampleObject( - name = "라벨러 할당 예시", - description = "라벨러 할당 예시", - value = - """ + value = { + @ApiResponse( + responseCode = "201", + description = "등록 성공", + content = + @Content( + mediaType = "application/json", + schema = @Schema(implementation = Long.class), + examples = { + @ExampleObject( + name = "라벨러 할당 예시", + description = "라벨러 할당 예시", + value = + """ { "autoType": "AUTO", "stage": 4, @@ -148,59 +149,62 @@ public class LabelAllocateApiController { ] } - """)} - )), - @ApiResponse(responseCode = "400", description = "잘못된 요청 데이터", content = @Content), - @ApiResponse(responseCode = "404", description = "코드를 찾을 수 없음", content = @Content), - @ApiResponse(responseCode = "500", description = "서버 오류", content = @Content) - }) + """) + })), + @ApiResponse(responseCode = "400", description = "잘못된 요청 데이터", content = @Content), + @ApiResponse(responseCode = "404", description = "코드를 찾을 수 없음", content = @Content), + @ApiResponse(responseCode = "500", description = "서버 오류", content = @Content) + }) @PostMapping("/allocate") public ApiResponseDto labelAllocate(@RequestBody LabelAllocateDto.AllocateDto dto) { labelAllocateService.allocateAsc( - dto.getAutoType(), - dto.getStage(), - dto.getLabelers(), - dto.getInspectors(), - dto.getAnalUid()); + dto.getAutoType(), + dto.getStage(), + dto.getLabelers(), + dto.getInspectors(), + dto.getAnalUid()); return ApiResponseDto.ok(null); } @Operation(summary = "추론 상세 조회", description = "분석 ID에 해당하는 추론 상세 정보를 조회합니다.") @ApiResponses( - value = { - @ApiResponse(responseCode = "200", description = "조회 성공"), - @ApiResponse(responseCode = "404", description = "데이터를 찾을 수 없음"), - @ApiResponse(responseCode = "500", description = "서버 오류") - }) + value = { + @ApiResponse(responseCode = "200", description = "조회 성공"), + @ApiResponse(responseCode = "404", description = "데이터를 찾을 수 없음"), + @ApiResponse(responseCode = "500", description = "서버 오류") + }) @GetMapping("/stage-detail") public ApiResponseDto findInferenceDetail( - @Parameter(description = "분석 ID", required = true, example = "3") @RequestParam - Long analUid) { + @Parameter(description = "분석 ID", required = true, example = "3") @RequestParam + Long analUid) { return ApiResponseDto.ok(labelAllocateService.findInferenceDetail(analUid)); } @Operation(summary = "작업이관 > 라벨러 상세 정보", description = "작업이관 > 라벨러 상세 정보") @GetMapping("/labeler-detail") - public ApiResponseDto findLabelerDetail(@RequestParam(defaultValue = "01022223333") String userId, @RequestParam(defaultValue = "3") Long analUid) { + public ApiResponseDto findLabelerDetail( + @RequestParam(defaultValue = "01022223333") String userId, + @RequestParam(defaultValue = "3") Long analUid) { return ApiResponseDto.ok(labelAllocateService.findLabelerDetail(userId, analUid)); } @Operation(summary = "작업 이관", description = "작업 이관") @ApiResponses( - value = { - @ApiResponse( - responseCode = "201", - description = "등록 성공", - content = - @Content( - mediaType = "application/json", - schema = @Schema(implementation = Long.class), - examples = {@ExampleObject( - name = "라벨러 할당 예시", - description = "라벨러 할당 예시", - value = - """ + value = { + @ApiResponse( + responseCode = "201", + description = "등록 성공", + content = + @Content( + mediaType = "application/json", + schema = @Schema(implementation = Long.class), + examples = { + @ExampleObject( + name = "라벨러 할당 예시", + description = "라벨러 할당 예시", + value = + """ { "autoType": "AUTO", "stage": 4, @@ -215,26 +219,25 @@ public class LabelAllocateApiController { } ] } - """)} - )), - @ApiResponse(responseCode = "400", description = "잘못된 요청 데이터", content = @Content), - @ApiResponse(responseCode = "404", description = "코드를 찾을 수 없음", content = @Content), - @ApiResponse(responseCode = "500", description = "서버 오류", content = @Content) - }) + """) + })), + @ApiResponse(responseCode = "400", description = "잘못된 요청 데이터", content = @Content), + @ApiResponse(responseCode = "404", description = "코드를 찾을 수 없음", content = @Content), + @ApiResponse(responseCode = "500", description = "서버 오류", content = @Content) + }) @PostMapping("/allocate-move") public ApiResponseDto labelAllocateMove( - @io.swagger.v3.oas.annotations.parameters.RequestBody( - description = "라벨링 이관", - required = true, - content = - @Content( - mediaType = "application/json", - schema = @Schema(implementation = LabelAllocateDto.AllocateMoveDto.class))) - @RequestBody - LabelAllocateDto.AllocateMoveDto dto) { + @io.swagger.v3.oas.annotations.parameters.RequestBody( + description = "라벨링 이관", + required = true, + content = + @Content( + mediaType = "application/json", + schema = @Schema(implementation = LabelAllocateDto.AllocateMoveDto.class))) + @RequestBody + LabelAllocateDto.AllocateMoveDto dto) { - labelAllocateService.allocateMove( - dto.getAutoType(), dto.getStage(), dto.getLabelers()); + labelAllocateService.allocateMove(dto.getAutoType(), dto.getStage(), dto.getLabelers()); return ApiResponseDto.ok(null); } diff --git a/src/main/java/com/kamco/cd/kamcoback/label/dto/LabelAllocateDto.java b/src/main/java/com/kamco/cd/kamcoback/label/dto/LabelAllocateDto.java index 3706c9c3..3d126943 100644 --- a/src/main/java/com/kamco/cd/kamcoback/label/dto/LabelAllocateDto.java +++ b/src/main/java/com/kamco/cd/kamcoback/label/dto/LabelAllocateDto.java @@ -177,7 +177,7 @@ public class LabelAllocateDto { private String roleType; private String name; - private String userId; //사번 + private String userId; // 사번 private Long count; private Long completeCnt; private Long skipCnt; @@ -198,5 +198,4 @@ public class LabelAllocateDto { @Schema(description = "라벨러 할당 목록") private List labelers; } - } diff --git a/src/main/java/com/kamco/cd/kamcoback/label/service/LabelAllocateService.java b/src/main/java/com/kamco/cd/kamcoback/label/service/LabelAllocateService.java index ef6bd4f5..79ad856b 100644 --- a/src/main/java/com/kamco/cd/kamcoback/label/service/LabelAllocateService.java +++ b/src/main/java/com/kamco/cd/kamcoback/label/service/LabelAllocateService.java @@ -34,19 +34,19 @@ public class LabelAllocateService { /** * 도엽 기준 asc sorting 해서 할당 수만큼 배정하는 로직 * - * @param autoType 자동/수동 배정 타입 - * @param stage 회차 - * @param targetUsers 라벨러 목록 + * @param autoType 자동/수동 배정 타입 + * @param stage 회차 + * @param targetUsers 라벨러 목록 * @param targetInspectors 검수자 목록 - * @param analUid 분석 ID + * @param analUid 분석 ID */ @Transactional public void allocateAsc( - String autoType, - Integer stage, - List targetUsers, - List targetInspectors, - Long analUid) { + String autoType, + Integer stage, + List targetUsers, + List targetInspectors, + Long analUid) { Long lastId = null; // geom 잔여건수 조회 @@ -83,7 +83,7 @@ public class LabelAllocateService { } List assignmentUids = - list.subList(from, to).stream().map(LabelAllocateDto.Basic::getAssignmentUid).toList(); + list.subList(from, to).stream().map(LabelAllocateDto.Basic::getAssignmentUid).toList(); labelAllocateCoreService.assignInspectorBulk(assignmentUids, inspector.getInspectorUid()); @@ -98,50 +98,50 @@ public class LabelAllocateService { /** * 작업자 목록 및 3일치 통계 조회 * - * @param analUid 분석 ID - * @param workerType 작업자 유형 (LABELER/INSPECTOR) - * @param searchName 이름 검색 + * @param analUid 분석 ID + * @param workerType 작업자 유형 (LABELER/INSPECTOR) + * @param searchName 이름 검색 * @param searchEmployeeNo 사번 검색 - * @param sortType 정렬 조건 + * @param sortType 정렬 조건 * @return 작업자 목록 및 통계 */ public WorkerListResponse getWorkerStatistics( - Long analUid, - String workerType, - String searchName, - String searchEmployeeNo, - String sortType) { + Long analUid, + String workerType, + String searchName, + String searchEmployeeNo, + String sortType) { // 작업 진행 현황 조회 var progressInfo = labelAllocateCoreService.findWorkProgressInfo(analUid); // 작업자 통계 조회 List workers = - labelAllocateCoreService.findWorkerStatistics( - analUid, workerType, searchName, searchEmployeeNo, sortType); + labelAllocateCoreService.findWorkerStatistics( + analUid, workerType, searchName, searchEmployeeNo, sortType); // 각 작업자별 3일치 처리량 조회 LocalDate today = LocalDate.now(); for (WorkerStatistics worker : workers) { Long day1Count = - labelAllocateCoreService.findDailyProcessedCount( - worker.getWorkerId(), workerType, today.minusDays(1), analUid); + labelAllocateCoreService.findDailyProcessedCount( + worker.getWorkerId(), workerType, today.minusDays(1), analUid); Long day2Count = - labelAllocateCoreService.findDailyProcessedCount( - worker.getWorkerId(), workerType, today.minusDays(2), analUid); + labelAllocateCoreService.findDailyProcessedCount( + worker.getWorkerId(), workerType, today.minusDays(2), analUid); Long day3Count = - labelAllocateCoreService.findDailyProcessedCount( - worker.getWorkerId(), workerType, today.minusDays(3), analUid); + labelAllocateCoreService.findDailyProcessedCount( + worker.getWorkerId(), workerType, today.minusDays(3), analUid); long average = (day1Count + day2Count + day3Count) / 3; DailyHistory history = - DailyHistory.builder() - .day1Ago(day1Count) - .day2Ago(day2Count) - .day3Ago(day3Count) - .average(average) - .build(); + DailyHistory.builder() + .day1Ago(day1Count) + .day2Ago(day2Count) + .day3Ago(day3Count) + .average(average) + .build(); worker.setHistory(history); @@ -176,7 +176,6 @@ public class LabelAllocateService { labelAllocateCoreService.assignOwnerMove(sub, target.getUserId()); index = end; } - } public LabelerDetail findLabelerDetail(String userId, Long analUid) { diff --git a/src/main/java/com/kamco/cd/kamcoback/postgres/core/LabelAllocateCoreService.java b/src/main/java/com/kamco/cd/kamcoback/postgres/core/LabelAllocateCoreService.java index a205013a..d9244fb0 100644 --- a/src/main/java/com/kamco/cd/kamcoback/postgres/core/LabelAllocateCoreService.java +++ b/src/main/java/com/kamco/cd/kamcoback/postgres/core/LabelAllocateCoreService.java @@ -30,8 +30,8 @@ public class LabelAllocateCoreService { public List findAssignedLabelerList(Long analUid) { return labelAllocateRepository.findAssignedLabelerList(analUid).stream() - .map(LabelingAssignmentEntity::toDto) - .toList(); + .map(LabelingAssignmentEntity::toDto) + .toList(); } public Long findLabelUnAssignedCnt(Long analUid, Integer stage) { @@ -47,13 +47,13 @@ public class LabelAllocateCoreService { } public List findWorkerStatistics( - Long analUid, - String workerType, - String searchName, - String searchEmployeeNo, - String sortType) { + Long analUid, + String workerType, + String searchName, + String searchEmployeeNo, + String sortType) { return labelAllocateRepository.findWorkerStatistics( - analUid, workerType, searchName, searchEmployeeNo, sortType); + analUid, workerType, searchName, searchEmployeeNo, sortType); } public WorkProgressInfo findWorkProgressInfo(Long analUid) { @@ -61,7 +61,7 @@ public class LabelAllocateCoreService { } public Long findDailyProcessedCount( - String workerId, String workerType, LocalDate date, Long analUid) { + String workerId, String workerType, LocalDate date, Long analUid) { return labelAllocateRepository.findDailyProcessedCount(workerId, workerType, date, analUid); } diff --git a/src/main/java/com/kamco/cd/kamcoback/postgres/repository/label/LabelAllocateRepositoryCustom.java b/src/main/java/com/kamco/cd/kamcoback/postgres/repository/label/LabelAllocateRepositoryCustom.java index 4ad518eb..a1309169 100644 --- a/src/main/java/com/kamco/cd/kamcoback/postgres/repository/label/LabelAllocateRepositoryCustom.java +++ b/src/main/java/com/kamco/cd/kamcoback/postgres/repository/label/LabelAllocateRepositoryCustom.java @@ -26,7 +26,7 @@ public interface LabelAllocateRepositoryCustom { // 작업자 통계 조회 List findWorkerStatistics( - Long analUid, String workerType, String searchName, String searchEmployeeNo, String sortType); + Long analUid, String workerType, String searchName, String searchEmployeeNo, String sortType); // 작업 진행 현황 조회 WorkProgressInfo findWorkProgressInfo(Long analUid); diff --git a/src/main/java/com/kamco/cd/kamcoback/postgres/repository/label/LabelAllocateRepositoryImpl.java b/src/main/java/com/kamco/cd/kamcoback/postgres/repository/label/LabelAllocateRepositoryImpl.java index b33b0509..3c32e99b 100644 --- a/src/main/java/com/kamco/cd/kamcoback/postgres/repository/label/LabelAllocateRepositoryImpl.java +++ b/src/main/java/com/kamco/cd/kamcoback/postgres/repository/label/LabelAllocateRepositoryImpl.java @@ -44,33 +44,32 @@ public class LabelAllocateRepositoryImpl implements LabelAllocateRepositoryCusto private final JPAQueryFactory queryFactory; - @PersistenceContext - private EntityManager em; + @PersistenceContext private EntityManager em; @Override public List fetchNextIds(Long lastId, Long batchSize, Long analUid) { // analUid로 분석 정보 조회 MapSheetAnalEntity analEntity = - queryFactory - .selectFrom(mapSheetAnalEntity) - .where(mapSheetAnalEntity.id.eq(analUid)) - .fetchOne(); + queryFactory + .selectFrom(mapSheetAnalEntity) + .where(mapSheetAnalEntity.id.eq(analUid)) + .fetchOne(); if (Objects.isNull(analEntity)) { throw new EntityNotFoundException("MapSheetAnalEntity not found for analUid: " + analUid); } return queryFactory - .select(mapSheetAnalDataInferenceGeomEntity.geoUid) - .from(mapSheetAnalDataInferenceGeomEntity) - .where( - lastId == null ? null : mapSheetAnalDataInferenceGeomEntity.geoUid.gt(lastId), - mapSheetAnalDataInferenceGeomEntity.compareYyyy.eq(analEntity.getCompareYyyy()), - mapSheetAnalDataInferenceGeomEntity.targetYyyy.eq(analEntity.getTargetYyyy()), - mapSheetAnalDataInferenceGeomEntity.labelState.isNull()) - .orderBy(mapSheetAnalDataInferenceGeomEntity.mapSheetNum.asc()) - .limit(batchSize) - .fetch(); + .select(mapSheetAnalDataInferenceGeomEntity.geoUid) + .from(mapSheetAnalDataInferenceGeomEntity) + .where( + lastId == null ? null : mapSheetAnalDataInferenceGeomEntity.geoUid.gt(lastId), + mapSheetAnalDataInferenceGeomEntity.compareYyyy.eq(analEntity.getCompareYyyy()), + mapSheetAnalDataInferenceGeomEntity.targetYyyy.eq(analEntity.getTargetYyyy()), + mapSheetAnalDataInferenceGeomEntity.labelState.isNull()) + .orderBy(mapSheetAnalDataInferenceGeomEntity.mapSheetNum.asc()) + .limit(batchSize) + .fetch(); } @Override @@ -78,17 +77,17 @@ public class LabelAllocateRepositoryImpl implements LabelAllocateRepositoryCusto // data_geom 테이블에 label state 를 ASSIGNED 로 update queryFactory - .update(mapSheetAnalDataInferenceGeomEntity) - .set(mapSheetAnalDataInferenceGeomEntity.labelState, LabelState.ASSIGNED.getId()) - .set(mapSheetAnalDataInferenceGeomEntity.labelStateDttm, ZonedDateTime.now()) - .set(mapSheetAnalDataInferenceGeomEntity.testState, InspectState.UNCONFIRM.getId()) - .set(mapSheetAnalDataInferenceGeomEntity.testStateDttm, ZonedDateTime.now()) - .where(mapSheetAnalDataInferenceGeomEntity.geoUid.in(ids)) - .execute(); + .update(mapSheetAnalDataInferenceGeomEntity) + .set(mapSheetAnalDataInferenceGeomEntity.labelState, LabelState.ASSIGNED.getId()) + .set(mapSheetAnalDataInferenceGeomEntity.labelStateDttm, ZonedDateTime.now()) + .set(mapSheetAnalDataInferenceGeomEntity.testState, InspectState.UNCONFIRM.getId()) + .set(mapSheetAnalDataInferenceGeomEntity.testStateDttm, ZonedDateTime.now()) + .where(mapSheetAnalDataInferenceGeomEntity.geoUid.in(ids)) + .execute(); // 라벨러 할당 테이블에 insert String sql = - """ + """ insert into tb_labeling_assignment (assignment_uid, inference_geom_uid, worker_uid, work_state, assign_group_id, anal_uid) @@ -97,13 +96,13 @@ public class LabelAllocateRepositoryImpl implements LabelAllocateRepositoryCusto for (Long geoUid : ids) { em.createNativeQuery(sql) - .setParameter(1, UUID.randomUUID()) - .setParameter(2, geoUid) - .setParameter(3, userId) - .setParameter(4, LabelState.ASSIGNED.getId()) - .setParameter(5, "") - .setParameter(6, analUid) - .executeUpdate(); + .setParameter(1, UUID.randomUUID()) + .setParameter(2, geoUid) + .setParameter(3, userId) + .setParameter(4, LabelState.ASSIGNED.getId()) + .setParameter(5, "") + .setParameter(6, analUid) + .executeUpdate(); } em.flush(); @@ -113,82 +112,82 @@ public class LabelAllocateRepositoryImpl implements LabelAllocateRepositoryCusto @Override public List findAssignedLabelerList(Long analUid) { return queryFactory - .selectFrom(labelingAssignmentEntity) - .where( - labelingAssignmentEntity.analUid.eq(analUid), - labelingAssignmentEntity.workState.eq(LabelState.ASSIGNED.getId()), - labelingAssignmentEntity.inspectorUid.isNull()) - .orderBy(labelingAssignmentEntity.workerUid.asc()) - .fetch(); + .selectFrom(labelingAssignmentEntity) + .where( + labelingAssignmentEntity.analUid.eq(analUid), + labelingAssignmentEntity.workState.eq(LabelState.ASSIGNED.getId()), + labelingAssignmentEntity.inspectorUid.isNull()) + .orderBy(labelingAssignmentEntity.workerUid.asc()) + .fetch(); } @Override public Long findLabelUnAssignedCnt(Long analUid, Integer stage) { MapSheetAnalEntity entity = - queryFactory - .selectFrom(mapSheetAnalEntity) - .where(mapSheetAnalEntity.id.eq(analUid)) - .fetchOne(); + queryFactory + .selectFrom(mapSheetAnalEntity) + .where(mapSheetAnalEntity.id.eq(analUid)) + .fetchOne(); if (Objects.isNull(entity)) { throw new EntityNotFoundException("MapSheetAnalEntity not found for analUid: " + analUid); } return queryFactory - .select(mapSheetAnalDataInferenceGeomEntity.geoUid.count()) - .from(mapSheetAnalDataInferenceGeomEntity) - .where( - mapSheetAnalDataInferenceGeomEntity.compareYyyy.eq(entity.getCompareYyyy()), - mapSheetAnalDataInferenceGeomEntity.targetYyyy.eq(entity.getTargetYyyy()), - mapSheetAnalDataInferenceGeomEntity.stage.eq(stage), - mapSheetAnalDataInferenceGeomEntity.labelState.isNull()) - .fetchOne(); + .select(mapSheetAnalDataInferenceGeomEntity.geoUid.count()) + .from(mapSheetAnalDataInferenceGeomEntity) + .where( + mapSheetAnalDataInferenceGeomEntity.compareYyyy.eq(entity.getCompareYyyy()), + mapSheetAnalDataInferenceGeomEntity.targetYyyy.eq(entity.getTargetYyyy()), + mapSheetAnalDataInferenceGeomEntity.stage.eq(stage), + mapSheetAnalDataInferenceGeomEntity.labelState.isNull()) + .fetchOne(); } @Override public void assignInspector(UUID assignmentUid, String inspectorUid) { queryFactory - .update(labelingAssignmentEntity) - .set(labelingAssignmentEntity.inspectorUid, inspectorUid) - .where(labelingAssignmentEntity.assignmentUid.eq(assignmentUid)) - .execute(); + .update(labelingAssignmentEntity) + .set(labelingAssignmentEntity.inspectorUid, inspectorUid) + .where(labelingAssignmentEntity.assignmentUid.eq(assignmentUid)) + .execute(); } @Override public List availUserList(String role) { return queryFactory - .select( - Projections.constructor( - LabelAllocateDto.UserList.class, - memberEntity.userRole, - memberEntity.employeeNo, - memberEntity.name)) - .from(memberEntity) - .where( - memberEntity.userRole.eq(role), - memberEntity.status.eq(com.kamco.cd.kamcoback.common.enums.StatusType.ACTIVE.getId())) - .orderBy(memberEntity.name.asc()) - .fetch(); + .select( + Projections.constructor( + LabelAllocateDto.UserList.class, + memberEntity.userRole, + memberEntity.employeeNo, + memberEntity.name)) + .from(memberEntity) + .where( + memberEntity.userRole.eq(role), + memberEntity.status.eq(com.kamco.cd.kamcoback.common.enums.StatusType.ACTIVE.getId())) + .orderBy(memberEntity.name.asc()) + .fetch(); } @Override public List findWorkerStatistics( - Long analUid, - String workerType, - String searchName, - String searchEmployeeNo, - String sortType) { + Long analUid, + String workerType, + String searchName, + String searchEmployeeNo, + String sortType) { // 작업자 유형에 따른 필드 선택 StringExpression workerIdField = - "REVIEWER".equals(workerType) - ? labelingAssignmentEntity.inspectorUid - : labelingAssignmentEntity.workerUid; + "REVIEWER".equals(workerType) + ? labelingAssignmentEntity.inspectorUid + : labelingAssignmentEntity.workerUid; BooleanExpression workerCondition = - "REVIEWER".equals(workerType) - ? labelingAssignmentEntity.inspectorUid.isNotNull() - : labelingAssignmentEntity.workerUid.isNotNull(); + "REVIEWER".equals(workerType) + ? labelingAssignmentEntity.inspectorUid.isNotNull() + : labelingAssignmentEntity.workerUid.isNotNull(); // 검색 조건 BooleanExpression searchCondition = null; @@ -202,49 +201,49 @@ public class LabelAllocateRepositoryImpl implements LabelAllocateRepositoryCusto // 완료, 스킵, 남은 작업 계산 NumberExpression completedSum = - new CaseBuilder() - .when(labelingAssignmentEntity.workState.eq("DONE")) - .then(1L) - .otherwise(0L) - .sum(); + new CaseBuilder() + .when(labelingAssignmentEntity.workState.eq("DONE")) + .then(1L) + .otherwise(0L) + .sum(); NumberExpression skippedSum = - new CaseBuilder() - .when(labelingAssignmentEntity.workState.eq("SKIP")) - .then(1L) - .otherwise(0L) - .sum(); + new CaseBuilder() + .when(labelingAssignmentEntity.workState.eq("SKIP")) + .then(1L) + .otherwise(0L) + .sum(); NumberExpression remainingSum = - new CaseBuilder() - .when( - labelingAssignmentEntity - .workState - .notIn("DONE", "SKIP") - .and(labelingAssignmentEntity.workState.isNotNull())) - .then(1L) - .otherwise(0L) - .sum(); + new CaseBuilder() + .when( + labelingAssignmentEntity + .workState + .notIn("DONE", "SKIP") + .and(labelingAssignmentEntity.workState.isNotNull())) + .then(1L) + .otherwise(0L) + .sum(); // 기본 통계 조회 쿼리 var baseQuery = - queryFactory - .select( - workerIdField, - memberEntity.name, - workerIdField.count(), - completedSum, - skippedSum, - remainingSum, - labelingAssignmentEntity.stagnationYn.max()) - .from(labelingAssignmentEntity) - .leftJoin(memberEntity) - .on( - "REVIEWER".equals(workerType) - ? memberEntity.employeeNo.eq(labelingAssignmentEntity.inspectorUid) - : memberEntity.employeeNo.eq(labelingAssignmentEntity.workerUid)) - .where(labelingAssignmentEntity.analUid.eq(analUid), workerCondition, searchCondition) - .groupBy(workerIdField, memberEntity.name); + queryFactory + .select( + workerIdField, + memberEntity.name, + workerIdField.count(), + completedSum, + skippedSum, + remainingSum, + labelingAssignmentEntity.stagnationYn.max()) + .from(labelingAssignmentEntity) + .leftJoin(memberEntity) + .on( + "REVIEWER".equals(workerType) + ? memberEntity.employeeNo.eq(labelingAssignmentEntity.inspectorUid) + : memberEntity.employeeNo.eq(labelingAssignmentEntity.workerUid)) + .where(labelingAssignmentEntity.analUid.eq(analUid), workerCondition, searchCondition) + .groupBy(workerIdField, memberEntity.name); // 정렬 조건 적용 if (sortType != null) { @@ -270,132 +269,132 @@ public class LabelAllocateRepositoryImpl implements LabelAllocateRepositoryCusto // 결과를 DTO로 변환 return baseQuery.fetch().stream() - .map( - tuple -> { - Character maxStagnationYn = tuple.get(labelingAssignmentEntity.stagnationYn.max()); - return WorkerStatistics.builder() - .workerId(tuple.get(workerIdField)) - .workerName(tuple.get(memberEntity.name)) - .workerType(workerType) - .totalAssigned(tuple.get(workerIdField.count())) - .completed(tuple.get(completedSum)) - .skipped(tuple.get(skippedSum)) - .remaining(tuple.get(remainingSum)) - .history(null) // 3일 이력은 Service에서 채움 - .isStagnated(maxStagnationYn != null && maxStagnationYn == 'Y') - .build(); - }) - .toList(); + .map( + tuple -> { + Character maxStagnationYn = tuple.get(labelingAssignmentEntity.stagnationYn.max()); + return WorkerStatistics.builder() + .workerId(tuple.get(workerIdField)) + .workerName(tuple.get(memberEntity.name)) + .workerType(workerType) + .totalAssigned(tuple.get(workerIdField.count())) + .completed(tuple.get(completedSum)) + .skipped(tuple.get(skippedSum)) + .remaining(tuple.get(remainingSum)) + .history(null) // 3일 이력은 Service에서 채움 + .isStagnated(maxStagnationYn != null && maxStagnationYn == 'Y') + .build(); + }) + .toList(); } @Override public WorkProgressInfo findWorkProgressInfo(Long analUid) { // 전체 배정 건수 Long totalAssigned = - queryFactory - .select(labelingAssignmentEntity.count()) - .from(labelingAssignmentEntity) - .where(labelingAssignmentEntity.analUid.eq(analUid)) - .fetchOne(); + queryFactory + .select(labelingAssignmentEntity.count()) + .from(labelingAssignmentEntity) + .where(labelingAssignmentEntity.analUid.eq(analUid)) + .fetchOne(); // 완료 + 스킵 건수 Long completedCount = - queryFactory - .select(labelingAssignmentEntity.count()) - .from(labelingAssignmentEntity) - .where( - labelingAssignmentEntity.analUid.eq(analUid), - labelingAssignmentEntity.workState.in("DONE", "SKIP")) - .fetchOne(); + queryFactory + .select(labelingAssignmentEntity.count()) + .from(labelingAssignmentEntity) + .where( + labelingAssignmentEntity.analUid.eq(analUid), + labelingAssignmentEntity.workState.in("DONE", "SKIP")) + .fetchOne(); // 투입된 라벨러 수 (고유한 worker_uid 수) Long labelerCount = - queryFactory - .select(labelingAssignmentEntity.workerUid.countDistinct()) - .from(labelingAssignmentEntity) - .where( - labelingAssignmentEntity.analUid.eq(analUid), - labelingAssignmentEntity.workerUid.isNotNull()) - .fetchOne(); + queryFactory + .select(labelingAssignmentEntity.workerUid.countDistinct()) + .from(labelingAssignmentEntity) + .where( + labelingAssignmentEntity.analUid.eq(analUid), + labelingAssignmentEntity.workerUid.isNotNull()) + .fetchOne(); // 남은 라벨링 작업 데이터 수 Long remainingLabelCount = - queryFactory - .select(labelingAssignmentEntity.count()) - .from(labelingAssignmentEntity) - .where( - labelingAssignmentEntity.analUid.eq(analUid), - labelingAssignmentEntity.workerUid.isNotNull(), - labelingAssignmentEntity.workState.notIn("DONE", "SKIP")) - .fetchOne(); + queryFactory + .select(labelingAssignmentEntity.count()) + .from(labelingAssignmentEntity) + .where( + labelingAssignmentEntity.analUid.eq(analUid), + labelingAssignmentEntity.workerUid.isNotNull(), + labelingAssignmentEntity.workState.notIn("DONE", "SKIP")) + .fetchOne(); // 투입된 검수자 수 (고유한 inspector_uid 수) Long inspectorCount = - queryFactory - .select(labelingAssignmentEntity.inspectorUid.countDistinct()) - .from(labelingAssignmentEntity) - .where( - labelingAssignmentEntity.analUid.eq(analUid), - labelingAssignmentEntity.inspectorUid.isNotNull()) - .fetchOne(); + queryFactory + .select(labelingAssignmentEntity.inspectorUid.countDistinct()) + .from(labelingAssignmentEntity) + .where( + labelingAssignmentEntity.analUid.eq(analUid), + labelingAssignmentEntity.inspectorUid.isNotNull()) + .fetchOne(); // 남은 검수 작업 데이터 수 Long remainingInspectCount = - queryFactory - .select(labelingAssignmentEntity.count()) - .from(labelingAssignmentEntity) - .where( - labelingAssignmentEntity.analUid.eq(analUid), - labelingAssignmentEntity.inspectorUid.isNotNull(), - labelingAssignmentEntity.workState.notIn("DONE")) - .fetchOne(); + queryFactory + .select(labelingAssignmentEntity.count()) + .from(labelingAssignmentEntity) + .where( + labelingAssignmentEntity.analUid.eq(analUid), + labelingAssignmentEntity.inspectorUid.isNotNull(), + labelingAssignmentEntity.workState.notIn("DONE")) + .fetchOne(); // 진행률 계산 double progressRate = 0.0; if (totalAssigned != null && totalAssigned > 0) { progressRate = - (completedCount != null ? completedCount.doubleValue() : 0.0) / totalAssigned * 100; + (completedCount != null ? completedCount.doubleValue() : 0.0) / totalAssigned * 100; } // 작업 상태 판단 (간단하게 진행률 100%면 종료, 아니면 진행중) String workStatus = (progressRate >= 100.0) ? "종료" : "진행중"; return WorkProgressInfo.builder() - .labelingProgressRate(progressRate) - .workStatus(workStatus) - .completedCount(completedCount != null ? completedCount : 0L) - .totalAssignedCount(totalAssigned != null ? totalAssigned : 0L) - .labelerCount(labelerCount != null ? labelerCount : 0L) - .remainingLabelCount(remainingLabelCount != null ? remainingLabelCount : 0L) - .inspectorCount(inspectorCount != null ? inspectorCount : 0L) - .remainingInspectCount(remainingInspectCount != null ? remainingInspectCount : 0L) - .build(); + .labelingProgressRate(progressRate) + .workStatus(workStatus) + .completedCount(completedCount != null ? completedCount : 0L) + .totalAssignedCount(totalAssigned != null ? totalAssigned : 0L) + .labelerCount(labelerCount != null ? labelerCount : 0L) + .remainingLabelCount(remainingLabelCount != null ? remainingLabelCount : 0L) + .inspectorCount(inspectorCount != null ? inspectorCount : 0L) + .remainingInspectCount(remainingInspectCount != null ? remainingInspectCount : 0L) + .build(); } @Override public Long findDailyProcessedCount( - String workerId, String workerType, LocalDate date, Long analUid) { + String workerId, String workerType, LocalDate date, Long analUid) { // 해당 날짜의 시작과 끝 시간 ZonedDateTime startOfDay = date.atStartOfDay(ZoneId.systemDefault()); ZonedDateTime endOfDay = date.atTime(LocalTime.MAX).atZone(ZoneId.systemDefault()); BooleanExpression workerCondition = - "REVIEWER".equals(workerType) - ? labelingAssignmentEntity.inspectorUid.eq(workerId) - : labelingAssignmentEntity.workerUid.eq(workerId); + "REVIEWER".equals(workerType) + ? labelingAssignmentEntity.inspectorUid.eq(workerId) + : labelingAssignmentEntity.workerUid.eq(workerId); Long count = - queryFactory - .select(labelingAssignmentEntity.count()) - .from(labelingAssignmentEntity) - .where( - labelingAssignmentEntity.analUid.eq(analUid), - workerCondition, - labelingAssignmentEntity.workState.in( - LabelState.DONE.getId(), LabelState.SKIP.getId()), - labelingAssignmentEntity.modifiedDate.between(startOfDay, endOfDay)) - .fetchOne(); + queryFactory + .select(labelingAssignmentEntity.count()) + .from(labelingAssignmentEntity) + .where( + labelingAssignmentEntity.analUid.eq(analUid), + workerCondition, + labelingAssignmentEntity.workState.in( + LabelState.DONE.getId(), LabelState.SKIP.getId()), + labelingAssignmentEntity.modifiedDate.between(startOfDay, endOfDay)) + .fetchOne(); return count != null ? count : 0L; } @@ -403,10 +402,10 @@ public class LabelAllocateRepositoryImpl implements LabelAllocateRepositoryCusto @Override public void assignInspectorBulk(List assignmentUids, String inspectorUid) { queryFactory - .update(labelingAssignmentEntity) - .set(labelingAssignmentEntity.inspectorUid, inspectorUid) - .where(labelingAssignmentEntity.assignmentUid.in(assignmentUids)) - .execute(); + .update(labelingAssignmentEntity) + .set(labelingAssignmentEntity.inspectorUid, inspectorUid) + .where(labelingAssignmentEntity.assignmentUid.in(assignmentUids)) + .execute(); em.clear(); } @@ -414,84 +413,85 @@ public class LabelAllocateRepositoryImpl implements LabelAllocateRepositoryCusto @Override public InferenceDetail findInferenceDetail(Long analUid) { return queryFactory - .select( - Projections.constructor( - InferenceDetail.class, - mapSheetAnalEntity.analTitle, - Expressions.numberTemplate(Integer.class, "{0}", 4), - mapSheetAnalEntity.gukyuinApplyDttm, - mapSheetAnalEntity.detectingCnt, - labelingAssignmentEntity.workerUid.countDistinct(), - labelingAssignmentEntity.inspectorUid.countDistinct())) - .from(mapSheetAnalEntity) - .innerJoin(labelingAssignmentEntity) - .on(mapSheetAnalEntity.id.eq(labelingAssignmentEntity.analUid)) - .where(mapSheetAnalEntity.id.eq(analUid)) - .groupBy( - mapSheetAnalEntity.analTitle, - mapSheetAnalEntity.gukyuinApplyDttm, - mapSheetAnalEntity.detectingCnt) - .fetchOne(); + .select( + Projections.constructor( + InferenceDetail.class, + mapSheetAnalEntity.analTitle, + Expressions.numberTemplate(Integer.class, "{0}", 4), + mapSheetAnalEntity.gukyuinApplyDttm, + mapSheetAnalEntity.detectingCnt, + labelingAssignmentEntity.workerUid.countDistinct(), + labelingAssignmentEntity.inspectorUid.countDistinct())) + .from(mapSheetAnalEntity) + .innerJoin(labelingAssignmentEntity) + .on(mapSheetAnalEntity.id.eq(labelingAssignmentEntity.analUid)) + .where(mapSheetAnalEntity.id.eq(analUid)) + .groupBy( + mapSheetAnalEntity.analTitle, + mapSheetAnalEntity.gukyuinApplyDttm, + mapSheetAnalEntity.detectingCnt) + .fetchOne(); } - @Override public List fetchNextMoveIds(Long lastId, Long batchSize) { MapSheetAnalEntity entity = - queryFactory - .selectFrom(mapSheetAnalEntity) - .where(mapSheetAnalEntity.id.eq(3L)) //TODO - .fetchOne(); + queryFactory + .selectFrom(mapSheetAnalEntity) + .where(mapSheetAnalEntity.id.eq(3L)) // TODO + .fetchOne(); if (Objects.isNull(entity)) { throw new EntityNotFoundException(); } return queryFactory - .select(mapSheetAnalDataInferenceGeomEntity.geoUid) - .from(mapSheetAnalDataInferenceGeomEntity) - .where( - // mapSheetAnalDataGeomEntity.pnu.isNotNull(), //TODO: Mockup 진행 이후 확인하기 - lastId == null ? null : mapSheetAnalDataInferenceGeomEntity.geoUid.gt(lastId), - mapSheetAnalDataInferenceGeomEntity.compareYyyy.eq(entity.getCompareYyyy()), - mapSheetAnalDataInferenceGeomEntity.targetYyyy.eq(entity.getTargetYyyy()), - mapSheetAnalDataInferenceGeomEntity.labelState.in(LabelState.ASSIGNED.getId(), LabelState.SKIP.getId())) - .orderBy(mapSheetAnalDataInferenceGeomEntity.mapSheetNum.asc()) - .limit(batchSize) - .fetch(); + .select(mapSheetAnalDataInferenceGeomEntity.geoUid) + .from(mapSheetAnalDataInferenceGeomEntity) + .where( + // mapSheetAnalDataGeomEntity.pnu.isNotNull(), //TODO: Mockup 진행 이후 확인하기 + lastId == null ? null : mapSheetAnalDataInferenceGeomEntity.geoUid.gt(lastId), + mapSheetAnalDataInferenceGeomEntity.compareYyyy.eq(entity.getCompareYyyy()), + mapSheetAnalDataInferenceGeomEntity.targetYyyy.eq(entity.getTargetYyyy()), + mapSheetAnalDataInferenceGeomEntity.labelState.in( + LabelState.ASSIGNED.getId(), LabelState.SKIP.getId())) + .orderBy(mapSheetAnalDataInferenceGeomEntity.mapSheetNum.asc()) + .limit(batchSize) + .fetch(); } @Override public Long findLabelUnCompleteCnt(Long analUid) { MapSheetAnalEntity entity = - queryFactory - .selectFrom(mapSheetAnalEntity) - .where(mapSheetAnalEntity.id.eq(analUid)) - .fetchOne(); + queryFactory + .selectFrom(mapSheetAnalEntity) + .where(mapSheetAnalEntity.id.eq(analUid)) + .fetchOne(); if (Objects.isNull(entity)) { throw new EntityNotFoundException(); } return queryFactory - .select(mapSheetAnalDataInferenceGeomEntity.geoUid.count()) - .from(mapSheetAnalDataInferenceGeomEntity) - .where( - mapSheetAnalDataInferenceGeomEntity.compareYyyy.eq(entity.getCompareYyyy()), - mapSheetAnalDataInferenceGeomEntity.targetYyyy.eq(entity.getTargetYyyy()), - mapSheetAnalDataInferenceGeomEntity.stage.eq(4), // TODO: 회차 컬럼을 가져와야 할 듯? - mapSheetAnalDataInferenceGeomEntity.labelState.in(LabelState.ASSIGNED.getId(), LabelState.SKIP.getId())) - .fetchOne(); + .select(mapSheetAnalDataInferenceGeomEntity.geoUid.count()) + .from(mapSheetAnalDataInferenceGeomEntity) + .where( + mapSheetAnalDataInferenceGeomEntity.compareYyyy.eq(entity.getCompareYyyy()), + mapSheetAnalDataInferenceGeomEntity.targetYyyy.eq(entity.getTargetYyyy()), + mapSheetAnalDataInferenceGeomEntity.stage.eq(4), // TODO: 회차 컬럼을 가져와야 할 듯? + mapSheetAnalDataInferenceGeomEntity.labelState.in( + LabelState.ASSIGNED.getId(), LabelState.SKIP.getId())) + .fetchOne(); } @Transactional @Override public void assignOwnerMove(List sub, String userId) { queryFactory - .update(labelingAssignmentEntity) - .set(labelingAssignmentEntity.workerUid, userId) - .where(labelingAssignmentEntity.inferenceGeomUid.in(sub)) - .execute(); + .update(labelingAssignmentEntity) + .set(labelingAssignmentEntity.workerUid, userId) + .where(labelingAssignmentEntity.inferenceGeomUid.in(sub)) + .execute(); em.clear(); } @@ -499,56 +499,55 @@ public class LabelAllocateRepositoryImpl implements LabelAllocateRepositoryCusto @Override public LabelerDetail findLabelerDetail(String userId, Long analUid) { NumberExpression assignedCnt = - new CaseBuilder() - .when(labelingAssignmentEntity.workState.eq(LabelState.ASSIGNED.getId())).then(1L) - .otherwise((Long) null) - .count(); + new CaseBuilder() + .when(labelingAssignmentEntity.workState.eq(LabelState.ASSIGNED.getId())) + .then(1L) + .otherwise((Long) null) + .count(); NumberExpression skipCnt = - new CaseBuilder() - .when(labelingAssignmentEntity.workState.eq(LabelState.SKIP.getId())).then(1L) - .otherwise((Long) null) - .count(); + new CaseBuilder() + .when(labelingAssignmentEntity.workState.eq(LabelState.SKIP.getId())) + .then(1L) + .otherwise((Long) null) + .count(); NumberExpression completeCnt = - new CaseBuilder() - .when(labelingAssignmentEntity.workState.eq(LabelState.COMPLETE.getId())).then(1L) - .otherwise((Long) null) - .count(); + new CaseBuilder() + .when(labelingAssignmentEntity.workState.eq(LabelState.COMPLETE.getId())) + .then(1L) + .otherwise((Long) null) + .count(); NumberExpression percent = - new CaseBuilder() - .when(completeCnt.eq(0L)) - .then(0.0) - .otherwise( - Expressions.numberTemplate( - Double.class, - "round({0} / {1}, 2)", - labelingAssignmentEntity.count(), - completeCnt - ) - ); + new CaseBuilder() + .when(completeCnt.eq(0L)) + .then(0.0) + .otherwise( + Expressions.numberTemplate( + Double.class, + "round({0} / {1}, 2)", + labelingAssignmentEntity.count(), + completeCnt)); return queryFactory - .select(Projections.constructor(LabelerDetail.class, - memberEntity.userRole, - memberEntity.name, - memberEntity.employeeNo, - assignedCnt, - skipCnt, - completeCnt, - percent - )) - .from(memberEntity) - .innerJoin(labelingAssignmentEntity) - .on(memberEntity.employeeNo.eq(labelingAssignmentEntity.workerUid), - labelingAssignmentEntity.analUid.eq(analUid) - ) - .where(memberEntity.employeeNo.eq(userId)) - .groupBy(memberEntity.userRole, - memberEntity.name, - memberEntity.employeeNo) - .fetchOne() - ; + .select( + Projections.constructor( + LabelerDetail.class, + memberEntity.userRole, + memberEntity.name, + memberEntity.employeeNo, + assignedCnt, + skipCnt, + completeCnt, + percent)) + .from(memberEntity) + .innerJoin(labelingAssignmentEntity) + .on( + memberEntity.employeeNo.eq(labelingAssignmentEntity.workerUid), + labelingAssignmentEntity.analUid.eq(analUid)) + .where(memberEntity.employeeNo.eq(userId)) + .groupBy(memberEntity.userRole, memberEntity.name, memberEntity.employeeNo) + .fetchOne(); } }