Merge remote-tracking branch 'origin/feat/infer_dev_260107' into feat/infer_dev_260107

This commit is contained in:
Moon
2026-01-19 16:19:00 +09:00
26 changed files with 705 additions and 78 deletions

View File

@@ -11,6 +11,7 @@ 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.InferenceLearnDto;
import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.InferenceServerStatusDto;
import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.InferenceStatusDetailDto;
import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.ResultList;
@@ -461,4 +462,20 @@ public class InferenceResultCoreService {
inferenceResultsTetingRepository.getInferenceResultList(batchIds);
return list.stream().map(InferenceResultsTestingDto.ShpDto::fromEntity).toList();
}
/**
* uid 조회
*
* @param uuid
* @return
*/
public InferenceLearnDto getInferenceUid(UUID uuid) {
MapSheetLearnEntity entity =
inferenceResultRepository
.getInferenceUid(uuid)
.orElseThrow(() -> new CustomApiException("NOT_FOUND_DATA", HttpStatus.NOT_FOUND));
InferenceLearnDto dto = new InferenceLearnDto();
dto.setUid(entity.getUid());
return dto;
}
}

View File

@@ -0,0 +1,40 @@
package com.kamco.cd.kamcoback.postgres.core;
import com.kamco.cd.kamcoback.postgres.repository.scheduler.TrainingDataReviewJobRepository;
import com.kamco.cd.kamcoback.scheduler.dto.TrainingDataReviewJobDto.InspectorPendingDto;
import com.kamco.cd.kamcoback.scheduler.dto.TrainingDataReviewJobDto.Tasks;
import java.util.List;
import java.util.UUID;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
@Service
@RequiredArgsConstructor
public class TrainingDataReviewJobCoreService {
private final TrainingDataReviewJobRepository trainingDataReviewJobRepository;
public List<Tasks> findCompletedYesterdayUnassigned() {
return trainingDataReviewJobRepository.findCompletedYesterdayUnassigned();
}
public void assignReviewer(UUID assignmentUid, String reviewerId) {
trainingDataReviewJobRepository.assignReviewer(assignmentUid, reviewerId);
}
public void assignReviewerBatch(List<UUID> assignmentUids, String reviewerId) {
trainingDataReviewJobRepository.assignReviewerBatch(assignmentUids, reviewerId);
}
public Tasks findAssignmentTask(String assignmentUid) {
return trainingDataReviewJobRepository.findAssignmentTask(assignmentUid);
}
public List<InspectorPendingDto> findInspectorPendingByRound(Long analUid) {
return trainingDataReviewJobRepository.findInspectorPendingByRound(analUid);
}
public void lockInspectors(Long analUid, List<String> reviewerIds) {
trainingDataReviewJobRepository.lockInspectors(analUid, reviewerIds);
}
}

View File

@@ -1,7 +1,9 @@
package com.kamco.cd.kamcoback.postgres.repository.Inference;
import com.kamco.cd.kamcoback.postgres.entity.MapSheetAnalDataInferenceGeomEntity;
import com.kamco.cd.kamcoback.postgres.entity.MapSheetLearnEntity;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
public interface InferenceResultRepositoryCustom {
@@ -27,4 +29,6 @@ public interface InferenceResultRepositoryCustom {
List<MapSheetAnalDataInferenceGeomEntity> findGeomEntitiesByDataUid(Long dataUid, int limit);
Long getInferenceLearnIdByUuid(UUID uuid);
public Optional<MapSheetLearnEntity> getInferenceUid(UUID uuid);
}

View File

@@ -3,6 +3,7 @@ package com.kamco.cd.kamcoback.postgres.repository.Inference;
import static com.kamco.cd.kamcoback.postgres.entity.QMapSheetLearnEntity.mapSheetLearnEntity;
import com.kamco.cd.kamcoback.postgres.entity.MapSheetAnalDataInferenceGeomEntity;
import com.kamco.cd.kamcoback.postgres.entity.MapSheetLearnEntity;
import com.kamco.cd.kamcoback.postgres.entity.QMapSheetAnalDataInferenceEntity;
import com.kamco.cd.kamcoback.postgres.entity.QMapSheetAnalDataInferenceGeomEntity;
import com.kamco.cd.kamcoback.postgres.entity.QMapSheetAnalInferenceEntity;
@@ -10,6 +11,7 @@ import com.querydsl.jpa.impl.JPAQueryFactory;
import jakarta.persistence.EntityManager;
import java.time.ZonedDateTime;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Repository;
@@ -468,4 +470,14 @@ public class InferenceResultRepositoryImpl implements InferenceResultRepositoryC
.where(mapSheetLearnEntity.uuid.eq(uuid))
.fetchOne();
}
@Override
public Optional<MapSheetLearnEntity> getInferenceUid(UUID uuid) {
return Optional.ofNullable(
queryFactory
.select(mapSheetLearnEntity)
.from(mapSheetLearnEntity)
.where(mapSheetLearnEntity.uuid.eq(uuid))
.fetchOne());
}
}

