diff --git a/src/main/java/com/kamco/cd/kamcoback/label/LabelAllocateApiController.java b/src/main/java/com/kamco/cd/kamcoback/label/LabelAllocateApiController.java index ce6636c4..9e26cc36 100644 --- a/src/main/java/com/kamco/cd/kamcoback/label/LabelAllocateApiController.java +++ b/src/main/java/com/kamco/cd/kamcoback/label/LabelAllocateApiController.java @@ -191,12 +191,9 @@ public class LabelAllocateApiController { @RequestBody LabelAllocateDto.AllocateMoveDto dto) { - int compareYyyy = Integer.parseInt(dto.getYyyy().split("-")[0]); - int targetYyyy = Integer.parseInt(dto.getYyyy().split("-")[1]); - return ApiResponseDto.okObject( labelAllocateService.allocateMove( - dto.getStage(), dto.getLabelers(), compareYyyy, targetYyyy, dto.getUserId())); + dto.getTotalCnt(), dto.getUuid(), dto.getLabelers(), dto.getUserId())); } @Operation( @@ -232,7 +229,7 @@ public class LabelAllocateApiController { @ApiResponse(responseCode = "500", description = "서버 오류") }) @GetMapping("/move-user") - public ApiResponseDto> availMoveUserList( + public ApiResponseDto availMoveUserList( @Parameter(description = "해당 사용자 사번", example = "01022223333") @RequestParam String userId, @Parameter(description = "회차 마스터 key", example = "f97dc186-e6d3-4645-9737-3173dde8dc64") @RequestParam diff --git a/src/main/java/com/kamco/cd/kamcoback/label/dto/LabelAllocateDto.java b/src/main/java/com/kamco/cd/kamcoback/label/dto/LabelAllocateDto.java index 9153a305..6314bfff 100644 --- a/src/main/java/com/kamco/cd/kamcoback/label/dto/LabelAllocateDto.java +++ b/src/main/java/com/kamco/cd/kamcoback/label/dto/LabelAllocateDto.java @@ -229,34 +229,20 @@ public class LabelAllocateDto { @AllArgsConstructor public static class AllocateMoveDto { - @Schema(description = "회차", example = "4") - private Integer stage; + @Schema(description = "총 잔여 건수", example = "5061") + private Integer totalCnt; @Schema( - description = "이관할 라벨러 할당량", - example = - """ + description = "이관할 라벨러", + example = """ [ - { - "userId": "123456", - "demand": 10 - }, - { - "userId": "010222297501", - "demand": 5 - } + "87654321" ] """) - private List labelers; + private List labelers; - @Schema(description = "비교년도-기준년도", example = "2022-2024") - private String yyyy; - - // @Schema(description = "비교년도", example = "2022") - // private Integer compareYyyy; - // - // @Schema(description = "기준년도", example = "2024") - // private Integer targetYyyy; + @Schema(description = "회차 마스터 key", example = "f97dc186-e6d3-4645-9737-3173dde8dc64") + private String uuid; @Schema(description = "대상 사번", example = "01022223333") private String userId; @@ -269,6 +255,7 @@ public class LabelAllocateDto { private Long geoUid; private Long mapSheetNum; + private Long pnu; } @Getter @@ -320,4 +307,13 @@ public class LabelAllocateDto { private Long remainCnt; private Double percent; } + + @Getter + @Setter + @AllArgsConstructor + public static class MoveInfo { + + private Long totalCnt; + private List moveUserList; + } } diff --git a/src/main/java/com/kamco/cd/kamcoback/label/dto/LabelLabelerDto.java b/src/main/java/com/kamco/cd/kamcoback/label/dto/LabelLabelerDto.java new file mode 100644 index 00000000..80360db1 --- /dev/null +++ b/src/main/java/com/kamco/cd/kamcoback/label/dto/LabelLabelerDto.java @@ -0,0 +1,27 @@ +package com.kamco.cd.kamcoback.label.dto; + +import java.time.ZonedDateTime; +import java.util.UUID; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.Setter; + +public class LabelLabelerDto { + + @Getter + @Setter + @AllArgsConstructor + public static class Basic { + + private UUID lbUsrUid; + private Long analUid; + private String workerUid; + private Long allocateCnt; + private Boolean deleted; + private Boolean reAllocateYn; + private Long reAllocateCnt; + private String reAllocateWorkerUid; + private ZonedDateTime createdDttm; + private ZonedDateTime updatedDttm; + } +} diff --git a/src/main/java/com/kamco/cd/kamcoback/label/service/LabelAllocateService.java b/src/main/java/com/kamco/cd/kamcoback/label/service/LabelAllocateService.java index 445f208f..a344561e 100644 --- a/src/main/java/com/kamco/cd/kamcoback/label/service/LabelAllocateService.java +++ b/src/main/java/com/kamco/cd/kamcoback/label/service/LabelAllocateService.java @@ -7,12 +7,14 @@ 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.MoveUserList; +import com.kamco.cd.kamcoback.label.dto.LabelAllocateDto.MoveInfo; import com.kamco.cd.kamcoback.label.dto.LabelAllocateDto.TargetUser; import com.kamco.cd.kamcoback.label.dto.LabelAllocateDto.UserList; import com.kamco.cd.kamcoback.label.dto.WorkerStatsDto.WorkerListResponse; import com.kamco.cd.kamcoback.postgres.core.LabelAllocateCoreService; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; import java.util.Objects; import lombok.extern.slf4j.Slf4j; import org.springframework.data.domain.Page; @@ -73,6 +75,9 @@ public class LabelAllocateService { labelAllocateCoreService.assignOwner(sub, target.getUserId(), analUid); index = end; + + // 라벨러 유저 테이블에 insert + labelAllocateCoreService.insertLabelerUser(analUid, target.getUserId(), target.getDemand()); } // 검수자 할당 테이블에 insert. TODO: 익일 배치로 라벨링 완료된 내역을 검수자에게 할당해야 함 @@ -116,31 +121,51 @@ public class LabelAllocateService { } public ApiResponseDto.ResponseObj allocateMove( - Integer stage, - List targetUsers, - Integer compareYyyy, - Integer targetYyyy, - String userId) { - Long lastId = null; + Integer totalCnt, String uuid, List targetUsers, String userId) { - Long chargeCnt = targetUsers.stream().mapToLong(TargetUser::getDemand).sum(); - - if (chargeCnt <= 0) { - return new ApiResponseDto.ResponseObj(ApiResponseCode.BAD_REQUEST, "이관할 데이터를 입력해주세요."); + Map result = new LinkedHashMap<>(); + int userCount = targetUsers.size(); + if (userCount <= 0) { + return new ApiResponseDto.ResponseObj(ApiResponseCode.BAD_REQUEST, "재할당할 라벨러를 선택해주세요."); } - List allIds = - labelAllocateCoreService.fetchNextMoveIds( - lastId, chargeCnt, compareYyyy, targetYyyy, stage, userId); - int index = 0; - for (TargetUser target : targetUsers) { - int end = index + target.getDemand(); - List sub = allIds.subList(index, end); + int base = totalCnt / userCount; + int remainder = totalCnt % userCount; - labelAllocateCoreService.assignOwnerMove(sub, target.getUserId()); - index = end; + for (int i = 0; i < userCount; i++) { + int assignCount = base; + + // 마지막 사람에게 나머지 몰아주기 + if (i == userCount - 1) { + assignCount += remainder; + } + + result.put(targetUsers.get(i), assignCount); + + // TODO: 재할당 테이블에 update 까지만 하고 나머지는 배치에서 처리하기? + labelAllocateCoreService.assignOwnerReAllocate( + uuid, userId, targetUsers.get(i), (long) assignCount); } + // Long lastId = null; + // Long chargeCnt = targetUsers.stream().mapToLong(TargetUser::getDemand).sum(); + // + // if (chargeCnt <= 0) { + // return new ApiResponseDto.ResponseObj(ApiResponseCode.BAD_REQUEST, "이관할 데이터를 입력해주세요."); + // } + // + // List allIds = + // labelAllocateCoreService.fetchNextMoveIds( + // lastId, chargeCnt, compareYyyy, targetYyyy, stage, userId); + // int index = 0; + // for (TargetUser target : targetUsers) { + // int end = index + target.getDemand(); + // List sub = allIds.subList(index, end); + // + // labelAllocateCoreService.assignOwnerMove(sub, target.getUserId()); + // index = end; + // } + return new ApiResponseDto.ResponseObj(ApiResponseCode.OK, "이관을 완료하였습니다."); } @@ -161,7 +186,7 @@ public class LabelAllocateService { } } - public List moveAvailUserList(String userId, String uuid) { + public MoveInfo moveAvailUserList(String userId, String uuid) { return labelAllocateCoreService.moveAvailUserList(userId, uuid); } } diff --git a/src/main/java/com/kamco/cd/kamcoback/postgres/core/LabelAllocateCoreService.java b/src/main/java/com/kamco/cd/kamcoback/postgres/core/LabelAllocateCoreService.java index af40387a..9c2e2612 100644 --- a/src/main/java/com/kamco/cd/kamcoback/postgres/core/LabelAllocateCoreService.java +++ b/src/main/java/com/kamco/cd/kamcoback/postgres/core/LabelAllocateCoreService.java @@ -5,7 +5,7 @@ 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.MoveUserList; +import com.kamco.cd.kamcoback.label.dto.LabelAllocateDto.MoveInfo; 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; @@ -128,7 +128,16 @@ public class LabelAllocateCoreService { return labelAllocateRepository.findInspectorDetail(userId, uuid); } - public List moveAvailUserList(String userId, String uuid) { + public MoveInfo moveAvailUserList(String userId, String uuid) { return labelAllocateRepository.moveAvailUserList(userId, uuid); } + + public void insertLabelerUser(Long analUid, String userId, int demand) { + labelAllocateRepository.insertLabelerUser(analUid, userId, demand); + } + + public void assignOwnerReAllocate( + String uuid, String userId, String paramUserId, Long assignCount) { + labelAllocateRepository.assignOwnerReAllocate(uuid, userId, paramUserId, assignCount); + } } diff --git a/src/main/java/com/kamco/cd/kamcoback/postgres/entity/LabelingLabelerEntity.java b/src/main/java/com/kamco/cd/kamcoback/postgres/entity/LabelingLabelerEntity.java new file mode 100644 index 00000000..764de515 --- /dev/null +++ b/src/main/java/com/kamco/cd/kamcoback/postgres/entity/LabelingLabelerEntity.java @@ -0,0 +1,48 @@ +package com.kamco.cd.kamcoback.postgres.entity; + +import com.kamco.cd.kamcoback.label.dto.LabelLabelerDto; +import com.kamco.cd.kamcoback.postgres.CommonDateEntity; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import java.util.UUID; + +@Entity +@Table(name = "tb_labeling_labeler") +public class LabelingLabelerEntity extends CommonDateEntity { + + @Id + @Column(name = "lb_usr_uid") + private UUID lbUsrUid; + + @Column(name = "anal_uid") + private Long analUid; + + @Column(name = "worker_uid") + private String workerUid; + + private Long allocateCnt; + + private Boolean deleted; + + private Boolean reAllocateYn; + + private Long reAllocateCnt; + + private String reAllocateWorkerUid; + + public LabelLabelerDto.Basic toDto() { + return new LabelLabelerDto.Basic( + this.lbUsrUid, + this.analUid, + this.workerUid, + this.allocateCnt, + this.deleted, + this.reAllocateYn, + this.reAllocateCnt, + this.reAllocateWorkerUid, + super.getCreatedDate(), + super.getModifiedDate()); + } +} diff --git a/src/main/java/com/kamco/cd/kamcoback/postgres/repository/label/LabelAllocateRepositoryCustom.java b/src/main/java/com/kamco/cd/kamcoback/postgres/repository/label/LabelAllocateRepositoryCustom.java index d2753114..a1279830 100644 --- a/src/main/java/com/kamco/cd/kamcoback/postgres/repository/label/LabelAllocateRepositoryCustom.java +++ b/src/main/java/com/kamco/cd/kamcoback/postgres/repository/label/LabelAllocateRepositoryCustom.java @@ -5,7 +5,7 @@ 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.MoveUserList; +import com.kamco.cd.kamcoback.label.dto.LabelAllocateDto.MoveInfo; 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; @@ -78,5 +78,9 @@ public interface LabelAllocateRepositoryCustom { LabelerDetail findInspectorDetail(String userId, String uuid); - List moveAvailUserList(String userId, String uuid); + MoveInfo moveAvailUserList(String userId, String uuid); + + void insertLabelerUser(Long analUid, String userId, int demand); + + void assignOwnerReAllocate(String uuid, String userId, String paramUserId, Long assignCount); } diff --git a/src/main/java/com/kamco/cd/kamcoback/postgres/repository/label/LabelAllocateRepositoryImpl.java b/src/main/java/com/kamco/cd/kamcoback/postgres/repository/label/LabelAllocateRepositoryImpl.java index 8a421e7b..626253f9 100644 --- a/src/main/java/com/kamco/cd/kamcoback/postgres/repository/label/LabelAllocateRepositoryImpl.java +++ b/src/main/java/com/kamco/cd/kamcoback/postgres/repository/label/LabelAllocateRepositoryImpl.java @@ -2,6 +2,7 @@ package com.kamco.cd.kamcoback.postgres.repository.label; import static com.kamco.cd.kamcoback.postgres.entity.QLabelingAssignmentEntity.labelingAssignmentEntity; import static com.kamco.cd.kamcoback.postgres.entity.QLabelingInspectorEntity.labelingInspectorEntity; +import static com.kamco.cd.kamcoback.postgres.entity.QLabelingLabelerEntity.labelingLabelerEntity; 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.QMemberEntity.memberEntity; @@ -13,6 +14,7 @@ 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.MoveInfo; import com.kamco.cd.kamcoback.label.dto.LabelAllocateDto.MoveUserList; import com.kamco.cd.kamcoback.label.dto.LabelAllocateDto.UserList; import com.kamco.cd.kamcoback.label.dto.WorkerStatsDto.ProjectInfo; @@ -68,7 +70,8 @@ public class LabelAllocateRepositoryImpl implements LabelAllocateRepositoryCusto Projections.constructor( AllocateInfoDto.class, mapSheetAnalDataInferenceGeomEntity.geoUid, - mapSheetAnalDataInferenceGeomEntity.mapSheetNum)) + mapSheetAnalDataInferenceGeomEntity.mapSheetNum, + mapSheetAnalDataInferenceGeomEntity.pnu)) .from(mapSheetAnalDataInferenceGeomEntity) .where( lastId == null ? null : mapSheetAnalDataInferenceGeomEntity.geoUid.gt(lastId), @@ -116,8 +119,8 @@ public class LabelAllocateRepositoryImpl implements LabelAllocateRepositoryCusto """ insert into tb_labeling_assignment (assignment_uid, inference_geom_uid, worker_uid, - work_state, assign_group_id, anal_uid) - values (?, ?, ?, ?, ?, ?) + work_state, assign_group_id, anal_uid, pnu) + values (?, ?, ?, ?, ?, ?, ?) """; try (PreparedStatement ps = connection.prepareStatement(sql)) { @@ -130,6 +133,7 @@ public class LabelAllocateRepositoryImpl implements LabelAllocateRepositoryCusto ps.setString(4, LabelState.ASSIGNED.getId()); ps.setString(5, String.valueOf(info.getMapSheetNum())); ps.setLong(6, analEntity.getId()); + ps.setLong(7, info.getPnu()); ps.addBatch(); batchSize++; @@ -1038,12 +1042,16 @@ public class LabelAllocateRepositoryImpl implements LabelAllocateRepositoryCusto } @Override - public List moveAvailUserList(String userId, String uuid) { + public MoveInfo moveAvailUserList(String userId, String uuid) { NumberExpression totalCnt = labelingAssignmentEntity.count(); NumberExpression completeCnt = new CaseBuilder() - .when(labelingAssignmentEntity.workState.eq(LabelState.COMPLETE.getId())) + .when( + labelingAssignmentEntity + .workState + .eq(LabelState.COMPLETE.getId()) + .or(labelingAssignmentEntity.workState.eq(LabelState.SKIP.getId()))) .then(1L) .otherwise(0L) .sum(); @@ -1073,24 +1081,78 @@ public class LabelAllocateRepositoryImpl implements LabelAllocateRepositoryCusto throw new EntityNotFoundException("MapSheetAnalInferenceEntity not found for analUid: "); } - return queryFactory - .select( - Projections.constructor( - MoveUserList.class, - memberEntity.userRole, - memberEntity.employeeNo, - memberEntity.name, - remainCnt, - percent)) - .from(labelingAssignmentEntity) - .innerJoin(memberEntity) - .on(labelingAssignmentEntity.workerUid.eq(memberEntity.employeeNo)) + Long userChargeCnt = + queryFactory + .select(labelingAssignmentEntity.inferenceGeomUid.count()) + .from(labelingAssignmentEntity) + .where( + labelingAssignmentEntity.analUid.eq(analEntity.getId()), + labelingAssignmentEntity.workerUid.eq(userId), + labelingAssignmentEntity.workState.eq(LabelState.ASSIGNED.getId())) + .fetchOne(); + + List list = + queryFactory + .select( + Projections.constructor( + MoveUserList.class, + memberEntity.userRole, + memberEntity.employeeNo, + memberEntity.name, + remainCnt, + percent)) + .from(labelingLabelerEntity) + .innerJoin(memberEntity) + .on(labelingAssignmentEntity.workerUid.eq(memberEntity.employeeNo)) + .where( + labelingAssignmentEntity.analUid.eq(analEntity.getId()), + labelingAssignmentEntity.workerUid.ne(userId)) + .groupBy(memberEntity.userRole, memberEntity.employeeNo, memberEntity.name) + .having( + completeCnt + .multiply(2) + .goe(totalCnt)) // 진행률 평균 이상인 것들만 조회 => percent 를 바로 쓰면 having절에 무리가 갈 수 있다고 함 + .orderBy(completeCnt.desc()) // TODO: 도엽번호? PNU? 로 정렬하여 보여주기? + .fetch(); + + return new MoveInfo(userChargeCnt, list); + } + + @Override + public void insertLabelerUser(Long analUid, String userId, int demand) { + queryFactory + .insert(labelingLabelerEntity) + .columns( + labelingLabelerEntity.lbUsrUid, + labelingLabelerEntity.analUid, + labelingLabelerEntity.workerUid, + labelingLabelerEntity.allocateCnt) + .values(UUID.randomUUID(), analUid, userId, demand) + .execute(); + } + + @Override + public void assignOwnerReAllocate( + String uuid, String userId, String paramUserId, Long assignCount) { + // 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: "); + } + + queryFactory + .update(labelingLabelerEntity) + .set(labelingLabelerEntity.reAllocateYn, true) + .set(labelingLabelerEntity.reAllocateWorkerUid, userId) + .set(labelingLabelerEntity.reAllocateCnt, assignCount) .where( - labelingAssignmentEntity.analUid.eq(analEntity.getId()), - labelingAssignmentEntity.workerUid.ne(userId)) - .groupBy(memberEntity.userRole, memberEntity.employeeNo, memberEntity.name) - .having(completeCnt.multiply(2).goe(totalCnt)) // 진행률 평균 이상인 것들만 조회 => percent 를 바로 쓰면 - .orderBy(completeCnt.desc()) - .fetch(); + labelingLabelerEntity.analUid.eq(analEntity.getId()), + labelingLabelerEntity.workerUid.eq(paramUserId)) + .execute(); } }