From d83fb01cbe92130b296a5ef799baab628f28af11 Mon Sep 17 00:00:00 2001 From: "gayoun.park" Date: Mon, 20 Apr 2026 16:37:22 +0900 Subject: [PATCH] =?UTF-8?q?=ED=95=99=EC=8A=B5=EB=8D=B0=EC=9D=B4=ED=84=B0?= =?UTF-8?q?=20=EB=AA=A9=EB=A1=9D=20=EC=BF=BC=EB=A6=AC=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cd/kamcoback/label/dto/LabelWorkDto.java | 15 + .../label/LabelWorkRepositoryImpl.java | 257 +++++++++--------- 2 files changed, 144 insertions(+), 128 deletions(-) diff --git a/src/main/java/com/kamco/cd/kamcoback/label/dto/LabelWorkDto.java b/src/main/java/com/kamco/cd/kamcoback/label/dto/LabelWorkDto.java index b9af35e6..8daae725 100644 --- a/src/main/java/com/kamco/cd/kamcoback/label/dto/LabelWorkDto.java +++ b/src/main/java/com/kamco/cd/kamcoback/label/dto/LabelWorkDto.java @@ -34,6 +34,7 @@ public class LabelWorkDto { @AllArgsConstructor public static class LabelWorkMng { + private Long analUid; private UUID uuid; private Integer compareYyyy; private Integer targetYyyy; @@ -260,4 +261,18 @@ public class LabelWorkDto { return PageRequest.of(page, size); } } + + @Getter + @Setter + @AllArgsConstructor + @NoArgsConstructor + public static class LabelCntInfo { + + private Long assignedCnt; + private Long skipCnt; + private Long doneCnt; + private Long unconfirmCnt; + private Long exceptCnt; + private Long completeCnt; + } } diff --git a/src/main/java/com/kamco/cd/kamcoback/postgres/repository/label/LabelWorkRepositoryImpl.java b/src/main/java/com/kamco/cd/kamcoback/postgres/repository/label/LabelWorkRepositoryImpl.java index 9a578244..d67acbe1 100644 --- a/src/main/java/com/kamco/cd/kamcoback/postgres/repository/label/LabelWorkRepositoryImpl.java +++ b/src/main/java/com/kamco/cd/kamcoback/postgres/repository/label/LabelWorkRepositoryImpl.java @@ -7,6 +7,7 @@ import com.kamco.cd.kamcoback.label.dto.LabelAllocateDto.InspectState; import com.kamco.cd.kamcoback.label.dto.LabelAllocateDto.LabelMngState; import com.kamco.cd.kamcoback.label.dto.LabelAllocateDto.LabelState; import com.kamco.cd.kamcoback.label.dto.LabelWorkDto; +import com.kamco.cd.kamcoback.label.dto.LabelWorkDto.LabelCntInfo; import com.kamco.cd.kamcoback.label.dto.LabelWorkDto.LabelWorkMng; import com.kamco.cd.kamcoback.label.dto.LabelWorkDto.LabelWorkMngDetail; import com.kamco.cd.kamcoback.label.dto.LabelWorkDto.WorkerState; @@ -18,7 +19,6 @@ import com.kamco.cd.kamcoback.postgres.entity.QMapSheetAnalDataInferenceGeomEnti import com.kamco.cd.kamcoback.postgres.entity.QMapSheetAnalInferenceEntity; import com.kamco.cd.kamcoback.postgres.entity.QMemberEntity; import com.querydsl.core.BooleanBuilder; -import com.querydsl.core.types.Expression; import com.querydsl.core.types.OrderSpecifier; import com.querydsl.core.types.Projections; import com.querydsl.core.types.dsl.BooleanExpression; @@ -86,7 +86,7 @@ public class LabelWorkRepositoryImpl implements LabelWorkRepositoryCustom { } /** - * 라벨링 작업관리 목록 조회 (복잡한 집계 쿼리로 인해 DTO 직접 반환) + * 라벨링 작업관리 목록 조회 :: 전체 geom 집계 -> inference 먼저 쿼리 하고 count 정보 따로 집계하도록 수정 * * @param searchReq 검색 조건 * @return 라벨링 작업관리 목록 페이지 @@ -95,9 +95,8 @@ public class LabelWorkRepositoryImpl implements LabelWorkRepositoryCustom { public Page labelWorkMngList(LabelWorkDto.LabelWorkMngSearchReq searchReq) { Pageable pageable = PageRequest.of(searchReq.getPage(), searchReq.getSize()); - BooleanBuilder whereBuilder = new BooleanBuilder(); - BooleanBuilder whereSubDataBuilder = new BooleanBuilder(); - BooleanBuilder whereSubBuilder = new BooleanBuilder(); + + BooleanBuilder baseWhereBuilder = new BooleanBuilder(); if (StringUtils.isNotBlank(searchReq.getDetectYear())) { String[] years = searchReq.getDetectYear().split("-"); @@ -106,67 +105,14 @@ public class LabelWorkRepositoryImpl implements LabelWorkRepositoryCustom { Integer compareYear = Integer.valueOf(years[0]); Integer targetYear = Integer.valueOf(years[1]); - whereBuilder.and( - mapSheetAnalDataInferenceEntity + baseWhereBuilder.and( + mapSheetAnalInferenceEntity .compareYyyy .eq(compareYear) - .and(mapSheetAnalDataInferenceEntity.targetYyyy.eq(targetYear))); + .and(mapSheetAnalInferenceEntity.targetYyyy.eq(targetYear))); } } - whereSubDataBuilder.and( - mapSheetAnalInferenceEntity.id.eq(mapSheetAnalDataInferenceEntity.analUid)); - - whereSubBuilder.and( - mapSheetAnalDataInferenceGeomEntity.dataUid.eq(mapSheetAnalDataInferenceEntity.id)); - - if (searchReq.getStrtDttm() != null && searchReq.getEndDttm() != null) { - - ZoneId zoneId = ZoneId.of("Asia/Seoul"); - - ZonedDateTime start = searchReq.getStrtDttm().atStartOfDay(zoneId); - - ZonedDateTime end = searchReq.getEndDttm().plusDays(1).atStartOfDay(zoneId); - - whereSubBuilder.and( - mapSheetAnalDataInferenceGeomEntity - .labelStateDttm - .goe(start) - .and(mapSheetAnalDataInferenceGeomEntity.labelStateDttm.lt(end))); - } - - // labelTotCnt: pnu가 있고 fitState = UNFIT 인 건수만 라벨링 대상 - NumberExpression labelTotCntExpr = - new CaseBuilder() - .when( - mapSheetAnalDataInferenceGeomEntity - .pnu - .gt(0) - .and( - mapSheetAnalDataInferenceGeomEntity.fitState.eq( - ImageryFitStatus.UNFIT.getId()))) - .then(1L) - .otherwise(0L) - .sum(); - - // stagnation_yn = 'N'인 정상 진행 건수 (서브쿼리로 별도 집계) - Expression normalProgressCntSubQuery = - JPAExpressions.select( - new CaseBuilder() - .when(labelingAssignmentEntity.stagnationYn.eq('N')) - .then(1L) - .otherwise(0L) - .sum() - .coalesce(0L)) - .from(labelingAssignmentEntity) - .where(labelingAssignmentEntity.analUid.eq(mapSheetAnalInferenceEntity.id)); - - // 총 배정 건수 (서브쿼리로 별도 집계) - Expression totalAssignmentCntSubQuery = - JPAExpressions.select(labelingAssignmentEntity.count().coalesce(0L)) - .from(labelingAssignmentEntity) - .where(labelingAssignmentEntity.analUid.eq(mapSheetAnalInferenceEntity.id)); - /** 순서 ING 진행중 ASSIGNED 작업할당 PENDING 작업대기 FINISH 종료 */ NumberExpression stateOrder = new CaseBuilder() @@ -180,64 +126,31 @@ public class LabelWorkRepositoryImpl implements LabelWorkRepositoryCustom { .then(3) .otherwise(99); + // 1단계: anal_inference 기준으로 상위 페이지 목록만 조회 List foundContent = queryFactory .select( Projections.constructor( LabelWorkMng.class, + mapSheetAnalInferenceEntity.id, // analUid 추가 mapSheetAnalInferenceEntity.uuid, mapSheetAnalInferenceEntity.compareYyyy, mapSheetAnalInferenceEntity.targetYyyy, mapSheetAnalInferenceEntity.stage, - // 국유인 반영 컬럼 gukyuinApplyDttm mapSheetAnalInferenceEntity.gukyuinApplyDttm, - mapSheetAnalDataInferenceGeomEntity.dataUid.count(), - // labelTotCnt: pnu 있고 pass_yn = false인 건수 - labelTotCntExpr, - new CaseBuilder() - .when( - mapSheetAnalDataInferenceGeomEntity.labelState.eq( - LabelState.ASSIGNED.getId())) - .then(1L) - .otherwise(0L) - .sum(), - new CaseBuilder() - .when( - mapSheetAnalDataInferenceGeomEntity.labelState.eq( - LabelState.SKIP.getId())) // "STOP"? - .then(1L) - .otherwise(0L) - .sum(), - new CaseBuilder() - .when( - mapSheetAnalDataInferenceGeomEntity.labelState.eq( - LabelState.DONE.getId())) - .then(1L) - .otherwise(0L) - .sum(), - mapSheetAnalDataInferenceGeomEntity.labelStateDttm.min(), - // analState: tb_map_sheet_anal_inference.anal_state + mapSheetAnalInferenceEntity.detectingCnt, + Expressions.constant(0L), + Expressions.constant(0L), + Expressions.constant(0L), + Expressions.constant(0L), + mapSheetAnalInferenceEntity.createdDttm, mapSheetAnalInferenceEntity.analState, - // normalProgressCnt: stagnation_yn = 'N'인 건수 (서브쿼리) - normalProgressCntSubQuery, - // totalAssignmentCnt: 총 배정 건수 (서브쿼리) - totalAssignmentCntSubQuery, + Expressions.constant(0L), + Expressions.constant(0L), mapSheetAnalInferenceEntity.labelingClosedYn, mapSheetAnalInferenceEntity.inspectionClosedYn, - new CaseBuilder() - .when( - mapSheetAnalDataInferenceGeomEntity.testState.eq( - InspectState.COMPLETE.getId())) - .then(1L) - .otherwise(0L) - .sum(), - new CaseBuilder() - .when( - mapSheetAnalDataInferenceGeomEntity.testState.eq( - InspectState.UNCONFIRM.getId())) - .then(1L) - .otherwise(0L) - .sum(), + Expressions.constant(0L), + Expressions.constant(0L), new CaseBuilder() .when(mapSheetAnalInferenceEntity.inspectionClosedYn.eq("Y")) .then(mapSheetAnalInferenceEntity.updatedDttm) @@ -247,23 +160,9 @@ public class LabelWorkRepositoryImpl implements LabelWorkRepositoryCustom { "substring({0} from 1 for 8)", mapSheetLearnEntity.uid), mapSheetLearnEntity.uuid)) .from(mapSheetAnalInferenceEntity) - .innerJoin(mapSheetAnalDataInferenceEntity) - .on(whereSubDataBuilder) - .innerJoin(mapSheetAnalDataInferenceGeomEntity) - .on(whereSubBuilder) .leftJoin(mapSheetLearnEntity) .on(mapSheetAnalInferenceEntity.learnId.eq(mapSheetLearnEntity.id)) - .where(whereBuilder) - .groupBy( - mapSheetAnalInferenceEntity.uuid, - mapSheetAnalInferenceEntity.compareYyyy, - mapSheetAnalInferenceEntity.targetYyyy, - mapSheetAnalInferenceEntity.stage, - mapSheetAnalInferenceEntity.createdDttm, - mapSheetAnalInferenceEntity.analState, - mapSheetAnalInferenceEntity.id, - mapSheetLearnEntity.uid, - mapSheetLearnEntity.uuid) + .where(baseWhereBuilder) .orderBy( stateOrder.asc(), mapSheetAnalInferenceEntity.targetYyyy.desc(), @@ -273,20 +172,122 @@ public class LabelWorkRepositoryImpl implements LabelWorkRepositoryCustom { .limit(pageable.getPageSize()) .fetch(); - // Count 쿼리 별도 실행 (null safe handling) + // total count long total = Optional.ofNullable( queryFactory - .select(mapSheetAnalInferenceEntity.uuid.countDistinct()) + .select(mapSheetAnalInferenceEntity.count()) .from(mapSheetAnalInferenceEntity) - .innerJoin(mapSheetAnalDataInferenceEntity) - .on(whereSubDataBuilder) - .innerJoin(mapSheetAnalDataInferenceGeomEntity) - .on(whereSubBuilder) - .where(whereBuilder) + .where(baseWhereBuilder) .fetchOne()) .orElse(0L); + // 2단계: 각 analUid별 count 조회 후 세팅 + for (LabelWorkMng item : foundContent) { + Long analUid = item.getAnalUid(); + + BooleanBuilder geomWhereBuilder = new BooleanBuilder(); + geomWhereBuilder.and(mapSheetAnalDataInferenceEntity.analUid.eq(analUid)); + geomWhereBuilder.and( + mapSheetAnalDataInferenceGeomEntity.dataUid.eq(mapSheetAnalDataInferenceEntity.id)); + + if (searchReq.getStrtDttm() != null && searchReq.getEndDttm() != null) { + ZoneId zoneId = ZoneId.of("Asia/Seoul"); + ZonedDateTime start = searchReq.getStrtDttm().atStartOfDay(zoneId); + ZonedDateTime end = searchReq.getEndDttm().plusDays(1).atStartOfDay(zoneId); + + geomWhereBuilder.and( + mapSheetAnalDataInferenceGeomEntity + .createdDttm + .goe(start) + .and(mapSheetAnalDataInferenceGeomEntity.createdDttm.lt(end))); + } + + // labelTotCnt: pnu > 0 and fitState = UNFIT + Long labelTotCnt = + Optional.ofNullable( + queryFactory + .select(mapSheetAnalDataInferenceGeomEntity.count()) + .from(mapSheetAnalDataInferenceEntity) + .innerJoin(mapSheetAnalDataInferenceGeomEntity) + .on( + mapSheetAnalDataInferenceGeomEntity.dataUid.eq( + mapSheetAnalDataInferenceEntity.id)) + .where( + geomWhereBuilder, + mapSheetAnalDataInferenceGeomEntity.pnu.gt(0), + mapSheetAnalDataInferenceGeomEntity.fitState.eq( + ImageryFitStatus.UNFIT.getId())) + .fetchOne()) + .orElse(0L); + + LabelCntInfo cntInfo = + queryFactory + .select( + Projections.constructor( + LabelCntInfo.class, + new CaseBuilder() + .when(labelingAssignmentEntity.workState.eq(LabelState.ASSIGNED.getId())) + .then(1L) + .otherwise(0L) + .sum() + .coalesce(0L), + new CaseBuilder() + .when(labelingAssignmentEntity.workState.eq(LabelState.SKIP.getId())) + .then(1L) + .otherwise(0L) + .sum() + .coalesce(0L), + new CaseBuilder() + .when(labelingAssignmentEntity.workState.eq(LabelState.DONE.getId())) + .then(1L) + .otherwise(0L) + .sum() + .coalesce(0L), + new CaseBuilder() + .when( + labelingAssignmentEntity.inspectState.eq( + InspectState.UNCONFIRM.getId())) + .then(1L) + .otherwise(0L) + .sum() + .coalesce(0L), + new CaseBuilder() + .when( + labelingAssignmentEntity.inspectState.eq(InspectState.EXCEPT.getId())) + .then(1L) + .otherwise(0L) + .sum() + .coalesce(0L), + new CaseBuilder() + .when( + labelingAssignmentEntity.inspectState.eq( + InspectState.COMPLETE.getId())) + .then(1L) + .otherwise(0L) + .sum() + .coalesce(0L))) + .from(labelingAssignmentEntity) + .where(labelingAssignmentEntity.analUid.eq(analUid)) + .fetchOne(); + + Long normalProgressCnt = 0L; + Long totalAssignmentCnt = 0L; + + if (cntInfo == null) { + cntInfo = new LabelCntInfo(0L, 0L, 0L, 0L, 0L, 0L); + } + + item.setLabelTotCnt(labelTotCnt); + item.setLabelAssignCnt(cntInfo.getAssignedCnt()); + item.setLabelSkipTotCnt(cntInfo.getSkipCnt()); + item.setLabelCompleteTotCnt(cntInfo.getDoneCnt()); + item.setNormalProgressCnt(normalProgressCnt); + item.setTotalAssignmentCnt(totalAssignmentCnt); + item.setInspectorCompleteTotCnt(cntInfo.getCompleteCnt()); + item.setInspectorRemainCnt(cntInfo.getUnconfirmCnt()); + } + return new PageImpl<>(foundContent, pageable, total); }