From 40b91b7deab8f1d8a564f696a91c3ab171165075 Mon Sep 17 00:00:00 2001 From: DanielLee <198891672+sanghyeonhd@users.noreply.github.com> Date: Fri, 9 Jan 2026 11:01:46 +0900 Subject: [PATCH] =?UTF-8?q?projectinfo=20uuid=EC=A1=B0=ED=9A=8C=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- login.json | 0 .../label/LabelAllocateApiController.java | 13 +- .../label/service/LabelAllocateService.java | 32 +++ .../core/LabelAllocateCoreService.java | 8 + .../label/LabelAllocateRepositoryCustom.java | 6 + .../label/LabelAllocateRepositoryImpl.java | 198 ++++++++++++++++++ 6 files changed, 253 insertions(+), 4 deletions(-) delete mode 100644 login.json diff --git a/login.json b/login.json deleted file mode 100644 index e69de29b..00000000 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 663ab9e0..cd70855f 100644 --- a/src/main/java/com/kamco/cd/kamcoback/label/LabelAllocateApiController.java +++ b/src/main/java/com/kamco/cd/kamcoback/label/LabelAllocateApiController.java @@ -55,7 +55,9 @@ public class LabelAllocateApiController { return ApiResponseDto.ok(labelAllocateService.availUserList(role)); } - @Operation(summary = "작업현황 관리 > 프로젝트 및 진행 상황 정보", description = "작업현황 관리 > 프로젝트 및 진행 상황 정보") + @Operation( + summary = "작업현황 관리 > 프로젝트 및 진행 상황 정보", + description = "작업현황 관리 > 프로젝트 및 진행 상황 정보. UUID를 입력하면 해당 프로젝트 정보를 조회하고, 미입력 시 최신 프로젝트를 조회합니다.") @ApiResponses( value = { @ApiResponse(responseCode = "200", description = "조회 성공"), @@ -64,8 +66,11 @@ public class LabelAllocateApiController { }) @GetMapping("/projectinfo") public ApiResponseDto getWorkerStatistics( - // @Parameter(description = "분석 ID (선택)", example = "3") @RequestParam(required = false) - // Long analUid, + @Parameter( + description = "프로젝트 UUID (선택) - 미입력 시 최신 프로젝트 조회", + example = "f97dc186-e6d3-4645-9737-3173dde8dc64") + @RequestParam(required = false) + String uuid, @Parameter( description = "작업자 유형 (선택) - 미입력 시 LABELER로 조회", example = "LABELER", @@ -99,7 +104,7 @@ public class LabelAllocateApiController { String workerType = (type == null || type.isEmpty()) ? RoleType.LABELER.name() : type; return ApiResponseDto.ok( - labelAllocateService.getWorkerStatistics(null, workerType, search, sort)); + labelAllocateService.getWorkerStatisticsByUuid(uuid, workerType, search, sort)); } @Operation(summary = "라벨링작업 관리 > 작업 배정", description = "라벨링작업 관리 > 작업 배정") 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 caa84cfb..e4fa63df 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 @@ -10,6 +10,8 @@ import com.kamco.cd.kamcoback.label.dto.LabelAllocateDto.LabelingStatDto; import com.kamco.cd.kamcoback.label.dto.LabelAllocateDto.MoveInfo; import com.kamco.cd.kamcoback.label.dto.LabelAllocateDto.TargetUser; import com.kamco.cd.kamcoback.label.dto.LabelAllocateDto.UserList; +import com.kamco.cd.kamcoback.label.dto.WorkerStatsDto.ProjectInfo; +import com.kamco.cd.kamcoback.label.dto.WorkerStatsDto.WorkProgressInfo; import com.kamco.cd.kamcoback.label.dto.WorkerStatsDto.WorkerListResponse; import com.kamco.cd.kamcoback.postgres.core.LabelAllocateCoreService; import java.util.List; @@ -114,6 +116,36 @@ public class LabelAllocateService { return WorkerListResponse.builder().projectInfo(projectInfo).progressInfo(progressInfo).build(); } + /** + * 작업자 통계 조회 (UUID 기반) + * + * @param uuid 프로젝트 UUID (선택, 미입력 시 최신 프로젝트 조회) + * @param workerType 작업자 유형 (LABELER/INSPECTOR) + * @param search 검색어 (이름 또는 사번) + * @param sortType 정렬 조건 + * @return 작업자 목록 및 통계 + */ + public WorkerListResponse getWorkerStatisticsByUuid( + String uuid, String workerType, String search, String sortType) { + + ProjectInfo projectInfo; + WorkProgressInfo progressInfo; + + if (uuid != null && !uuid.isBlank()) { + // UUID로 프로젝트 정보 조회 + projectInfo = labelAllocateCoreService.findProjectInfoByUuid(uuid); + // UUID로 작업 진행 현황 조회 + progressInfo = labelAllocateCoreService.findWorkProgressInfoByUuid(uuid); + } else { + // 최신 프로젝트 정보 조회 + projectInfo = labelAllocateCoreService.findLatestProjectInfo(); + // 최신 프로젝트 작업 진행 현황 조회 + progressInfo = labelAllocateCoreService.findWorkProgressInfo(null); + } + + return WorkerListResponse.builder().projectInfo(projectInfo).progressInfo(progressInfo).build(); + } + public InferenceDetail findInferenceDetail(String uuid) { return labelAllocateCoreService.findInferenceDetail(uuid); } 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 2cf1422d..65b2c78c 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 @@ -61,6 +61,10 @@ public class LabelAllocateCoreService { return labelAllocateRepository.findLatestProjectInfo(); } + public ProjectInfo findProjectInfoByUuid(String uuid) { + return labelAllocateRepository.findProjectInfoByUuid(uuid); + } + public UUID findLastLabelWorkState() { return labelAllocateRepository.findLastLabelWorkState(); } @@ -74,6 +78,10 @@ public class LabelAllocateCoreService { return labelAllocateRepository.findWorkProgressInfo(analUid); } + public WorkProgressInfo findWorkProgressInfoByUuid(String uuid) { + return labelAllocateRepository.findWorkProgressInfoByUuid(uuid); + } + public Long findDailyProcessedCount( 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 319a118b..9a7bcc8c 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 @@ -37,6 +37,9 @@ public interface LabelAllocateRepositoryCustom { // 최신 프로젝트 정보 조회 (analUid 없이) ProjectInfo findLatestProjectInfo(); + // UUID로 프로젝트 정보 조회 + ProjectInfo findProjectInfoByUuid(String uuid); + // 최신 작업 상태의 UUID 조회 UUID findLastLabelWorkState(); @@ -47,6 +50,9 @@ public interface LabelAllocateRepositoryCustom { // 작업 진행 현황 조회 WorkProgressInfo findWorkProgressInfo(Long analUid); + // UUID로 작업 진행 현황 조회 + WorkProgressInfo findWorkProgressInfoByUuid(String uuid); + // 작업자별 일일 처리량 조회 Long findDailyProcessedCount(String workerId, String workerType, LocalDate date, 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 28b2f625..12c5450b 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 @@ -496,6 +496,154 @@ public class LabelAllocateRepositoryImpl implements LabelAllocateRepositoryCusto .build(); } + @Override + public WorkProgressInfo findWorkProgressInfoByUuid(String uuid) { + if (uuid == null || uuid.isBlank()) { + return findWorkProgressInfo(null); + } + + UUID targetUuid = UUID.fromString(uuid); + + // UUID로 analUid 조회 + Long effectiveAnalUid = + queryFactory + .select(mapSheetAnalInferenceEntity.id) + .from(mapSheetAnalInferenceEntity) + .where(mapSheetAnalInferenceEntity.uuid.eq(targetUuid)) + .fetchOne(); + + if (effectiveAnalUid == null) { + return null; + } + + BooleanExpression analUidCondition = labelingAssignmentEntity.analUid.eq(effectiveAnalUid); + + // analUid로 분석 정보(compareYyyy, targetYyyy, stage) 조회 + MapSheetAnalInferenceEntity analEntity = + queryFactory + .selectFrom(mapSheetAnalInferenceEntity) + .where(mapSheetAnalInferenceEntity.id.eq(effectiveAnalUid)) + .fetchOne(); + + // 라벨링 대상 건수: tb_map_sheet_anal_data_inference_geom에서 pnu > 0 AND pass_yn = false(부적합)인 건수 + Long labelingTargetCount = 0L; + if (analEntity != null) { + labelingTargetCount = + queryFactory + .select(mapSheetAnalDataInferenceGeomEntity.geoUid.count()) + .from(mapSheetAnalDataInferenceGeomEntity) + .where( + mapSheetAnalDataInferenceGeomEntity.compareYyyy.eq(analEntity.getCompareYyyy()), + mapSheetAnalDataInferenceGeomEntity.targetYyyy.eq(analEntity.getTargetYyyy()), + mapSheetAnalDataInferenceGeomEntity.stage.eq(analEntity.getStage()), + mapSheetAnalDataInferenceGeomEntity.pnu.gt(0L), + mapSheetAnalDataInferenceGeomEntity.passYn.isFalse()) + .fetchOne(); + } + + // 전체 배정 건수 (tb_labeling_assignment 기준) + Long totalAssigned = + queryFactory + .select(labelingAssignmentEntity.count()) + .from(labelingAssignmentEntity) + .where(analUidCondition) + .fetchOne(); + + // === 라벨링 통계 === + Long labelingCompleted = + queryFactory + .select(labelingAssignmentEntity.count()) + .from(labelingAssignmentEntity) + .where( + analUidCondition, + labelingAssignmentEntity.workState.in("LABEL_FIN", "TEST_ING", "DONE")) + .fetchOne(); + + Long skipCount = + queryFactory + .select(labelingAssignmentEntity.count()) + .from(labelingAssignmentEntity) + .where(analUidCondition, labelingAssignmentEntity.workState.eq("SKIP")) + .fetchOne(); + + Long labelerCount = + queryFactory + .select(labelingAssignmentEntity.workerUid.countDistinct()) + .from(labelingAssignmentEntity) + .where(analUidCondition, labelingAssignmentEntity.workerUid.isNotNull()) + .fetchOne(); + + // === 검수 통계 === + Long inspectionCompleted = + queryFactory + .select(labelingAssignmentEntity.count()) + .from(labelingAssignmentEntity) + .where(analUidCondition, labelingAssignmentEntity.workState.eq("DONE")) + .fetchOne(); + + Long inspectorCount = + queryFactory + .select(labelingAssignmentEntity.inspectorUid.countDistinct()) + .from(labelingAssignmentEntity) + .where(analUidCondition, labelingAssignmentEntity.inspectorUid.isNotNull()) + .fetchOne(); + + // 계산 + long labelingTotal = labelingTargetCount != null ? labelingTargetCount : 0L; + long labelCompleted = labelingCompleted != null ? labelingCompleted : 0L; + long inspectCompleted = inspectionCompleted != null ? inspectionCompleted : 0L; + long skipped = skipCount != null ? skipCount : 0L; + + long labelingRemaining = labelingTotal - labelCompleted - skipped; + if (labelingRemaining < 0) labelingRemaining = 0; + + long inspectionTotal = labelingTotal; + long inspectionRemaining = inspectionTotal - inspectCompleted - skipped; + if (inspectionRemaining < 0) inspectionRemaining = 0; + + double labelingRate = labelingTotal > 0 ? (double) labelCompleted / labelingTotal * 100 : 0.0; + double inspectionRate = + inspectionTotal > 0 ? (double) inspectCompleted / inspectionTotal * 100 : 0.0; + + // 상태 판단 + String labelingStatus; + String inspectionStatus; + + if (analEntity != null && "Y".equals(analEntity.getLabelingClosedYn())) { + labelingStatus = "종료"; + } else { + labelingStatus = labelingRemaining > 0 ? "진행중" : "완료"; + } + + if (analEntity != null && "Y".equals(analEntity.getInspectionClosedYn())) { + inspectionStatus = "종료"; + } else { + inspectionStatus = inspectionRemaining > 0 ? "진행중" : "완료"; + } + + return WorkProgressInfo.builder() + .labelingProgressRate(labelingRate) + .labelingStatus(labelingStatus) + .labelingTotalCount(labelingTotal) + .labelingCompletedCount(labelCompleted) + .labelingSkipCount(skipped) + .labelingRemainingCount(labelingRemaining) + .labelerCount(labelerCount != null ? labelerCount : 0L) + .inspectionProgressRate(inspectionRate) + .inspectionStatus(inspectionStatus) + .inspectionTotalCount(inspectionTotal) + .inspectionCompletedCount(inspectCompleted) + .inspectionSkipCount(skipped) + .inspectionRemainingCount(inspectionRemaining) + .inspectorCount(inspectorCount != null ? inspectorCount : 0L) + .progressRate(labelingRate) + .totalAssignedCount(labelingTotal) + .completedCount(labelCompleted) + .remainingLabelCount(labelingRemaining) + .remainingInspectCount(inspectionRemaining) + .build(); + } + @Override public Long findDailyProcessedCount( String workerId, String workerType, LocalDate date, Long analUid) { @@ -832,6 +980,56 @@ public class LabelAllocateRepositoryImpl implements LabelAllocateRepositoryCusto .build(); } + @Override + public ProjectInfo findProjectInfoByUuid(String uuid) { + if (uuid == null || uuid.isBlank()) { + return null; + } + + UUID targetUuid = UUID.fromString(uuid); + + var result = + queryFactory + .select( + mapSheetAnalInferenceEntity.compareYyyy, + mapSheetAnalInferenceEntity.targetYyyy, + mapSheetAnalInferenceEntity.stage, + mapSheetAnalInferenceEntity.gukyuinApplyDttm, + mapSheetAnalInferenceEntity.createdDttm, + mapSheetAnalInferenceEntity.uuid, + mapSheetAnalInferenceEntity.labelingClosedYn, + mapSheetAnalInferenceEntity.inspectionClosedYn) + .from(mapSheetAnalInferenceEntity) + .where(mapSheetAnalInferenceEntity.uuid.eq(targetUuid)) + .fetchOne(); + + if (result == null) { + return null; + } + + Integer compareYyyy = result.get(mapSheetAnalInferenceEntity.compareYyyy); + Integer targetYyyy = result.get(mapSheetAnalInferenceEntity.targetYyyy); + Integer stage = result.get(mapSheetAnalInferenceEntity.stage); + ZonedDateTime gukyuinApplyDttm = result.get(mapSheetAnalInferenceEntity.gukyuinApplyDttm); + ZonedDateTime createdDttm = result.get(mapSheetAnalInferenceEntity.createdDttm); + String labelingClosedYn = result.get(mapSheetAnalInferenceEntity.labelingClosedYn); + String inspectionClosedYn = result.get(mapSheetAnalInferenceEntity.inspectionClosedYn); + + String detectionYear = + (compareYyyy != null && targetYyyy != null) ? compareYyyy + "-" + targetYyyy : null; + String round = stage != null ? String.valueOf(stage) : null; + + return ProjectInfo.builder() + .detectionYear(detectionYear) + .stage(round) + .gukyuinApplyDttm(gukyuinApplyDttm) + .startDttm(createdDttm) + .uuid(uuid) + .labelingClosedYn(labelingClosedYn) + .inspectionClosedYn(inspectionClosedYn) + .build(); + } + @Override public UUID findLastLabelWorkState() { BooleanBuilder whereBuilder = new BooleanBuilder();