From 0f7dda261b82f483b808504e84a97f31fd0f07ab Mon Sep 17 00:00:00 2001 From: "gayoun.park" Date: Wed, 31 Dec 2025 09:10:11 +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=20=EB=A1=9C=EC=A7=81=20=EC=9E=84=EC=8B=9C=20?= =?UTF-8?q?=EC=BB=A4=EB=B0=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../label/LabelAllocateApiController.java | 76 ++++++++++++++++ .../kamcoback/label/dto/LabelAllocateDto.java | 48 ++++++++++ .../label/service/LabelAllocateService.java | 90 +++++++++++++++++++ 3 files changed, 214 insertions(+) create mode 100644 src/main/java/com/kamco/cd/kamcoback/label/LabelAllocateApiController.java create mode 100644 src/main/java/com/kamco/cd/kamcoback/label/dto/LabelAllocateDto.java create mode 100644 src/main/java/com/kamco/cd/kamcoback/label/service/LabelAllocateService.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 new file mode 100644 index 00000000..29e5ab8f --- /dev/null +++ b/src/main/java/com/kamco/cd/kamcoback/label/LabelAllocateApiController.java @@ -0,0 +1,76 @@ +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; +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.RestController; + +@Slf4j +@Tag(name = "라벨링 작업 관리", description = "라벨링 작업 관리") +@RequestMapping({"/api/label/mng"}) +@RequiredArgsConstructor +@RestController +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.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 하고 다음 으로 넘어가기 + + 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 new file mode 100644 index 00000000..612e3592 --- /dev/null +++ b/src/main/java/com/kamco/cd/kamcoback/label/dto/LabelAllocateDto.java @@ -0,0 +1,48 @@ +package com.kamco.cd.kamcoback.label.dto; + +import java.util.ArrayList; +import java.util.List; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.Setter; + +public class LabelAllocateDto { + + @Getter + @AllArgsConstructor + public static class Sheet { + + private final String sheetId; + private int count; + + public void decrease(int amount) { + this.count -= amount; + } + } + + @Getter + public static class TargetUser { + + 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; + } + + public void assign(String sheetId, int count) { + assigned.add(new Sheet(sheetId, count)); + allocated += count; + } + + } +} 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 new file mode 100644 index 00000000..9336893c --- /dev/null +++ b/src/main/java/com/kamco/cd/kamcoback/label/service/LabelAllocateService.java @@ -0,0 +1,90 @@ +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 java.util.Comparator; +import java.util.Iterator; +import java.util.List; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +@Slf4j +@Service +public class LabelAllocateService { + + public static void allocate(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); + } + } + +}