From d72e9de61e07127b5462386ec4c492ee315da361 Mon Sep 17 00:00:00 2001 From: "gayoun.park" Date: Fri, 2 Jan 2026 17:58:25 +0900 Subject: [PATCH 1/3] =?UTF-8?q?=EB=9D=BC=EB=B2=A8=ED=95=A0=EB=8B=B9=20?= =?UTF-8?q?=EC=9E=84=EC=8B=9C=20=EC=BB=A4=EB=B0=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../label/LabelAllocateApiController.java | 32 +---- .../kamcoback/label/dto/LabelAllocateDto.java | 106 ++++++++++++--- .../label/service/LabelAllocateService.java | 109 +++++----------- .../core/LabelAllocateCoreService.java | 19 ++- .../entity/LabelingAssignmentEntity.java | 57 +++++++++ .../label/LabelAllocateRepositoryCustom.java | 10 +- .../label/LabelAllocateRepositoryImpl.java | 121 +++++++++++++++--- 7 files changed, 313 insertions(+), 141 deletions(-) create mode 100644 src/main/java/com/kamco/cd/kamcoback/postgres/entity/LabelingAssignmentEntity.java 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 5014147e..243965a2 100644 --- a/src/main/java/com/kamco/cd/kamcoback/label/LabelAllocateApiController.java +++ b/src/main/java/com/kamco/cd/kamcoback/label/LabelAllocateApiController.java @@ -2,6 +2,7 @@ package com.kamco.cd.kamcoback.label; import com.kamco.cd.kamcoback.config.api.ApiResponseDto; import com.kamco.cd.kamcoback.label.dto.LabelAllocateDto; +import com.kamco.cd.kamcoback.label.dto.LabelAllocateDto.TargetInspector; import com.kamco.cd.kamcoback.label.dto.LabelAllocateDto.TargetUser; import com.kamco.cd.kamcoback.label.service.LabelAllocateService; import io.swagger.v3.oas.annotations.tags.Tag; @@ -26,35 +27,10 @@ public class LabelAllocateApiController { @PostMapping("/allocate") public ApiResponseDto labelAllocate(@RequestBody LabelAllocateDto dto) { - // 도엽별 카운트 쿼리 - // List sheets = - // List.of( - // new Sheet("1", 261), - // new Sheet("2", 500), - // new Sheet("3", 350), - // new Sheet("4", 250), - // new Sheet("5", 380), - // new Sheet("6", 459)); - - // 사용자별 할당 입력한 값 - // List targets = - // List.of(new TargetUser("A", 1000), new TargetUser("B", 500), new TargetUser("C", 700)); - // LabelAllocateService.allocateSheetCount(new ArrayList<>(sheets), new - // ArrayList<>(targets)); - - // targets.forEach( - // t -> { - // log.info("[" + t.getUserId() + "]"); - // t.getAssigned() - // .forEach( - // u -> { - // log.info(" - 도엽: " + u.getSheetId() + " (" + u.getCount() + ")"); - // }); - // }); - List targets = - List.of(new TargetUser("1", 1000), new TargetUser("2", 400), new TargetUser("3", 440)); - labelAllocateService.allocateAsc(targets); + List.of(new TargetUser("1234567", 1000), new TargetUser("2345678", 400), new TargetUser("3456789", 440)); + List inspectors = List.of(new TargetInspector("9876543", 1000), new TargetInspector("8765432", 340), new TargetInspector("98765432", 500)); + labelAllocateService.allocateAsc(targets, inspectors); return ApiResponseDto.ok(null); } 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 a6802547..b3509a38 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 @@ -1,22 +1,80 @@ package com.kamco.cd.kamcoback.label.dto; -import java.util.ArrayList; -import java.util.List; +import com.kamco.cd.kamcoback.common.utils.enums.CodeExpose; +import com.kamco.cd.kamcoback.common.utils.enums.EnumType; +import java.time.ZonedDateTime; +import java.util.UUID; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.Setter; public class LabelAllocateDto { + @CodeExpose @Getter @AllArgsConstructor - public static class Sheet { + public enum LabelMngState implements EnumType { + PENDING("작업대기"), + ASSIGNED("작업할당"), + STOP("중단"), + LABEL_ING("라벨진행중"), + LABEL_COMPLETE("라벨완료"), + INSPECT_REQ("검수요청"), + INSPECT_ING("검수진행중"), + INSPECT_COMPLETE("검수완료"); - private final String sheetId; - private int count; + private String desc; - public void decrease(int amount) { - this.count -= amount; + @Override + public String getId() { + return name(); + } + + @Override + public String getText() { + return desc; + } + } + + @CodeExpose + @Getter + @AllArgsConstructor + public enum LabelState implements EnumType { + ASSIGNED("배정"), + SKIP("스킵"), + COMPLETE("완료"); + + private String desc; + + @Override + public String getId() { + return name(); + } + + @Override + public String getText() { + return desc; + } + } + + @CodeExpose + @Getter + @AllArgsConstructor + public enum InspectState implements EnumType { + UNCONFIRM("미확인"), + EXCEPT("제외"), + COMPLETE("완료"); + + private String desc; + + @Override + public String getId() { + return name(); + } + + @Override + public String getText() { + return desc; } } @@ -25,22 +83,36 @@ public class LabelAllocateDto { private final String userId; private final int demand; - private final List assigned = new ArrayList<>(); - private int allocated = 0; - @Setter private int shortage = 0; public TargetUser(String userId, int demand) { this.userId = userId; this.demand = demand; } + } - public int getRemainDemand() { - return demand - allocated; - } + @Getter + @AllArgsConstructor + public static class TargetInspector { - public void assign(String sheetId, int count) { - assigned.add(new Sheet(sheetId, count)); - allocated += count; - } + private final String inspectorUid; + private int userCount; + } + + @Getter + @Setter + @AllArgsConstructor + public static class Basic { + + private UUID assignmentUid; + private Long inferenceGeomUid; + private String workerUid; + private String inspectorUid; + private String workState; + private Character stagnationYn; + private String assignGroupId; + private Long learnGeomUid; + private Long analUid; + 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 59cdbd9a..28dfa8ad 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 @@ -1,12 +1,12 @@ package com.kamco.cd.kamcoback.label.service; -import com.kamco.cd.kamcoback.label.dto.LabelAllocateDto.Sheet; +import com.kamco.cd.kamcoback.label.dto.LabelAllocateDto; +import com.kamco.cd.kamcoback.label.dto.LabelAllocateDto.TargetInspector; import com.kamco.cd.kamcoback.label.dto.LabelAllocateDto.TargetUser; import com.kamco.cd.kamcoback.postgres.core.LabelAllocateCoreService; import jakarta.transaction.Transactional; -import java.util.Comparator; -import java.util.Iterator; import java.util.List; +import java.util.Objects; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -20,91 +20,30 @@ public class LabelAllocateService { this.labelAllocateCoreService = labelAllocateCoreService; } - /** - * 도엽 count 수와 할당된 count 수를 비교해서 많은 수부터 먼저 배정하고 나머지를 분배 배정하는 로직 - * - * @param sheets - * @param targetUsers - */ - public static void allocateSheetCount(List sheets, List targetUsers) { - - // 1️⃣ 실제 도엽 기준 할당 - allocateSheets(sheets, targetUsers); - - // 2️⃣ 전체 부족분 비율 계산 - distributeShortage(targetUsers); // 항상 마지막에 한 번만 호출해야함 - } - - public static void allocateSheets(List sheets, List targets) { - // 도엽 큰 것부터 (선택 사항) - sheets.sort(Comparator.comparingInt(Sheet::getCount).reversed()); - - for (TargetUser target : targets) { - Iterator it = sheets.iterator(); - - while (it.hasNext() && target.getRemainDemand() > 0) { - Sheet sheet = it.next(); - - int assignable = Math.min(sheet.getCount(), target.getRemainDemand()); - - if (assignable > 0) { - target.assign(sheet.getSheetId(), assignable); - sheet.decrease(assignable); - } - - if (sheet.getCount() == 0) { - it.remove(); - } - } - } - } - - public static void distributeShortage(List targets) { - - int totalDemand = targets.stream().mapToInt(TargetUser::getDemand).sum(); - - int totalAllocated = targets.stream().mapToInt(t -> t.getAllocated()).sum(); - - int shortage = totalDemand - totalAllocated; - - if (shortage <= 0) { - return; - } - - int distributed = 0; - - for (int i = 0; i < targets.size(); i++) { - TargetUser t = targets.get(i); - - // 마지막 타겟이 나머지 가져가게 (반올림 오차 방지) - int share; - if (i == targets.size() - 1) { - share = shortage - distributed; - } else { - double ratio = (double) t.getDemand() / totalDemand; - share = (int) Math.round(shortage * ratio); - distributed += share; - } - - t.setShortage(share); - } - } - /** * 도엽 기준 asc sorting 해서 할당 수만큼 배정하는 로직 * * @param targetUsers */ @Transactional - public void allocateAsc(List targetUsers) { + public void allocateAsc(List targetUsers, List targetInspectors) { Long lastId = null; + //geom 잔여건수 != 프론트에서 넘어 온 총 건수 -> return + Long chargeCnt = labelAllocateCoreService.findLabelUnAssignedCnt(3L); //TODO + Long totalDemand = targetUsers.stream().mapToLong(TargetUser::getDemand).sum(); + if (!Objects.equals(chargeCnt, totalDemand)) { + log.info("chargeCnt != totalDemand"); + return; + } + + //라벨러에게 건수만큼 할당 for (TargetUser target : targetUsers) { int remaining = target.getDemand(); while (remaining > 0) { - int batchSize = Math.min(remaining, 500); + int batchSize = Math.min(remaining, 100); List ids = labelAllocateCoreService.fetchNextIds(lastId, batchSize); if (ids.isEmpty()) { @@ -112,11 +51,29 @@ public class LabelAllocateService { } labelAllocateCoreService.assignOwner( - ids, Long.valueOf(target.getUserId())); // TODO : userId를 숫자값을 가져올지 사번을 가져올지 고민 + ids, target.getUserId()); remaining -= ids.size(); lastId = ids.get(ids.size() - 1); } } + + //검수자에게 userCount명 만큼 할당 + List list = labelAllocateCoreService.findAssignedLabelerList(3L); + int reviewerIndex = 0; + int count = 0; + log.info("list : " + list.size()); + + for (LabelAllocateDto.Basic labeler : list) { + TargetInspector inspector = targetInspectors.get(reviewerIndex); + labelAllocateCoreService.assignInspector(labeler.getAssignmentUid(), inspector.getInspectorUid()); + count++; + + if (count == inspector.getUserCount()) { + reviewerIndex++; + count = 0; + } + } + } } 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 d4f3da10..3d7ce013 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 @@ -1,7 +1,10 @@ package com.kamco.cd.kamcoback.postgres.core; +import com.kamco.cd.kamcoback.label.dto.LabelAllocateDto; +import com.kamco.cd.kamcoback.postgres.entity.LabelingAssignmentEntity; import com.kamco.cd.kamcoback.postgres.repository.label.LabelAllocateRepository; import java.util.List; +import java.util.UUID; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -15,7 +18,19 @@ public class LabelAllocateCoreService { return labelAllocateRepository.fetchNextIds(lastId, batchSize); } - public Long assignOwner(List ids, Long userId) { - return labelAllocateRepository.assignOwner(ids, userId); + public void assignOwner(List ids, String userId) { + labelAllocateRepository.assignOwner(ids, userId); + } + + public List findAssignedLabelerList(Long analUid) { + return labelAllocateRepository.findAssignedLabelerList(analUid).stream().map(LabelingAssignmentEntity::toDto).toList(); + } + + public Long findLabelUnAssignedCnt(Long analUid) { + return labelAllocateRepository.findLabelUnAssignedCnt(analUid); + } + + public void assignInspector(UUID assignmentUid, String inspectorUid) { + labelAllocateRepository.assignInspector(assignmentUid, inspectorUid); } } diff --git a/src/main/java/com/kamco/cd/kamcoback/postgres/entity/LabelingAssignmentEntity.java b/src/main/java/com/kamco/cd/kamcoback/postgres/entity/LabelingAssignmentEntity.java new file mode 100644 index 00000000..513b513e --- /dev/null +++ b/src/main/java/com/kamco/cd/kamcoback/postgres/entity/LabelingAssignmentEntity.java @@ -0,0 +1,57 @@ +package com.kamco.cd.kamcoback.postgres.entity; + +import com.kamco.cd.kamcoback.label.dto.LabelAllocateDto; +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_assignment") +public class LabelingAssignmentEntity extends CommonDateEntity { + + @Id + @Column(name = "assignment_uid") + private UUID assignmentUid; + + @Column(name = "inference_geom_uid") + private Long inferenceGeomUid; + + @Column(name = "worker_uid") + private String workerUid; + + @Column(name = "inspector_uid") + private String inspectorUid; + + @Column(name = "work_state") + private String workState; + + @Column(name = "stagnation_yn") + private Character stagnationYn; + + @Column(name = "assign_group_id") + private String assignGroupId; + + @Column(name = "learn_geom_uid") + private Long learnGeomUid; + + @Column(name = "anal_uid") + private Long analUid; + + 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()); + } +} 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 fa4ae5e5..0fed2538 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 @@ -1,10 +1,18 @@ package com.kamco.cd.kamcoback.postgres.repository.label; +import com.kamco.cd.kamcoback.postgres.entity.LabelingAssignmentEntity; import java.util.List; +import java.util.UUID; public interface LabelAllocateRepositoryCustom { List fetchNextIds(Long lastId, int batchSize); - Long assignOwner(List ids, Long userId); + void assignOwner(List ids, String userId); + + List findAssignedLabelerList(Long analUid); + + Long findLabelUnAssignedCnt(Long analUid); + + void assignInspector(UUID assignmentUid, String userId); } 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 dfdec9ae..6b33ad4e 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 @@ -1,12 +1,22 @@ 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.QMapSheetAnalDataInferenceGeomEntity.mapSheetAnalDataInferenceGeomEntity; +import static com.kamco.cd.kamcoback.postgres.entity.QMapSheetAnalEntity.mapSheetAnalEntity; +import com.kamco.cd.kamcoback.label.dto.LabelAllocateDto.LabelState; +import com.kamco.cd.kamcoback.postgres.entity.LabelingAssignmentEntity; import com.kamco.cd.kamcoback.postgres.entity.MapSheetAnalDataGeomEntity; +import com.kamco.cd.kamcoback.postgres.entity.MapSheetAnalEntity; import com.querydsl.core.types.dsl.Expressions; import com.querydsl.core.types.dsl.StringExpression; import com.querydsl.jpa.impl.JPAQueryFactory; +import jakarta.persistence.EntityManager; +import jakarta.persistence.EntityNotFoundException; +import jakarta.persistence.PersistenceContext; import java.util.List; +import java.util.Objects; +import java.util.UUID; import lombok.extern.slf4j.Slf4j; import org.springframework.data.jpa.repository.support.QuerydslRepositorySupport; import org.springframework.stereotype.Repository; @@ -14,11 +24,14 @@ import org.springframework.stereotype.Repository; @Slf4j @Repository public class LabelAllocateRepositoryImpl extends QuerydslRepositorySupport - implements LabelAllocateRepositoryCustom { + implements LabelAllocateRepositoryCustom { private final JPAQueryFactory queryFactory; private final StringExpression NULL_STRING = Expressions.stringTemplate("cast(null as text)"); + @PersistenceContext + private EntityManager em; + public LabelAllocateRepositoryImpl(JPAQueryFactory queryFactory) { super(MapSheetAnalDataGeomEntity.class); this.queryFactory = queryFactory; @@ -26,26 +39,100 @@ public class LabelAllocateRepositoryImpl extends QuerydslRepositorySupport @Override public List fetchNextIds(Long lastId, int batchSize) { + return queryFactory - .select(mapSheetAnalDataInferenceGeomEntity.geoUid) - .from(mapSheetAnalDataInferenceGeomEntity) - .where( - // mapSheetAnalDataGeomEntity.pnu.isNotNull(), //TODO: Mockup 진행 이후 확인하기 - lastId == null ? null : mapSheetAnalDataInferenceGeomEntity.geoUid.gt(lastId), - mapSheetAnalDataInferenceGeomEntity.compareYyyy.eq(2022), - mapSheetAnalDataInferenceGeomEntity.targetYyyy.eq(2024), - mapSheetAnalDataInferenceGeomEntity.labelerUid.isNull()) - .orderBy(mapSheetAnalDataInferenceGeomEntity.geoUid.asc()) - .limit(batchSize) - .fetch(); + .select(mapSheetAnalDataInferenceGeomEntity.geoUid) + .from(mapSheetAnalDataInferenceGeomEntity) + .where( + // mapSheetAnalDataGeomEntity.pnu.isNotNull(), //TODO: Mockup 진행 이후 확인하기 + lastId == null ? null : mapSheetAnalDataInferenceGeomEntity.geoUid.gt(lastId), + mapSheetAnalDataInferenceGeomEntity.compareYyyy.eq(2022), + mapSheetAnalDataInferenceGeomEntity.targetYyyy.eq(2024), + mapSheetAnalDataInferenceGeomEntity.labelState.isNull()) + .orderBy(mapSheetAnalDataInferenceGeomEntity.geoUid.asc()) + .limit(batchSize) + .fetch(); } @Override - public Long assignOwner(List ids, Long userId) { - return queryFactory - .update(mapSheetAnalDataInferenceGeomEntity) - .set(mapSheetAnalDataInferenceGeomEntity.labelerUid, userId) - .where(mapSheetAnalDataInferenceGeomEntity.geoUid.in(ids)) + public void assignOwner(List ids, String userId) { + + //data_geom 테이블에 label state 를 ASSIGNED 로 update + queryFactory + .update(mapSheetAnalDataInferenceGeomEntity) + .set(mapSheetAnalDataInferenceGeomEntity.labelState, LabelState.ASSIGNED.getId()) + .where(mapSheetAnalDataInferenceGeomEntity.geoUid.in(ids)) + .execute(); + + //라벨러 할당 테이블에 insert + for (Long geoUid : ids) { + queryFactory + .insert(labelingAssignmentEntity) + .columns( + labelingAssignmentEntity.assignmentUid, + labelingAssignmentEntity.inferenceGeomUid, + labelingAssignmentEntity.workerUid, + labelingAssignmentEntity.workState, + labelingAssignmentEntity.assignGroupId, + labelingAssignmentEntity.analUid + ) + .values( + UUID.randomUUID(), + geoUid, + userId, + LabelState.ASSIGNED.getId(), + "", //TODO: 도엽번호 + 3 + ) .execute(); + } + + em.flush(); + em.clear(); + } + + @Override + public List findAssignedLabelerList(Long analUid) { + return queryFactory + .selectFrom(labelingAssignmentEntity) + .where( + labelingAssignmentEntity.analUid.eq(analUid), + labelingAssignmentEntity.workState.eq(LabelState.ASSIGNED.getId()), + labelingAssignmentEntity.inspectorUid.isNull() + ) + .orderBy(labelingAssignmentEntity.workerUid.asc()) + .fetch(); + } + + @Override + public Long findLabelUnAssignedCnt(Long analUid) { + MapSheetAnalEntity entity = queryFactory.selectFrom(mapSheetAnalEntity).where(mapSheetAnalEntity.id.eq(analUid)).fetchOne(); + + if (Objects.isNull(entity)) { + throw new EntityNotFoundException(); + } + + return queryFactory + .select(mapSheetAnalDataInferenceGeomEntity.geoUid.count()) + .from(mapSheetAnalDataInferenceGeomEntity) + .where( + mapSheetAnalDataInferenceGeomEntity.compareYyyy.eq(entity.getCompareYyyy()), + mapSheetAnalDataInferenceGeomEntity.targetYyyy.eq(entity.getTargetYyyy()), + mapSheetAnalDataInferenceGeomEntity.stage.eq(4), //TODO: 회차 컬럼을 가져와야 할 듯? + mapSheetAnalDataInferenceGeomEntity.labelState.isNull() + ) + .fetchOne() + ; + } + + @Override + public void assignInspector(UUID assignmentUid, String inspectorUid) { + queryFactory + .update(labelingAssignmentEntity) + .set(labelingAssignmentEntity.inspectorUid, inspectorUid) + .where( + labelingAssignmentEntity.assignmentUid.eq(assignmentUid) + ) + .execute(); } } From fd5d0976fc38c4dc5f06bda7403bcfdc95ba16ca Mon Sep 17 00:00:00 2001 From: "gayoun.park" Date: Fri, 2 Jan 2026 18:17:46 +0900 Subject: [PATCH 2/3] =?UTF-8?q?=ED=95=A0=EB=8B=B9=20=EA=B0=80=EB=8A=A5?= =?UTF-8?q?=ED=95=9C=20=EB=9D=BC=EB=B2=A8=EB=9F=AC,=20=EA=B2=80=EC=88=98?= =?UTF-8?q?=EC=9E=90=20=EB=AA=A9=EB=A1=9D=20API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../label/LabelAllocateApiController.java | 37 ++++- .../kamcoback/label/dto/LabelAllocateDto.java | 10 ++ .../label/service/LabelAllocateService.java | 18 ++- .../core/LabelAllocateCoreService.java | 9 +- .../entity/LabelingAssignmentEntity.java | 22 +-- .../label/LabelAllocateRepositoryCustom.java | 3 + .../label/LabelAllocateRepositoryImpl.java | 141 ++++++++++-------- 7 files changed, 156 insertions(+), 84 deletions(-) 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 243965a2..868cd29c 100644 --- a/src/main/java/com/kamco/cd/kamcoback/label/LabelAllocateApiController.java +++ b/src/main/java/com/kamco/cd/kamcoback/label/LabelAllocateApiController.java @@ -1,17 +1,25 @@ package com.kamco.cd.kamcoback.label; +import com.kamco.cd.kamcoback.code.dto.CommonCodeDto; import com.kamco.cd.kamcoback.config.api.ApiResponseDto; import com.kamco.cd.kamcoback.label.dto.LabelAllocateDto; import com.kamco.cd.kamcoback.label.dto.LabelAllocateDto.TargetInspector; import com.kamco.cd.kamcoback.label.dto.LabelAllocateDto.TargetUser; import com.kamco.cd.kamcoback.label.service.LabelAllocateService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.swagger.v3.oas.annotations.tags.Tag; import java.util.List; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @Slf4j @@ -23,13 +31,38 @@ public class LabelAllocateApiController { private final LabelAllocateService labelAllocateService; + @Operation(summary = "배정 가능한 사용자 목록 조회", description = "배정 가능한 사용자 목록 조회") + @ApiResponses( + value = { + @ApiResponse( + responseCode = "200", + description = "조회 성공", + content = + @Content( + mediaType = "application/json", + schema = @Schema(implementation = CommonCodeDto.Basic.class))), + @ApiResponse(responseCode = "404", description = "코드를 찾을 수 없음", content = @Content), + @ApiResponse(responseCode = "500", description = "서버 오류", content = @Content) + }) + @GetMapping("/avail-user") + public ApiResponseDto> availUserList(@RequestParam @Schema() String role) { + return ApiResponseDto.ok(labelAllocateService.availUserList(role)); + } + // 라벨링 수량 할당하는 로직 테스트 @PostMapping("/allocate") public ApiResponseDto labelAllocate(@RequestBody LabelAllocateDto dto) { List targets = - List.of(new TargetUser("1234567", 1000), new TargetUser("2345678", 400), new TargetUser("3456789", 440)); - List inspectors = List.of(new TargetInspector("9876543", 1000), new TargetInspector("8765432", 340), new TargetInspector("98765432", 500)); + List.of( + new TargetUser("1234567", 1000), + new TargetUser("2345678", 400), + new TargetUser("3456789", 440)); + List inspectors = + List.of( + new TargetInspector("9876543", 1000), + new TargetInspector("8765432", 340), + new TargetInspector("98765432", 500)); labelAllocateService.allocateAsc(targets, inspectors); return ApiResponseDto.ok(null); 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 b3509a38..d1705e92 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 @@ -115,4 +115,14 @@ public class LabelAllocateDto { private ZonedDateTime createdDttm; private ZonedDateTime updatedDttm; } + + @Getter + @Setter + @AllArgsConstructor + public static class UserList { + + private String userRole; + private String employeeNo; + private String name; + } } 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 28dfa8ad..6d273a95 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 @@ -3,6 +3,7 @@ package com.kamco.cd.kamcoback.label.service; import com.kamco.cd.kamcoback.label.dto.LabelAllocateDto; import com.kamco.cd.kamcoback.label.dto.LabelAllocateDto.TargetInspector; import com.kamco.cd.kamcoback.label.dto.LabelAllocateDto.TargetUser; +import com.kamco.cd.kamcoback.label.dto.LabelAllocateDto.UserList; import com.kamco.cd.kamcoback.postgres.core.LabelAllocateCoreService; import jakarta.transaction.Transactional; import java.util.List; @@ -29,15 +30,15 @@ public class LabelAllocateService { public void allocateAsc(List targetUsers, List targetInspectors) { Long lastId = null; - //geom 잔여건수 != 프론트에서 넘어 온 총 건수 -> return - Long chargeCnt = labelAllocateCoreService.findLabelUnAssignedCnt(3L); //TODO + // geom 잔여건수 != 프론트에서 넘어 온 총 건수 -> return + Long chargeCnt = labelAllocateCoreService.findLabelUnAssignedCnt(3L); // TODO Long totalDemand = targetUsers.stream().mapToLong(TargetUser::getDemand).sum(); if (!Objects.equals(chargeCnt, totalDemand)) { log.info("chargeCnt != totalDemand"); return; } - //라벨러에게 건수만큼 할당 + // 라벨러에게 건수만큼 할당 for (TargetUser target : targetUsers) { int remaining = target.getDemand(); @@ -50,15 +51,14 @@ public class LabelAllocateService { return; // 더이상 할당할 데이터가 없으면 return } - labelAllocateCoreService.assignOwner( - ids, target.getUserId()); + labelAllocateCoreService.assignOwner(ids, target.getUserId()); remaining -= ids.size(); lastId = ids.get(ids.size() - 1); } } - //검수자에게 userCount명 만큼 할당 + // 검수자에게 userCount명 만큼 할당 List list = labelAllocateCoreService.findAssignedLabelerList(3L); int reviewerIndex = 0; int count = 0; @@ -66,7 +66,8 @@ public class LabelAllocateService { for (LabelAllocateDto.Basic labeler : list) { TargetInspector inspector = targetInspectors.get(reviewerIndex); - labelAllocateCoreService.assignInspector(labeler.getAssignmentUid(), inspector.getInspectorUid()); + labelAllocateCoreService.assignInspector( + labeler.getAssignmentUid(), inspector.getInspectorUid()); count++; if (count == inspector.getUserCount()) { @@ -74,6 +75,9 @@ public class LabelAllocateService { count = 0; } } + } + public List availUserList(String role) { + return labelAllocateCoreService.availUserList(role); } } 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 3d7ce013..1d8fa075 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 @@ -1,6 +1,7 @@ package com.kamco.cd.kamcoback.postgres.core; import com.kamco.cd.kamcoback.label.dto.LabelAllocateDto; +import com.kamco.cd.kamcoback.label.dto.LabelAllocateDto.UserList; import com.kamco.cd.kamcoback.postgres.entity.LabelingAssignmentEntity; import com.kamco.cd.kamcoback.postgres.repository.label.LabelAllocateRepository; import java.util.List; @@ -23,7 +24,9 @@ public class LabelAllocateCoreService { } public List findAssignedLabelerList(Long analUid) { - return labelAllocateRepository.findAssignedLabelerList(analUid).stream().map(LabelingAssignmentEntity::toDto).toList(); + return labelAllocateRepository.findAssignedLabelerList(analUid).stream() + .map(LabelingAssignmentEntity::toDto) + .toList(); } public Long findLabelUnAssignedCnt(Long analUid) { @@ -33,4 +36,8 @@ public class LabelAllocateCoreService { public void assignInspector(UUID assignmentUid, String inspectorUid) { labelAllocateRepository.assignInspector(assignmentUid, inspectorUid); } + + public List availUserList(String role) { + return labelAllocateRepository.availUserList(role); + } } diff --git a/src/main/java/com/kamco/cd/kamcoback/postgres/entity/LabelingAssignmentEntity.java b/src/main/java/com/kamco/cd/kamcoback/postgres/entity/LabelingAssignmentEntity.java index 513b513e..22afd4aa 100644 --- a/src/main/java/com/kamco/cd/kamcoback/postgres/entity/LabelingAssignmentEntity.java +++ b/src/main/java/com/kamco/cd/kamcoback/postgres/entity/LabelingAssignmentEntity.java @@ -42,16 +42,16 @@ public class LabelingAssignmentEntity extends CommonDateEntity { 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()); } } 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 0fed2538..659f5c7b 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 @@ -1,5 +1,6 @@ package com.kamco.cd.kamcoback.postgres.repository.label; +import com.kamco.cd.kamcoback.label.dto.LabelAllocateDto.UserList; import com.kamco.cd.kamcoback.postgres.entity.LabelingAssignmentEntity; import java.util.List; import java.util.UUID; @@ -15,4 +16,6 @@ public interface LabelAllocateRepositoryCustom { Long findLabelUnAssignedCnt(Long analUid); void assignInspector(UUID assignmentUid, String userId); + + List availUserList(String role); } 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 6b33ad4e..f9e419d2 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 @@ -3,11 +3,15 @@ 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.QMapSheetAnalDataInferenceGeomEntity.mapSheetAnalDataInferenceGeomEntity; import static com.kamco.cd.kamcoback.postgres.entity.QMapSheetAnalEntity.mapSheetAnalEntity; +import static com.kamco.cd.kamcoback.postgres.entity.QMemberEntity.memberEntity; +import com.kamco.cd.kamcoback.label.dto.LabelAllocateDto; import com.kamco.cd.kamcoback.label.dto.LabelAllocateDto.LabelState; +import com.kamco.cd.kamcoback.label.dto.LabelAllocateDto.UserList; import com.kamco.cd.kamcoback.postgres.entity.LabelingAssignmentEntity; import com.kamco.cd.kamcoback.postgres.entity.MapSheetAnalDataGeomEntity; import com.kamco.cd.kamcoback.postgres.entity.MapSheetAnalEntity; +import com.querydsl.core.types.Projections; import com.querydsl.core.types.dsl.Expressions; import com.querydsl.core.types.dsl.StringExpression; import com.querydsl.jpa.impl.JPAQueryFactory; @@ -24,13 +28,12 @@ import org.springframework.stereotype.Repository; @Slf4j @Repository public class LabelAllocateRepositoryImpl extends QuerydslRepositorySupport - implements LabelAllocateRepositoryCustom { + implements LabelAllocateRepositoryCustom { private final JPAQueryFactory queryFactory; private final StringExpression NULL_STRING = Expressions.stringTemplate("cast(null as text)"); - @PersistenceContext - private EntityManager em; + @PersistenceContext private EntityManager em; public LabelAllocateRepositoryImpl(JPAQueryFactory queryFactory) { super(MapSheetAnalDataGeomEntity.class); @@ -41,50 +44,48 @@ public class LabelAllocateRepositoryImpl extends QuerydslRepositorySupport public List fetchNextIds(Long lastId, int batchSize) { return queryFactory - .select(mapSheetAnalDataInferenceGeomEntity.geoUid) - .from(mapSheetAnalDataInferenceGeomEntity) - .where( - // mapSheetAnalDataGeomEntity.pnu.isNotNull(), //TODO: Mockup 진행 이후 확인하기 - lastId == null ? null : mapSheetAnalDataInferenceGeomEntity.geoUid.gt(lastId), - mapSheetAnalDataInferenceGeomEntity.compareYyyy.eq(2022), - mapSheetAnalDataInferenceGeomEntity.targetYyyy.eq(2024), - mapSheetAnalDataInferenceGeomEntity.labelState.isNull()) - .orderBy(mapSheetAnalDataInferenceGeomEntity.geoUid.asc()) - .limit(batchSize) - .fetch(); + .select(mapSheetAnalDataInferenceGeomEntity.geoUid) + .from(mapSheetAnalDataInferenceGeomEntity) + .where( + // mapSheetAnalDataGeomEntity.pnu.isNotNull(), //TODO: Mockup 진행 이후 확인하기 + lastId == null ? null : mapSheetAnalDataInferenceGeomEntity.geoUid.gt(lastId), + mapSheetAnalDataInferenceGeomEntity.compareYyyy.eq(2022), + mapSheetAnalDataInferenceGeomEntity.targetYyyy.eq(2024), + mapSheetAnalDataInferenceGeomEntity.labelState.isNull()) + .orderBy(mapSheetAnalDataInferenceGeomEntity.geoUid.asc()) + .limit(batchSize) + .fetch(); } @Override public void assignOwner(List ids, String userId) { - //data_geom 테이블에 label state 를 ASSIGNED 로 update + // data_geom 테이블에 label state 를 ASSIGNED 로 update queryFactory - .update(mapSheetAnalDataInferenceGeomEntity) - .set(mapSheetAnalDataInferenceGeomEntity.labelState, LabelState.ASSIGNED.getId()) - .where(mapSheetAnalDataInferenceGeomEntity.geoUid.in(ids)) - .execute(); + .update(mapSheetAnalDataInferenceGeomEntity) + .set(mapSheetAnalDataInferenceGeomEntity.labelState, LabelState.ASSIGNED.getId()) + .where(mapSheetAnalDataInferenceGeomEntity.geoUid.in(ids)) + .execute(); - //라벨러 할당 테이블에 insert + // 라벨러 할당 테이블에 insert for (Long geoUid : ids) { queryFactory - .insert(labelingAssignmentEntity) - .columns( - labelingAssignmentEntity.assignmentUid, - labelingAssignmentEntity.inferenceGeomUid, - labelingAssignmentEntity.workerUid, - labelingAssignmentEntity.workState, - labelingAssignmentEntity.assignGroupId, - labelingAssignmentEntity.analUid - ) - .values( - UUID.randomUUID(), - geoUid, - userId, - LabelState.ASSIGNED.getId(), - "", //TODO: 도엽번호 - 3 - ) - .execute(); + .insert(labelingAssignmentEntity) + .columns( + labelingAssignmentEntity.assignmentUid, + labelingAssignmentEntity.inferenceGeomUid, + labelingAssignmentEntity.workerUid, + labelingAssignmentEntity.workState, + labelingAssignmentEntity.assignGroupId, + labelingAssignmentEntity.analUid) + .values( + UUID.randomUUID(), + geoUid, + userId, + LabelState.ASSIGNED.getId(), + "", // TODO: 도엽번호 + 3) + .execute(); } em.flush(); @@ -94,45 +95,59 @@ public class LabelAllocateRepositoryImpl extends QuerydslRepositorySupport @Override public List findAssignedLabelerList(Long analUid) { return queryFactory - .selectFrom(labelingAssignmentEntity) - .where( - labelingAssignmentEntity.analUid.eq(analUid), - labelingAssignmentEntity.workState.eq(LabelState.ASSIGNED.getId()), - labelingAssignmentEntity.inspectorUid.isNull() - ) - .orderBy(labelingAssignmentEntity.workerUid.asc()) - .fetch(); + .selectFrom(labelingAssignmentEntity) + .where( + labelingAssignmentEntity.analUid.eq(analUid), + labelingAssignmentEntity.workState.eq(LabelState.ASSIGNED.getId()), + labelingAssignmentEntity.inspectorUid.isNull()) + .orderBy(labelingAssignmentEntity.workerUid.asc()) + .fetch(); } @Override public Long findLabelUnAssignedCnt(Long analUid) { - MapSheetAnalEntity entity = queryFactory.selectFrom(mapSheetAnalEntity).where(mapSheetAnalEntity.id.eq(analUid)).fetchOne(); + MapSheetAnalEntity entity = + queryFactory + .selectFrom(mapSheetAnalEntity) + .where(mapSheetAnalEntity.id.eq(analUid)) + .fetchOne(); if (Objects.isNull(entity)) { throw new EntityNotFoundException(); } return queryFactory - .select(mapSheetAnalDataInferenceGeomEntity.geoUid.count()) - .from(mapSheetAnalDataInferenceGeomEntity) - .where( - mapSheetAnalDataInferenceGeomEntity.compareYyyy.eq(entity.getCompareYyyy()), - mapSheetAnalDataInferenceGeomEntity.targetYyyy.eq(entity.getTargetYyyy()), - mapSheetAnalDataInferenceGeomEntity.stage.eq(4), //TODO: 회차 컬럼을 가져와야 할 듯? - mapSheetAnalDataInferenceGeomEntity.labelState.isNull() - ) - .fetchOne() - ; + .select(mapSheetAnalDataInferenceGeomEntity.geoUid.count()) + .from(mapSheetAnalDataInferenceGeomEntity) + .where( + mapSheetAnalDataInferenceGeomEntity.compareYyyy.eq(entity.getCompareYyyy()), + mapSheetAnalDataInferenceGeomEntity.targetYyyy.eq(entity.getTargetYyyy()), + mapSheetAnalDataInferenceGeomEntity.stage.eq(4), // TODO: 회차 컬럼을 가져와야 할 듯? + mapSheetAnalDataInferenceGeomEntity.labelState.isNull()) + .fetchOne(); } @Override public void assignInspector(UUID assignmentUid, String inspectorUid) { queryFactory - .update(labelingAssignmentEntity) - .set(labelingAssignmentEntity.inspectorUid, inspectorUid) - .where( - labelingAssignmentEntity.assignmentUid.eq(assignmentUid) - ) - .execute(); + .update(labelingAssignmentEntity) + .set(labelingAssignmentEntity.inspectorUid, inspectorUid) + .where(labelingAssignmentEntity.assignmentUid.eq(assignmentUid)) + .execute(); + } + + @Override + public List availUserList(String role) { + return queryFactory + .select( + Projections.constructor( + LabelAllocateDto.UserList.class, + memberEntity.userRole, + memberEntity.employeeNo, + memberEntity.name)) + .from(memberEntity) + .where(memberEntity.userRole.eq(role), memberEntity.status.eq("ACTIVE")) + .orderBy(memberEntity.name.asc()) + .fetch(); } } From 563d7b576851ada79571a1160afd995a3a7e8ca0 Mon Sep 17 00:00:00 2001 From: "gayoun.park" Date: Fri, 2 Jan 2026 18:18:15 +0900 Subject: [PATCH 3/3] spotless --- .../label/LabelAllocateApiController.java | 41 ++++++++++--------- 1 file changed, 21 insertions(+), 20 deletions(-) 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 868cd29c..0a52baf2 100644 --- a/src/main/java/com/kamco/cd/kamcoback/label/LabelAllocateApiController.java +++ b/src/main/java/com/kamco/cd/kamcoback/label/LabelAllocateApiController.java @@ -33,19 +33,20 @@ public class LabelAllocateApiController { @Operation(summary = "배정 가능한 사용자 목록 조회", description = "배정 가능한 사용자 목록 조회") @ApiResponses( - value = { - @ApiResponse( - responseCode = "200", - description = "조회 성공", - content = - @Content( - mediaType = "application/json", - schema = @Schema(implementation = CommonCodeDto.Basic.class))), - @ApiResponse(responseCode = "404", description = "코드를 찾을 수 없음", content = @Content), - @ApiResponse(responseCode = "500", description = "서버 오류", content = @Content) - }) + value = { + @ApiResponse( + responseCode = "200", + description = "조회 성공", + content = + @Content( + mediaType = "application/json", + schema = @Schema(implementation = CommonCodeDto.Basic.class))), + @ApiResponse(responseCode = "404", description = "코드를 찾을 수 없음", content = @Content), + @ApiResponse(responseCode = "500", description = "서버 오류", content = @Content) + }) @GetMapping("/avail-user") - public ApiResponseDto> availUserList(@RequestParam @Schema() String role) { + public ApiResponseDto> availUserList( + @RequestParam @Schema() String role) { return ApiResponseDto.ok(labelAllocateService.availUserList(role)); } @@ -54,15 +55,15 @@ public class LabelAllocateApiController { public ApiResponseDto labelAllocate(@RequestBody LabelAllocateDto dto) { List targets = - List.of( - new TargetUser("1234567", 1000), - new TargetUser("2345678", 400), - new TargetUser("3456789", 440)); + List.of( + new TargetUser("1234567", 1000), + new TargetUser("2345678", 400), + new TargetUser("3456789", 440)); List inspectors = - List.of( - new TargetInspector("9876543", 1000), - new TargetInspector("8765432", 340), - new TargetInspector("98765432", 500)); + List.of( + new TargetInspector("9876543", 1000), + new TargetInspector("8765432", 340), + new TargetInspector("98765432", 500)); labelAllocateService.allocateAsc(targets, inspectors); return ApiResponseDto.ok(null);