diff --git a/src/main/java/com/kamco/cd/kamcoback/inference/InferenceResultApiController.java b/src/main/java/com/kamco/cd/kamcoback/inference/InferenceResultApiController.java index 99fe158d..35391d74 100644 --- a/src/main/java/com/kamco/cd/kamcoback/inference/InferenceResultApiController.java +++ b/src/main/java/com/kamco/cd/kamcoback/inference/InferenceResultApiController.java @@ -307,4 +307,64 @@ public class InferenceResultApiController { String uuid) { return ApiResponseDto.ok(inferenceResultService.getInferenceResultInfo(uuid)); } + + @Operation(summary = "추론결과 분류별 탐지 건수", description = "추론결과 분류별 탐지 건수") + @ApiResponses( + value = { + @ApiResponse( + responseCode = "200", + description = "검색 성공", + content = + @Content( + mediaType = "application/json", + schema = @Schema(implementation = InferenceDetailDto.AnalResSummary.class))), + @ApiResponse(responseCode = "400", description = "잘못된 검색 조건", content = @Content), + @ApiResponse(responseCode = "500", description = "서버 오류", content = @Content) + }) + @GetMapping("/infer-class-count") + public ApiResponseDto> getInferenceClassCountList( + @Parameter(description = "회차 uuid", example = "8584e8d4-53b3-4582-bde2-28a81495a626") + @RequestParam + String uuid) { + return ApiResponseDto.ok(inferenceResultService.getInferenceClassCountList(uuid)); + } + + @Operation(summary = "추론관리 분석결과 상세 목록", description = "추론관리 분석결과 상세 목록 geojson 데이터 조회") + @ApiResponses( + value = { + @ApiResponse( + responseCode = "200", + description = "검색 성공", + content = + @Content( + mediaType = "application/json", + schema = @Schema(implementation = Page.class))), + @ApiResponse(responseCode = "400", description = "잘못된 검색 조건", content = @Content), + @ApiResponse(responseCode = "500", description = "서버 오류", content = @Content) + }) + @GetMapping("/geom-list") + public ApiResponseDto> getInferenceGeomList( + @Parameter(description = "회차 uuid", example = "8584e8d4-53b3-4582-bde2-28a81495a626") + @RequestParam(required = true) + String uuid, + @Parameter(description = "기준년도 분류", example = "land") @RequestParam(required = false) + String targetClass, + @Parameter(description = "비교년도 분류", example = "waste") @RequestParam(required = false) + String compareClass, + @Parameter(description = "5000:1 도엽번호 37801011,37801012") @RequestParam(required = false) + List mapSheetNum, + @Parameter(description = "페이지 번호 (0부터 시작)", example = "0") @RequestParam(defaultValue = "0") + int page, + @Parameter(description = "페이지 크기", example = "20") @RequestParam(defaultValue = "20") + int size, + @Parameter(description = "정렬 조건 (형식: 필드명,방향)", example = "name,asc") + @RequestParam(required = false) + String sort) { + InferenceDetailDto.SearchGeoReq searchGeoReq = + new InferenceDetailDto.SearchGeoReq( + targetClass, compareClass, mapSheetNum, page, size, sort); + Page geomList = + inferenceResultService.getInferenceGeomList(uuid, searchGeoReq); + return ApiResponseDto.ok(geomList); + } } diff --git a/src/main/java/com/kamco/cd/kamcoback/inference/dto/InferenceDetailDto.java b/src/main/java/com/kamco/cd/kamcoback/inference/dto/InferenceDetailDto.java index cd3a136a..50e51e8f 100644 --- a/src/main/java/com/kamco/cd/kamcoback/inference/dto/InferenceDetailDto.java +++ b/src/main/java/com/kamco/cd/kamcoback/inference/dto/InferenceDetailDto.java @@ -294,8 +294,10 @@ public class InferenceDetailDto { @Getter public static class Geom { + UUID uuid; Integer compareYyyy; Integer targetYyyy; + Double cdProb; String classBeforeCd; String classBeforeName; Double classBeforeProb; @@ -303,23 +305,29 @@ public class InferenceDetailDto { String classAfterName; Double classAfterProb; Long mapSheetNum; + String mapSheetName; @JsonIgnore String gemoStr; @JsonIgnore String geomCenterStr; JsonNode gemo; JsonNode geomCenter; public Geom( + UUID uuid, Integer compareYyyy, Integer targetYyyy, + Double cdProb, String classBeforeCd, Double classBeforeProb, String classAfterCd, Double classAfterProb, Long mapSheetNum, + String mapSheetName, String gemoStr, String geomCenterStr) { + this.uuid = uuid; this.compareYyyy = compareYyyy; this.targetYyyy = targetYyyy; + this.cdProb = cdProb; this.classBeforeCd = classBeforeCd; this.classBeforeName = DetectionClassification.fromString(classBeforeCd).getDesc(); this.classBeforeProb = classBeforeProb; @@ -327,6 +335,7 @@ public class InferenceDetailDto { this.classAfterName = DetectionClassification.fromString(classAfterCd).getDesc(); this.classAfterProb = classAfterProb; this.mapSheetNum = mapSheetNum; + this.mapSheetName = mapSheetName; this.gemoStr = gemoStr; this.geomCenterStr = geomCenterStr; diff --git a/src/main/java/com/kamco/cd/kamcoback/inference/service/InferenceResultService.java b/src/main/java/com/kamco/cd/kamcoback/inference/service/InferenceResultService.java index 71307c16..9a768285 100644 --- a/src/main/java/com/kamco/cd/kamcoback/inference/service/InferenceResultService.java +++ b/src/main/java/com/kamco/cd/kamcoback/inference/service/InferenceResultService.java @@ -10,7 +10,9 @@ import com.kamco.cd.kamcoback.inference.dto.InferenceDetailDto; import com.kamco.cd.kamcoback.inference.dto.InferenceDetailDto.AnalResultInfo; import com.kamco.cd.kamcoback.inference.dto.InferenceDetailDto.Dashboard; import com.kamco.cd.kamcoback.inference.dto.InferenceDetailDto.Detail; +import com.kamco.cd.kamcoback.inference.dto.InferenceDetailDto.Geom; import com.kamco.cd.kamcoback.inference.dto.InferenceDetailDto.MapSheet; +import com.kamco.cd.kamcoback.inference.dto.InferenceDetailDto.SearchGeoReq; import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto; import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.DetectOption; import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.InferenceServerStatusDto; @@ -486,4 +488,12 @@ public class InferenceResultService { public AnalResultInfo getInferenceResultInfo(String uuid) { return inferenceResultCoreService.getInferenceResultInfo(uuid); } + + public List getInferenceClassCountList(String uuid) { + return inferenceResultCoreService.getInferenceClassCountList(uuid); + } + + public Page getInferenceGeomList(String uuid, SearchGeoReq searchGeoReq) { + return inferenceResultCoreService.getInferenceGeomList(uuid, searchGeoReq); + } } diff --git a/src/main/java/com/kamco/cd/kamcoback/postgres/core/InferenceResultCoreService.java b/src/main/java/com/kamco/cd/kamcoback/postgres/core/InferenceResultCoreService.java index b77d7afa..d9956e90 100644 --- a/src/main/java/com/kamco/cd/kamcoback/postgres/core/InferenceResultCoreService.java +++ b/src/main/java/com/kamco/cd/kamcoback/postgres/core/InferenceResultCoreService.java @@ -5,8 +5,10 @@ import com.kamco.cd.kamcoback.common.utils.UserUtil; import com.kamco.cd.kamcoback.inference.dto.InferenceDetailDto; import com.kamco.cd.kamcoback.inference.dto.InferenceDetailDto.AnalResultInfo; import com.kamco.cd.kamcoback.inference.dto.InferenceDetailDto.Dashboard; +import com.kamco.cd.kamcoback.inference.dto.InferenceDetailDto.Geom; import com.kamco.cd.kamcoback.inference.dto.InferenceDetailDto.InferenceBatchSheet; import com.kamco.cd.kamcoback.inference.dto.InferenceDetailDto.MapSheet; +import com.kamco.cd.kamcoback.inference.dto.InferenceDetailDto.SearchGeoReq; import com.kamco.cd.kamcoback.inference.dto.InferenceProgressDto; import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto; import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.InferenceServerStatusDto; @@ -388,4 +390,12 @@ public class InferenceResultCoreService { public AnalResultInfo getInferenceResultInfo(String uuid) { return mapSheetLearnRepository.getInferenceResultInfo(uuid); } + + public List getInferenceClassCountList(String uuid) { + return mapSheetLearnRepository.getInferenceClassCountList(uuid); + } + + public Page getInferenceGeomList(String uuid, SearchGeoReq searchGeoReq) { + return mapSheetLearnRepository.getInferenceGeomList(uuid, searchGeoReq); + } } 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 fd6e985e..a5cd7f07 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 @@ -105,7 +105,43 @@ public class LabelAllocateCoreService { } public LabelerDetail findLabelerDetail(String userId, String uuid) { - return labelAllocateRepository.findLabelerDetail(userId, uuid); + // 1. 기본 정보 조회 (순위 0으로 들어옴) + LabelerDetail detail = labelAllocateRepository.findLabelerDetail(userId, uuid); + + if (detail == null) { + return null; + } + + // 2. 해당 회차의 모든 라벨러 정보 조회 (완료 건수 기준 내림차순) + // UUID를 analUid로 변환 + Long analUid = labelAllocateRepository.findAnalUidByUuid(uuid); + if (analUid == null) { + return detail; // analUid 없으면 순위 0으로 반환 + } + + List allLabelers = labelAllocateRepository.findAllLabelersForRanking(analUid); + + // 3. 순위 계산 (DENSE_RANK 로직: 동점자는 같은 순위, 다음 순위는 연속) + Long lastCompleteCnt = null; + int currentRank = 0; + + for (int i = 0; i < allLabelers.size(); i++) { + LabelerDetail labeler = allLabelers.get(i); + + // 완료 건수가 바뀌면 순위 갱신 + if (lastCompleteCnt == null || !lastCompleteCnt.equals(labeler.getCompleteCnt())) { + currentRank = i + 1; + lastCompleteCnt = labeler.getCompleteCnt(); + } + + // 현재 조회한 사용자의 순위 찾기 + if (labeler.getUserId().equals(userId)) { + detail.setRanking(currentRank); + break; + } + } + + return detail; } public Long findMapSheetAnalInferenceUid(Integer compareYyyy, Integer targetYyyy, Integer stage) { @@ -127,7 +163,43 @@ public class LabelAllocateCoreService { } public LabelerDetail findInspectorDetail(String userId, String uuid) { - return labelAllocateRepository.findInspectorDetail(userId, uuid); + // 1. 기본 정보 조회 (순위 0으로 들어옴) + LabelerDetail detail = labelAllocateRepository.findInspectorDetail(userId, uuid); + + if (detail == null) { + return null; + } + + // 2. 해당 회차의 모든 검수자 정보 조회 (완료 건수 기준 내림차순) + Long analUid = labelAllocateRepository.findAnalUidByUuid(uuid); + if (analUid == null) { + return detail; // analUid 없으면 순위 0으로 반환 + } + + List allInspectors = + labelAllocateRepository.findAllInspectorsForRanking(analUid); + + // 3. 순위 계산 (DENSE_RANK 로직: 동점자는 같은 순위, 다음 순위는 연속) + Long lastCompleteCnt = null; + int currentRank = 0; + + for (int i = 0; i < allInspectors.size(); i++) { + LabelerDetail inspector = allInspectors.get(i); + + // 완료 건수가 바뀌면 순위 갱신 + if (lastCompleteCnt == null || !lastCompleteCnt.equals(inspector.getCompleteCnt())) { + currentRank = i + 1; + lastCompleteCnt = inspector.getCompleteCnt(); + } + + // 현재 조회한 사용자의 순위 찾기 + if (inspector.getUserId().equals(userId)) { + detail.setRanking(currentRank); + break; + } + } + + return detail; } public MoveInfo moveAvailUserList(String userId, String uuid) { diff --git a/src/main/java/com/kamco/cd/kamcoback/postgres/repository/Inference/MapSheetLearnRepositoryCustom.java b/src/main/java/com/kamco/cd/kamcoback/postgres/repository/Inference/MapSheetLearnRepositoryCustom.java index 5e8ec142..ec5f5eda 100644 --- a/src/main/java/com/kamco/cd/kamcoback/postgres/repository/Inference/MapSheetLearnRepositoryCustom.java +++ b/src/main/java/com/kamco/cd/kamcoback/postgres/repository/Inference/MapSheetLearnRepositoryCustom.java @@ -1,6 +1,9 @@ package com.kamco.cd.kamcoback.postgres.repository.Inference; import com.kamco.cd.kamcoback.inference.dto.InferenceDetailDto.AnalResultInfo; +import com.kamco.cd.kamcoback.inference.dto.InferenceDetailDto.Dashboard; +import com.kamco.cd.kamcoback.inference.dto.InferenceDetailDto.Geom; +import com.kamco.cd.kamcoback.inference.dto.InferenceDetailDto.SearchGeoReq; import com.kamco.cd.kamcoback.inference.dto.InferenceProgressDto; import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto; import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.InferenceServerStatusDto; @@ -30,4 +33,8 @@ public interface MapSheetLearnRepositoryCustom { Integer getLearnStage(Integer compareYear, Integer targetYear); AnalResultInfo getInferenceResultInfo(String uuid); + + List getInferenceClassCountList(String uuid); + + Page getInferenceGeomList(String uuid, SearchGeoReq searchGeoReq); } diff --git a/src/main/java/com/kamco/cd/kamcoback/postgres/repository/Inference/MapSheetLearnRepositoryImpl.java b/src/main/java/com/kamco/cd/kamcoback/postgres/repository/Inference/MapSheetLearnRepositoryImpl.java index 2c471fee..40d5a570 100644 --- a/src/main/java/com/kamco/cd/kamcoback/postgres/repository/Inference/MapSheetLearnRepositoryImpl.java +++ b/src/main/java/com/kamco/cd/kamcoback/postgres/repository/Inference/MapSheetLearnRepositoryImpl.java @@ -1,24 +1,35 @@ package com.kamco.cd.kamcoback.postgres.repository.Inference; import static com.kamco.cd.kamcoback.postgres.entity.QGpuMetricEntity.gpuMetricEntity; +import static com.kamco.cd.kamcoback.postgres.entity.QMapInkx5kEntity.mapInkx5kEntity; +import static com.kamco.cd.kamcoback.postgres.entity.QMapSheetAnalDataInferenceGeomEntity.mapSheetAnalDataInferenceGeomEntity; +import static com.kamco.cd.kamcoback.postgres.entity.QMapSheetAnalInferenceEntity.mapSheetAnalInferenceEntity; +import static com.kamco.cd.kamcoback.postgres.entity.QMapSheetAnalSttcEntity.mapSheetAnalSttcEntity; import static com.kamco.cd.kamcoback.postgres.entity.QMapSheetLearnEntity.mapSheetLearnEntity; import static com.kamco.cd.kamcoback.postgres.entity.QSystemMetricEntity.systemMetricEntity; import com.kamco.cd.kamcoback.common.utils.DateRange; import com.kamco.cd.kamcoback.inference.dto.InferenceDetailDto.AnalResultInfo; +import com.kamco.cd.kamcoback.inference.dto.InferenceDetailDto.Dashboard; +import com.kamco.cd.kamcoback.inference.dto.InferenceDetailDto.Geom; +import com.kamco.cd.kamcoback.inference.dto.InferenceDetailDto.SearchGeoReq; import com.kamco.cd.kamcoback.inference.dto.InferenceProgressDto; import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto; import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.InferenceServerStatusDto; import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.InferenceStatusDetailDto; import com.kamco.cd.kamcoback.model.service.ModelMngService; +import com.kamco.cd.kamcoback.postgres.entity.MapSheetAnalInferenceEntity; import com.kamco.cd.kamcoback.postgres.entity.MapSheetLearnEntity; import com.kamco.cd.kamcoback.postgres.entity.QModelMngEntity; import com.querydsl.core.BooleanBuilder; import com.querydsl.core.types.Projections; import com.querydsl.core.types.dsl.CaseBuilder; +import com.querydsl.core.types.dsl.Expressions; import com.querydsl.core.types.dsl.NumberExpression; import com.querydsl.jpa.impl.JPAQueryFactory; +import jakarta.persistence.EntityNotFoundException; import java.util.List; +import java.util.Objects; import java.util.Optional; import java.util.UUID; import lombok.RequiredArgsConstructor; @@ -293,4 +304,149 @@ public class MapSheetLearnRepositoryImpl implements MapSheetLearnRepositoryCusto .where(mapSheetLearnEntity.uuid.eq(UUID.fromString(uuid))) .fetchOne(); } + + @Override + public List getInferenceClassCountList(String uuid) { + + // analUid로 분석 정보 조회 + MapSheetAnalInferenceEntity analEntity = + queryFactory + .selectFrom(mapSheetAnalInferenceEntity) + .where(mapSheetAnalInferenceEntity.uuid.eq(UUID.fromString(uuid))) + .fetchOne(); + + if (Objects.isNull(analEntity)) { + throw new EntityNotFoundException("MapSheetAnalInferenceEntity not found for analUid: "); + } + + return queryFactory + .select( + Projections.constructor( + Dashboard.class, + mapSheetAnalSttcEntity.id.classAfterCd, + mapSheetAnalSttcEntity.classAfterCnt.sum())) + .from(mapSheetAnalSttcEntity) + .where(mapSheetAnalSttcEntity.id.analUid.eq(analEntity.getId())) + .groupBy(mapSheetAnalSttcEntity.id.classAfterCd) + .orderBy(mapSheetAnalSttcEntity.id.classAfterCd.asc()) + .fetch(); + } + + /** + * 분석결과 상세 목록 + * + * @param searchGeoReq + * @return + */ + @Override + public Page getInferenceGeomList(String uuid, SearchGeoReq searchGeoReq) { + Pageable pageable = searchGeoReq.toPageable(); + BooleanBuilder builder = new BooleanBuilder(); + + // analUid로 분석 정보 조회 + MapSheetAnalInferenceEntity analEntity = + queryFactory + .selectFrom(mapSheetAnalInferenceEntity) + .where(mapSheetAnalInferenceEntity.uuid.eq(UUID.fromString(uuid))) + .fetchOne(); + + if (Objects.isNull(analEntity)) { + throw new EntityNotFoundException("MapSheetAnalInferenceEntity not found for analUid: "); + } + + // 추론결과 id + builder.and(mapSheetAnalInferenceEntity.id.eq(analEntity.getId())); + + // 기준년도 분류 + if (searchGeoReq.getTargetClass() != null && !searchGeoReq.getTargetClass().equals("")) { + builder.and( + mapSheetAnalDataInferenceGeomEntity + .classAfterCd + .toLowerCase() + .eq(searchGeoReq.getTargetClass().toLowerCase())); + } + + // 비교년도 분류 + if (searchGeoReq.getCompareClass() != null && !searchGeoReq.getCompareClass().equals("")) { + builder.and( + mapSheetAnalDataInferenceGeomEntity + .classBeforeCd + .toLowerCase() + .eq(searchGeoReq.getCompareClass().toLowerCase())); + } + + // 분석도엽 + if (searchGeoReq.getMapSheetNum() != null && !searchGeoReq.getMapSheetNum().isEmpty()) { + List mapSheetNum = searchGeoReq.getMapSheetNum(); + builder.and(mapSheetAnalDataInferenceGeomEntity.mapSheetNum.in(mapSheetNum)); + } + + List content = + queryFactory + .select( + Projections.constructor( + Geom.class, + mapSheetAnalDataInferenceGeomEntity.uuid, + mapSheetAnalDataInferenceGeomEntity.compareYyyy, + mapSheetAnalDataInferenceGeomEntity.targetYyyy, + mapSheetAnalDataInferenceGeomEntity.cdProb, + mapSheetAnalDataInferenceGeomEntity.classBeforeCd, + mapSheetAnalDataInferenceGeomEntity.classBeforeProb, + mapSheetAnalDataInferenceGeomEntity.classAfterCd, + mapSheetAnalDataInferenceGeomEntity.classAfterProb, + mapSheetAnalDataInferenceGeomEntity.mapSheetNum, + mapInkx5kEntity.mapidNm, + Expressions.stringTemplate( + "ST_AsGeoJSON({0})", mapSheetAnalDataInferenceGeomEntity.geom), + Expressions.stringTemplate( + "ST_AsGeoJSON({0})", mapSheetAnalDataInferenceGeomEntity.geomCenter))) + .from(mapSheetAnalInferenceEntity) + .join(mapSheetAnalDataInferenceGeomEntity) + .on( + mapSheetAnalDataInferenceGeomEntity.compareYyyy.eq( + mapSheetAnalInferenceEntity.compareYyyy), + mapSheetAnalDataInferenceGeomEntity.targetYyyy.eq( + mapSheetAnalInferenceEntity.targetYyyy), + mapSheetAnalDataInferenceGeomEntity.stage.eq(mapSheetAnalInferenceEntity.stage)) + .join(mapInkx5kEntity) + .on( + mapSheetAnalDataInferenceGeomEntity + .mapSheetNum + .stringValue() + .eq(mapInkx5kEntity.mapidcdNo)) + .where(builder) + .offset(pageable.getOffset()) + .limit(pageable.getPageSize()) + .fetch(); + + long total = + queryFactory + .select(mapSheetAnalDataInferenceGeomEntity.geoUid) + .from(mapSheetAnalInferenceEntity) + .join(mapSheetAnalDataInferenceGeomEntity) + .on( + mapSheetAnalDataInferenceGeomEntity.compareYyyy.eq( + mapSheetAnalInferenceEntity.compareYyyy), + mapSheetAnalDataInferenceGeomEntity.targetYyyy.eq( + mapSheetAnalInferenceEntity.targetYyyy), + mapSheetAnalDataInferenceGeomEntity.stage.eq(mapSheetAnalInferenceEntity.stage)) + .from(mapSheetAnalInferenceEntity) + .join(mapSheetAnalDataInferenceGeomEntity) + .on( + mapSheetAnalDataInferenceGeomEntity.compareYyyy.eq( + mapSheetAnalInferenceEntity.compareYyyy), + mapSheetAnalDataInferenceGeomEntity.targetYyyy.eq( + mapSheetAnalInferenceEntity.targetYyyy), + mapSheetAnalDataInferenceGeomEntity.stage.eq(mapSheetAnalInferenceEntity.stage)) + .join(mapInkx5kEntity) + .on( + mapSheetAnalDataInferenceGeomEntity + .mapSheetNum + .stringValue() + .eq(mapInkx5kEntity.mapidcdNo)) + .where(builder) + .fetchCount(); + + return new PageImpl<>(content, pageable, total); + } } 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 a43df4e1..bc05fe58 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 @@ -68,6 +68,12 @@ public interface LabelAllocateRepositoryCustom { LabelerDetail findLabelerDetail(String userId, String uuid); + /** 순위 계산용 - 특정 회차의 모든 라벨러 통계 조회 */ + List findAllLabelersForRanking(Long analUid); + + /** UUID로 analUid 조회 */ + Long findAnalUidByUuid(String uuid); + Long findMapSheetAnalInferenceUid(Integer compareYyyy, Integer targetYyyy, Integer stage); void insertInspector(Long analUid, String inspector); @@ -80,6 +86,9 @@ public interface LabelAllocateRepositoryCustom { LabelerDetail findInspectorDetail(String userId, String uuid); + /** 순위 계산용 - 특정 회차의 모든 검수자 통계 조회 */ + List findAllInspectorsForRanking(Long analUid); + MoveInfo moveAvailUserList(String userId, String uuid); void insertLabelerUser(Long analUid, String userId, int demand); 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 97f7e375..e3ec3ff6 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 @@ -777,11 +777,25 @@ public class LabelAllocateRepositoryImpl implements LabelAllocateRepositoryCusto @Override public LabelerDetail findLabelerDetail(String userId, String uuid) { + // analUid로 분석 정보 조회 + MapSheetAnalInferenceEntity analEntity = + queryFactory + .selectFrom(mapSheetAnalInferenceEntity) + .where(mapSheetAnalInferenceEntity.uuid.eq(UUID.fromString(uuid))) + .fetchOne(); + + if (Objects.isNull(analEntity)) { + throw new EntityNotFoundException("MapSheetAnalInferenceEntity not found for uuid: " + uuid); + } + + QMemberEntity worker = QMemberEntity.memberEntity; + QMemberEntity inspector = new QMemberEntity("inspector"); + NumberExpression totalCnt = labelingAssignmentEntity.assignmentUid.count(); - NumberExpression assignedCnt = + NumberExpression completeCnt = new CaseBuilder() - .when(labelingAssignmentEntity.workState.eq(LabelState.ASSIGNED.getId())) + .when(labelingAssignmentEntity.workState.eq(LabelState.DONE.getId())) .then(1L) .otherwise((Long) null) .count(); @@ -793,13 +807,6 @@ public class LabelAllocateRepositoryImpl implements LabelAllocateRepositoryCusto .otherwise((Long) null) .count(); - NumberExpression completeCnt = - new CaseBuilder() - .when(labelingAssignmentEntity.workState.eq(LabelState.DONE.getId())) - .then(1L) - .otherwise((Long) null) - .count(); - NumberExpression percent = new CaseBuilder() .when(completeCnt.eq(0L)) @@ -807,25 +814,10 @@ public class LabelAllocateRepositoryImpl implements LabelAllocateRepositoryCusto .otherwise( Expressions.numberTemplate( Double.class, - "round({0} / {1}, 2)", + "ROUND(({0} * 1.0 / NULLIF({1}, 0)) * 100, 2)", completeCnt, - labelingAssignmentEntity.count())); + totalCnt)); - // analUid로 분석 정보 조회 - MapSheetAnalInferenceEntity analEntity = - queryFactory - .selectFrom(mapSheetAnalInferenceEntity) - .where(mapSheetAnalInferenceEntity.uuid.eq(UUID.fromString(uuid))) - .fetchOne(); - - if (Objects.isNull(analEntity)) { - throw new EntityNotFoundException("MapSheetAnalInferenceEntity not found for analUid: "); - } - - QMemberEntity worker = QMemberEntity.memberEntity; - QMemberEntity inspector = new QMemberEntity("inspector"); - - // remainCnt Expression remainCnt = Expressions.numberTemplate(Long.class, "({0} - {1} - {2})", totalCnt, skipCnt, completeCnt); @@ -840,7 +832,7 @@ public class LabelAllocateRepositoryImpl implements LabelAllocateRepositoryCusto completeCnt, skipCnt, percent, - Expressions.constant(0), // TODO: 순위, 꼭 해야할지? + Expressions.constant(0), // 순위는 Service 레이어에서 계산 labelingAssignmentEntity.workStatDttm.min(), inspector.name.min(), remainCnt)) @@ -856,6 +848,64 @@ public class LabelAllocateRepositoryImpl implements LabelAllocateRepositoryCusto .fetchOne(); } + /** 특정 회차의 모든 라벨러 통계 조회 (순위 계산용) */ + public List findAllLabelersForRanking(Long analUid) { + QMemberEntity worker = QMemberEntity.memberEntity; + + NumberExpression totalCnt = labelingAssignmentEntity.assignmentUid.count(); + + NumberExpression completeCnt = + new CaseBuilder() + .when(labelingAssignmentEntity.workState.eq(LabelState.DONE.getId())) + .then(1L) + .otherwise((Long) null) + .count(); + + NumberExpression skipCnt = + new CaseBuilder() + .when(labelingAssignmentEntity.workState.eq(LabelState.SKIP.getId())) + .then(1L) + .otherwise((Long) null) + .count(); + + Expression remainCnt = + Expressions.numberTemplate(Long.class, "({0} - {1} - {2})", totalCnt, skipCnt, completeCnt); + + return queryFactory + .select( + Projections.constructor( + LabelerDetail.class, + worker.userRole, + worker.name, + worker.employeeNo, + totalCnt, + completeCnt, + skipCnt, + Expressions.constant(0.0), // percent + Expressions.constant(0), // ranking + labelingAssignmentEntity.workStatDttm.min(), + Expressions.nullExpression(String.class), // ownerName + remainCnt)) + .from(worker) + .innerJoin(labelingAssignmentEntity) + .on( + worker.employeeNo.eq(labelingAssignmentEntity.workerUid), + labelingAssignmentEntity.analUid.eq(analUid)) + .groupBy(worker.userRole, worker.name, worker.employeeNo) + .orderBy(completeCnt.desc()) // 완료 건수 내림차순 + .fetch(); + } + + /** UUID로 analUid 조회 */ + @Override + public Long findAnalUidByUuid(String uuid) { + return queryFactory + .select(mapSheetAnalInferenceEntity.id) + .from(mapSheetAnalInferenceEntity) + .where(mapSheetAnalInferenceEntity.uuid.eq(UUID.fromString(uuid))) + .fetchOne(); + } + @Override public Long findMapSheetAnalInferenceUid(Integer compareYyyy, Integer targetYyyy, Integer stage) { return queryFactory @@ -1301,11 +1351,11 @@ public class LabelAllocateRepositoryImpl implements LabelAllocateRepositoryCusto inspector.userRole, inspector.name, inspector.employeeNo, - assignedCnt, - skipCnt, - completeCnt, + assignedCnt, // count (총 배정 건수) + completeCnt, // completeCnt (완료 건수) + skipCnt, // skipCnt (스킵 건수) percent, - Expressions.constant(0), // TODO: 순위, 꼭 해야할지? + Expressions.constant(0), // 순위는 Service 레이어에서 계산 labelingAssignmentEntity.inspectStatDttm.min(), worker.name.min(), remainCnt)) @@ -1321,6 +1371,60 @@ public class LabelAllocateRepositoryImpl implements LabelAllocateRepositoryCusto .fetchOne(); } + /** 순위 계산용 - 특정 회차의 모든 검수자 통계 조회 */ + public List findAllInspectorsForRanking(Long analUid) { + QMemberEntity inspector = QMemberEntity.memberEntity; + + NumberExpression assignedCnt = + new CaseBuilder() + .when(labelingAssignmentEntity.inspectState.eq(InspectState.UNCONFIRM.getId())) + .then(1L) + .otherwise((Long) null) + .count(); + + NumberExpression skipCnt = + new CaseBuilder() + .when(labelingAssignmentEntity.inspectState.eq(InspectState.EXCEPT.getId())) + .then(1L) + .otherwise((Long) null) + .count(); + + NumberExpression completeCnt = + new CaseBuilder() + .when(labelingAssignmentEntity.inspectState.eq(InspectState.COMPLETE.getId())) + .then(1L) + .otherwise((Long) null) + .count(); + + Expression remainCnt = + Expressions.numberTemplate( + Long.class, "({0} - {1} - {2})", assignedCnt, skipCnt, completeCnt); + + return queryFactory + .select( + Projections.constructor( + LabelerDetail.class, + inspector.userRole, + inspector.name, + inspector.employeeNo, + assignedCnt, // count (총 배정 건수) + completeCnt, // completeCnt (완료 건수) + skipCnt, // skipCnt (스킵 건수) + Expressions.constant(0.0), // percent + Expressions.constant(0), // ranking + labelingAssignmentEntity.inspectStatDttm.min(), + Expressions.nullExpression(String.class), // ownerName + remainCnt)) + .from(inspector) + .innerJoin(labelingAssignmentEntity) + .on( + inspector.employeeNo.eq(labelingAssignmentEntity.inspectorUid), + labelingAssignmentEntity.analUid.eq(analUid)) + .groupBy(inspector.userRole, inspector.name, inspector.employeeNo) + .orderBy(completeCnt.desc()) // 완료 건수 내림차순 + .fetch(); + } + @Override public MoveInfo moveAvailUserList(String userId, String uuid) { diff --git a/src/main/java/com/kamco/cd/kamcoback/postgres/repository/trainingdata/TrainingDataLabelRepositoryImpl.java b/src/main/java/com/kamco/cd/kamcoback/postgres/repository/trainingdata/TrainingDataLabelRepositoryImpl.java index 9a0b3f34..b5fa25ca 100644 --- a/src/main/java/com/kamco/cd/kamcoback/postgres/repository/trainingdata/TrainingDataLabelRepositoryImpl.java +++ b/src/main/java/com/kamco/cd/kamcoback/postgres/repository/trainingdata/TrainingDataLabelRepositoryImpl.java @@ -472,6 +472,10 @@ public class TrainingDataLabelRepositoryImpl extends QuerydslRepositorySupport mapSheetAnalDataInferenceGeomEntityEntity.getPnu() != null ? mapSheetAnalDataInferenceGeomEntityEntity.getPnu() : 0L) + .mapSheetNum( + mapSheetAnalDataInferenceGeomEntityEntity.getMapSheetNum() != null + ? mapSheetAnalDataInferenceGeomEntityEntity.getMapSheetNum() + : 0L) .build(); var inspectionResultInfo = diff --git a/src/main/java/com/kamco/cd/kamcoback/postgres/repository/trainingdata/TrainingDataReviewRepositoryImpl.java b/src/main/java/com/kamco/cd/kamcoback/postgres/repository/trainingdata/TrainingDataReviewRepositoryImpl.java index fd3e8e4c..956e336b 100644 --- a/src/main/java/com/kamco/cd/kamcoback/postgres/repository/trainingdata/TrainingDataReviewRepositoryImpl.java +++ b/src/main/java/com/kamco/cd/kamcoback/postgres/repository/trainingdata/TrainingDataReviewRepositoryImpl.java @@ -491,6 +491,10 @@ public class TrainingDataReviewRepositoryImpl extends QuerydslRepositorySupport mapSheetAnalDataInferenceGeomEntityEntity.getPnu() != null ? mapSheetAnalDataInferenceGeomEntityEntity.getPnu() : 0L) + .mapSheetNum( + mapSheetAnalDataInferenceGeomEntityEntity.getMapSheetNum() != null + ? mapSheetAnalDataInferenceGeomEntityEntity.getMapSheetNum() + : 0L) .build(); var inspectionResultInfo = diff --git a/src/main/java/com/kamco/cd/kamcoback/trainingdata/dto/TrainingDataLabelDto.java b/src/main/java/com/kamco/cd/kamcoback/trainingdata/dto/TrainingDataLabelDto.java index 83b5f13d..7be9ee6c 100644 --- a/src/main/java/com/kamco/cd/kamcoback/trainingdata/dto/TrainingDataLabelDto.java +++ b/src/main/java/com/kamco/cd/kamcoback/trainingdata/dto/TrainingDataLabelDto.java @@ -331,6 +331,9 @@ public class TrainingDataLabelDto { @Schema(description = "PNU (필지고유번호)", example = "36221202306020") private Long pnu; + + @Schema(description = "도엽번호 (map_sheet_num)", example = "34602057") + private Long mapSheetNum; } @Schema(name = "ClassificationInfo", description = "분류정보") diff --git a/src/main/java/com/kamco/cd/kamcoback/trainingdata/dto/TrainingDataReviewDto.java b/src/main/java/com/kamco/cd/kamcoback/trainingdata/dto/TrainingDataReviewDto.java index a82b7c53..35176897 100644 --- a/src/main/java/com/kamco/cd/kamcoback/trainingdata/dto/TrainingDataReviewDto.java +++ b/src/main/java/com/kamco/cd/kamcoback/trainingdata/dto/TrainingDataReviewDto.java @@ -337,6 +337,9 @@ public class TrainingDataReviewDto { @Schema(description = "PNU (필지고유번호)", example = "36221202306020") private Long pnu; + + @Schema(description = "도엽번호 (map_sheet_num)", example = "34602057") + private Long mapSheetNum; } @Schema(name = "ClassificationInfo", description = "분류정보")