From c65a3be807b28061f2ca345fbdcc7f94bb13c0f9 Mon Sep 17 00:00:00 2001 From: "gayoun.park" Date: Wed, 31 Dec 2025 12:20:10 +0900 Subject: [PATCH] =?UTF-8?q?=EB=9D=BC=EB=B2=A8=20=EC=9E=90=EB=8F=99?= =?UTF-8?q?=ED=95=A0=EB=8B=B9=20update=20=EB=A1=9C=EC=A7=81=20->=20?= =?UTF-8?q?=EC=B6=94=ED=9B=84=20=EB=9D=BC=EB=B2=A8=EB=A7=81=20=ED=95=A0?= =?UTF-8?q?=EB=8B=B9=20=ED=85=8C=EC=9D=B4=EB=B8=94=EB=A1=9C=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=ED=95=A0=20=EC=98=88=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../label/LabelAllocateApiController.java | 62 +++++++++---------- .../label/service/LabelAllocateService.java | 46 +++++++++++++- .../core/LabelAllocateCoreService.java | 21 +++++++ .../label/LabelAllocateRepository.java | 8 +++ .../label/LabelAllocateRepositoryCustom.java | 10 +++ .../label/LabelAllocateRepositoryImpl.java | 51 +++++++++++++++ 6 files changed, 163 insertions(+), 35 deletions(-) create mode 100644 src/main/java/com/kamco/cd/kamcoback/postgres/core/LabelAllocateCoreService.java create mode 100644 src/main/java/com/kamco/cd/kamcoback/postgres/repository/label/LabelAllocateRepository.java create mode 100644 src/main/java/com/kamco/cd/kamcoback/postgres/repository/label/LabelAllocateRepositoryCustom.java create mode 100644 src/main/java/com/kamco/cd/kamcoback/postgres/repository/label/LabelAllocateRepositoryImpl.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 5a739715..5014147e 100644 --- a/src/main/java/com/kamco/cd/kamcoback/label/LabelAllocateApiController.java +++ b/src/main/java/com/kamco/cd/kamcoback/label/LabelAllocateApiController.java @@ -2,11 +2,9 @@ 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.Sheet; 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; -import java.util.ArrayList; import java.util.List; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -17,50 +15,46 @@ import org.springframework.web.bind.annotation.RestController; @Slf4j @Tag(name = "라벨링 작업 관리", description = "라벨링 작업 관리") -@RequestMapping({"/api/label/mng"}) +@RequestMapping({"/api/label"}) @RequiredArgsConstructor @RestController public class LabelAllocateApiController { + private final LabelAllocateService labelAllocateService; + // 라벨링 수량 할당하는 로직 테스트 @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 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("A", 1000), new TargetUser("B", 500), new TargetUser("C", 700)); - - LabelAllocateService.allocate(new ArrayList<>(sheets), new ArrayList<>(targets)); - - targets.forEach( - t -> { - log.info("[" + t.getUserId() + "]"); - t.getAssigned() - .forEach( - u -> { - log.info(" - 도엽: " + u.getSheetId() + " (" + u.getCount() + ")"); - }); - }); - /** - * [A] 입력한 수 : 1000 - 도엽: 2 (500) - 도엽: 6 (459) - 도엽: 5 (41) - * - *

[B] 입력한 수 : 500 - 도엽: 5 (339) - 도엽: 3 (161) - * - *

[C] 입력한 수 : 700 - 도엽: 3 (189) - 도엽: 1 (261) - 도엽: 4 (250) - */ - // A 에게 도엽 2 asc 해서 500건 할당 -> 도엽 6 asc 해서 459 할당 -> 도엽 5 asc 해서 41건 할당 -> insert - // B 에게 도엽 5 위에 41건 할당한 것 빼고 asc 해서 339건 할당 -> 도엽 3 asc 해서 161건 할당 -> insert - // .... for문에서 할당한 것 빼고 asc 해서 건수만큼 할당 insert 하고 다음 으로 넘어가기 + List.of(new TargetUser("1", 1000), new TargetUser("2", 400), new TargetUser("3", 440)); + labelAllocateService.allocateAsc(targets); return ApiResponseDto.ok(null); } 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 547284b6..59cdbd9a 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 @@ -2,6 +2,8 @@ package com.kamco.cd.kamcoback.label.service; import com.kamco.cd.kamcoback.label.dto.LabelAllocateDto.Sheet; 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; @@ -12,7 +14,19 @@ import org.springframework.stereotype.Service; @Service public class LabelAllocateService { - public static void allocate(List sheets, List targetUsers) { + private final LabelAllocateCoreService labelAllocateCoreService; + + public LabelAllocateService(LabelAllocateCoreService labelAllocateCoreService) { + this.labelAllocateCoreService = labelAllocateCoreService; + } + + /** + * 도엽 count 수와 할당된 count 수를 비교해서 많은 수부터 먼저 배정하고 나머지를 분배 배정하는 로직 + * + * @param sheets + * @param targetUsers + */ + public static void allocateSheetCount(List sheets, List targetUsers) { // 1️⃣ 실제 도엽 기준 할당 allocateSheets(sheets, targetUsers); @@ -75,4 +89,34 @@ public class LabelAllocateService { t.setShortage(share); } } + + /** + * 도엽 기준 asc sorting 해서 할당 수만큼 배정하는 로직 + * + * @param targetUsers + */ + @Transactional + public void allocateAsc(List targetUsers) { + Long lastId = null; + + for (TargetUser target : targetUsers) { + int remaining = target.getDemand(); + + while (remaining > 0) { + + int batchSize = Math.min(remaining, 500); + List ids = labelAllocateCoreService.fetchNextIds(lastId, batchSize); + + if (ids.isEmpty()) { + return; // 더이상 할당할 데이터가 없으면 return + } + + labelAllocateCoreService.assignOwner( + ids, Long.valueOf(target.getUserId())); // TODO : userId를 숫자값을 가져올지 사번을 가져올지 고민 + + remaining -= ids.size(); + lastId = ids.get(ids.size() - 1); + } + } + } } 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 new file mode 100644 index 00000000..d4f3da10 --- /dev/null +++ b/src/main/java/com/kamco/cd/kamcoback/postgres/core/LabelAllocateCoreService.java @@ -0,0 +1,21 @@ +package com.kamco.cd.kamcoback.postgres.core; + +import com.kamco.cd.kamcoback.postgres.repository.label.LabelAllocateRepository; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class LabelAllocateCoreService { + + private final LabelAllocateRepository labelAllocateRepository; + + public List fetchNextIds(Long lastId, int batchSize) { + return labelAllocateRepository.fetchNextIds(lastId, batchSize); + } + + public Long assignOwner(List ids, Long userId) { + return labelAllocateRepository.assignOwner(ids, userId); + } +} diff --git a/src/main/java/com/kamco/cd/kamcoback/postgres/repository/label/LabelAllocateRepository.java b/src/main/java/com/kamco/cd/kamcoback/postgres/repository/label/LabelAllocateRepository.java new file mode 100644 index 00000000..976a083e --- /dev/null +++ b/src/main/java/com/kamco/cd/kamcoback/postgres/repository/label/LabelAllocateRepository.java @@ -0,0 +1,8 @@ +package com.kamco.cd.kamcoback.postgres.repository.label; + +import com.kamco.cd.kamcoback.postgres.entity.MapSheetAnalDataInferenceGeomEntity; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface LabelAllocateRepository + extends JpaRepository, + LabelAllocateRepositoryCustom {} 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 new file mode 100644 index 00000000..fa4ae5e5 --- /dev/null +++ b/src/main/java/com/kamco/cd/kamcoback/postgres/repository/label/LabelAllocateRepositoryCustom.java @@ -0,0 +1,10 @@ +package com.kamco.cd.kamcoback.postgres.repository.label; + +import java.util.List; + +public interface LabelAllocateRepositoryCustom { + + List fetchNextIds(Long lastId, int batchSize); + + Long assignOwner(List ids, Long 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 new file mode 100644 index 00000000..dfdec9ae --- /dev/null +++ b/src/main/java/com/kamco/cd/kamcoback/postgres/repository/label/LabelAllocateRepositoryImpl.java @@ -0,0 +1,51 @@ +package com.kamco.cd.kamcoback.postgres.repository.label; + +import static com.kamco.cd.kamcoback.postgres.entity.QMapSheetAnalDataInferenceGeomEntity.mapSheetAnalDataInferenceGeomEntity; + +import com.kamco.cd.kamcoback.postgres.entity.MapSheetAnalDataGeomEntity; +import com.querydsl.core.types.dsl.Expressions; +import com.querydsl.core.types.dsl.StringExpression; +import com.querydsl.jpa.impl.JPAQueryFactory; +import java.util.List; +import lombok.extern.slf4j.Slf4j; +import org.springframework.data.jpa.repository.support.QuerydslRepositorySupport; +import org.springframework.stereotype.Repository; + +@Slf4j +@Repository +public class LabelAllocateRepositoryImpl extends QuerydslRepositorySupport + implements LabelAllocateRepositoryCustom { + + private final JPAQueryFactory queryFactory; + private final StringExpression NULL_STRING = Expressions.stringTemplate("cast(null as text)"); + + public LabelAllocateRepositoryImpl(JPAQueryFactory queryFactory) { + super(MapSheetAnalDataGeomEntity.class); + this.queryFactory = queryFactory; + } + + @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(); + } + + @Override + public Long assignOwner(List ids, Long userId) { + return queryFactory + .update(mapSheetAnalDataInferenceGeomEntity) + .set(mapSheetAnalDataInferenceGeomEntity.labelerUid, userId) + .where(mapSheetAnalDataInferenceGeomEntity.geoUid.in(ids)) + .execute(); + } +}