diff --git a/src/main/java/com/kamco/cd/kamcoback/Innopam/InnopamApiController.java b/src/main/java/com/kamco/cd/kamcoback/Innopam/InnopamApiController.java new file mode 100644 index 00000000..13bd6456 --- /dev/null +++ b/src/main/java/com/kamco/cd/kamcoback/Innopam/InnopamApiController.java @@ -0,0 +1,145 @@ +package com.kamco.cd.kamcoback.Innopam; + +import com.kamco.cd.kamcoback.Innopam.dto.DetectMastDto; +import com.kamco.cd.kamcoback.Innopam.dto.DetectMastDto.Basic; +import com.kamco.cd.kamcoback.Innopam.dto.DetectMastDto.DetectMastReq; +import com.kamco.cd.kamcoback.Innopam.dto.DetectMastDto.DetectMastSearch; +import com.kamco.cd.kamcoback.Innopam.service.DetectMastService; +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 jakarta.validation.Valid; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +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; + +@Tag(name = "이노펨 mockup API", description = "이노펨 mockup API") +@RestController +@RequiredArgsConstructor +@RequestMapping("/api/kcd/cdi/detect") +public class InnopamApiController { + + private final DetectMastService detectMastService; + + /** 탐지결과 등록 */ + @Operation(summary = "탐지결과 등록", description = "탐지결과 등록") + @ApiResponses( + value = { + @ApiResponse( + responseCode = "201", + description = "등록 성공", + content = + @Content( + mediaType = "application/json", + schema = @Schema(implementation = DetectMastReq.class))), + @ApiResponse(responseCode = "404", description = "코드를 찾을 수 없음", content = @Content), + @ApiResponse(responseCode = "500", description = "서버 오류", content = @Content) + }) + @PostMapping("/mast/regist") + public DetectMastReq setChangeDetection( + @RequestBody @Valid DetectMastDto.DetectMastReq detectMast) { + detectMastService.saveDetectMast(detectMast); + return detectMast; + } + + @Operation(summary = "탐지결과 삭제", description = "탐지결과 삭제") + @ApiResponses( + value = { + @ApiResponse( + responseCode = "201", + description = "등록 성공", + content = + @Content( + mediaType = "application/json", + schema = @Schema(implementation = DetectMastReq.class))), + @ApiResponse(responseCode = "404", description = "코드를 찾을 수 없음", content = @Content), + @ApiResponse(responseCode = "500", description = "서버 오류", content = @Content) + }) + @PostMapping("/mast/remove") + public String deleteChangeDetection(@RequestBody DetectMastReq detectMast) { + return "OK"; + } + + @Operation(summary = "탐지결과 등록목록 조회", description = "탐지결과 등록목록 조회") + @GetMapping("/mast/list") + @ApiResponses( + value = { + @ApiResponse( + responseCode = "200", + description = "목록 성공", + content = + @Content( + mediaType = "application/json", + schema = @Schema(implementation = Basic.class))), + @ApiResponse(responseCode = "404", description = "코드를 찾을 수 없음", content = @Content), + @ApiResponse(responseCode = "500", description = "서버 오류", content = @Content) + }) + public List selectChangeDetectionList( + @RequestParam(required = false) String cprsBfYr, + @RequestParam(required = false) String cprsAdYr, + @RequestParam(required = false) Integer dtctSno) { + DetectMastSearch detectMastSearch = new DetectMastSearch(); + detectMastSearch.setCprsAdYr(cprsAdYr); + detectMastSearch.setCprsBfYr(cprsBfYr); + detectMastSearch.setDtctSno(dtctSno); + return detectMastService.selectDetectMast(detectMastSearch); + } + + @Operation(summary = "탐지결과 등록목록 상세 조회", description = "탐지결과 등록목록 상세 조회") + @ApiResponses( + value = { + @ApiResponse( + responseCode = "200", + description = "목록 성공", + content = + @Content( + mediaType = "application/json", + schema = @Schema(implementation = Basic.class))), + @ApiResponse(responseCode = "404", description = "코드를 찾을 수 없음", content = @Content), + @ApiResponse(responseCode = "500", description = "서버 오류", content = @Content) + }) + @GetMapping("/mast/list/{dtctMstId}") + public Basic selectChangeDetectionDetail(@PathVariable Long dtctMstId) { + return detectMastService.selectDetectMast(dtctMstId); + } + + @Operation(summary = "탐지객체 PNU 리스트 조회", description = "탐지객체 PNU 리스트 조회") + @ApiResponses( + value = { + @ApiResponse( + responseCode = "200", + description = "목록 성공", + content = + @Content( + mediaType = "application/json", + schema = @Schema(implementation = Basic.class))), + @ApiResponse(responseCode = "404", description = "코드를 찾을 수 없음", content = @Content), + @ApiResponse(responseCode = "500", description = "서버 오류", content = @Content) + }) + @GetMapping("/pnu/{cprsBfYr}/{cprsAfYr}/{dtctSno}") + public void selectPnuList( + @PathVariable String cprsBfYr, @PathVariable String cprsAfYr, @PathVariable String dtctSno) { + DetectMastSearch detectMastSearch = new DetectMastSearch(); + detectMastSearch.setCprsAdYr(cprsAfYr); + detectMastSearch.setCprsBfYr(cprsBfYr); + detectMastSearch.setDtctSno(Integer.parseInt(dtctSno)); + detectMastService.findPnuData(detectMastSearch); + } + + /** + * 탐지객체 PNU 단건 조회 + * + * @param detectMast + */ + @GetMapping("/pnu/{cprsBfYr}/{cprsAfYr}/{dtctSno}/{featureId}") + public void selectPnuDetail(@RequestBody DetectMastDto detectMast) {} +} diff --git a/src/main/java/com/kamco/cd/kamcoback/Innopam/dto/DetectMastDto.java b/src/main/java/com/kamco/cd/kamcoback/Innopam/dto/DetectMastDto.java new file mode 100644 index 00000000..77ef145e --- /dev/null +++ b/src/main/java/com/kamco/cd/kamcoback/Innopam/dto/DetectMastDto.java @@ -0,0 +1,91 @@ +package com.kamco.cd.kamcoback.Innopam.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +public class DetectMastDto { + + @Getter + @Setter + @NoArgsConstructor + @AllArgsConstructor + public static class Basic { + + private Long dtctMstId; + private String cprsBfYr; + private String cprsAdYr; + private Integer dtctSno; + private String pathNm; + private String crtEpno; + private String crtIp; + } + + @Getter + @Setter + @NoArgsConstructor + @AllArgsConstructor + public static class DetectMastReq { + + @NotBlank + @Schema(description = "before 연도", example = "2023") + private String cprsBfYr; + + @NotBlank + @Schema(description = "after 연도", example = "2024") + private String cprsAdYr; + + @NotBlank + @Schema(description = "차수(회차)", example = "4") + private Integer dtctSno; + + @NotBlank + @Schema(description = "파일경로", example = "/app/detect/result/2023_2024/4") + private String pathNm; + + @NotBlank + @Schema(description = "사원번호", example = "1234567") + private String crtEpno; + + @NotBlank + @Schema(description = "아이피", example = "0.0.0.0") + private String crtIp; + } + + @Getter + @Setter + @NoArgsConstructor + @AllArgsConstructor + public static class DetectMastSearch { + + private String cprsBfYr; + private String cprsAdYr; + private Integer dtctSno; + private String featureId; + } + + /** before 연도 */ + private String cprsBfYr; + + /** after 연도 */ + private String cprsAdYr; + + /** 차수 */ + private Integer dtctSno; + + /** shp 파일경로 */ + private String pathNm; + + /** 등록한 사람 사번 */ + private String crtEpno; + + /** 등록한 사람 ip */ + private String crtIp; +} diff --git a/src/main/java/com/kamco/cd/kamcoback/Innopam/postgres/core/DetectMastCoreService.java b/src/main/java/com/kamco/cd/kamcoback/Innopam/postgres/core/DetectMastCoreService.java new file mode 100644 index 00000000..6125209b --- /dev/null +++ b/src/main/java/com/kamco/cd/kamcoback/Innopam/postgres/core/DetectMastCoreService.java @@ -0,0 +1,62 @@ +package com.kamco.cd.kamcoback.Innopam.postgres.core; + +import com.kamco.cd.kamcoback.Innopam.dto.DetectMastDto; +import com.kamco.cd.kamcoback.Innopam.dto.DetectMastDto.Basic; +import com.kamco.cd.kamcoback.Innopam.dto.DetectMastDto.DetectMastReq; +import com.kamco.cd.kamcoback.Innopam.dto.DetectMastDto.DetectMastSearch; +import com.kamco.cd.kamcoback.Innopam.postgres.entity.DetectMastEntity; +import com.kamco.cd.kamcoback.Innopam.postgres.repository.DetectMastRepository; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class DetectMastCoreService { + + private final DetectMastRepository detectMastRepository; + + public void saveDetectMast(DetectMastReq detectMast) { + DetectMastEntity detectMastEntity = new DetectMastEntity(); + detectMastEntity.setCprsBfYr(detectMast.getCprsBfYr()); + detectMastEntity.setCprsAdYr(detectMast.getCprsAdYr()); + detectMastEntity.setDtctSno(detectMast.getDtctSno()); + detectMastEntity.setPathNm(detectMast.getPathNm()); + detectMastEntity.setCrtEpno(detectMast.getCrtEpno()); + detectMastEntity.setCrtIp(detectMast.getCrtIp()); + detectMastRepository.save(detectMastEntity); + } + + public List selectDetectMast(DetectMastSearch detectMast) { + return detectMastRepository.findDetectMastList(detectMast).stream() + .map( + e -> + new DetectMastDto.Basic( + e.getId(), + e.getCprsBfYr(), + e.getCprsAdYr(), + e.getDtctSno(), + e.getPathNm(), + e.getCrtEpno(), + e.getCrtIp())) + .toList(); + } + + public Basic selectDetectMast(Long id) { + DetectMastEntity e = + detectMastRepository.findById(id).orElseThrow(() -> new RuntimeException("등록 데이터가 없습니다.")); + return new DetectMastDto.Basic( + e.getId(), + e.getCprsBfYr(), + e.getCprsAdYr(), + e.getDtctSno(), + e.getPathNm(), + e.getCrtEpno(), + e.getCrtIp()); + } + + public String findPnuData(DetectMastSearch detectMast) { + DetectMastEntity detectMastEntity = detectMastRepository.findPnuData(detectMast); + return detectMastEntity.getPathNm(); + } +} diff --git a/src/main/java/com/kamco/cd/kamcoback/Innopam/postgres/entity/DetectMastEntity.java b/src/main/java/com/kamco/cd/kamcoback/Innopam/postgres/entity/DetectMastEntity.java new file mode 100644 index 00000000..5d326b83 --- /dev/null +++ b/src/main/java/com/kamco/cd/kamcoback/Innopam/postgres/entity/DetectMastEntity.java @@ -0,0 +1,85 @@ +package com.kamco.cd.kamcoback.Innopam.postgres.entity; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.SequenceGenerator; +import jakarta.persistence.Table; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import java.time.ZonedDateTime; +import java.util.UUID; +import lombok.Getter; +import lombok.Setter; +import org.hibernate.annotations.ColumnDefault; + +@Getter +@Setter +@Entity +@Table(name = "detect_mast") +public class DetectMastEntity { + + @Id + @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "detect_mast_id_gen") + @SequenceGenerator( + name = "detect_mast_id_gen", + sequenceName = "seq_detect_mast_id", + allocationSize = 1) + @Column(name = "dtct_mst_id", nullable = false) + private Long id; + + @NotNull + @ColumnDefault("gen_random_uuid()") + @Column(name = "dtct_mst_uuid", nullable = false) + private UUID dtctMstUuid = UUID.randomUUID(); + + @Size(max = 4) + @NotNull + @Column(name = "cprs_bf_yr", nullable = false, length = 4) + private String cprsBfYr; + + @Size(max = 4) + @NotNull + @Column(name = "cprs_ad_yr", nullable = false, length = 4) + private String cprsAdYr; + + @NotNull + @Column(name = "dtct_sno", nullable = false) + private Integer dtctSno; + + @NotNull + @Column(name = "path_nm", nullable = false, length = Integer.MAX_VALUE) + private String pathNm; + + @Size(max = 50) + @Column(name = "feature_id", length = 50) + private String featureId; + + @Size(max = 30) + @NotNull + @Column(name = "crt_epno", nullable = false, length = 30) + private String crtEpno; + + @Size(max = 45) + @NotNull + @Column(name = "crt_ip", nullable = false, length = 45) + private String crtIp; + + @NotNull + @ColumnDefault("now()") + @Column(name = "crt_dttm", nullable = false) + private ZonedDateTime crtDttm = ZonedDateTime.now(); + + @Size(max = 30) + @Column(name = "chg_epno", length = 30) + private String chgEpno; + + @Size(max = 45) + @Column(name = "chg_ip", length = 45) + private String chgIp; + + @Column(name = "chg_dttm") + private ZonedDateTime chgDttm; +} diff --git a/src/main/java/com/kamco/cd/kamcoback/Innopam/postgres/repository/DetectMastRepository.java b/src/main/java/com/kamco/cd/kamcoback/Innopam/postgres/repository/DetectMastRepository.java new file mode 100644 index 00000000..c6aa24f3 --- /dev/null +++ b/src/main/java/com/kamco/cd/kamcoback/Innopam/postgres/repository/DetectMastRepository.java @@ -0,0 +1,7 @@ +package com.kamco.cd.kamcoback.Innopam.postgres.repository; + +import com.kamco.cd.kamcoback.Innopam.postgres.entity.DetectMastEntity; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface DetectMastRepository + extends JpaRepository, DetectMastRepositoryCustom {} diff --git a/src/main/java/com/kamco/cd/kamcoback/Innopam/postgres/repository/DetectMastRepositoryCustom.java b/src/main/java/com/kamco/cd/kamcoback/Innopam/postgres/repository/DetectMastRepositoryCustom.java new file mode 100644 index 00000000..93ce4fb4 --- /dev/null +++ b/src/main/java/com/kamco/cd/kamcoback/Innopam/postgres/repository/DetectMastRepositoryCustom.java @@ -0,0 +1,12 @@ +package com.kamco.cd.kamcoback.Innopam.postgres.repository; + +import com.kamco.cd.kamcoback.Innopam.dto.DetectMastDto.DetectMastSearch; +import com.kamco.cd.kamcoback.Innopam.postgres.entity.DetectMastEntity; +import java.util.List; + +public interface DetectMastRepositoryCustom { + + public List findDetectMastList(DetectMastSearch detectMast); + + public DetectMastEntity findPnuData(DetectMastSearch detectMast); +} diff --git a/src/main/java/com/kamco/cd/kamcoback/Innopam/postgres/repository/DetectMastRepositoryImpl.java b/src/main/java/com/kamco/cd/kamcoback/Innopam/postgres/repository/DetectMastRepositoryImpl.java new file mode 100644 index 00000000..c2c658e2 --- /dev/null +++ b/src/main/java/com/kamco/cd/kamcoback/Innopam/postgres/repository/DetectMastRepositoryImpl.java @@ -0,0 +1,59 @@ +package com.kamco.cd.kamcoback.Innopam.postgres.repository; + +import static com.kamco.cd.kamcoback.Innopam.postgres.entity.QDetectMastEntity.detectMastEntity; + +import com.kamco.cd.kamcoback.Innopam.dto.DetectMastDto.DetectMastSearch; +import com.kamco.cd.kamcoback.Innopam.postgres.entity.DetectMastEntity; +import com.querydsl.core.BooleanBuilder; +import com.querydsl.jpa.impl.JPAQueryFactory; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Repository; + +@Repository +@RequiredArgsConstructor +public class DetectMastRepositoryImpl implements DetectMastRepositoryCustom { + + private final JPAQueryFactory queryFactory; + + @Override + public List findDetectMastList(DetectMastSearch detectMast) { + + BooleanBuilder whereBuilder = new BooleanBuilder(); + + if (StringUtils.isNotBlank(detectMast.getCprsAdYr())) { + whereBuilder.and(detectMastEntity.cprsAdYr.eq(detectMast.getCprsAdYr())); + } + + if (StringUtils.isNotBlank(detectMast.getCprsBfYr())) { + whereBuilder.and(detectMastEntity.cprsBfYr.eq(detectMast.getCprsBfYr())); + } + + if (detectMast.getDtctSno() != null) { + whereBuilder.and(detectMastEntity.dtctSno.eq(detectMast.getDtctSno())); + } + + return queryFactory.select(detectMastEntity).from(detectMastEntity).where(whereBuilder).fetch(); + } + + @Override + public DetectMastEntity findPnuData(DetectMastSearch detectMast) { + + BooleanBuilder whereBuilder = new BooleanBuilder(); + + whereBuilder.and(detectMastEntity.cprsAdYr.eq(detectMast.getCprsAdYr())); + whereBuilder.and(detectMastEntity.cprsBfYr.eq(detectMast.getCprsBfYr())); + whereBuilder.and(detectMastEntity.dtctSno.eq(detectMast.getDtctSno())); + + if (detectMast.getFeatureId() != null) { + whereBuilder.and(detectMastEntity.featureId.eq(detectMast.getFeatureId())); + } + + return queryFactory + .select(detectMastEntity) + .from(detectMastEntity) + .where(whereBuilder) + .fetchOne(); + } +} diff --git a/src/main/java/com/kamco/cd/kamcoback/Innopam/service/DetectMastService.java b/src/main/java/com/kamco/cd/kamcoback/Innopam/service/DetectMastService.java new file mode 100644 index 00000000..9b1c9d08 --- /dev/null +++ b/src/main/java/com/kamco/cd/kamcoback/Innopam/service/DetectMastService.java @@ -0,0 +1,59 @@ +package com.kamco.cd.kamcoback.Innopam.service; + +import com.kamco.cd.kamcoback.Innopam.dto.DetectMastDto.Basic; +import com.kamco.cd.kamcoback.Innopam.dto.DetectMastDto.DetectMastReq; +import com.kamco.cd.kamcoback.Innopam.dto.DetectMastDto.DetectMastSearch; +import com.kamco.cd.kamcoback.Innopam.postgres.core.DetectMastCoreService; +import java.io.File; +import java.util.List; +import java.util.concurrent.ThreadLocalRandom; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@Transactional(readOnly = true) +@RequiredArgsConstructor +public class DetectMastService { + + private final DetectMastCoreService detectMastCoreService; + + @Transactional + public void saveDetectMast(DetectMastReq detectMast) { + detectMastCoreService.saveDetectMast(detectMast); + } + + public List selectDetectMast(DetectMastSearch detectMast) { + return detectMastCoreService.selectDetectMast(detectMast); + } + + public Basic selectDetectMast(Long id) { + return detectMastCoreService.selectDetectMast(id); + } + + public void findPnuData(DetectMastSearch detectMast) { + String pathNm = detectMastCoreService.findPnuData(detectMast); + + File dir = new File(pathNm); + if (dir.exists() && dir.isDirectory()) { + File[] files = dir.listFiles(); + + if (files != null) { + for (File file : files) { + System.out.println(file.getName()); + } + } + } + } + + public static String randomPnu() { + ThreadLocalRandom r = ThreadLocalRandom.current(); + + String lawCode = String.valueOf(r.nextLong(1000000000L, 9999999999L)); // 10자리 + String sanFlag = r.nextBoolean() ? "1" : "2"; // 산/대지 + String bon = String.format("%04d", r.nextInt(1, 10000)); // 본번 + String bu = String.format("%04d", r.nextInt(0, 10000)); // 부번 + + return lawCode + sanFlag + bon + bu; + } +} 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..5a739715 --- /dev/null +++ b/src/main/java/com/kamco/cd/kamcoback/label/LabelAllocateApiController.java @@ -0,0 +1,67 @@ +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..a6802547 --- /dev/null +++ b/src/main/java/com/kamco/cd/kamcoback/label/dto/LabelAllocateDto.java @@ -0,0 +1,46 @@ +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..547284b6 --- /dev/null +++ b/src/main/java/com/kamco/cd/kamcoback/label/service/LabelAllocateService.java @@ -0,0 +1,78 @@ +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); + } + } +} diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index 4a00ed71..579b7a42 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -74,5 +74,5 @@ mapsheet: upload: skipGdalValidation: true shp: - baseurl: /app/detect/result + baseurl: /app/tmp/detect/result