Merge pull request 'feat/infer_dev_260107' (#231) from feat/infer_dev_260107 into develop

Reviewed-on: https://kamco.gitea.gs.dabeeo.com/dabeeo/kamco-dabeeo-backoffice/pulls/231
This commit is contained in:
2026-01-14 17:03:22 +09:00
13 changed files with 484 additions and 33 deletions

View File

@@ -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<List<InferenceDetailDto.Dashboard>> 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<Page<InferenceDetailDto.Geom>> 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<Long> 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<InferenceDetailDto.Geom> geomList =
inferenceResultService.getInferenceGeomList(uuid, searchGeoReq);
return ApiResponseDto.ok(geomList);
}
}

View File

@@ -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;

View File

@@ -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<Dashboard> getInferenceClassCountList(String uuid) {
return inferenceResultCoreService.getInferenceClassCountList(uuid);
}
public Page<Geom> getInferenceGeomList(String uuid, SearchGeoReq searchGeoReq) {
return inferenceResultCoreService.getInferenceGeomList(uuid, searchGeoReq);
}
}

View File

@@ -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<Dashboard> getInferenceClassCountList(String uuid) {
return mapSheetLearnRepository.getInferenceClassCountList(uuid);
}
public Page<Geom> getInferenceGeomList(String uuid, SearchGeoReq searchGeoReq) {
return mapSheetLearnRepository.getInferenceGeomList(uuid, searchGeoReq);
}
}

View File

@@ -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<LabelerDetail> 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<LabelerDetail> 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) {

View File

@@ -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<Dashboard> getInferenceClassCountList(String uuid);
Page<Geom> getInferenceGeomList(String uuid, SearchGeoReq searchGeoReq);
}

View File

@@ -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<Dashboard> 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<Geom> 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<Long> mapSheetNum = searchGeoReq.getMapSheetNum();
builder.and(mapSheetAnalDataInferenceGeomEntity.mapSheetNum.in(mapSheetNum));
}
List<Geom> 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);
}
}

View File

@@ -68,6 +68,12 @@ public interface LabelAllocateRepositoryCustom {
LabelerDetail findLabelerDetail(String userId, String uuid);
/** 순위 계산용 - 특정 회차의 모든 라벨러 통계 조회 */
List<LabelerDetail> 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<LabelerDetail> findAllInspectorsForRanking(Long analUid);
MoveInfo moveAvailUserList(String userId, String uuid);
void insertLabelerUser(Long analUid, String userId, int demand);

View File

@@ -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<Long> totalCnt = labelingAssignmentEntity.assignmentUid.count();
NumberExpression<Long> assignedCnt =
NumberExpression<Long> 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<Long> completeCnt =
new CaseBuilder()
.when(labelingAssignmentEntity.workState.eq(LabelState.DONE.getId()))
.then(1L)
.otherwise((Long) null)
.count();
NumberExpression<Double> 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<Long> 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<LabelerDetail> findAllLabelersForRanking(Long analUid) {
QMemberEntity worker = QMemberEntity.memberEntity;
NumberExpression<Long> totalCnt = labelingAssignmentEntity.assignmentUid.count();
NumberExpression<Long> completeCnt =
new CaseBuilder()
.when(labelingAssignmentEntity.workState.eq(LabelState.DONE.getId()))
.then(1L)
.otherwise((Long) null)
.count();
NumberExpression<Long> skipCnt =
new CaseBuilder()
.when(labelingAssignmentEntity.workState.eq(LabelState.SKIP.getId()))
.then(1L)
.otherwise((Long) null)
.count();
Expression<Long> 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<LabelerDetail> findAllInspectorsForRanking(Long analUid) {
QMemberEntity inspector = QMemberEntity.memberEntity;
NumberExpression<Long> assignedCnt =
new CaseBuilder()
.when(labelingAssignmentEntity.inspectState.eq(InspectState.UNCONFIRM.getId()))
.then(1L)
.otherwise((Long) null)
.count();
NumberExpression<Long> skipCnt =
new CaseBuilder()
.when(labelingAssignmentEntity.inspectState.eq(InspectState.EXCEPT.getId()))
.then(1L)
.otherwise((Long) null)
.count();
NumberExpression<Long> completeCnt =
new CaseBuilder()
.when(labelingAssignmentEntity.inspectState.eq(InspectState.COMPLETE.getId()))
.then(1L)
.otherwise((Long) null)
.count();
Expression<Long> 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) {

View File

@@ -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 =

View File

@@ -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 =

View File

@@ -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 = "분류정보")

View File

@@ -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 = "분류정보")