View File

@@ -2,6 +2,7 @@ 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.QMapSheetAnalDataInferenceEntity.mapSheetAnalDataInferenceEntity;
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;
@@ -339,7 +340,7 @@ public class MapSheetLearnRepositoryImpl implements MapSheetLearnRepositoryCusto
.select(
Projections.constructor(
Dashboard.class,
mapSheetAnalSttcEntity.id.classAfterCd,
mapSheetAnalSttcEntity.id.classAfterCd.toUpperCase(),
mapSheetAnalSttcEntity.classAfterCnt.sum()))
.from(mapSheetAnalInferenceEntity)
.innerJoin(mapSheetAnalSttcEntity)
@@ -419,13 +420,10 @@ public class MapSheetLearnRepositoryImpl implements MapSheetLearnRepositoryCusto
// mapSheetAnalDataInferenceGeomEntity.geomCenter)
))
.from(mapSheetAnalInferenceEntity)
.join(mapSheetAnalDataInferenceEntity)
.on(mapSheetAnalDataInferenceEntity.analUid.eq(mapSheetAnalInferenceEntity.id))
.join(mapSheetAnalDataInferenceGeomEntity)
.on(
mapSheetAnalDataInferenceGeomEntity.compareYyyy.eq(
mapSheetAnalInferenceEntity.compareYyyy),
mapSheetAnalDataInferenceGeomEntity.targetYyyy.eq(
mapSheetAnalInferenceEntity.targetYyyy),
mapSheetAnalDataInferenceGeomEntity.stage.eq(mapSheetAnalInferenceEntity.stage))
.on(mapSheetAnalDataInferenceGeomEntity.dataUid.eq(mapSheetAnalDataInferenceEntity.id))
.join(mapInkx5kEntity)
.on(
mapSheetAnalDataInferenceGeomEntity.mapSheetNum.eq(
@@ -440,13 +438,10 @@ public class MapSheetLearnRepositoryImpl implements MapSheetLearnRepositoryCusto
queryFactory
.select(mapSheetAnalDataInferenceGeomEntity.geoUid)
.from(mapSheetAnalInferenceEntity)
.join(mapSheetAnalDataInferenceEntity)
.on(mapSheetAnalDataInferenceEntity.analUid.eq(mapSheetAnalInferenceEntity.id))
.join(mapSheetAnalDataInferenceGeomEntity)
.on(
mapSheetAnalDataInferenceGeomEntity.compareYyyy.eq(
mapSheetAnalInferenceEntity.compareYyyy),
mapSheetAnalDataInferenceGeomEntity.targetYyyy.eq(
mapSheetAnalInferenceEntity.targetYyyy),
mapSheetAnalDataInferenceGeomEntity.stage.eq(mapSheetAnalInferenceEntity.stage))
.on(mapSheetAnalDataInferenceGeomEntity.dataUid.eq(mapSheetAnalDataInferenceEntity.id))
.join(mapInkx5kEntity)
.on(
mapSheetAnalDataInferenceGeomEntity.mapSheetNum.eq(

View File

@@ -0,0 +1,23 @@
package com.kamco.cd.kamcoback.postgres.repository.scheduler;
import com.kamco.cd.kamcoback.postgres.entity.LabelingInspectorEntity;
import jakarta.persistence.LockModeType;
import java.util.List;
import java.util.UUID;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Lock;
import org.springframework.data.jpa.repository.Query;
public interface TrainingDataReviewJobRepository
extends JpaRepository<LabelingInspectorEntity, UUID>, TrainingDataReviewJobRepositoryCustom {
@Lock(LockModeType.PESSIMISTIC_WRITE)
@Query(
"""
select r
from LabelingInspectorEntity r
where r.analUid = :analUid
and r.inspectorUid in :inspectorUids
""")
List<LabelingInspectorEntity> lockInspectors(Long analUid, List<String> inspectorUids);
}

View File

@@ -0,0 +1,19 @@
package com.kamco.cd.kamcoback.postgres.repository.scheduler;
import com.kamco.cd.kamcoback.scheduler.dto.TrainingDataReviewJobDto.InspectorPendingDto;
import com.kamco.cd.kamcoback.scheduler.dto.TrainingDataReviewJobDto.Tasks;
import java.util.List;
import java.util.UUID;
public interface TrainingDataReviewJobRepositoryCustom {
List<Tasks> findCompletedYesterdayUnassigned();
List<InspectorPendingDto> findInspectorPendingByRound(Long analUid);
void assignReviewer(UUID assignmentUid, String reviewerId);
void assignReviewerBatch(List<UUID> assignmentUids, String reviewerId);
Tasks findAssignmentTask(String assignmentUid);
}

View File

@@ -0,0 +1,135 @@
package com.kamco.cd.kamcoback.postgres.repository.scheduler;
import static com.kamco.cd.kamcoback.postgres.entity.QLabelingAssignmentEntity.labelingAssignmentEntity;
import static com.kamco.cd.kamcoback.postgres.entity.QLabelingInspectorEntity.labelingInspectorEntity;
import com.kamco.cd.kamcoback.label.dto.LabelAllocateDto.InspectState;
import com.kamco.cd.kamcoback.label.dto.LabelAllocateDto.LabelState;
import com.kamco.cd.kamcoback.postgres.entity.LabelingAssignmentEntity;
import com.kamco.cd.kamcoback.scheduler.dto.TrainingDataReviewJobDto.InspectorPendingDto;
import com.kamco.cd.kamcoback.scheduler.dto.TrainingDataReviewJobDto.Tasks;
import com.querydsl.core.types.Projections;
import com.querydsl.core.types.dsl.BooleanExpression;
import com.querydsl.core.types.dsl.Expressions;
import com.querydsl.core.types.dsl.StringExpression;
import com.querydsl.jpa.impl.JPAQueryFactory;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.List;
import java.util.UUID;
import org.springframework.data.jpa.repository.support.QuerydslRepositorySupport;
public class TrainingDataReviewJobRepositoryImpl extends QuerydslRepositorySupport
implements TrainingDataReviewJobRepositoryCustom {
private final JPAQueryFactory queryFactory;
private final StringExpression NULL_STRING = Expressions.stringTemplate("cast(null as text)");
public TrainingDataReviewJobRepositoryImpl(JPAQueryFactory queryFactory) {
super(LabelingAssignmentEntity.class);
this.queryFactory = queryFactory;
}
@Override
public List<Tasks> findCompletedYesterdayUnassigned() {
ZoneId zone = ZoneId.of("Asia/Seoul");
ZonedDateTime todayStart = ZonedDateTime.now(zone).toLocalDate().atStartOfDay(zone);
ZonedDateTime yesterdayStart = todayStart.minusDays(1);
BooleanExpression isYesterday =
labelingAssignmentEntity
.workStatDttm
.goe(yesterdayStart)
.and(labelingAssignmentEntity.workStatDttm.lt(todayStart));
return queryFactory
.select(
Projections.constructor(
Tasks.class,
labelingAssignmentEntity.assignmentUid,
labelingAssignmentEntity.inferenceGeomUid,
labelingAssignmentEntity.analUid))
.from(labelingAssignmentEntity)
.where(
labelingAssignmentEntity.workState.in(LabelState.SKIP.getId(), LabelState.DONE.getId()),
labelingAssignmentEntity.inspectorUid.isNull(),
isYesterday)
.orderBy(
labelingAssignmentEntity.analUid.asc(),
labelingAssignmentEntity.assignGroupId.asc(),
labelingAssignmentEntity.inferenceGeomUid.asc())
.fetch();
}
/**
* 해당 회차에 라벨링 할당받은 검수자별 완료 건수 count(), 완료한 게 적은 순으로 해야 일이 한 사람에게 몰리지 않음
*
* @param analUid
* @return
*/
@Override
public List<InspectorPendingDto> findInspectorPendingByRound(Long analUid) {
return queryFactory
.select(
Projections.constructor(
InspectorPendingDto.class,
labelingInspectorEntity.inspectorUid,
labelingAssignmentEntity.assignmentUid.count()))
.from(labelingInspectorEntity)
.leftJoin(labelingAssignmentEntity)
.on(
labelingInspectorEntity.inspectorUid.eq(labelingAssignmentEntity.inspectorUid),
labelingAssignmentEntity.inspectState.in(
InspectState.EXCEPT.getId(), InspectState.COMPLETE.getId()))
.where(labelingInspectorEntity.analUid.eq(analUid))
.groupBy(labelingInspectorEntity.inspectorUid)
.orderBy(labelingAssignmentEntity.assignmentUid.count().asc())
.fetch();
}
/**
* 실시간 분배용 1건 update
*
* @param assignmentUid
* @param reviewerId
*/
@Override
public void assignReviewer(UUID assignmentUid, String reviewerId) {
queryFactory
.update(labelingAssignmentEntity)
.set(labelingAssignmentEntity.inspectorUid, reviewerId)
.set(labelingAssignmentEntity.inspectState, InspectState.UNCONFIRM.getId())
.where(labelingAssignmentEntity.assignmentUid.eq(assignmentUid))
.execute();
}
/**
* 배치용 여러 건 update
*
* @param assignmentUids
* @param reviewerId
*/
@Override
public void assignReviewerBatch(List<UUID> assignmentUids, String reviewerId) {
queryFactory
.update(labelingAssignmentEntity)
.set(labelingAssignmentEntity.inspectorUid, reviewerId)
.set(labelingAssignmentEntity.inspectState, InspectState.UNCONFIRM.getId())
.where(labelingAssignmentEntity.assignmentUid.in(assignmentUids))
.execute();
}
@Override
public Tasks findAssignmentTask(String assignmentUid) {
return queryFactory
.select(
Projections.constructor(
Tasks.class,
labelingAssignmentEntity.assignmentUid,
labelingAssignmentEntity.inferenceGeomUid,
labelingAssignmentEntity.analUid))
.from(labelingAssignmentEntity)
.where(labelingAssignmentEntity.assignmentUid.eq(UUID.fromString(assignmentUid)))
.fetchOne();
}
}

View File

@@ -5,6 +5,7 @@ import static com.kamco.cd.kamcoback.postgres.entity.QLabelingAssignmentEntity.l
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.QMapSheetLearnDataGeomEntity.mapSheetLearnDataGeomEntity;
import static com.kamco.cd.kamcoback.postgres.entity.QMemberEntity.memberEntity;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
@@ -442,6 +443,26 @@ public class TrainingDataReviewRepositoryImpl extends QuerydslRepositorySupport
// COG URL 조회 실패 시 빈 문자열 유지
}
// 4-1. 라벨러 성명 조회 (worker_uid로 tb_member의 name 조회)
String workerName = "";
try {
if (assignment.toDto().getWorkerUid() != null
&& !assignment.toDto().getWorkerUid().isEmpty()) {
workerName =
queryFactory
.select(memberEntity.name)
.from(memberEntity)
.where(memberEntity.userId.eq(assignment.toDto().getWorkerUid()))
.fetchFirst();
if (workerName == null) {
workerName = "";
}
}
} catch (Exception e) {
System.err.println("Worker name retrieval error: " + e.getMessage());
// 라벨러 성명 조회 실패 시 빈 문자열 유지
}
// 5. DTO 생성
var changeDetectionInfo =
ChangeDetectionInfo.builder()
@@ -492,6 +513,7 @@ public class TrainingDataReviewRepositoryImpl extends QuerydslRepositorySupport
mapSheetAnalDataInferenceGeomEntityEntity.getMapSheetNum() != null
? mapSheetAnalDataInferenceGeomEntityEntity.getMapSheetNum()
: 0L)
.workerName(workerName)
.build();
var inspectionResultInfo =