학습데이터 목록 쿼리 수정

This commit is contained in:
2026-04-20 16:37:22 +09:00
parent 0425a6486d
commit d83fb01cbe
2 changed files with 144 additions and 128 deletions

View File

@@ -34,6 +34,7 @@ public class LabelWorkDto {
@AllArgsConstructor @AllArgsConstructor
public static class LabelWorkMng { public static class LabelWorkMng {
private Long analUid;
private UUID uuid; private UUID uuid;
private Integer compareYyyy; private Integer compareYyyy;
private Integer targetYyyy; private Integer targetYyyy;
@@ -260,4 +261,18 @@ public class LabelWorkDto {
return PageRequest.of(page, size); 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;
}
} }

View File

@@ -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.LabelMngState;
import com.kamco.cd.kamcoback.label.dto.LabelAllocateDto.LabelState; 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;
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.LabelWorkMng;
import com.kamco.cd.kamcoback.label.dto.LabelWorkDto.LabelWorkMngDetail; import com.kamco.cd.kamcoback.label.dto.LabelWorkDto.LabelWorkMngDetail;
import com.kamco.cd.kamcoback.label.dto.LabelWorkDto.WorkerState; 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.QMapSheetAnalInferenceEntity;
import com.kamco.cd.kamcoback.postgres.entity.QMemberEntity; import com.kamco.cd.kamcoback.postgres.entity.QMemberEntity;
import com.querydsl.core.BooleanBuilder; import com.querydsl.core.BooleanBuilder;
import com.querydsl.core.types.Expression;
import com.querydsl.core.types.OrderSpecifier; import com.querydsl.core.types.OrderSpecifier;
import com.querydsl.core.types.Projections; import com.querydsl.core.types.Projections;
import com.querydsl.core.types.dsl.BooleanExpression; import com.querydsl.core.types.dsl.BooleanExpression;
@@ -86,7 +86,7 @@ public class LabelWorkRepositoryImpl implements LabelWorkRepositoryCustom {
} }
/** /**
* 라벨링 작업관리 목록 조회 (복잡한 집계 쿼리로 인해 DTO 직접 반환) * 라벨링 작업관리 목록 조회 :: 전체 geom 집계 -> inference 먼저 쿼리 하고 count 정보 따로 집계하도록 수정
* *
* @param searchReq 검색 조건 * @param searchReq 검색 조건
* @return 라벨링 작업관리 목록 페이지 * @return 라벨링 작업관리 목록 페이지
@@ -95,9 +95,8 @@ public class LabelWorkRepositoryImpl implements LabelWorkRepositoryCustom {
public Page<LabelWorkMng> labelWorkMngList(LabelWorkDto.LabelWorkMngSearchReq searchReq) { public Page<LabelWorkMng> labelWorkMngList(LabelWorkDto.LabelWorkMngSearchReq searchReq) {
Pageable pageable = PageRequest.of(searchReq.getPage(), searchReq.getSize()); Pageable pageable = PageRequest.of(searchReq.getPage(), searchReq.getSize());
BooleanBuilder whereBuilder = new BooleanBuilder();
BooleanBuilder whereSubDataBuilder = new BooleanBuilder(); BooleanBuilder baseWhereBuilder = new BooleanBuilder();
BooleanBuilder whereSubBuilder = new BooleanBuilder();
if (StringUtils.isNotBlank(searchReq.getDetectYear())) { if (StringUtils.isNotBlank(searchReq.getDetectYear())) {
String[] years = searchReq.getDetectYear().split("-"); String[] years = searchReq.getDetectYear().split("-");
@@ -106,67 +105,14 @@ public class LabelWorkRepositoryImpl implements LabelWorkRepositoryCustom {
Integer compareYear = Integer.valueOf(years[0]); Integer compareYear = Integer.valueOf(years[0]);
Integer targetYear = Integer.valueOf(years[1]); Integer targetYear = Integer.valueOf(years[1]);
whereBuilder.and( baseWhereBuilder.and(
mapSheetAnalDataInferenceEntity mapSheetAnalInferenceEntity
.compareYyyy .compareYyyy
.eq(compareYear) .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<Long> labelTotCntExpr =
new CaseBuilder()
.when(
mapSheetAnalDataInferenceGeomEntity
.pnu
.gt(0)
.and(
mapSheetAnalDataInferenceGeomEntity.fitState.eq(
ImageryFitStatus.UNFIT.getId())))
.then(1L)
.otherwise(0L)
.sum();
// stagnation_yn = 'N'인 정상 진행 건수 (서브쿼리로 별도 집계)
Expression<Long> 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<Long> totalAssignmentCntSubQuery =
JPAExpressions.select(labelingAssignmentEntity.count().coalesce(0L))
.from(labelingAssignmentEntity)
.where(labelingAssignmentEntity.analUid.eq(mapSheetAnalInferenceEntity.id));
/** 순서 ING 진행중 ASSIGNED 작업할당 PENDING 작업대기 FINISH 종료 */ /** 순서 ING 진행중 ASSIGNED 작업할당 PENDING 작업대기 FINISH 종료 */
NumberExpression<Integer> stateOrder = NumberExpression<Integer> stateOrder =
new CaseBuilder() new CaseBuilder()
@@ -180,64 +126,31 @@ public class LabelWorkRepositoryImpl implements LabelWorkRepositoryCustom {
.then(3) .then(3)
.otherwise(99); .otherwise(99);
// 1단계: anal_inference 기준으로 상위 페이지 목록만 조회
List<LabelWorkMng> foundContent = List<LabelWorkMng> foundContent =
queryFactory queryFactory
.select( .select(
Projections.constructor( Projections.constructor(
LabelWorkMng.class, LabelWorkMng.class,
mapSheetAnalInferenceEntity.id, // analUid 추가
mapSheetAnalInferenceEntity.uuid, mapSheetAnalInferenceEntity.uuid,
mapSheetAnalInferenceEntity.compareYyyy, mapSheetAnalInferenceEntity.compareYyyy,
mapSheetAnalInferenceEntity.targetYyyy, mapSheetAnalInferenceEntity.targetYyyy,
mapSheetAnalInferenceEntity.stage, mapSheetAnalInferenceEntity.stage,
// 국유인 반영 컬럼 gukyuinApplyDttm
mapSheetAnalInferenceEntity.gukyuinApplyDttm, mapSheetAnalInferenceEntity.gukyuinApplyDttm,
mapSheetAnalDataInferenceGeomEntity.dataUid.count(), mapSheetAnalInferenceEntity.detectingCnt,
// labelTotCnt: pnu 있고 pass_yn = false인 건수 Expressions.constant(0L),
labelTotCntExpr, Expressions.constant(0L),
new CaseBuilder() Expressions.constant(0L),
.when( Expressions.constant(0L),
mapSheetAnalDataInferenceGeomEntity.labelState.eq( mapSheetAnalInferenceEntity.createdDttm,
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.analState, mapSheetAnalInferenceEntity.analState,
// normalProgressCnt: stagnation_yn = 'N'인 건수 (서브쿼리) Expressions.constant(0L),
normalProgressCntSubQuery, Expressions.constant(0L),
// totalAssignmentCnt: 총 배정 건수 (서브쿼리)
totalAssignmentCntSubQuery,
mapSheetAnalInferenceEntity.labelingClosedYn, mapSheetAnalInferenceEntity.labelingClosedYn,
mapSheetAnalInferenceEntity.inspectionClosedYn, mapSheetAnalInferenceEntity.inspectionClosedYn,
new CaseBuilder() Expressions.constant(0L),
.when( Expressions.constant(0L),
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(),
new CaseBuilder() new CaseBuilder()
.when(mapSheetAnalInferenceEntity.inspectionClosedYn.eq("Y")) .when(mapSheetAnalInferenceEntity.inspectionClosedYn.eq("Y"))
.then(mapSheetAnalInferenceEntity.updatedDttm) .then(mapSheetAnalInferenceEntity.updatedDttm)
@@ -247,23 +160,9 @@ public class LabelWorkRepositoryImpl implements LabelWorkRepositoryCustom {
"substring({0} from 1 for 8)", mapSheetLearnEntity.uid), "substring({0} from 1 for 8)", mapSheetLearnEntity.uid),
mapSheetLearnEntity.uuid)) mapSheetLearnEntity.uuid))
.from(mapSheetAnalInferenceEntity) .from(mapSheetAnalInferenceEntity)
.innerJoin(mapSheetAnalDataInferenceEntity)
.on(whereSubDataBuilder)
.innerJoin(mapSheetAnalDataInferenceGeomEntity)
.on(whereSubBuilder)
.leftJoin(mapSheetLearnEntity) .leftJoin(mapSheetLearnEntity)
.on(mapSheetAnalInferenceEntity.learnId.eq(mapSheetLearnEntity.id)) .on(mapSheetAnalInferenceEntity.learnId.eq(mapSheetLearnEntity.id))
.where(whereBuilder) .where(baseWhereBuilder)
.groupBy(
mapSheetAnalInferenceEntity.uuid,
mapSheetAnalInferenceEntity.compareYyyy,
mapSheetAnalInferenceEntity.targetYyyy,
mapSheetAnalInferenceEntity.stage,
mapSheetAnalInferenceEntity.createdDttm,
mapSheetAnalInferenceEntity.analState,
mapSheetAnalInferenceEntity.id,
mapSheetLearnEntity.uid,
mapSheetLearnEntity.uuid)
.orderBy( .orderBy(
stateOrder.asc(), stateOrder.asc(),
mapSheetAnalInferenceEntity.targetYyyy.desc(), mapSheetAnalInferenceEntity.targetYyyy.desc(),
@@ -273,20 +172,122 @@ public class LabelWorkRepositoryImpl implements LabelWorkRepositoryCustom {
.limit(pageable.getPageSize()) .limit(pageable.getPageSize())
.fetch(); .fetch();
// Count 쿼리 별도 실행 (null safe handling) // total count
long total = long total =
Optional.ofNullable( Optional.ofNullable(
queryFactory queryFactory
.select(mapSheetAnalInferenceEntity.uuid.countDistinct()) .select(mapSheetAnalInferenceEntity.count())
.from(mapSheetAnalInferenceEntity) .from(mapSheetAnalInferenceEntity)
.innerJoin(mapSheetAnalDataInferenceEntity) .where(baseWhereBuilder)
.on(whereSubDataBuilder)
.innerJoin(mapSheetAnalDataInferenceGeomEntity)
.on(whereSubBuilder)
.where(whereBuilder)
.fetchOne()) .fetchOne())
.orElse(0L); .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); return new PageImpl<>(foundContent, pageable, total);
} }