작업현황 관리, 라벨러/검수자 순위추가
This commit is contained in:
@@ -105,7 +105,43 @@ public class LabelAllocateCoreService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public LabelerDetail findLabelerDetail(String userId, String uuid) {
|
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) {
|
public Long findMapSheetAnalInferenceUid(Integer compareYyyy, Integer targetYyyy, Integer stage) {
|
||||||
@@ -127,7 +163,42 @@ public class LabelAllocateCoreService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public LabelerDetail findInspectorDetail(String userId, String uuid) {
|
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) {
|
public MoveInfo moveAvailUserList(String userId, String uuid) {
|
||||||
|
|||||||
@@ -68,6 +68,16 @@ public interface LabelAllocateRepositoryCustom {
|
|||||||
|
|
||||||
LabelerDetail findLabelerDetail(String userId, String uuid);
|
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);
|
Long findMapSheetAnalInferenceUid(Integer compareYyyy, Integer targetYyyy, Integer stage);
|
||||||
|
|
||||||
void insertInspector(Long analUid, String inspector);
|
void insertInspector(Long analUid, String inspector);
|
||||||
@@ -80,6 +90,11 @@ public interface LabelAllocateRepositoryCustom {
|
|||||||
|
|
||||||
LabelerDetail findInspectorDetail(String userId, String uuid);
|
LabelerDetail findInspectorDetail(String userId, String uuid);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 순위 계산용 - 특정 회차의 모든 검수자 통계 조회
|
||||||
|
*/
|
||||||
|
List<LabelerDetail> findAllInspectorsForRanking(Long analUid);
|
||||||
|
|
||||||
MoveInfo moveAvailUserList(String userId, String uuid);
|
MoveInfo moveAvailUserList(String userId, String uuid);
|
||||||
|
|
||||||
void insertLabelerUser(Long analUid, String userId, int demand);
|
void insertLabelerUser(Long analUid, String userId, int demand);
|
||||||
|
|||||||
@@ -777,11 +777,25 @@ public class LabelAllocateRepositoryImpl implements LabelAllocateRepositoryCusto
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public LabelerDetail findLabelerDetail(String userId, String uuid) {
|
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> totalCnt = labelingAssignmentEntity.assignmentUid.count();
|
||||||
|
|
||||||
NumberExpression<Long> assignedCnt =
|
NumberExpression<Long> completeCnt =
|
||||||
new CaseBuilder()
|
new CaseBuilder()
|
||||||
.when(labelingAssignmentEntity.workState.eq(LabelState.ASSIGNED.getId()))
|
.when(labelingAssignmentEntity.workState.eq(LabelState.DONE.getId()))
|
||||||
.then(1L)
|
.then(1L)
|
||||||
.otherwise((Long) null)
|
.otherwise((Long) null)
|
||||||
.count();
|
.count();
|
||||||
@@ -793,13 +807,6 @@ public class LabelAllocateRepositoryImpl implements LabelAllocateRepositoryCusto
|
|||||||
.otherwise((Long) null)
|
.otherwise((Long) null)
|
||||||
.count();
|
.count();
|
||||||
|
|
||||||
NumberExpression<Long> completeCnt =
|
|
||||||
new CaseBuilder()
|
|
||||||
.when(labelingAssignmentEntity.workState.eq(LabelState.DONE.getId()))
|
|
||||||
.then(1L)
|
|
||||||
.otherwise((Long) null)
|
|
||||||
.count();
|
|
||||||
|
|
||||||
NumberExpression<Double> percent =
|
NumberExpression<Double> percent =
|
||||||
new CaseBuilder()
|
new CaseBuilder()
|
||||||
.when(completeCnt.eq(0L))
|
.when(completeCnt.eq(0L))
|
||||||
@@ -807,25 +814,10 @@ public class LabelAllocateRepositoryImpl implements LabelAllocateRepositoryCusto
|
|||||||
.otherwise(
|
.otherwise(
|
||||||
Expressions.numberTemplate(
|
Expressions.numberTemplate(
|
||||||
Double.class,
|
Double.class,
|
||||||
"round({0} / {1}, 2)",
|
"ROUND(({0} * 1.0 / NULLIF({1}, 0)) * 100, 2)",
|
||||||
completeCnt,
|
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 =
|
Expression<Long> remainCnt =
|
||||||
Expressions.numberTemplate(Long.class, "({0} - {1} - {2})", totalCnt, skipCnt, completeCnt);
|
Expressions.numberTemplate(Long.class, "({0} - {1} - {2})", totalCnt, skipCnt, completeCnt);
|
||||||
|
|
||||||
@@ -840,7 +832,7 @@ public class LabelAllocateRepositoryImpl implements LabelAllocateRepositoryCusto
|
|||||||
completeCnt,
|
completeCnt,
|
||||||
skipCnt,
|
skipCnt,
|
||||||
percent,
|
percent,
|
||||||
Expressions.constant(0), // TODO: 순위, 꼭 해야할지?
|
Expressions.constant(0), // 순위는 Service 레이어에서 계산
|
||||||
labelingAssignmentEntity.workStatDttm.min(),
|
labelingAssignmentEntity.workStatDttm.min(),
|
||||||
inspector.name.min(),
|
inspector.name.min(),
|
||||||
remainCnt))
|
remainCnt))
|
||||||
@@ -856,6 +848,68 @@ public class LabelAllocateRepositoryImpl implements LabelAllocateRepositoryCusto
|
|||||||
.fetchOne();
|
.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
|
@Override
|
||||||
public Long findMapSheetAnalInferenceUid(Integer compareYyyy, Integer targetYyyy, Integer stage) {
|
public Long findMapSheetAnalInferenceUid(Integer compareYyyy, Integer targetYyyy, Integer stage) {
|
||||||
return queryFactory
|
return queryFactory
|
||||||
@@ -1301,11 +1355,11 @@ public class LabelAllocateRepositoryImpl implements LabelAllocateRepositoryCusto
|
|||||||
inspector.userRole,
|
inspector.userRole,
|
||||||
inspector.name,
|
inspector.name,
|
||||||
inspector.employeeNo,
|
inspector.employeeNo,
|
||||||
assignedCnt,
|
assignedCnt, // count (총 배정 건수)
|
||||||
skipCnt,
|
completeCnt, // completeCnt (완료 건수)
|
||||||
completeCnt,
|
skipCnt, // skipCnt (스킵 건수)
|
||||||
percent,
|
percent,
|
||||||
Expressions.constant(0), // TODO: 순위, 꼭 해야할지?
|
Expressions.constant(0), // 순위는 Service 레이어에서 계산
|
||||||
labelingAssignmentEntity.inspectStatDttm.min(),
|
labelingAssignmentEntity.inspectStatDttm.min(),
|
||||||
worker.name.min(),
|
worker.name.min(),
|
||||||
remainCnt))
|
remainCnt))
|
||||||
@@ -1321,6 +1375,61 @@ public class LabelAllocateRepositoryImpl implements LabelAllocateRepositoryCusto
|
|||||||
.fetchOne();
|
.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
|
@Override
|
||||||
public MoveInfo moveAvailUserList(String userId, String uuid) {
|
public MoveInfo moveAvailUserList(String userId, String uuid) {
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user