Merge remote-tracking branch 'origin/feat/dev_251201' into feat/dev_251201
This commit is contained in:
@@ -4,7 +4,9 @@ import com.kamco.cd.kamcoback.label.dto.LabelAllocateDto;
|
||||
import com.kamco.cd.kamcoback.label.dto.LabelAllocateDto.AllocateInfoDto;
|
||||
import com.kamco.cd.kamcoback.label.dto.LabelAllocateDto.InferenceDetail;
|
||||
import com.kamco.cd.kamcoback.label.dto.LabelAllocateDto.LabelerDetail;
|
||||
import com.kamco.cd.kamcoback.label.dto.LabelAllocateDto.LabelingStatDto;
|
||||
import com.kamco.cd.kamcoback.label.dto.LabelAllocateDto.UserList;
|
||||
import com.kamco.cd.kamcoback.label.dto.LabelAllocateDto.searchReq;
|
||||
import com.kamco.cd.kamcoback.label.dto.WorkerStatsDto.ProjectInfo;
|
||||
import com.kamco.cd.kamcoback.label.dto.WorkerStatsDto.WorkProgressInfo;
|
||||
import com.kamco.cd.kamcoback.label.dto.WorkerStatsDto.WorkerStatistics;
|
||||
@@ -14,6 +16,7 @@ import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
@@ -96,4 +99,16 @@ public class LabelAllocateCoreService {
|
||||
public void insertInspector(Long analUid, String inspector) {
|
||||
labelAllocateRepository.insertInspector(analUid, inspector);
|
||||
}
|
||||
|
||||
public Page<LabelingStatDto> findLabelerDailyStat(searchReq searchReq, String uuid, String userId) {
|
||||
return labelAllocateRepository.findLabelerDailyStat(searchReq, uuid, userId);
|
||||
}
|
||||
|
||||
public Page<LabelingStatDto> findInspectorDailyStat(searchReq searchReq, String uuid, String userId) {
|
||||
return labelAllocateRepository.findInspectorDailyStat(searchReq, uuid, userId);
|
||||
}
|
||||
|
||||
public LabelerDetail findInspectorDetail(String userId, String uuid) {
|
||||
return labelAllocateRepository.findInspectorDetail(userId, uuid);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.UUID;
|
||||
|
||||
@Entity
|
||||
@@ -40,18 +41,31 @@ public class LabelingAssignmentEntity extends CommonDateEntity {
|
||||
@Column(name = "anal_uid")
|
||||
private Long analUid;
|
||||
|
||||
@Column(name = "inspect_state")
|
||||
private String inspectState;
|
||||
|
||||
@Column(name = "work_stat_dttm")
|
||||
private ZonedDateTime workStatDttm;
|
||||
|
||||
@Column(name = "inspect_stat_dttm")
|
||||
private ZonedDateTime inspectStatDttm;
|
||||
|
||||
public LabelAllocateDto.Basic toDto() {
|
||||
return new LabelAllocateDto.Basic(
|
||||
this.assignmentUid,
|
||||
this.inferenceGeomUid,
|
||||
this.workerUid,
|
||||
this.inspectorUid,
|
||||
this.workState,
|
||||
this.stagnationYn,
|
||||
this.assignGroupId,
|
||||
this.learnGeomUid,
|
||||
this.analUid,
|
||||
super.getCreatedDate(),
|
||||
super.getModifiedDate());
|
||||
this.assignmentUid,
|
||||
this.inferenceGeomUid,
|
||||
this.workerUid,
|
||||
this.inspectorUid,
|
||||
this.workState,
|
||||
this.stagnationYn,
|
||||
this.assignGroupId,
|
||||
this.learnGeomUid,
|
||||
this.analUid,
|
||||
super.getCreatedDate(),
|
||||
super.getModifiedDate(),
|
||||
this.inspectState,
|
||||
this.workStatDttm,
|
||||
this.inspectStatDttm
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
package com.kamco.cd.kamcoback.postgres.repository.label;
|
||||
|
||||
import com.kamco.cd.kamcoback.label.dto.LabelAllocateDto;
|
||||
import com.kamco.cd.kamcoback.label.dto.LabelAllocateDto.AllocateInfoDto;
|
||||
import com.kamco.cd.kamcoback.label.dto.LabelAllocateDto.InferenceDetail;
|
||||
import com.kamco.cd.kamcoback.label.dto.LabelAllocateDto.LabelerDetail;
|
||||
import com.kamco.cd.kamcoback.label.dto.LabelAllocateDto.LabelingStatDto;
|
||||
import com.kamco.cd.kamcoback.label.dto.LabelAllocateDto.UserList;
|
||||
import com.kamco.cd.kamcoback.label.dto.WorkerStatsDto.ProjectInfo;
|
||||
import com.kamco.cd.kamcoback.label.dto.WorkerStatsDto.WorkProgressInfo;
|
||||
@@ -11,11 +13,12 @@ import com.kamco.cd.kamcoback.postgres.entity.LabelingAssignmentEntity;
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import org.springframework.data.domain.Page;
|
||||
|
||||
public interface LabelAllocateRepositoryCustom {
|
||||
|
||||
List<AllocateInfoDto> fetchNextIds(
|
||||
Long lastId, Long batchSize, Integer compareYyyy, Integer targetYyyy, Integer stage);
|
||||
Long lastId, Long batchSize, Integer compareYyyy, Integer targetYyyy, Integer stage);
|
||||
|
||||
void assignOwner(List<AllocateInfoDto> ids, String userId, Long analUid);
|
||||
|
||||
@@ -32,7 +35,7 @@ public interface LabelAllocateRepositoryCustom {
|
||||
|
||||
// 작업자 통계 조회
|
||||
List<WorkerStatistics> findWorkerStatistics(
|
||||
Long analUid, String workerType, String search, String sortType);
|
||||
Long analUid, String workerType, String search, String sortType);
|
||||
|
||||
// 작업 진행 현황 조회
|
||||
WorkProgressInfo findWorkProgressInfo(Long analUid);
|
||||
@@ -45,7 +48,7 @@ public interface LabelAllocateRepositoryCustom {
|
||||
InferenceDetail findInferenceDetail(String uuid);
|
||||
|
||||
List<Long> fetchNextMoveIds(
|
||||
Long lastId, Long batchSize, Integer compareYyyy, Integer targetYyyy, Integer stage);
|
||||
Long lastId, Long batchSize, Integer compareYyyy, Integer targetYyyy, Integer stage);
|
||||
|
||||
void assignOwnerMove(List<Long> sub, String userId);
|
||||
|
||||
@@ -54,4 +57,10 @@ public interface LabelAllocateRepositoryCustom {
|
||||
Long findMapSheetAnalInferenceUid(Integer compareYyyy, Integer targetYyyy, Integer stage);
|
||||
|
||||
void insertInspector(Long analUid, String inspector);
|
||||
|
||||
Page<LabelingStatDto> findLabelerDailyStat(LabelAllocateDto.searchReq searchReq, String uuid, String userId);
|
||||
|
||||
Page<LabelingStatDto> findInspectorDailyStat(LabelAllocateDto.searchReq searchReq, String uuid, String userId);
|
||||
|
||||
LabelerDetail findInspectorDetail(String userId, String uuid);
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import com.kamco.cd.kamcoback.label.dto.LabelAllocateDto.InferenceDetail;
|
||||
import com.kamco.cd.kamcoback.label.dto.LabelAllocateDto.InspectState;
|
||||
import com.kamco.cd.kamcoback.label.dto.LabelAllocateDto.LabelState;
|
||||
import com.kamco.cd.kamcoback.label.dto.LabelAllocateDto.LabelerDetail;
|
||||
import com.kamco.cd.kamcoback.label.dto.LabelAllocateDto.LabelingStatDto;
|
||||
import com.kamco.cd.kamcoback.label.dto.LabelAllocateDto.UserList;
|
||||
import com.kamco.cd.kamcoback.label.dto.WorkerStatsDto.ProjectInfo;
|
||||
import com.kamco.cd.kamcoback.label.dto.WorkerStatsDto.WorkProgressInfo;
|
||||
@@ -20,6 +21,7 @@ import com.kamco.cd.kamcoback.label.dto.WorkerStatsDto.WorkerStatistics;
|
||||
import com.kamco.cd.kamcoback.postgres.entity.LabelingAssignmentEntity;
|
||||
import com.kamco.cd.kamcoback.postgres.entity.MapSheetAnalInferenceEntity;
|
||||
import com.kamco.cd.kamcoback.postgres.entity.QMemberEntity;
|
||||
import com.querydsl.core.types.Expression;
|
||||
import com.querydsl.core.types.Projections;
|
||||
import com.querydsl.core.types.dsl.BooleanExpression;
|
||||
import com.querydsl.core.types.dsl.CaseBuilder;
|
||||
@@ -43,6 +45,9 @@ import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageImpl;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Slf4j
|
||||
@@ -567,29 +572,29 @@ public class LabelAllocateRepositoryImpl implements LabelAllocateRepositoryCusto
|
||||
QMemberEntity inspector = new QMemberEntity("inspector");
|
||||
|
||||
return queryFactory
|
||||
.select(
|
||||
Projections.constructor(
|
||||
LabelerDetail.class,
|
||||
worker.userRole,
|
||||
worker.name,
|
||||
worker.employeeNo,
|
||||
assignedCnt,
|
||||
skipCnt,
|
||||
completeCnt,
|
||||
percent,
|
||||
Expressions.constant(0), // TODO: 순위, 꼭 해야할지?
|
||||
labelingAssignmentEntity.createdDate.min(),
|
||||
inspector.name.min()))
|
||||
.from(worker)
|
||||
.innerJoin(labelingAssignmentEntity)
|
||||
.on(
|
||||
worker.employeeNo.eq(labelingAssignmentEntity.workerUid),
|
||||
labelingAssignmentEntity.analUid.eq(analEntity.getId()))
|
||||
.leftJoin(inspector)
|
||||
.on(labelingAssignmentEntity.inspectorUid.eq(inspector.employeeNo))
|
||||
.where(worker.employeeNo.eq(userId))
|
||||
.groupBy(worker.userRole, worker.name, worker.employeeNo)
|
||||
.fetchOne();
|
||||
.select(
|
||||
Projections.constructor(
|
||||
LabelerDetail.class,
|
||||
worker.userRole,
|
||||
worker.name,
|
||||
worker.employeeNo,
|
||||
assignedCnt,
|
||||
skipCnt,
|
||||
completeCnt,
|
||||
percent,
|
||||
Expressions.constant(0), // TODO: 순위, 꼭 해야할지?
|
||||
labelingAssignmentEntity.workStatDttm.min(),
|
||||
inspector.name.min()))
|
||||
.from(worker)
|
||||
.innerJoin(labelingAssignmentEntity)
|
||||
.on(
|
||||
worker.employeeNo.eq(labelingAssignmentEntity.workerUid),
|
||||
labelingAssignmentEntity.analUid.eq(analEntity.getId()))
|
||||
.leftJoin(inspector)
|
||||
.on(labelingAssignmentEntity.inspectorUid.eq(inspector.employeeNo))
|
||||
.where(worker.employeeNo.eq(userId))
|
||||
.groupBy(worker.userRole, worker.name, worker.employeeNo)
|
||||
.fetchOne();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -681,4 +686,288 @@ public class LabelAllocateRepositoryImpl implements LabelAllocateRepositoryCusto
|
||||
}
|
||||
return dateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<LabelingStatDto> findLabelerDailyStat(LabelAllocateDto.searchReq searchReq, String uuid, String userId) {
|
||||
// 날짜 포맷
|
||||
Expression<String> workDate =
|
||||
Expressions.stringTemplate(
|
||||
"TO_CHAR({0}, 'YYYY-MM-DD')",
|
||||
labelingAssignmentEntity.workStatDttm
|
||||
);
|
||||
|
||||
// 날짜별 전체 건수
|
||||
Expression<Long> dailyTotalCnt =
|
||||
Expressions.numberTemplate(
|
||||
Long.class,
|
||||
"COUNT(*)"
|
||||
);
|
||||
|
||||
// ⭐ 전체 기간 총 건수 (윈도우 함수)
|
||||
Expression<Long> totalCnt =
|
||||
Expressions.numberTemplate(
|
||||
Long.class,
|
||||
"SUM(COUNT(*)) OVER ()"
|
||||
);
|
||||
|
||||
// 상태별 카운트 (Postgres FILTER 사용)
|
||||
Expression<Long> assignedCnt =
|
||||
Expressions.numberTemplate(
|
||||
Long.class,
|
||||
"COUNT(*) FILTER (WHERE {0} = 'ASSIGNED')",
|
||||
labelingAssignmentEntity.workState
|
||||
);
|
||||
|
||||
Expression<Long> skipCnt =
|
||||
Expressions.numberTemplate(
|
||||
Long.class,
|
||||
"COUNT(*) FILTER (WHERE {0} = 'SKIP')",
|
||||
labelingAssignmentEntity.workState
|
||||
);
|
||||
|
||||
Expression<Long> completeCnt =
|
||||
Expressions.numberTemplate(
|
||||
Long.class,
|
||||
"COUNT(*) FILTER (WHERE {0} = 'COMPLETE')",
|
||||
labelingAssignmentEntity.workState
|
||||
);
|
||||
|
||||
Expression<Long> remainCnt =
|
||||
Expressions.numberTemplate(
|
||||
Long.class,
|
||||
"({0} - {1} - {2})",
|
||||
totalCnt,
|
||||
skipCnt,
|
||||
completeCnt
|
||||
);
|
||||
|
||||
// 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: ");
|
||||
}
|
||||
|
||||
Pageable pageable = searchReq.toPageable();
|
||||
List<LabelingStatDto> foundContent = queryFactory
|
||||
.select(
|
||||
Projections.constructor(
|
||||
LabelingStatDto.class,
|
||||
workDate,
|
||||
dailyTotalCnt,
|
||||
totalCnt, // ⭐ 전체 일자 배정 건수
|
||||
assignedCnt,
|
||||
skipCnt,
|
||||
completeCnt,
|
||||
remainCnt
|
||||
)
|
||||
)
|
||||
.from(labelingAssignmentEntity)
|
||||
.where(
|
||||
labelingAssignmentEntity.workerUid.eq(userId),
|
||||
labelingAssignmentEntity.analUid.eq(analEntity.getId())
|
||||
)
|
||||
.groupBy(workDate)
|
||||
.orderBy(labelingAssignmentEntity.workStatDttm.min().asc())
|
||||
.offset(pageable.getOffset())
|
||||
.limit(pageable.getPageSize())
|
||||
.fetch();
|
||||
|
||||
Long countQuery = queryFactory
|
||||
.select(workDate)
|
||||
.from(labelingAssignmentEntity)
|
||||
.where(
|
||||
labelingAssignmentEntity.workerUid.eq(userId),
|
||||
labelingAssignmentEntity.analUid.eq(analEntity.getId())
|
||||
)
|
||||
.distinct()
|
||||
.fetch()
|
||||
.stream()
|
||||
.count();
|
||||
|
||||
return new PageImpl<>(foundContent, pageable, countQuery);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<LabelingStatDto> findInspectorDailyStat(searchReq searchReq, String uuid, String userId) {
|
||||
// 날짜 포맷
|
||||
Expression<String> workDate =
|
||||
Expressions.stringTemplate(
|
||||
"TO_CHAR({0}, 'YYYY-MM-DD')",
|
||||
labelingAssignmentEntity.inspectStatDttm
|
||||
);
|
||||
|
||||
// 날짜별 전체 건수
|
||||
Expression<Long> dailyTotalCnt =
|
||||
Expressions.numberTemplate(
|
||||
Long.class,
|
||||
"COUNT(*)"
|
||||
);
|
||||
|
||||
// ⭐ 전체 기간 총 건수 (윈도우 함수)
|
||||
Expression<Long> totalCnt =
|
||||
Expressions.numberTemplate(
|
||||
Long.class,
|
||||
"SUM(COUNT(*)) OVER ()"
|
||||
);
|
||||
|
||||
// 상태별 카운트 (Postgres FILTER 사용)
|
||||
Expression<Long> assignedCnt =
|
||||
Expressions.numberTemplate(
|
||||
Long.class,
|
||||
"COUNT(*) FILTER (WHERE {0} = 'UNCONFIRM')",
|
||||
labelingAssignmentEntity.inspectState
|
||||
);
|
||||
|
||||
Expression<Long> skipCnt =
|
||||
Expressions.numberTemplate(
|
||||
Long.class,
|
||||
"COUNT(*) FILTER (WHERE {0} = 'EXCEPT')",
|
||||
labelingAssignmentEntity.inspectState
|
||||
);
|
||||
|
||||
Expression<Long> completeCnt =
|
||||
Expressions.numberTemplate(
|
||||
Long.class,
|
||||
"COUNT(*) FILTER (WHERE {0} = 'COMPLETE')",
|
||||
labelingAssignmentEntity.inspectState
|
||||
);
|
||||
|
||||
Expression<Long> remainCnt =
|
||||
Expressions.numberTemplate(
|
||||
Long.class,
|
||||
"({0} - {1} - {2})",
|
||||
totalCnt,
|
||||
skipCnt,
|
||||
completeCnt
|
||||
);
|
||||
|
||||
// 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: ");
|
||||
}
|
||||
|
||||
Pageable pageable = searchReq.toPageable();
|
||||
List<LabelingStatDto> foundContent = queryFactory
|
||||
.select(
|
||||
Projections.constructor(
|
||||
LabelingStatDto.class,
|
||||
workDate,
|
||||
dailyTotalCnt,
|
||||
totalCnt, // ⭐ 전체 일자 배정 건수
|
||||
assignedCnt,
|
||||
skipCnt,
|
||||
completeCnt,
|
||||
remainCnt
|
||||
)
|
||||
)
|
||||
.from(labelingAssignmentEntity)
|
||||
.where(
|
||||
labelingAssignmentEntity.inspectorUid.eq(userId),
|
||||
labelingAssignmentEntity.analUid.eq(analEntity.getId())
|
||||
)
|
||||
.groupBy(workDate)
|
||||
.orderBy(labelingAssignmentEntity.inspectStatDttm.min().asc())
|
||||
.offset(pageable.getOffset())
|
||||
.limit(pageable.getPageSize())
|
||||
.fetch();
|
||||
|
||||
Long countQuery = queryFactory
|
||||
.select(workDate)
|
||||
.from(labelingAssignmentEntity)
|
||||
.where(
|
||||
labelingAssignmentEntity.inspectorUid.eq(userId),
|
||||
labelingAssignmentEntity.analUid.eq(analEntity.getId())
|
||||
)
|
||||
.distinct()
|
||||
.fetch()
|
||||
.stream()
|
||||
.count();
|
||||
|
||||
return new PageImpl<>(foundContent, pageable, countQuery);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LabelerDetail findInspectorDetail(String userId, String uuid) {
|
||||
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();
|
||||
|
||||
NumberExpression<Double> percent =
|
||||
new CaseBuilder()
|
||||
.when(completeCnt.eq(0L))
|
||||
.then(0.0)
|
||||
.otherwise(
|
||||
Expressions.numberTemplate(
|
||||
Double.class,
|
||||
"round({0} / {1}, 2)",
|
||||
labelingAssignmentEntity.count(),
|
||||
completeCnt));
|
||||
|
||||
// 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 inspector = QMemberEntity.memberEntity;
|
||||
QMemberEntity worker = new QMemberEntity("worker");
|
||||
|
||||
return queryFactory
|
||||
.select(
|
||||
Projections.constructor(
|
||||
LabelerDetail.class,
|
||||
inspector.userRole,
|
||||
inspector.name,
|
||||
inspector.employeeNo,
|
||||
assignedCnt,
|
||||
skipCnt,
|
||||
completeCnt,
|
||||
percent,
|
||||
Expressions.constant(0), // TODO: 순위, 꼭 해야할지?
|
||||
labelingAssignmentEntity.inspectStatDttm.min(),
|
||||
worker.name.min()))
|
||||
.from(inspector)
|
||||
.innerJoin(labelingAssignmentEntity)
|
||||
.on(
|
||||
inspector.employeeNo.eq(labelingAssignmentEntity.inspectorUid),
|
||||
labelingAssignmentEntity.analUid.eq(analEntity.getId()))
|
||||
.leftJoin(worker)
|
||||
.on(labelingAssignmentEntity.workerUid.eq(worker.employeeNo))
|
||||
.where(inspector.employeeNo.eq(userId))
|
||||
.groupBy(inspector.userRole, inspector.name, inspector.employeeNo)
|
||||
.fetchOne();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user