From 9c395cb0eec717b24d0e12a96d3baaba3fde04e7 Mon Sep 17 00:00:00 2001 From: "gayoun.park" Date: Fri, 9 Jan 2026 09:40:41 +0900 Subject: [PATCH 01/12] spotless --- .../model/ModelMngApiController.java | 58 +++++++++---------- .../label/LabelWorkRepositoryImpl.java | 11 ++-- 2 files changed, 34 insertions(+), 35 deletions(-) diff --git a/src/main/java/com/kamco/cd/kamcoback/model/ModelMngApiController.java b/src/main/java/com/kamco/cd/kamcoback/model/ModelMngApiController.java index 7bb50358..28914ec1 100644 --- a/src/main/java/com/kamco/cd/kamcoback/model/ModelMngApiController.java +++ b/src/main/java/com/kamco/cd/kamcoback/model/ModelMngApiController.java @@ -83,43 +83,43 @@ public class ModelMngApiController { @Operation(summary = "모델삭제", description = "모델을 삭제 합니다.") @ApiResponses( - value = { - @ApiResponse( - responseCode = "201", - description = "등록 성공", - content = - @Content( - mediaType = "application/json", - schema = @Schema(implementation = Long.class))), - @ApiResponse(responseCode = "400", description = "잘못된 요청 데이터", content = @Content), - @ApiResponse(responseCode = "404", description = "코드를 찾을 수 없음", content = @Content), - @ApiResponse(responseCode = "500", description = "서버 오류", content = @Content) - }) + value = { + @ApiResponse( + responseCode = "201", + description = "등록 성공", + content = + @Content( + mediaType = "application/json", + schema = @Schema(implementation = Long.class))), + @ApiResponse(responseCode = "400", description = "잘못된 요청 데이터", content = @Content), + @ApiResponse(responseCode = "404", description = "코드를 찾을 수 없음", content = @Content), + @ApiResponse(responseCode = "500", description = "서버 오류", content = @Content) + }) @DeleteMapping("/{uuid}") public ApiResponseDto removeModel( - @io.swagger.v3.oas.annotations.parameters.RequestBody( - description = "모델 삭제 요청 정보", - required = true) - @PathVariable - String uuid) { + @io.swagger.v3.oas.annotations.parameters.RequestBody( + description = "모델 삭제 요청 정보", + required = true) + @PathVariable + String uuid) { return ApiResponseDto.ok(modelMngService.removeModel(UUID.fromString(uuid))); } @Operation(summary = "모델등록", description = "모델을 등록 합니다.") @ApiResponses( - value = { - @ApiResponse( - responseCode = "201", - description = "등록 성공", - content = - @Content( - mediaType = "application/json", - schema = @Schema(implementation = Long.class))), - @ApiResponse(responseCode = "400", description = "잘못된 요청 데이터", content = @Content), - @ApiResponse(responseCode = "404", description = "코드를 찾을 수 없음", content = @Content), - @ApiResponse(responseCode = "500", description = "서버 오류", content = @Content) - }) + value = { + @ApiResponse( + responseCode = "201", + description = "등록 성공", + content = + @Content( + mediaType = "application/json", + schema = @Schema(implementation = Long.class))), + @ApiResponse(responseCode = "400", description = "잘못된 요청 데이터", content = @Content), + @ApiResponse(responseCode = "404", description = "코드를 찾을 수 없음", content = @Content), + @ApiResponse(responseCode = "500", description = "서버 오류", content = @Content) + }) @PostMapping public ApiResponseDto ModelMgmt( @RequestBody @Valid ModelMngDto.AddReq addReq) { diff --git a/src/main/java/com/kamco/cd/kamcoback/postgres/repository/label/LabelWorkRepositoryImpl.java b/src/main/java/com/kamco/cd/kamcoback/postgres/repository/label/LabelWorkRepositoryImpl.java index 10308aed..1ee68346 100644 --- a/src/main/java/com/kamco/cd/kamcoback/postgres/repository/label/LabelWorkRepositoryImpl.java +++ b/src/main/java/com/kamco/cd/kamcoback/postgres/repository/label/LabelWorkRepositoryImpl.java @@ -57,7 +57,6 @@ public class LabelWorkRepositoryImpl implements LabelWorkRepositoryCustom { QLabelingAssignmentEntity.labelingAssignmentEntity; private final QMemberEntity memberEntity = QMemberEntity.memberEntity; - /** * 변화탐지 년도 셀렉트박스 조회 * @@ -81,8 +80,7 @@ public class LabelWorkRepositoryImpl implements LabelWorkRepositoryCustom { } /** - * 라벨링 작업관리 목록 조회 - * (복잡한 집계 쿼리로 인해 DTO 직접 반환) + * 라벨링 작업관리 목록 조회 (복잡한 집계 쿼리로 인해 DTO 직접 반환) * * @param searchReq 검색 조건 * @return 라벨링 작업관리 목록 페이지 @@ -345,7 +343,9 @@ public class LabelWorkRepositoryImpl implements LabelWorkRepositoryCustom { mapSheetAnalInferenceEntity .uuid .eq(uuid) - .and(labelingAssignmentEntity.analUid.eq(mapSheetAnalInferenceEntity.id))) + .and( + labelingAssignmentEntity.analUid.eq( + mapSheetAnalInferenceEntity.id))) .innerJoin(memberEntity) .on(whereSubBuilder) .where(whereBuilder) @@ -390,8 +390,7 @@ public class LabelWorkRepositoryImpl implements LabelWorkRepositoryCustom { } /** - * 작업배정 상세조회 - * (복잡한 집계 쿼리로 인해 DTO 직접 반환) + * 작업배정 상세조회 (복잡한 집계 쿼리로 인해 DTO 직접 반환) * * @param uuid 작업배정 UUID * @return 작업배정 상세 정보 From 3b02296760d885cc94ab9176816a5ffa3ec4f76d Mon Sep 17 00:00:00 2001 From: "gayoun.park" Date: Fri, 9 Jan 2026 09:49:05 +0900 Subject: [PATCH 02/12] =?UTF-8?q?=EB=9D=BC=EB=B2=A8=EB=A7=81=20=EC=A2=85?= =?UTF-8?q?=EB=A3=8C=20=EC=83=81=ED=83=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/kamco/cd/kamcoback/label/dto/LabelAllocateDto.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 db764638..a945e5f0 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 @@ -27,7 +27,8 @@ public class LabelAllocateDto { LABEL_COMPLETE("라벨완료"), INSPECT_REQ("검수요청"), INSPECT_ING("검수진행중"), - INSPECT_COMPLETE("검수완료"); + INSPECT_COMPLETE("검수완료"), + FINISH("종료"); private String desc; From 3e6afde0d32ed2835f60a2acab6d1205677c408a Mon Sep 17 00:00:00 2001 From: teddy Date: Fri, 9 Jan 2026 09:50:17 +0900 Subject: [PATCH 03/12] =?UTF-8?q?[KC-99]=20=EC=B6=94=EB=A1=A0=EA=B4=80?= =?UTF-8?q?=EB=A6=AC=20=EB=93=B1=EB=A1=9D=20api=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../InferenceResultApiController.java | 27 ++++++ .../inference/dto/InferenceResultDto.java | 83 ++++++++++++++++--- .../service/InferenceResultService.java | 9 ++ .../core/InferenceResultCoreService.java | 19 +++++ .../label/LabelWorkRepositoryImpl.java | 11 ++- 5 files changed, 130 insertions(+), 19 deletions(-) diff --git a/src/main/java/com/kamco/cd/kamcoback/inference/InferenceResultApiController.java b/src/main/java/com/kamco/cd/kamcoback/inference/InferenceResultApiController.java index a5a82719..3325bd64 100644 --- a/src/main/java/com/kamco/cd/kamcoback/inference/InferenceResultApiController.java +++ b/src/main/java/com/kamco/cd/kamcoback/inference/InferenceResultApiController.java @@ -11,10 +11,13 @@ 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.time.LocalDate; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Page; 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; @@ -59,6 +62,30 @@ public class InferenceResultApiController { return ApiResponseDto.ok(analResList); } + @Operation(summary = "변화탐지 실행 정보 입력", description = "어드민 홈 > 추론관리 > 추론목록 > 변화탐지 실행 정보 입력") + @ApiResponses( + value = { + @ApiResponse( + responseCode = "201", + description = "변화탐지 실행 정보 생성 성공", + content = + @Content( + mediaType = "application/json", + schema = @Schema(implementation = Void.class))), + @ApiResponse(responseCode = "400", description = "잘못된 요청 데이터", content = @Content), + @ApiResponse(responseCode = "500", description = "서버 오류", content = @Content) + }) + @PostMapping("/reg") + public ApiResponseDto saveInferenceInfo( + @io.swagger.v3.oas.annotations.parameters.RequestBody( + description = "변화탐지 실행 정보 저장 요청 정보", + required = true) + @RequestBody + @Valid + InferenceResultDto.RegReq req) { + return ApiResponseDto.ok(null); + } + // @ApiResponses( // value = { // @ApiResponse( diff --git a/src/main/java/com/kamco/cd/kamcoback/inference/dto/InferenceResultDto.java b/src/main/java/com/kamco/cd/kamcoback/inference/dto/InferenceResultDto.java index 133b38cf..26efc23d 100644 --- a/src/main/java/com/kamco/cd/kamcoback/inference/dto/InferenceResultDto.java +++ b/src/main/java/com/kamco/cd/kamcoback/inference/dto/InferenceResultDto.java @@ -1,9 +1,14 @@ package com.kamco.cd.kamcoback.inference.dto; import com.kamco.cd.kamcoback.common.utils.enums.EnumType; +import com.kamco.cd.kamcoback.common.utils.interfaces.EnumValid; import com.kamco.cd.kamcoback.common.utils.interfaces.JsonFormatDttm; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; import java.time.LocalDate; import java.time.ZonedDateTime; +import java.util.List; import java.util.UUID; import lombok.AllArgsConstructor; import lombok.Getter; @@ -14,6 +19,7 @@ import org.springframework.data.domain.Pageable; public class InferenceResultDto { + /** 목록조회 dto */ @Getter @Setter @AllArgsConstructor @@ -32,6 +38,7 @@ public class InferenceResultDto { @JsonFormatDttm private ZonedDateTime applyDttm; } + /** 목록조회 검색 조건 dto */ @Getter @Setter @NoArgsConstructor @@ -53,6 +60,27 @@ public class InferenceResultDto { } } + /** 탐지 데이터 옵션 dto */ + @Getter + @AllArgsConstructor + public enum MapSheetScope implements EnumType { + EXCL("추론제외"), + PREV("이전 년도 도엽 사용"), + ; + private final String desc; + + @Override + public String getId() { + return name(); + } + + @Override + public String getText() { + return desc; + } + } + + /** 분석대상 도엽 enum */ @Getter @AllArgsConstructor public enum DetectOption implements EnumType { @@ -72,22 +100,51 @@ public class InferenceResultDto { } } + /** 변화탐지 실행 정보 저장 요청 정보 */ @Getter + @Setter + @NoArgsConstructor @AllArgsConstructor - public enum MapSheetScope implements EnumType { - EXCL("추론제외"), - PREV("이전 년도 도엽 사용"), - ; - private final String desc; + public static class RegReq { - @Override - public String getId() { - return name(); - } + @Schema(description = "제목", example = "2025-2026 track changes Pororo") + @NotBlank + private String title; - @Override - public String getText() { - return desc; - } + @Schema(description = "M1", example = "2") + @NotNull + private Long model1Uid; + + @Schema(description = "M2", example = "4") + @NotNull + private Long model2Uid; + + @Schema(description = "M3", example = "7") + @NotNull + private Long model3Uid; + + @Schema(description = "비교년도", example = "2003") + @NotNull + private Integer compareYyyy; + + @Schema(description = "탐지년도", example = "2004") + @NotNull + private Integer targetYyyy; + + @Schema(description = "탐지 데이터 옵션 - 추론제외(PREV), 이전 년도 도엽 사용(PREV)", example = "EXCL") + @NotBlank + @EnumValid( + enumClass = MapSheetScope.class, + message = "탐지 데이터 옵션은 '추론제외', '이전 년도 도엽 사용' 만 사용 가능합니다.") + private String mapSheetScope; + + @Schema(description = "분석대상 도엽 - 전체(ALL), 부분(PART)", example = "PART") + @NotBlank + @EnumValid(enumClass = DetectOption.class, message = "분석대상 도엽 옵션은 '전체', '부분' 만 사용 가능합니다.") + private String detectOption; + + @Schema(description = "5k 도협 번호 목록", example = "[34607067,34607067]") + @NotNull + private List mapSheetNum; } } diff --git a/src/main/java/com/kamco/cd/kamcoback/inference/service/InferenceResultService.java b/src/main/java/com/kamco/cd/kamcoback/inference/service/InferenceResultService.java index 285e1527..aa2a0daa 100644 --- a/src/main/java/com/kamco/cd/kamcoback/inference/service/InferenceResultService.java +++ b/src/main/java/com/kamco/cd/kamcoback/inference/service/InferenceResultService.java @@ -31,6 +31,15 @@ public class InferenceResultService { return inferenceResultCoreService.getInferenceResultList(req); } + /** + * 변화탐지 실행 정보 생성 + * + * @param req + */ + public void saveInferenceInfo(InferenceResultDto.RegReq req) { + inferenceResultCoreService.saveInferenceInfo(req); + } + /** * 분석결과 요약정보 * diff --git a/src/main/java/com/kamco/cd/kamcoback/postgres/core/InferenceResultCoreService.java b/src/main/java/com/kamco/cd/kamcoback/postgres/core/InferenceResultCoreService.java index 1b4443d4..34fe6e51 100644 --- a/src/main/java/com/kamco/cd/kamcoback/postgres/core/InferenceResultCoreService.java +++ b/src/main/java/com/kamco/cd/kamcoback/postgres/core/InferenceResultCoreService.java @@ -1,5 +1,6 @@ package com.kamco.cd.kamcoback.postgres.core; +import com.fasterxml.jackson.databind.ObjectMapper; import com.kamco.cd.kamcoback.inference.dto.InferenceDetailDto; import com.kamco.cd.kamcoback.inference.dto.InferenceDetailDto.Dashboard; import com.kamco.cd.kamcoback.inference.dto.InferenceDetailDto.MapSheet; @@ -16,6 +17,7 @@ import jakarta.persistence.EntityNotFoundException; import jakarta.validation.constraints.NotNull; import java.util.List; import lombok.RequiredArgsConstructor; +import org.locationtech.jts.io.geojson.GeoJsonWriter; import org.springframework.data.domain.Page; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -28,6 +30,9 @@ public class InferenceResultCoreService { private final MapSheetLearnRepository mapSheetLearnRepository; private final MapInkx5kRepository mapInkx5kRepository; + private final ObjectMapper objectMapper = new ObjectMapper(); + private final GeoJsonWriter geoJsonWriter = new GeoJsonWriter(); + /** * 추론관리 목록 * @@ -39,6 +44,20 @@ public class InferenceResultCoreService { return list.map(MapSheetLearnEntity::toDto); } + /** + * 변화탐지 실행 정보 생성 + * + * @param req + */ + public void saveInferenceInfo(InferenceResultDto.RegReq req) { + MapSheetLearnEntity mapSheetLearnEntity = new MapSheetLearnEntity(); + mapSheetLearnEntity.setTitle(req.getTitle()); + mapSheetLearnEntity.setM1ModelUid(req.getModel1Uid()); + mapSheetLearnEntity.setM2ModelUid(req.getModel2Uid()); + mapSheetLearnEntity.setM3ModelUid(req.getModel3Uid()); + // mapSheetLearnRepository.save() + } + /****/ /** diff --git a/src/main/java/com/kamco/cd/kamcoback/postgres/repository/label/LabelWorkRepositoryImpl.java b/src/main/java/com/kamco/cd/kamcoback/postgres/repository/label/LabelWorkRepositoryImpl.java index 10308aed..1ee68346 100644 --- a/src/main/java/com/kamco/cd/kamcoback/postgres/repository/label/LabelWorkRepositoryImpl.java +++ b/src/main/java/com/kamco/cd/kamcoback/postgres/repository/label/LabelWorkRepositoryImpl.java @@ -57,7 +57,6 @@ public class LabelWorkRepositoryImpl implements LabelWorkRepositoryCustom { QLabelingAssignmentEntity.labelingAssignmentEntity; private final QMemberEntity memberEntity = QMemberEntity.memberEntity; - /** * 변화탐지 년도 셀렉트박스 조회 * @@ -81,8 +80,7 @@ public class LabelWorkRepositoryImpl implements LabelWorkRepositoryCustom { } /** - * 라벨링 작업관리 목록 조회 - * (복잡한 집계 쿼리로 인해 DTO 직접 반환) + * 라벨링 작업관리 목록 조회 (복잡한 집계 쿼리로 인해 DTO 직접 반환) * * @param searchReq 검색 조건 * @return 라벨링 작업관리 목록 페이지 @@ -345,7 +343,9 @@ public class LabelWorkRepositoryImpl implements LabelWorkRepositoryCustom { mapSheetAnalInferenceEntity .uuid .eq(uuid) - .and(labelingAssignmentEntity.analUid.eq(mapSheetAnalInferenceEntity.id))) + .and( + labelingAssignmentEntity.analUid.eq( + mapSheetAnalInferenceEntity.id))) .innerJoin(memberEntity) .on(whereSubBuilder) .where(whereBuilder) @@ -390,8 +390,7 @@ public class LabelWorkRepositoryImpl implements LabelWorkRepositoryCustom { } /** - * 작업배정 상세조회 - * (복잡한 집계 쿼리로 인해 DTO 직접 반환) + * 작업배정 상세조회 (복잡한 집계 쿼리로 인해 DTO 직접 반환) * * @param uuid 작업배정 UUID * @return 작업배정 상세 정보 From 26e58b01d5444165d47eee672beb57d33723f456 Mon Sep 17 00:00:00 2001 From: DanielLee <198891672+sanghyeonhd@users.noreply.github.com> Date: Fri, 9 Jan 2026 10:10:28 +0900 Subject: [PATCH 04/12] =?UTF-8?q?=EB=9D=BC=EB=B2=A8=EB=A7=81=20=EC=A2=85?= =?UTF-8?q?=EB=A3=8C=EC=97=AC=EB=B6=80=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../label/LabelAllocateApiController.java | 63 ++++++++ .../kamcoback/label/dto/WorkerStatsDto.java | 45 +++++- .../label/service/LabelAllocateService.java | 23 +++ .../core/LabelAllocateCoreService.java | 5 + .../entity/MapSheetAnalInferenceEntity.java | 10 ++ .../label/LabelAllocateRepositoryCustom.java | 4 + .../label/LabelAllocateRepositoryImpl.java | 136 +++++++++++++++--- .../label/LabelWorkRepositoryImpl.java | 25 ++-- 8 files changed, 271 insertions(+), 40 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 8bd140e9..663ab9e0 100644 --- a/src/main/java/com/kamco/cd/kamcoback/label/LabelAllocateApiController.java +++ b/src/main/java/com/kamco/cd/kamcoback/label/LabelAllocateApiController.java @@ -6,6 +6,7 @@ import com.kamco.cd.kamcoback.label.dto.LabelAllocateDto; 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.WorkerStatsDto.UpdateClosedRequest; import com.kamco.cd.kamcoback.label.dto.WorkerStatsDto.WorkerListResponse; import com.kamco.cd.kamcoback.label.service.LabelAllocateService; import io.swagger.v3.oas.annotations.Operation; @@ -236,4 +237,66 @@ public class LabelAllocateApiController { String uuid) { return ApiResponseDto.ok(labelAllocateService.moveAvailUserList(userId, uuid)); } + + @Operation( + summary = "작업현황 관리 > 라벨링/검수 종료 여부 업데이트", + description = "라벨링/검수 종료 여부를 업데이트합니다. uuid 생략 시 최신 프로젝트 대상") + @ApiResponses( + value = { + @ApiResponse(responseCode = "200", description = "업데이트 성공"), + @ApiResponse(responseCode = "400", description = "잘못된 요청 데이터", content = @Content), + @ApiResponse(responseCode = "500", description = "서버 오류", content = @Content) + }) + @PostMapping("/projectinfo/closed") + public ApiResponseDto updateClosedYn( + @io.swagger.v3.oas.annotations.parameters.RequestBody( + description = "종료 여부 업데이트 요청", + required = true, + content = + @Content( + mediaType = "application/json", + schema = @Schema(implementation = UpdateClosedRequest.class), + examples = { + @io.swagger.v3.oas.annotations.media.ExampleObject( + name = "라벨링 종료", + value = """ + {"closedType": "LABELING", "closedYn": "Y"} + """), + @io.swagger.v3.oas.annotations.media.ExampleObject( + name = "검수 종료", + value = """ + {"closedType": "INSPECTION", "closedYn": "Y"} + """), + @io.swagger.v3.oas.annotations.media.ExampleObject( + name = "라벨링 재개", + value = """ + {"closedType": "LABELING", "closedYn": "N"} + """), + @io.swagger.v3.oas.annotations.media.ExampleObject( + name = "검수 재개", + value = """ + {"closedType": "INSPECTION", "closedYn": "N"} + """), + @io.swagger.v3.oas.annotations.media.ExampleObject( + name = "특정 프로젝트 라벨링 종료", + value = """ + {"uuid": "f97dc186-e6d3-4645-9737-3173dde8dc64", "closedType": "LABELING", "closedYn": "Y"} + """) + })) + @RequestBody + @Valid + UpdateClosedRequest request) { + + labelAllocateService.updateClosedYn( + request.getUuid(), request.getClosedType(), request.getClosedYn()); + + String typeLabel = "LABELING".equals(request.getClosedType()) ? "라벨링" : "검수"; + String statusMessage = + "Y".equals(request.getClosedYn()) + ? typeLabel + "이(가) 종료되었습니다." + : typeLabel + "이(가) 재개되었습니다."; + + return ApiResponseDto.okObject( + new ApiResponseDto.ResponseObj(ApiResponseDto.ApiResponseCode.OK, statusMessage)); + } } diff --git a/src/main/java/com/kamco/cd/kamcoback/label/dto/WorkerStatsDto.java b/src/main/java/com/kamco/cd/kamcoback/label/dto/WorkerStatsDto.java index e7a3e035..75f09508 100644 --- a/src/main/java/com/kamco/cd/kamcoback/label/dto/WorkerStatsDto.java +++ b/src/main/java/com/kamco/cd/kamcoback/label/dto/WorkerStatsDto.java @@ -2,6 +2,8 @@ package com.kamco.cd.kamcoback.label.dto; import com.kamco.cd.kamcoback.common.utils.interfaces.JsonFormatDttm; import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Pattern; import java.time.ZonedDateTime; import lombok.AllArgsConstructor; import lombok.Builder; @@ -35,6 +37,43 @@ public class WorkerStatsDto { @Schema(description = "프로젝트 UUID") private String uuid; + + @Schema(description = "라벨링 종료 여부 (Y: 종료, N: 진행중)") + private String labelingClosedYn; + + @Schema(description = "검수 종료 여부 (Y: 종료, N: 진행중)") + private String inspectionClosedYn; + } + + @Getter + @Setter + @NoArgsConstructor + @AllArgsConstructor + @Schema(description = "프로젝트 종료 여부 업데이트 요청") + public static class UpdateClosedRequest { + + @Schema( + description = "프로젝트 UUID (선택) - 미입력 시 현재 진행중인 최신 프로젝트가 대상", + example = "f97dc186-e6d3-4645-9737-3173dde8dc64") + private String uuid; + + @NotBlank(message = "종료 유형은 필수입니다.") + @Pattern(regexp = "^(LABELING|INSPECTION)$", message = "종료 유형은 LABELING 또는 INSPECTION이어야 합니다.") + @Schema( + description = "종료 유형 (LABELING: 라벨링, INSPECTION: 검수)", + example = "LABELING", + allowableValues = {"LABELING", "INSPECTION"}, + requiredMode = Schema.RequiredMode.REQUIRED) + private String closedType; + + @NotBlank(message = "종료 여부는 필수입니다.") + @Pattern(regexp = "^[YN]$", message = "종료 여부는 Y 또는 N이어야 합니다.") + @Schema( + description = "종료 여부 (Y: 종료, N: 진행중)", + example = "Y", + allowableValues = {"Y", "N"}, + requiredMode = Schema.RequiredMode.REQUIRED) + private String closedYn; } @Getter @@ -144,7 +183,7 @@ public class WorkerStatsDto { @Schema(description = "검수 작업 상태 (진행중/완료)") private String inspectionStatus; - @Schema(description = "검수 전체 대상 건수") + @Schema(description = "검수 대상 건수 (라벨링 대상과 동일)") private Long inspectionTotalCount; @Schema(description = "검수 완료 건수 (DONE)") @@ -179,10 +218,6 @@ public class WorkerStatsDto { @Deprecated @Schema(description = "[Deprecated] inspectionRemainingCount 사용 권장") private Long remainingInspectCount; - - @Deprecated - @Schema(description = "[Deprecated] labelingStatus/inspectionStatus 사용 권장") - private String workStatus; } @Getter 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 ac9bd2f3..caa84cfb 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 @@ -195,4 +195,27 @@ public class LabelAllocateService { public MoveInfo moveAvailUserList(String userId, String uuid) { return labelAllocateCoreService.moveAvailUserList(userId, uuid); } + + /** + * 프로젝트 종료 여부 업데이트 + * + * @param uuid 프로젝트 UUID (선택, 미입력 시 최신 프로젝트 대상) + * @param closedType 종료 유형 (LABELING/INSPECTION) + * @param closedYn 종료 여부 (Y/N) + */ + @Transactional + public void updateClosedYn(String uuid, String closedType, String closedYn) { + String targetUuid = uuid; + + // uuid가 없으면 최신 프로젝트 uuid 조회 + if (targetUuid == null || targetUuid.isBlank()) { + var latestProjectInfo = labelAllocateCoreService.findLatestProjectInfo(); + if (latestProjectInfo == null || latestProjectInfo.getUuid() == null) { + throw new IllegalArgumentException("진행중인 프로젝트가 없습니다."); + } + targetUuid = latestProjectInfo.getUuid(); + } + + labelAllocateCoreService.updateClosedYnByUuid(targetUuid, closedType, closedYn); + } } 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 ff7c00ba..2cf1422d 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 @@ -133,4 +133,9 @@ public class LabelAllocateCoreService { String uuid, String userId, String paramUserId, Long assignCount) { labelAllocateRepository.assignOwnerReAllocate(uuid, userId, paramUserId, assignCount); } + + + public void updateClosedYnByUuid(String uuid, String closedType, String closedYn) { + labelAllocateRepository.updateClosedYnByUuid(uuid, closedType, closedYn); + } } diff --git a/src/main/java/com/kamco/cd/kamcoback/postgres/entity/MapSheetAnalInferenceEntity.java b/src/main/java/com/kamco/cd/kamcoback/postgres/entity/MapSheetAnalInferenceEntity.java index 195721e1..73e61299 100644 --- a/src/main/java/com/kamco/cd/kamcoback/postgres/entity/MapSheetAnalInferenceEntity.java +++ b/src/main/java/com/kamco/cd/kamcoback/postgres/entity/MapSheetAnalInferenceEntity.java @@ -149,4 +149,14 @@ public class MapSheetAnalInferenceEntity { @Column(name = "stage") private Integer stage; + + @Size(max = 1) + @ColumnDefault("'N'") + @Column(name = "labeling_closed_yn", length = 1) + private String labelingClosedYn = "N"; + + @Size(max = 1) + @ColumnDefault("'N'") + @Column(name = "inspection_closed_yn", length = 1) + private String inspectionClosedYn = "N"; } 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 350b30c2..319a118b 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 @@ -77,4 +77,8 @@ public interface LabelAllocateRepositoryCustom { void insertLabelerUser(Long analUid, String userId, int demand); void assignOwnerReAllocate(String uuid, String userId, String paramUserId, Long assignCount); + + + // 프로젝트 종료 여부 업데이트 (uuid 기반) + void updateClosedYnByUuid(String uuid, String closedType, String closedYn); } 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 6d64776a..28b2f625 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 @@ -335,10 +335,50 @@ public class LabelAllocateRepositoryImpl implements LabelAllocateRepositoryCusto @Override public WorkProgressInfo findWorkProgressInfo(Long analUid) { - BooleanExpression analUidCondition = - analUid != null ? labelingAssignmentEntity.analUid.eq(analUid) : null; + // analUid가 null이면 최신 프로젝트의 analUid 조회 + Long effectiveAnalUid = analUid; + if (effectiveAnalUid == null) { + UUID latestUuid = findLastLabelWorkState(); + if (latestUuid != null) { + effectiveAnalUid = + queryFactory + .select(mapSheetAnalInferenceEntity.id) + .from(mapSheetAnalInferenceEntity) + .where(mapSheetAnalInferenceEntity.uuid.eq(latestUuid)) + .fetchOne(); + } + } - // 전체 배정 건수 + BooleanExpression analUidCondition = + effectiveAnalUid != null ? labelingAssignmentEntity.analUid.eq(effectiveAnalUid) : null; + + // analUid로 분석 정보(compareYyyy, targetYyyy, stage) 조회 + MapSheetAnalInferenceEntity analEntity = null; + if (effectiveAnalUid != null) { + analEntity = + queryFactory + .selectFrom(mapSheetAnalInferenceEntity) + .where(mapSheetAnalInferenceEntity.id.eq(effectiveAnalUid)) + .fetchOne(); + } + + // 라벨링 대상 건수: tb_map_sheet_anal_data_inference_geom에서 pnu > 0 AND pass_yn = false(부적합)인 건수 + Long labelingTargetCount = 0L; + if (analEntity != null) { + labelingTargetCount = + queryFactory + .select(mapSheetAnalDataInferenceGeomEntity.geoUid.count()) + .from(mapSheetAnalDataInferenceGeomEntity) + .where( + mapSheetAnalDataInferenceGeomEntity.compareYyyy.eq(analEntity.getCompareYyyy()), + mapSheetAnalDataInferenceGeomEntity.targetYyyy.eq(analEntity.getTargetYyyy()), + mapSheetAnalDataInferenceGeomEntity.stage.eq(analEntity.getStage()), + mapSheetAnalDataInferenceGeomEntity.pnu.gt(0L), + mapSheetAnalDataInferenceGeomEntity.passYn.isFalse()) + .fetchOne(); + } + + // 전체 배정 건수 (tb_labeling_assignment 기준) Long totalAssigned = queryFactory .select(labelingAssignmentEntity.count()) @@ -390,47 +430,69 @@ public class LabelAllocateRepositoryImpl implements LabelAllocateRepositoryCusto .where(analUidCondition, labelingAssignmentEntity.inspectorUid.isNotNull()) .fetchOne(); - // 남은 작업 건수 계산 - long total = totalAssigned != null ? totalAssigned : 0L; + // 라벨링 대상 건수 (pass_yn = false인 부적합 데이터 기준) + long labelingTotal = labelingTargetCount != null ? labelingTargetCount : 0L; + long assignedTotal = totalAssigned != null ? totalAssigned : 0L; long labelCompleted = labelingCompleted != null ? labelingCompleted : 0L; long inspectCompleted = inspectionCompleted != null ? inspectionCompleted : 0L; long skipped = skipCount != null ? skipCount : 0L; - long labelingRemaining = total - labelCompleted - skipped; - long inspectionRemaining = total - inspectCompleted - skipped; + // 라벨링 남은 건수: 라벨링 대상 건수 - 완료 - 스킵 + long labelingRemaining = labelingTotal - labelCompleted - skipped; + if (labelingRemaining < 0) labelingRemaining = 0; - // 진행률 계산 - double labelingRate = total > 0 ? (double) labelCompleted / total * 100 : 0.0; - double inspectionRate = total > 0 ? (double) inspectCompleted / total * 100 : 0.0; + // 검수 대상 건수: 라벨링 대상 건수와 동일 (기획서 기준) + long inspectionTotal = labelingTotal; + // 검수 남은 건수: 검수 대상 건수 - 검수완료(DONE) - 스킵 + long inspectionRemaining = inspectionTotal - inspectCompleted - skipped; + if (inspectionRemaining < 0) inspectionRemaining = 0; - // 상태 판단 - String labelingStatus = labelingRemaining > 0 ? "진행중" : "완료"; - String inspectionStatus = inspectionRemaining > 0 ? "진행중" : "완료"; + // 진행률 계산 (라벨링 대상 건수 기준) + double labelingRate = labelingTotal > 0 ? (double) labelCompleted / labelingTotal * 100 : 0.0; + double inspectionRate = + inspectionTotal > 0 ? (double) inspectCompleted / inspectionTotal * 100 : 0.0; + + // 상태 판단 (각각의 closedYn이 "Y"이면 "종료", 아니면 진행중/완료) + String labelingStatus; + String inspectionStatus; + + // 라벨링 상태 판단 + if (analEntity != null && "Y".equals(analEntity.getLabelingClosedYn())) { + labelingStatus = "종료"; + } else { + labelingStatus = labelingRemaining > 0 ? "진행중" : "완료"; + } + + // 검수 상태 판단 + if (analEntity != null && "Y".equals(analEntity.getInspectionClosedYn())) { + inspectionStatus = "종료"; + } else { + inspectionStatus = inspectionRemaining > 0 ? "진행중" : "완료"; + } return WorkProgressInfo.builder() - // 라벨링 + // 라벨링 (pass_yn = false인 부적합 데이터 기준) .labelingProgressRate(labelingRate) .labelingStatus(labelingStatus) - .labelingTotalCount(total) + .labelingTotalCount(labelingTotal) .labelingCompletedCount(labelCompleted) .labelingSkipCount(skipped) .labelingRemainingCount(labelingRemaining) .labelerCount(labelerCount != null ? labelerCount : 0L) - // 검수 + // 검수 (라벨링 완료 건수 기준) .inspectionProgressRate(inspectionRate) .inspectionStatus(inspectionStatus) - .inspectionTotalCount(total) + .inspectionTotalCount(inspectionTotal) .inspectionCompletedCount(inspectCompleted) .inspectionSkipCount(skipped) .inspectionRemainingCount(inspectionRemaining) .inspectorCount(inspectorCount != null ? inspectorCount : 0L) // 레거시 호환 필드 (Deprecated) .progressRate(labelingRate) - .totalAssignedCount(total) + .totalAssignedCount(labelingTotal) .completedCount(labelCompleted) .remainingLabelCount(labelingRemaining) .remainingInspectCount(inspectionRemaining) - .workStatus(labelingStatus) .build(); } @@ -677,7 +739,9 @@ public class LabelAllocateRepositoryImpl implements LabelAllocateRepositoryCusto mapSheetAnalInferenceEntity.stage, mapSheetAnalInferenceEntity.gukyuinApplyDttm, mapSheetAnalInferenceEntity.createdDttm, - mapSheetAnalInferenceEntity.uuid) + mapSheetAnalInferenceEntity.uuid, + mapSheetAnalInferenceEntity.labelingClosedYn, + mapSheetAnalInferenceEntity.inspectionClosedYn) .from(mapSheetAnalInferenceEntity) .where(mapSheetAnalInferenceEntity.id.eq(analUid)) .fetchOne(); @@ -692,6 +756,8 @@ public class LabelAllocateRepositoryImpl implements LabelAllocateRepositoryCusto ZonedDateTime gukyuinApplyDttm = result.get(mapSheetAnalInferenceEntity.gukyuinApplyDttm); ZonedDateTime createdDttm = result.get(mapSheetAnalInferenceEntity.createdDttm); UUID uuid = result.get(mapSheetAnalInferenceEntity.uuid); + String labelingClosedYn = result.get(mapSheetAnalInferenceEntity.labelingClosedYn); + String inspectionClosedYn = result.get(mapSheetAnalInferenceEntity.inspectionClosedYn); // 변화탐지년도 생성 String detectionYear = @@ -706,6 +772,8 @@ public class LabelAllocateRepositoryImpl implements LabelAllocateRepositoryCusto .gukyuinApplyDttm(gukyuinApplyDttm) .startDttm(createdDttm) .uuid(uuid != null ? uuid.toString() : null) + .labelingClosedYn(labelingClosedYn) + .inspectionClosedYn(inspectionClosedYn) .build(); } @@ -727,7 +795,9 @@ public class LabelAllocateRepositoryImpl implements LabelAllocateRepositoryCusto mapSheetAnalInferenceEntity.stage, mapSheetAnalInferenceEntity.gukyuinApplyDttm, mapSheetAnalInferenceEntity.createdDttm, - mapSheetAnalInferenceEntity.uuid) + mapSheetAnalInferenceEntity.uuid, + mapSheetAnalInferenceEntity.labelingClosedYn, + mapSheetAnalInferenceEntity.inspectionClosedYn) .from(mapSheetAnalInferenceEntity) .where(mapSheetAnalInferenceEntity.uuid.eq(uuid)) .fetchOne(); @@ -741,6 +811,8 @@ public class LabelAllocateRepositoryImpl implements LabelAllocateRepositoryCusto Integer stage = result.get(mapSheetAnalInferenceEntity.stage); ZonedDateTime gukyuinApplyDttm = result.get(mapSheetAnalInferenceEntity.gukyuinApplyDttm); ZonedDateTime createdDttm = result.get(mapSheetAnalInferenceEntity.createdDttm); + String labelingClosedYn = result.get(mapSheetAnalInferenceEntity.labelingClosedYn); + String inspectionClosedYn = result.get(mapSheetAnalInferenceEntity.inspectionClosedYn); // 변화탐지년도 생성 String detectionYear = @@ -755,6 +827,8 @@ public class LabelAllocateRepositoryImpl implements LabelAllocateRepositoryCusto .gukyuinApplyDttm(gukyuinApplyDttm) .startDttm(createdDttm) .uuid(uuid.toString()) + .labelingClosedYn(labelingClosedYn) + .inspectionClosedYn(inspectionClosedYn) .build(); } @@ -1160,4 +1234,24 @@ public class LabelAllocateRepositoryImpl implements LabelAllocateRepositoryCusto labelingLabelerEntity.workerUid.eq(paramUserId)) .execute(); } + + + @Override + public void updateClosedYnByUuid(String uuid, String closedType, String closedYn) { + var updateQuery = queryFactory.update(mapSheetAnalInferenceEntity); + + if ("LABELING".equals(closedType)) { + updateQuery.set(mapSheetAnalInferenceEntity.labelingClosedYn, closedYn); + } else if ("INSPECTION".equals(closedType)) { + updateQuery.set(mapSheetAnalInferenceEntity.inspectionClosedYn, closedYn); + } + + updateQuery + .set(mapSheetAnalInferenceEntity.updatedDttm, ZonedDateTime.now()) + .where(mapSheetAnalInferenceEntity.uuid.eq(UUID.fromString(uuid))) + .execute(); + + em.flush(); + em.clear(); + } } diff --git a/src/main/java/com/kamco/cd/kamcoback/postgres/repository/label/LabelWorkRepositoryImpl.java b/src/main/java/com/kamco/cd/kamcoback/postgres/repository/label/LabelWorkRepositoryImpl.java index 10308aed..57f28ff5 100644 --- a/src/main/java/com/kamco/cd/kamcoback/postgres/repository/label/LabelWorkRepositoryImpl.java +++ b/src/main/java/com/kamco/cd/kamcoback/postgres/repository/label/LabelWorkRepositoryImpl.java @@ -267,32 +267,29 @@ public class LabelWorkRepositoryImpl implements LabelWorkRepositoryCustom { whereSubBuilder.and(labelingAssignmentEntity.workerUid.eq(memberEntity.userId)); + // 공통 조건 추출 + BooleanExpression doneStateCondition = + labelingAssignmentEntity.workState.eq(LabelState.DONE.name()); + NumberExpression assignedCnt = labelingAssignmentEntity.workerUid.count(); - NumberExpression doneCnt = - this.caseSumExpression(labelingAssignmentEntity.workState.eq(LabelState.DONE.name())); + NumberExpression doneCnt = this.caseSumExpression(doneStateCondition); NumberExpression skipCnt = this.caseSumExpression(labelingAssignmentEntity.workState.eq(LabelState.SKIP.name())); NumberExpression day3AgoDoneCnt = this.caseSumExpression( - labelingAssignmentEntity - .workState - .eq(LabelState.DONE.name()) - .and(this.fromDateEqExpression(labelingAssignmentEntity.modifiedDate, -3))); + doneStateCondition.and( + this.fromDateEqExpression(labelingAssignmentEntity.modifiedDate, -3))); NumberExpression day2AgoDoneCnt = this.caseSumExpression( - labelingAssignmentEntity - .workState - .eq(LabelState.DONE.name()) - .and(this.fromDateEqExpression(labelingAssignmentEntity.modifiedDate, -2))); + doneStateCondition.and( + this.fromDateEqExpression(labelingAssignmentEntity.modifiedDate, -2))); NumberExpression day1AgoDoneCnt = this.caseSumExpression( - labelingAssignmentEntity - .workState - .eq(LabelState.DONE.name()) - .and(this.fromDateEqExpression(labelingAssignmentEntity.modifiedDate, -1))); + doneStateCondition.and( + this.fromDateEqExpression(labelingAssignmentEntity.modifiedDate, -1))); NumberExpression remainingCnt = assignedCnt.subtract(doneCnt); From 40b91b7deab8f1d8a564f696a91c3ab171165075 Mon Sep 17 00:00:00 2001 From: DanielLee <198891672+sanghyeonhd@users.noreply.github.com> Date: Fri, 9 Jan 2026 11:01:46 +0900 Subject: [PATCH 05/12] =?UTF-8?q?projectinfo=20uuid=EC=A1=B0=ED=9A=8C=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- login.json | 0 .../label/LabelAllocateApiController.java | 13 +- .../label/service/LabelAllocateService.java | 32 +++ .../core/LabelAllocateCoreService.java | 8 + .../label/LabelAllocateRepositoryCustom.java | 6 + .../label/LabelAllocateRepositoryImpl.java | 198 ++++++++++++++++++ 6 files changed, 253 insertions(+), 4 deletions(-) delete mode 100644 login.json diff --git a/login.json b/login.json deleted file mode 100644 index e69de29b..00000000 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 663ab9e0..cd70855f 100644 --- a/src/main/java/com/kamco/cd/kamcoback/label/LabelAllocateApiController.java +++ b/src/main/java/com/kamco/cd/kamcoback/label/LabelAllocateApiController.java @@ -55,7 +55,9 @@ public class LabelAllocateApiController { return ApiResponseDto.ok(labelAllocateService.availUserList(role)); } - @Operation(summary = "작업현황 관리 > 프로젝트 및 진행 상황 정보", description = "작업현황 관리 > 프로젝트 및 진행 상황 정보") + @Operation( + summary = "작업현황 관리 > 프로젝트 및 진행 상황 정보", + description = "작업현황 관리 > 프로젝트 및 진행 상황 정보. UUID를 입력하면 해당 프로젝트 정보를 조회하고, 미입력 시 최신 프로젝트를 조회합니다.") @ApiResponses( value = { @ApiResponse(responseCode = "200", description = "조회 성공"), @@ -64,8 +66,11 @@ public class LabelAllocateApiController { }) @GetMapping("/projectinfo") public ApiResponseDto getWorkerStatistics( - // @Parameter(description = "분석 ID (선택)", example = "3") @RequestParam(required = false) - // Long analUid, + @Parameter( + description = "프로젝트 UUID (선택) - 미입력 시 최신 프로젝트 조회", + example = "f97dc186-e6d3-4645-9737-3173dde8dc64") + @RequestParam(required = false) + String uuid, @Parameter( description = "작업자 유형 (선택) - 미입력 시 LABELER로 조회", example = "LABELER", @@ -99,7 +104,7 @@ public class LabelAllocateApiController { String workerType = (type == null || type.isEmpty()) ? RoleType.LABELER.name() : type; return ApiResponseDto.ok( - labelAllocateService.getWorkerStatistics(null, workerType, search, sort)); + labelAllocateService.getWorkerStatisticsByUuid(uuid, workerType, search, sort)); } @Operation(summary = "라벨링작업 관리 > 작업 배정", description = "라벨링작업 관리 > 작업 배정") 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 caa84cfb..e4fa63df 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 @@ -10,6 +10,8 @@ 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.TargetUser; 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; import com.kamco.cd.kamcoback.label.dto.WorkerStatsDto.WorkerListResponse; import com.kamco.cd.kamcoback.postgres.core.LabelAllocateCoreService; import java.util.List; @@ -114,6 +116,36 @@ public class LabelAllocateService { return WorkerListResponse.builder().projectInfo(projectInfo).progressInfo(progressInfo).build(); } + /** + * 작업자 통계 조회 (UUID 기반) + * + * @param uuid 프로젝트 UUID (선택, 미입력 시 최신 프로젝트 조회) + * @param workerType 작업자 유형 (LABELER/INSPECTOR) + * @param search 검색어 (이름 또는 사번) + * @param sortType 정렬 조건 + * @return 작업자 목록 및 통계 + */ + public WorkerListResponse getWorkerStatisticsByUuid( + String uuid, String workerType, String search, String sortType) { + + ProjectInfo projectInfo; + WorkProgressInfo progressInfo; + + if (uuid != null && !uuid.isBlank()) { + // UUID로 프로젝트 정보 조회 + projectInfo = labelAllocateCoreService.findProjectInfoByUuid(uuid); + // UUID로 작업 진행 현황 조회 + progressInfo = labelAllocateCoreService.findWorkProgressInfoByUuid(uuid); + } else { + // 최신 프로젝트 정보 조회 + projectInfo = labelAllocateCoreService.findLatestProjectInfo(); + // 최신 프로젝트 작업 진행 현황 조회 + progressInfo = labelAllocateCoreService.findWorkProgressInfo(null); + } + + return WorkerListResponse.builder().projectInfo(projectInfo).progressInfo(progressInfo).build(); + } + public InferenceDetail findInferenceDetail(String uuid) { return labelAllocateCoreService.findInferenceDetail(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 2cf1422d..65b2c78c 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 @@ -61,6 +61,10 @@ public class LabelAllocateCoreService { return labelAllocateRepository.findLatestProjectInfo(); } + public ProjectInfo findProjectInfoByUuid(String uuid) { + return labelAllocateRepository.findProjectInfoByUuid(uuid); + } + public UUID findLastLabelWorkState() { return labelAllocateRepository.findLastLabelWorkState(); } @@ -74,6 +78,10 @@ public class LabelAllocateCoreService { return labelAllocateRepository.findWorkProgressInfo(analUid); } + public WorkProgressInfo findWorkProgressInfoByUuid(String uuid) { + return labelAllocateRepository.findWorkProgressInfoByUuid(uuid); + } + public Long findDailyProcessedCount( String workerId, String workerType, LocalDate date, Long analUid) { return labelAllocateRepository.findDailyProcessedCount(workerId, workerType, date, analUid); 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 319a118b..9a7bcc8c 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 @@ -37,6 +37,9 @@ public interface LabelAllocateRepositoryCustom { // 최신 프로젝트 정보 조회 (analUid 없이) ProjectInfo findLatestProjectInfo(); + // UUID로 프로젝트 정보 조회 + ProjectInfo findProjectInfoByUuid(String uuid); + // 최신 작업 상태의 UUID 조회 UUID findLastLabelWorkState(); @@ -47,6 +50,9 @@ public interface LabelAllocateRepositoryCustom { // 작업 진행 현황 조회 WorkProgressInfo findWorkProgressInfo(Long analUid); + // UUID로 작업 진행 현황 조회 + WorkProgressInfo findWorkProgressInfoByUuid(String uuid); + // 작업자별 일일 처리량 조회 Long findDailyProcessedCount(String workerId, String workerType, LocalDate date, Long analUid); 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 28b2f625..12c5450b 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 @@ -496,6 +496,154 @@ public class LabelAllocateRepositoryImpl implements LabelAllocateRepositoryCusto .build(); } + @Override + public WorkProgressInfo findWorkProgressInfoByUuid(String uuid) { + if (uuid == null || uuid.isBlank()) { + return findWorkProgressInfo(null); + } + + UUID targetUuid = UUID.fromString(uuid); + + // UUID로 analUid 조회 + Long effectiveAnalUid = + queryFactory + .select(mapSheetAnalInferenceEntity.id) + .from(mapSheetAnalInferenceEntity) + .where(mapSheetAnalInferenceEntity.uuid.eq(targetUuid)) + .fetchOne(); + + if (effectiveAnalUid == null) { + return null; + } + + BooleanExpression analUidCondition = labelingAssignmentEntity.analUid.eq(effectiveAnalUid); + + // analUid로 분석 정보(compareYyyy, targetYyyy, stage) 조회 + MapSheetAnalInferenceEntity analEntity = + queryFactory + .selectFrom(mapSheetAnalInferenceEntity) + .where(mapSheetAnalInferenceEntity.id.eq(effectiveAnalUid)) + .fetchOne(); + + // 라벨링 대상 건수: tb_map_sheet_anal_data_inference_geom에서 pnu > 0 AND pass_yn = false(부적합)인 건수 + Long labelingTargetCount = 0L; + if (analEntity != null) { + labelingTargetCount = + queryFactory + .select(mapSheetAnalDataInferenceGeomEntity.geoUid.count()) + .from(mapSheetAnalDataInferenceGeomEntity) + .where( + mapSheetAnalDataInferenceGeomEntity.compareYyyy.eq(analEntity.getCompareYyyy()), + mapSheetAnalDataInferenceGeomEntity.targetYyyy.eq(analEntity.getTargetYyyy()), + mapSheetAnalDataInferenceGeomEntity.stage.eq(analEntity.getStage()), + mapSheetAnalDataInferenceGeomEntity.pnu.gt(0L), + mapSheetAnalDataInferenceGeomEntity.passYn.isFalse()) + .fetchOne(); + } + + // 전체 배정 건수 (tb_labeling_assignment 기준) + Long totalAssigned = + queryFactory + .select(labelingAssignmentEntity.count()) + .from(labelingAssignmentEntity) + .where(analUidCondition) + .fetchOne(); + + // === 라벨링 통계 === + Long labelingCompleted = + queryFactory + .select(labelingAssignmentEntity.count()) + .from(labelingAssignmentEntity) + .where( + analUidCondition, + labelingAssignmentEntity.workState.in("LABEL_FIN", "TEST_ING", "DONE")) + .fetchOne(); + + Long skipCount = + queryFactory + .select(labelingAssignmentEntity.count()) + .from(labelingAssignmentEntity) + .where(analUidCondition, labelingAssignmentEntity.workState.eq("SKIP")) + .fetchOne(); + + Long labelerCount = + queryFactory + .select(labelingAssignmentEntity.workerUid.countDistinct()) + .from(labelingAssignmentEntity) + .where(analUidCondition, labelingAssignmentEntity.workerUid.isNotNull()) + .fetchOne(); + + // === 검수 통계 === + Long inspectionCompleted = + queryFactory + .select(labelingAssignmentEntity.count()) + .from(labelingAssignmentEntity) + .where(analUidCondition, labelingAssignmentEntity.workState.eq("DONE")) + .fetchOne(); + + Long inspectorCount = + queryFactory + .select(labelingAssignmentEntity.inspectorUid.countDistinct()) + .from(labelingAssignmentEntity) + .where(analUidCondition, labelingAssignmentEntity.inspectorUid.isNotNull()) + .fetchOne(); + + // 계산 + long labelingTotal = labelingTargetCount != null ? labelingTargetCount : 0L; + long labelCompleted = labelingCompleted != null ? labelingCompleted : 0L; + long inspectCompleted = inspectionCompleted != null ? inspectionCompleted : 0L; + long skipped = skipCount != null ? skipCount : 0L; + + long labelingRemaining = labelingTotal - labelCompleted - skipped; + if (labelingRemaining < 0) labelingRemaining = 0; + + long inspectionTotal = labelingTotal; + long inspectionRemaining = inspectionTotal - inspectCompleted - skipped; + if (inspectionRemaining < 0) inspectionRemaining = 0; + + double labelingRate = labelingTotal > 0 ? (double) labelCompleted / labelingTotal * 100 : 0.0; + double inspectionRate = + inspectionTotal > 0 ? (double) inspectCompleted / inspectionTotal * 100 : 0.0; + + // 상태 판단 + String labelingStatus; + String inspectionStatus; + + if (analEntity != null && "Y".equals(analEntity.getLabelingClosedYn())) { + labelingStatus = "종료"; + } else { + labelingStatus = labelingRemaining > 0 ? "진행중" : "완료"; + } + + if (analEntity != null && "Y".equals(analEntity.getInspectionClosedYn())) { + inspectionStatus = "종료"; + } else { + inspectionStatus = inspectionRemaining > 0 ? "진행중" : "완료"; + } + + return WorkProgressInfo.builder() + .labelingProgressRate(labelingRate) + .labelingStatus(labelingStatus) + .labelingTotalCount(labelingTotal) + .labelingCompletedCount(labelCompleted) + .labelingSkipCount(skipped) + .labelingRemainingCount(labelingRemaining) + .labelerCount(labelerCount != null ? labelerCount : 0L) + .inspectionProgressRate(inspectionRate) + .inspectionStatus(inspectionStatus) + .inspectionTotalCount(inspectionTotal) + .inspectionCompletedCount(inspectCompleted) + .inspectionSkipCount(skipped) + .inspectionRemainingCount(inspectionRemaining) + .inspectorCount(inspectorCount != null ? inspectorCount : 0L) + .progressRate(labelingRate) + .totalAssignedCount(labelingTotal) + .completedCount(labelCompleted) + .remainingLabelCount(labelingRemaining) + .remainingInspectCount(inspectionRemaining) + .build(); + } + @Override public Long findDailyProcessedCount( String workerId, String workerType, LocalDate date, Long analUid) { @@ -832,6 +980,56 @@ public class LabelAllocateRepositoryImpl implements LabelAllocateRepositoryCusto .build(); } + @Override + public ProjectInfo findProjectInfoByUuid(String uuid) { + if (uuid == null || uuid.isBlank()) { + return null; + } + + UUID targetUuid = UUID.fromString(uuid); + + var result = + queryFactory + .select( + mapSheetAnalInferenceEntity.compareYyyy, + mapSheetAnalInferenceEntity.targetYyyy, + mapSheetAnalInferenceEntity.stage, + mapSheetAnalInferenceEntity.gukyuinApplyDttm, + mapSheetAnalInferenceEntity.createdDttm, + mapSheetAnalInferenceEntity.uuid, + mapSheetAnalInferenceEntity.labelingClosedYn, + mapSheetAnalInferenceEntity.inspectionClosedYn) + .from(mapSheetAnalInferenceEntity) + .where(mapSheetAnalInferenceEntity.uuid.eq(targetUuid)) + .fetchOne(); + + if (result == null) { + return null; + } + + Integer compareYyyy = result.get(mapSheetAnalInferenceEntity.compareYyyy); + Integer targetYyyy = result.get(mapSheetAnalInferenceEntity.targetYyyy); + Integer stage = result.get(mapSheetAnalInferenceEntity.stage); + ZonedDateTime gukyuinApplyDttm = result.get(mapSheetAnalInferenceEntity.gukyuinApplyDttm); + ZonedDateTime createdDttm = result.get(mapSheetAnalInferenceEntity.createdDttm); + String labelingClosedYn = result.get(mapSheetAnalInferenceEntity.labelingClosedYn); + String inspectionClosedYn = result.get(mapSheetAnalInferenceEntity.inspectionClosedYn); + + String detectionYear = + (compareYyyy != null && targetYyyy != null) ? compareYyyy + "-" + targetYyyy : null; + String round = stage != null ? String.valueOf(stage) : null; + + return ProjectInfo.builder() + .detectionYear(detectionYear) + .stage(round) + .gukyuinApplyDttm(gukyuinApplyDttm) + .startDttm(createdDttm) + .uuid(uuid) + .labelingClosedYn(labelingClosedYn) + .inspectionClosedYn(inspectionClosedYn) + .build(); + } + @Override public UUID findLastLabelWorkState() { BooleanBuilder whereBuilder = new BooleanBuilder(); From 6235cda5970895498842c243770b8387dc420c23 Mon Sep 17 00:00:00 2001 From: DanielLee <198891672+sanghyeonhd@users.noreply.github.com> Date: Fri, 9 Jan 2026 11:10:49 +0900 Subject: [PATCH 06/12] projectinfo explain fix --- .../label/LabelAllocateApiController.java | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 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 cd70855f..f6da8a1d 100644 --- a/src/main/java/com/kamco/cd/kamcoback/label/LabelAllocateApiController.java +++ b/src/main/java/com/kamco/cd/kamcoback/label/LabelAllocateApiController.java @@ -1,6 +1,5 @@ package com.kamco.cd.kamcoback.label; -import com.kamco.cd.kamcoback.common.enums.RoleType; import com.kamco.cd.kamcoback.config.api.ApiResponseDto; import com.kamco.cd.kamcoback.label.dto.LabelAllocateDto; import com.kamco.cd.kamcoback.label.dto.LabelAllocateDto.InferenceDetail; @@ -72,15 +71,14 @@ public class LabelAllocateApiController { @RequestParam(required = false) String uuid, @Parameter( - description = "작업자 유형 (선택) - 미입력 시 LABELER로 조회", + description = "작업자 유형 (선택) - 미입력 시 전체 조회", example = "LABELER", - schema = - @Schema( - allowableValues = {"LABELER", "REVIEWER"}, - defaultValue = "LABELER")) + schema = @Schema(allowableValues = {"LABELER", "REVIEWER"})) @RequestParam(required = false) String type, - @Parameter(description = "검색어 (작업자 이름 또는 사번으로 검색, 부분 일치)", example = "김라벨") + @Parameter( + description = "검색어 (작업자 이름 또는 사번으로 검색, 부분 일치) - 미입력 시 전체 조회", + example = "김라벨") @RequestParam(required = false) String search, @Parameter( @@ -100,11 +98,8 @@ public class LabelAllocateApiController { @RequestParam(required = false) String sort) { - // type이 null이면 기본값으로 LABELER 설정 - String workerType = (type == null || type.isEmpty()) ? RoleType.LABELER.name() : type; - return ApiResponseDto.ok( - labelAllocateService.getWorkerStatisticsByUuid(uuid, workerType, search, sort)); + labelAllocateService.getWorkerStatisticsByUuid(uuid, type, search, sort)); } @Operation(summary = "라벨링작업 관리 > 작업 배정", description = "라벨링작업 관리 > 작업 배정") From ac5423ef3e33b8baed978338e4dc2cafc700a1c8 Mon Sep 17 00:00:00 2001 From: teddy Date: Fri, 9 Jan 2026 11:21:43 +0900 Subject: [PATCH 07/12] =?UTF-8?q?[KC-99]=20=EC=B6=94=EB=A1=A0=EA=B4=80?= =?UTF-8?q?=EB=A6=AC=20=EB=93=B1=EB=A1=9D=20dto=20=EC=88=98=EC=A0=95,=20sp?= =?UTF-8?q?oless=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../inference/dto/InferenceResultDto.java | 19 +++---- .../label/LabelAllocateApiController.java | 19 ++++--- .../core/InferenceResultCoreService.java | 47 +++++++++++++++-- .../core/LabelAllocateCoreService.java | 1 - .../entity/MapSheetLearn5kEntity.java | 51 +++++++++++++++++++ .../postgres/entity/MapSheetLearnEntity.java | 4 +- .../Inference/MapSheetLearn5kRepository.java | 7 +++ .../MapSheetLearn5kRepositoryCustom.java | 3 ++ .../MapSheetLearn5kRepositoryImpl.java | 6 +++ .../label/LabelAllocateRepositoryCustom.java | 1 - .../label/LabelAllocateRepositoryImpl.java | 1 - src/main/resources/application-dev.yml | 3 ++ src/main/resources/application-prod.yml | 6 ++- 13 files changed, 140 insertions(+), 28 deletions(-) create mode 100644 src/main/java/com/kamco/cd/kamcoback/postgres/entity/MapSheetLearn5kEntity.java create mode 100644 src/main/java/com/kamco/cd/kamcoback/postgres/repository/Inference/MapSheetLearn5kRepository.java create mode 100644 src/main/java/com/kamco/cd/kamcoback/postgres/repository/Inference/MapSheetLearn5kRepositoryCustom.java create mode 100644 src/main/java/com/kamco/cd/kamcoback/postgres/repository/Inference/MapSheetLearn5kRepositoryImpl.java diff --git a/src/main/java/com/kamco/cd/kamcoback/inference/dto/InferenceResultDto.java b/src/main/java/com/kamco/cd/kamcoback/inference/dto/InferenceResultDto.java index 26efc23d..c9d5cabf 100644 --- a/src/main/java/com/kamco/cd/kamcoback/inference/dto/InferenceResultDto.java +++ b/src/main/java/com/kamco/cd/kamcoback/inference/dto/InferenceResultDto.java @@ -64,9 +64,10 @@ public class InferenceResultDto { @Getter @AllArgsConstructor public enum MapSheetScope implements EnumType { - EXCL("추론제외"), - PREV("이전 년도 도엽 사용"), + ALL("전체"), + PART("부분"), ; + private final String desc; @Override @@ -84,8 +85,8 @@ public class InferenceResultDto { @Getter @AllArgsConstructor public enum DetectOption implements EnumType { - ALL("전체"), - PART("부분"), + EXCL("추론제외"), + PREV("이전 년도 도엽 사용"), ; private final String desc; @@ -131,16 +132,16 @@ public class InferenceResultDto { @NotNull private Integer targetYyyy; + @Schema(description = "분석대상 도엽 - 전체(ALL), 부분(PART)", example = "PART") + @NotBlank + @EnumValid(enumClass = DetectOption.class, message = "분석대상 도엽 옵션은 '전체', '부분' 만 사용 가능합니다.") + private String mapSheetScope; + @Schema(description = "탐지 데이터 옵션 - 추론제외(PREV), 이전 년도 도엽 사용(PREV)", example = "EXCL") @NotBlank @EnumValid( enumClass = MapSheetScope.class, message = "탐지 데이터 옵션은 '추론제외', '이전 년도 도엽 사용' 만 사용 가능합니다.") - private String mapSheetScope; - - @Schema(description = "분석대상 도엽 - 전체(ALL), 부분(PART)", example = "PART") - @NotBlank - @EnumValid(enumClass = DetectOption.class, message = "분석대상 도엽 옵션은 '전체', '부분' 만 사용 가능합니다.") private String detectOption; @Schema(description = "5k 도협 번호 목록", example = "[34607067,34607067]") 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 f6da8a1d..31bd064e 100644 --- a/src/main/java/com/kamco/cd/kamcoback/label/LabelAllocateApiController.java +++ b/src/main/java/com/kamco/cd/kamcoback/label/LabelAllocateApiController.java @@ -76,9 +76,7 @@ public class LabelAllocateApiController { schema = @Schema(allowableValues = {"LABELER", "REVIEWER"})) @RequestParam(required = false) String type, - @Parameter( - description = "검색어 (작업자 이름 또는 사번으로 검색, 부분 일치) - 미입력 시 전체 조회", - example = "김라벨") + @Parameter(description = "검색어 (작업자 이름 또는 사번으로 검색, 부분 일치) - 미입력 시 전체 조회", example = "김라벨") @RequestParam(required = false) String search, @Parameter( @@ -259,27 +257,32 @@ public class LabelAllocateApiController { examples = { @io.swagger.v3.oas.annotations.media.ExampleObject( name = "라벨링 종료", - value = """ + value = + """ {"closedType": "LABELING", "closedYn": "Y"} """), @io.swagger.v3.oas.annotations.media.ExampleObject( name = "검수 종료", - value = """ + value = + """ {"closedType": "INSPECTION", "closedYn": "Y"} """), @io.swagger.v3.oas.annotations.media.ExampleObject( name = "라벨링 재개", - value = """ + value = + """ {"closedType": "LABELING", "closedYn": "N"} """), @io.swagger.v3.oas.annotations.media.ExampleObject( name = "검수 재개", - value = """ + value = + """ {"closedType": "INSPECTION", "closedYn": "N"} """), @io.swagger.v3.oas.annotations.media.ExampleObject( name = "특정 프로젝트 라벨링 종료", - value = """ + value = + """ {"uuid": "f97dc186-e6d3-4645-9737-3173dde8dc64", "closedType": "LABELING", "closedYn": "Y"} """) })) diff --git a/src/main/java/com/kamco/cd/kamcoback/postgres/core/InferenceResultCoreService.java b/src/main/java/com/kamco/cd/kamcoback/postgres/core/InferenceResultCoreService.java index 34fe6e51..b3f32167 100644 --- a/src/main/java/com/kamco/cd/kamcoback/postgres/core/InferenceResultCoreService.java +++ b/src/main/java/com/kamco/cd/kamcoback/postgres/core/InferenceResultCoreService.java @@ -1,6 +1,6 @@ package com.kamco.cd.kamcoback.postgres.core; -import com.fasterxml.jackson.databind.ObjectMapper; +import com.kamco.cd.kamcoback.common.utils.UserUtil; import com.kamco.cd.kamcoback.inference.dto.InferenceDetailDto; import com.kamco.cd.kamcoback.inference.dto.InferenceDetailDto.Dashboard; import com.kamco.cd.kamcoback.inference.dto.InferenceDetailDto.MapSheet; @@ -9,15 +9,18 @@ import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.ResultList; import com.kamco.cd.kamcoback.postgres.entity.MapInkx5kEntity; import com.kamco.cd.kamcoback.postgres.entity.MapSheetAnalDataInferenceEntity; import com.kamco.cd.kamcoback.postgres.entity.MapSheetAnalDataInferenceGeomEntity; +import com.kamco.cd.kamcoback.postgres.entity.MapSheetLearn5kEntity; import com.kamco.cd.kamcoback.postgres.entity.MapSheetLearnEntity; import com.kamco.cd.kamcoback.postgres.repository.Inference.MapSheetAnalDataInferenceRepository; +import com.kamco.cd.kamcoback.postgres.repository.Inference.MapSheetLearn5kRepository; import com.kamco.cd.kamcoback.postgres.repository.Inference.MapSheetLearnRepository; import com.kamco.cd.kamcoback.postgres.repository.scene.MapInkx5kRepository; +import jakarta.persistence.EntityManager; import jakarta.persistence.EntityNotFoundException; import jakarta.validation.constraints.NotNull; +import java.util.ArrayList; import java.util.List; import lombok.RequiredArgsConstructor; -import org.locationtech.jts.io.geojson.GeoJsonWriter; import org.springframework.data.domain.Page; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -29,9 +32,10 @@ public class InferenceResultCoreService { private final MapSheetAnalDataInferenceRepository mapSheetAnalDataRepository; private final MapSheetLearnRepository mapSheetLearnRepository; private final MapInkx5kRepository mapInkx5kRepository; + private final MapSheetLearn5kRepository mapSheetLearn5kRepository; - private final ObjectMapper objectMapper = new ObjectMapper(); - private final GeoJsonWriter geoJsonWriter = new GeoJsonWriter(); + private final EntityManager entityManager; + private final UserUtil userUtil; /** * 추론관리 목록 @@ -55,7 +59,40 @@ public class InferenceResultCoreService { mapSheetLearnEntity.setM1ModelUid(req.getModel1Uid()); mapSheetLearnEntity.setM2ModelUid(req.getModel2Uid()); mapSheetLearnEntity.setM3ModelUid(req.getModel3Uid()); - // mapSheetLearnRepository.save() + mapSheetLearnEntity.setCompareYyyy(req.getCompareYyyy()); + mapSheetLearnEntity.setTargetYyyy(req.getTargetYyyy()); + mapSheetLearnEntity.setMapSheetScope(req.getMapSheetScope()); + mapSheetLearnEntity.setDetectOption(req.getDetectOption()); + mapSheetLearnEntity.setCreatedUid(userUtil.getId()); + + // learn 테이블 저장 + MapSheetLearnEntity savedLearn = mapSheetLearnRepository.save(mapSheetLearnEntity); + + final int CHUNK = 1000; + List buffer = new ArrayList<>(CHUNK); + List mapSheetNumList = req.getMapSheetNum(); + + // learn 도엽 저장 + for (String mapSheetNum : mapSheetNumList) { + MapSheetLearn5kEntity e = new MapSheetLearn5kEntity(); + e.setLearn(savedLearn); + e.setMapSheetNum(Long.parseLong(mapSheetNum)); + e.setCreatedUid(userUtil.getId()); + buffer.add(e); + + if (buffer.size() == CHUNK) { + mapSheetLearn5kRepository.saveAll(buffer); + mapSheetLearn5kRepository.flush(); + entityManager.clear(); + buffer.clear(); + } + } + + if (!buffer.isEmpty()) { + mapSheetLearn5kRepository.saveAll(buffer); + mapSheetLearn5kRepository.flush(); + entityManager.clear(); + } } /****/ 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 65b2c78c..86c165e9 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 @@ -142,7 +142,6 @@ public class LabelAllocateCoreService { labelAllocateRepository.assignOwnerReAllocate(uuid, userId, paramUserId, assignCount); } - public void updateClosedYnByUuid(String uuid, String closedType, String closedYn) { labelAllocateRepository.updateClosedYnByUuid(uuid, closedType, closedYn); } diff --git a/src/main/java/com/kamco/cd/kamcoback/postgres/entity/MapSheetLearn5kEntity.java b/src/main/java/com/kamco/cd/kamcoback/postgres/entity/MapSheetLearn5kEntity.java new file mode 100644 index 00000000..8c44ef8a --- /dev/null +++ b/src/main/java/com/kamco/cd/kamcoback/postgres/entity/MapSheetLearn5kEntity.java @@ -0,0 +1,51 @@ +package com.kamco.cd.kamcoback.postgres.entity; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.SequenceGenerator; +import jakarta.persistence.Table; +import jakarta.validation.constraints.NotNull; +import java.time.ZonedDateTime; +import lombok.Getter; +import lombok.Setter; +import org.hibernate.annotations.OnDelete; +import org.hibernate.annotations.OnDeleteAction; + +@Getter +@Setter +@Entity +@Table(name = "tb_map_sheet_learn_5k") +public class MapSheetLearn5kEntity { + + @Id + @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "tb_map_sheet_learn_5k_id_gen") + @SequenceGenerator( + name = "tb_map_sheet_learn_5k_id_gen", + sequenceName = "tb_map_sheet_learn_5k_seq", + allocationSize = 1) + @Column(name = "id", nullable = false) + private Long id; + + @NotNull + @ManyToOne(fetch = FetchType.LAZY, optional = false) + @OnDelete(action = OnDeleteAction.CASCADE) + @JoinColumn(name = "learn_id", nullable = false, referencedColumnName = "id") + private MapSheetLearnEntity learn; + + @NotNull + @Column(name = "map_sheet_num", nullable = false) + private Long mapSheetNum; + + @org.hibernate.annotations.CreationTimestamp + @Column(name = "created_dttm") + private ZonedDateTime createdDttm; + + @Column(name = "created_uid") + private Long createdUid; +} diff --git a/src/main/java/com/kamco/cd/kamcoback/postgres/entity/MapSheetLearnEntity.java b/src/main/java/com/kamco/cd/kamcoback/postgres/entity/MapSheetLearnEntity.java index 062c7d87..5399a611 100644 --- a/src/main/java/com/kamco/cd/kamcoback/postgres/entity/MapSheetLearnEntity.java +++ b/src/main/java/com/kamco/cd/kamcoback/postgres/entity/MapSheetLearnEntity.java @@ -33,7 +33,7 @@ public class MapSheetLearnEntity { @ColumnDefault("gen_random_uuid()") @Column(name = "uuid") - private UUID uuid; + private UUID uuid = UUID.randomUUID(); @Size(max = 200) @NotNull @@ -89,7 +89,7 @@ public class MapSheetLearnEntity { @Column(name = "apply_dttm") private ZonedDateTime applyDttm; - @ColumnDefault("now()") + @org.hibernate.annotations.CreationTimestamp @Column(name = "created_dttm") private ZonedDateTime createdDttm; diff --git a/src/main/java/com/kamco/cd/kamcoback/postgres/repository/Inference/MapSheetLearn5kRepository.java b/src/main/java/com/kamco/cd/kamcoback/postgres/repository/Inference/MapSheetLearn5kRepository.java new file mode 100644 index 00000000..a5969a4a --- /dev/null +++ b/src/main/java/com/kamco/cd/kamcoback/postgres/repository/Inference/MapSheetLearn5kRepository.java @@ -0,0 +1,7 @@ +package com.kamco.cd.kamcoback.postgres.repository.Inference; + +import com.kamco.cd.kamcoback.postgres.entity.MapSheetLearn5kEntity; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface MapSheetLearn5kRepository + extends JpaRepository, MapSheetLearn5kRepositoryCustom {} diff --git a/src/main/java/com/kamco/cd/kamcoback/postgres/repository/Inference/MapSheetLearn5kRepositoryCustom.java b/src/main/java/com/kamco/cd/kamcoback/postgres/repository/Inference/MapSheetLearn5kRepositoryCustom.java new file mode 100644 index 00000000..a81faeae --- /dev/null +++ b/src/main/java/com/kamco/cd/kamcoback/postgres/repository/Inference/MapSheetLearn5kRepositoryCustom.java @@ -0,0 +1,3 @@ +package com.kamco.cd.kamcoback.postgres.repository.Inference; + +public interface MapSheetLearn5kRepositoryCustom {} diff --git a/src/main/java/com/kamco/cd/kamcoback/postgres/repository/Inference/MapSheetLearn5kRepositoryImpl.java b/src/main/java/com/kamco/cd/kamcoback/postgres/repository/Inference/MapSheetLearn5kRepositoryImpl.java new file mode 100644 index 00000000..68c7593c --- /dev/null +++ b/src/main/java/com/kamco/cd/kamcoback/postgres/repository/Inference/MapSheetLearn5kRepositoryImpl.java @@ -0,0 +1,6 @@ +package com.kamco.cd.kamcoback.postgres.repository.Inference; + +import org.springframework.stereotype.Repository; + +@Repository +public class MapSheetLearn5kRepositoryImpl implements MapSheetLearn5kRepositoryCustom {} 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 9a7bcc8c..5980da85 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 @@ -84,7 +84,6 @@ public interface LabelAllocateRepositoryCustom { void assignOwnerReAllocate(String uuid, String userId, String paramUserId, Long assignCount); - // 프로젝트 종료 여부 업데이트 (uuid 기반) void updateClosedYnByUuid(String uuid, String closedType, String closedYn); } 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 12c5450b..95a5105a 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 @@ -1433,7 +1433,6 @@ public class LabelAllocateRepositoryImpl implements LabelAllocateRepositoryCusto .execute(); } - @Override public void updateClosedYnByUuid(String uuid, String closedType, String closedYn) { var updateQuery = queryFactory.update(mapSheetAnalInferenceEntity); diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index 579b7a42..790a1c95 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -11,8 +11,11 @@ spring: hibernate: default_batch_fetch_size: 100 # ✅ 성능 - N+1 쿼리 방지 order_updates: true # ✅ 성능 - 업데이트 순서 정렬로 데드락 방지 + order_inserts: true use_sql_comments: true # ⚠️ 선택 - SQL에 주석 추가 (디버깅용) format_sql: true # ⚠️ 선택 - SQL 포맷팅 (가독성) + jdbc: + batch_size: 1000 # ✅ 추가 (JDBC batch) datasource: url: jdbc:postgresql://192.168.2.127:15432/kamco_cds diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index 7c633ecd..5a23ca0e 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -4,14 +4,18 @@ spring: on-profile: prod jpa: - show-sql: false + show-sql: true hibernate: ddl-auto: validate properties: hibernate: default_batch_fetch_size: 100 # ✅ 성능 - N+1 쿼리 방지 order_updates: true # ✅ 성능 - 업데이트 순서 정렬로 데드락 방지 + order_inserts: true use_sql_comments: true # ⚠️ 선택 - SQL에 주석 추가 (디버깅용) + format_sql: true # ⚠️ 선택 - SQL 포맷팅 (가독성) + jdbc: + batch_size: 1000 # ✅ 추가 (JDBC batch) datasource: url: jdbc:postgresql://10.100.0.10:25432/temp From da9ef16f444301242d565d4fac8a9490491e509e Mon Sep 17 00:00:00 2001 From: teddy Date: Fri, 9 Jan 2026 11:23:57 +0900 Subject: [PATCH 08/12] =?UTF-8?q?[KC-99]=20=EC=B6=94=EB=A1=A0=EA=B4=80?= =?UTF-8?q?=EB=A6=AC=20=EB=93=B1=EB=A1=9D=20dto=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../inference/dto/InferenceResultDto.java | 38 +++++++++++++------ 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/src/main/java/com/kamco/cd/kamcoback/inference/dto/InferenceResultDto.java b/src/main/java/com/kamco/cd/kamcoback/inference/dto/InferenceResultDto.java index c9d5cabf..9d9d59b2 100644 --- a/src/main/java/com/kamco/cd/kamcoback/inference/dto/InferenceResultDto.java +++ b/src/main/java/com/kamco/cd/kamcoback/inference/dto/InferenceResultDto.java @@ -19,7 +19,9 @@ import org.springframework.data.domain.Pageable; public class InferenceResultDto { - /** 목록조회 dto */ + /** + * 목록조회 dto + */ @Getter @Setter @AllArgsConstructor @@ -31,14 +33,20 @@ public class InferenceResultDto { private String status; private String mapSheetCnt; private Long detectingCnt; - @JsonFormatDttm private ZonedDateTime startTime; - @JsonFormatDttm private ZonedDateTime endTime; - @JsonFormatDttm private ZonedDateTime elapsedTime; + @JsonFormatDttm + private ZonedDateTime startTime; + @JsonFormatDttm + private ZonedDateTime endTime; + @JsonFormatDttm + private ZonedDateTime elapsedTime; private Boolean applyYn; - @JsonFormatDttm private ZonedDateTime applyDttm; + @JsonFormatDttm + private ZonedDateTime applyDttm; } - /** 목록조회 검색 조건 dto */ + /** + * 목록조회 검색 조건 dto + */ @Getter @Setter @NoArgsConstructor @@ -60,7 +68,9 @@ public class InferenceResultDto { } } - /** 탐지 데이터 옵션 dto */ + /** + * 탐지 데이터 옵션 dto + */ @Getter @AllArgsConstructor public enum MapSheetScope implements EnumType { @@ -81,7 +91,9 @@ public class InferenceResultDto { } } - /** 분석대상 도엽 enum */ + /** + * 분석대상 도엽 enum + */ @Getter @AllArgsConstructor public enum DetectOption implements EnumType { @@ -101,7 +113,9 @@ public class InferenceResultDto { } } - /** 변화탐지 실행 정보 저장 요청 정보 */ + /** + * 변화탐지 실행 정보 저장 요청 정보 + */ @Getter @Setter @NoArgsConstructor @@ -137,11 +151,11 @@ public class InferenceResultDto { @EnumValid(enumClass = DetectOption.class, message = "분석대상 도엽 옵션은 '전체', '부분' 만 사용 가능합니다.") private String mapSheetScope; - @Schema(description = "탐지 데이터 옵션 - 추론제외(PREV), 이전 년도 도엽 사용(PREV)", example = "EXCL") + @Schema(description = "탐지 데이터 옵션 - 추론제외(EXCL), 이전 년도 도엽 사용(PREV)", example = "EXCL") @NotBlank @EnumValid( - enumClass = MapSheetScope.class, - message = "탐지 데이터 옵션은 '추론제외', '이전 년도 도엽 사용' 만 사용 가능합니다.") + enumClass = MapSheetScope.class, + message = "탐지 데이터 옵션은 '추론제외', '이전 년도 도엽 사용' 만 사용 가능합니다.") private String detectOption; @Schema(description = "5k 도협 번호 목록", example = "[34607067,34607067]") From 96081de63f9ddaa28f4b7c015e8c7acd24c41d6c Mon Sep 17 00:00:00 2001 From: teddy Date: Fri, 9 Jan 2026 11:29:10 +0900 Subject: [PATCH 09/12] =?UTF-8?q?[KC-99]=20=EC=B6=94=EB=A1=A0=EA=B4=80?= =?UTF-8?q?=EB=A6=AC=20=EB=93=B1=EB=A1=9D=20leran,=205k=20=ED=85=8C?= =?UTF-8?q?=EC=9D=B4=EB=B8=94=20=EC=A0=80=EC=9E=A5=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../InferenceResultApiController.java | 8 ++-- .../inference/dto/InferenceResultDto.java | 40 ++++++------------- .../service/InferenceResultService.java | 6 ++- .../core/InferenceResultCoreService.java | 5 ++- 4 files changed, 26 insertions(+), 33 deletions(-) diff --git a/src/main/java/com/kamco/cd/kamcoback/inference/InferenceResultApiController.java b/src/main/java/com/kamco/cd/kamcoback/inference/InferenceResultApiController.java index 3325bd64..a1ae6ac5 100644 --- a/src/main/java/com/kamco/cd/kamcoback/inference/InferenceResultApiController.java +++ b/src/main/java/com/kamco/cd/kamcoback/inference/InferenceResultApiController.java @@ -13,6 +13,7 @@ import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; import java.time.LocalDate; +import java.util.UUID; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Page; import org.springframework.web.bind.annotation.GetMapping; @@ -71,19 +72,20 @@ public class InferenceResultApiController { content = @Content( mediaType = "application/json", - schema = @Schema(implementation = Void.class))), + schema = @Schema(description = "저장 uuid", implementation = UUID.class))), @ApiResponse(responseCode = "400", description = "잘못된 요청 데이터", content = @Content), @ApiResponse(responseCode = "500", description = "서버 오류", content = @Content) }) @PostMapping("/reg") - public ApiResponseDto saveInferenceInfo( + public ApiResponseDto saveInferenceInfo( @io.swagger.v3.oas.annotations.parameters.RequestBody( description = "변화탐지 실행 정보 저장 요청 정보", required = true) @RequestBody @Valid InferenceResultDto.RegReq req) { - return ApiResponseDto.ok(null); + UUID uuid = inferenceResultService.saveInferenceInfo(req); + return ApiResponseDto.ok(uuid); } // @ApiResponses( diff --git a/src/main/java/com/kamco/cd/kamcoback/inference/dto/InferenceResultDto.java b/src/main/java/com/kamco/cd/kamcoback/inference/dto/InferenceResultDto.java index 9d9d59b2..fbd5d6e5 100644 --- a/src/main/java/com/kamco/cd/kamcoback/inference/dto/InferenceResultDto.java +++ b/src/main/java/com/kamco/cd/kamcoback/inference/dto/InferenceResultDto.java @@ -19,9 +19,7 @@ import org.springframework.data.domain.Pageable; public class InferenceResultDto { - /** - * 목록조회 dto - */ + /** 목록조회 dto */ @Getter @Setter @AllArgsConstructor @@ -33,20 +31,14 @@ public class InferenceResultDto { private String status; private String mapSheetCnt; private Long detectingCnt; - @JsonFormatDttm - private ZonedDateTime startTime; - @JsonFormatDttm - private ZonedDateTime endTime; - @JsonFormatDttm - private ZonedDateTime elapsedTime; + @JsonFormatDttm private ZonedDateTime startTime; + @JsonFormatDttm private ZonedDateTime endTime; + @JsonFormatDttm private ZonedDateTime elapsedTime; private Boolean applyYn; - @JsonFormatDttm - private ZonedDateTime applyDttm; + @JsonFormatDttm private ZonedDateTime applyDttm; } - /** - * 목록조회 검색 조건 dto - */ + /** 목록조회 검색 조건 dto */ @Getter @Setter @NoArgsConstructor @@ -68,9 +60,7 @@ public class InferenceResultDto { } } - /** - * 탐지 데이터 옵션 dto - */ + /** 탐지 데이터 옵션 dto */ @Getter @AllArgsConstructor public enum MapSheetScope implements EnumType { @@ -91,9 +81,7 @@ public class InferenceResultDto { } } - /** - * 분석대상 도엽 enum - */ + /** 분석대상 도엽 enum */ @Getter @AllArgsConstructor public enum DetectOption implements EnumType { @@ -113,9 +101,7 @@ public class InferenceResultDto { } } - /** - * 변화탐지 실행 정보 저장 요청 정보 - */ + /** 변화탐지 실행 정보 저장 요청 정보 */ @Getter @Setter @NoArgsConstructor @@ -148,17 +134,17 @@ public class InferenceResultDto { @Schema(description = "분석대상 도엽 - 전체(ALL), 부분(PART)", example = "PART") @NotBlank - @EnumValid(enumClass = DetectOption.class, message = "분석대상 도엽 옵션은 '전체', '부분' 만 사용 가능합니다.") + @EnumValid(enumClass = MapSheetScope.class, message = "분석대상 도엽 옵션은 '전체', '부분' 만 사용 가능합니다.") private String mapSheetScope; @Schema(description = "탐지 데이터 옵션 - 추론제외(EXCL), 이전 년도 도엽 사용(PREV)", example = "EXCL") @NotBlank @EnumValid( - enumClass = MapSheetScope.class, - message = "탐지 데이터 옵션은 '추론제외', '이전 년도 도엽 사용' 만 사용 가능합니다.") + enumClass = DetectOption.class, + message = "탐지 데이터 옵션은 '추론제외', '이전 년도 도엽 사용' 만 사용 가능합니다.") private String detectOption; - @Schema(description = "5k 도협 번호 목록", example = "[34607067,34607067]") + @Schema(description = "5k 도협 번호 목록", example = "[34607067,35802056]") @NotNull private List mapSheetNum; } diff --git a/src/main/java/com/kamco/cd/kamcoback/inference/service/InferenceResultService.java b/src/main/java/com/kamco/cd/kamcoback/inference/service/InferenceResultService.java index aa2a0daa..ce39c4d8 100644 --- a/src/main/java/com/kamco/cd/kamcoback/inference/service/InferenceResultService.java +++ b/src/main/java/com/kamco/cd/kamcoback/inference/service/InferenceResultService.java @@ -9,6 +9,7 @@ import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.ResultList; import com.kamco.cd.kamcoback.postgres.core.InferenceResultCoreService; import jakarta.validation.constraints.NotNull; import java.util.List; +import java.util.UUID; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Page; import org.springframework.stereotype.Service; @@ -36,8 +37,9 @@ public class InferenceResultService { * * @param req */ - public void saveInferenceInfo(InferenceResultDto.RegReq req) { - inferenceResultCoreService.saveInferenceInfo(req); + @Transactional + public UUID saveInferenceInfo(InferenceResultDto.RegReq req) { + return inferenceResultCoreService.saveInferenceInfo(req); } /** diff --git a/src/main/java/com/kamco/cd/kamcoback/postgres/core/InferenceResultCoreService.java b/src/main/java/com/kamco/cd/kamcoback/postgres/core/InferenceResultCoreService.java index b3f32167..dbc77fa4 100644 --- a/src/main/java/com/kamco/cd/kamcoback/postgres/core/InferenceResultCoreService.java +++ b/src/main/java/com/kamco/cd/kamcoback/postgres/core/InferenceResultCoreService.java @@ -20,6 +20,7 @@ import jakarta.persistence.EntityNotFoundException; import jakarta.validation.constraints.NotNull; import java.util.ArrayList; import java.util.List; +import java.util.UUID; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Page; import org.springframework.stereotype.Service; @@ -53,7 +54,7 @@ public class InferenceResultCoreService { * * @param req */ - public void saveInferenceInfo(InferenceResultDto.RegReq req) { + public UUID saveInferenceInfo(InferenceResultDto.RegReq req) { MapSheetLearnEntity mapSheetLearnEntity = new MapSheetLearnEntity(); mapSheetLearnEntity.setTitle(req.getTitle()); mapSheetLearnEntity.setM1ModelUid(req.getModel1Uid()); @@ -93,6 +94,8 @@ public class InferenceResultCoreService { mapSheetLearn5kRepository.flush(); entityManager.clear(); } + + return savedLearn.getUuid(); } /****/ From eb0825bf48162a6d26f603b36ec4054706c1d282 Mon Sep 17 00:00:00 2001 From: teddy Date: Fri, 9 Jan 2026 11:31:25 +0900 Subject: [PATCH 10/12] =?UTF-8?q?[KC-99]=20=EC=B6=94=EB=A1=A0=EA=B4=80?= =?UTF-8?q?=EB=A6=AC=20=EB=93=B1=EB=A1=9D=20dto=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kamco/cd/kamcoback/inference/dto/InferenceResultDto.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/kamco/cd/kamcoback/inference/dto/InferenceResultDto.java b/src/main/java/com/kamco/cd/kamcoback/inference/dto/InferenceResultDto.java index fbd5d6e5..38e76e01 100644 --- a/src/main/java/com/kamco/cd/kamcoback/inference/dto/InferenceResultDto.java +++ b/src/main/java/com/kamco/cd/kamcoback/inference/dto/InferenceResultDto.java @@ -144,7 +144,7 @@ public class InferenceResultDto { message = "탐지 데이터 옵션은 '추론제외', '이전 년도 도엽 사용' 만 사용 가능합니다.") private String detectOption; - @Schema(description = "5k 도협 번호 목록", example = "[34607067,35802056]") + @Schema(description = "5k 도협 번호 목록", example = "['34607067','35802056']") @NotNull private List mapSheetNum; } From 6bdee069ba8628e8819dbe59738ebbae3e788cac Mon Sep 17 00:00:00 2001 From: teddy Date: Fri, 9 Jan 2026 11:34:35 +0900 Subject: [PATCH 11/12] =?UTF-8?q?[KC-99]=20=EC=B6=94=EB=A1=A0=EA=B4=80?= =?UTF-8?q?=EB=A6=AC=20=EB=93=B1=EB=A1=9D=20dto=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kamco/cd/kamcoback/inference/dto/InferenceResultDto.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/kamco/cd/kamcoback/inference/dto/InferenceResultDto.java b/src/main/java/com/kamco/cd/kamcoback/inference/dto/InferenceResultDto.java index 38e76e01..599c8ff4 100644 --- a/src/main/java/com/kamco/cd/kamcoback/inference/dto/InferenceResultDto.java +++ b/src/main/java/com/kamco/cd/kamcoback/inference/dto/InferenceResultDto.java @@ -144,7 +144,7 @@ public class InferenceResultDto { message = "탐지 데이터 옵션은 '추론제외', '이전 년도 도엽 사용' 만 사용 가능합니다.") private String detectOption; - @Schema(description = "5k 도협 번호 목록", example = "['34607067','35802056']") + @Schema(description = "5k 도협 번호 목록", example = "[\"34607067\",\"35802056\"]") @NotNull private List mapSheetNum; } From 2b892f2c2fcf208dc7b0e847f2f4b0ee870be892 Mon Sep 17 00:00:00 2001 From: "gayoun.park" Date: Fri, 9 Jan 2026 11:47:55 +0900 Subject: [PATCH 12/12] =?UTF-8?q?[KC-148]=20=ED=95=99=EC=8A=B5=EB=8D=B0?= =?UTF-8?q?=EC=9D=B4=ED=84=B0=20=EB=AA=A9=EB=A1=9D=20API=20=ED=95=AD?= =?UTF-8?q?=EB=AA=A9=20=EC=88=98=EC=A0=95,=20=ED=98=84=ED=99=A9=EA=B4=80?= =?UTF-8?q?=EB=A6=AC=20=EC=83=81=EC=84=B8=20=EC=82=AC=EC=9A=A9=EC=9E=90=20?= =?UTF-8?q?=EB=AA=A9=EB=A1=9D=20API=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../label/LabelWorkerApiController.java | 8 ++-- .../cd/kamcoback/label/dto/LabelWorkDto.java | 40 ++++++++++--------- .../label/LabelWorkRepositoryImpl.java | 30 ++++++++------ 3 files changed, 44 insertions(+), 34 deletions(-) diff --git a/src/main/java/com/kamco/cd/kamcoback/label/LabelWorkerApiController.java b/src/main/java/com/kamco/cd/kamcoback/label/LabelWorkerApiController.java index a12a868c..862c9a0f 100644 --- a/src/main/java/com/kamco/cd/kamcoback/label/LabelWorkerApiController.java +++ b/src/main/java/com/kamco/cd/kamcoback/label/LabelWorkerApiController.java @@ -136,11 +136,13 @@ public class LabelWorkerApiController { @Parameter(description = "페이지 번호 (0부터 시작)", example = "0") @RequestParam(defaultValue = "0") int page, @Parameter(description = "페이지 크기", example = "20") @RequestParam(defaultValue = "20") - int size) { + int size, + @Parameter(description = "회차 UUID key", example = "f97dc186-e6d3-4645-9737-3173dde8dc64") + String uuid) { // 대상추출(최근) - UUID lstUuid = labelWorkService.findLastLabelWorkState(); - String uuid = lstUuid.toString(); + // UUID lstUuid = labelWorkService.findLastLabelWorkState(); + // String uuid = lstUuid.toString(); LabelWorkDto.WorkerStateSearchReq searchReq = new WorkerStateSearchReq(); searchReq.setUserRole(userRole); diff --git a/src/main/java/com/kamco/cd/kamcoback/label/dto/LabelWorkDto.java b/src/main/java/com/kamco/cd/kamcoback/label/dto/LabelWorkDto.java index a7913c2b..97016833 100644 --- a/src/main/java/com/kamco/cd/kamcoback/label/dto/LabelWorkDto.java +++ b/src/main/java/com/kamco/cd/kamcoback/label/dto/LabelWorkDto.java @@ -38,17 +38,16 @@ public class LabelWorkDto { private Integer compareYyyy; private Integer targetYyyy; private Integer stage; - @JsonFormatDttm private ZonedDateTime createdDttm; + @JsonFormatDttm private ZonedDateTime gukyuinApplyDttm; private Long detectionTotCnt; private Long labelTotCnt; private Long labelAssignCnt; - private Long labelStopTotCnt; - private Long labelIngTotCnt; + private Long labelSkipTotCnt; private Long labelCompleteTotCnt; @JsonFormatDttm private ZonedDateTime labelStartDttm; - // tb_map_sheet_anal_inference.anal_state 컬럼 값 - private String analState; + // tb_map_sheet_anal_inference.anal_state 컬럼 값 -> 미사용 + // private String analState; // tb_labeling_assignment 테이블에서 stagnation_yn = 'N'인 정상 진행 건수 private Long normalProgressCnt; @@ -56,6 +55,9 @@ public class LabelWorkDto { // tb_labeling_assignment 테이블에서 총 배정 건수 private Long totalAssignmentCnt; + private String labelingClosedYn; + private String inspectionClosedYn; + @JsonProperty("detectYear") public String getDetectYear() { if (compareYyyy == null || targetYyyy == null) { @@ -66,22 +68,24 @@ public class LabelWorkDto { /** 라벨링 상태 반환 (tb_map_sheet_anal_inference.anal_state 기준) */ public String getLabelState() { - // anal_state 값이 있으면 해당 값 사용 - if (this.analState != null && !this.analState.isEmpty()) { - return this.analState; - } + // anal_state 값이 있으면 해당 값 사용 -> 우선은 미사용 + // if (this.analState != null && !this.analState.isEmpty()) { + // return this.analState; + // } // anal_state 값이 없으면 기존 로직으로 폴백 - String mngState = "PENDING"; + String mngState = LabelMngState.PENDING.getId(); if (this.labelTotCnt == 0) { - mngState = "PENDING"; - } else if (this.labelTotCnt > 0 && this.labelAssignCnt > 0 && this.labelIngTotCnt == 0) { - mngState = "ASSIGNED"; - } else if (this.labelIngTotCnt > 0) { - mngState = "LABEL_ING"; + mngState = LabelMngState.PENDING.getId(); + } else if (this.labelTotCnt > 0 && this.labelAssignCnt > 0 && this.labelCompleteTotCnt == 0) { + mngState = LabelMngState.ASSIGNED.getId(); + } else if (this.labelCompleteTotCnt > 0) { + mngState = LabelMngState.LABEL_ING.getId(); } else if (this.labelTotCnt <= labelCompleteTotCnt) { - mngState = "LABEL_COMPLETE"; + mngState = LabelMngState.LABEL_COMPLETE.getId(); + } else if (this.labelingClosedYn.equals("Y") && this.inspectionClosedYn.equals("Y")) { + mngState = LabelMngState.FINISH.getId(); } return mngState; @@ -109,10 +113,10 @@ public class LabelWorkDto { if (this.totalAssignmentCnt == null || this.totalAssignmentCnt == 0) { return 0.0; } - if (this.normalProgressCnt == null) { + if (this.labelCompleteTotCnt == null) { return 0.0; } - return (double) this.normalProgressCnt / this.totalAssignmentCnt * 100.0; + return (double) this.labelCompleteTotCnt / this.totalAssignmentCnt * 100.0; } } diff --git a/src/main/java/com/kamco/cd/kamcoback/postgres/repository/label/LabelWorkRepositoryImpl.java b/src/main/java/com/kamco/cd/kamcoback/postgres/repository/label/LabelWorkRepositoryImpl.java index 4530a9d7..50bcd45b 100644 --- a/src/main/java/com/kamco/cd/kamcoback/postgres/repository/label/LabelWorkRepositoryImpl.java +++ b/src/main/java/com/kamco/cd/kamcoback/postgres/repository/label/LabelWorkRepositoryImpl.java @@ -169,38 +169,42 @@ public class LabelWorkRepositoryImpl implements LabelWorkRepositoryCustom { mapSheetAnalInferenceEntity.compareYyyy, mapSheetAnalInferenceEntity.targetYyyy, mapSheetAnalInferenceEntity.stage, - // createdDttm: tb_map_sheet_anal_inference.created_dttm 사용 - mapSheetAnalInferenceEntity.createdDttm, + // 국유인 반영 컬럼 gukyuinApplyDttm + mapSheetAnalInferenceEntity.gukyuinApplyDttm, mapSheetAnalDataInferenceGeomEntity.dataUid.count(), // labelTotCnt: pnu 있고 pass_yn = false인 건수 labelTotCntExpr, new CaseBuilder() - .when(mapSheetAnalDataInferenceGeomEntity.labelState.eq("ASSIGNED")) + .when( + mapSheetAnalDataInferenceGeomEntity.labelState.eq( + LabelState.ASSIGNED.getId())) .then(1L) .otherwise(0L) .sum(), new CaseBuilder() - .when(mapSheetAnalDataInferenceGeomEntity.labelState.eq("STOP")) + .when( + mapSheetAnalDataInferenceGeomEntity.labelState.eq( + LabelState.SKIP.getId())) // "STOP"? .then(1L) .otherwise(0L) .sum(), new CaseBuilder() - .when(mapSheetAnalDataInferenceGeomEntity.labelState.eq("LABEL_ING")) - .then(1L) - .otherwise(0L) - .sum(), - new CaseBuilder() - .when(mapSheetAnalDataInferenceGeomEntity.labelState.eq("LABEL_COMPLETE")) + .when( + mapSheetAnalDataInferenceGeomEntity.labelState.eq( + LabelState.DONE.getId())) // "LABEL_COMPLETE"? .then(1L) .otherwise(0L) .sum(), mapSheetAnalDataInferenceGeomEntity.labelStateDttm.min(), - // analState: tb_map_sheet_anal_inference.anal_state - mapSheetAnalInferenceEntity.analState, + // analState: tb_map_sheet_anal_inference.anal_state -> 우선은 미사용, java 단에서 로직화 해서 + // 내려줌 + // mapSheetAnalInferenceEntity.analState, // normalProgressCnt: stagnation_yn = 'N'인 건수 (서브쿼리) normalProgressCntSubQuery, // totalAssignmentCnt: 총 배정 건수 (서브쿼리) - totalAssignmentCntSubQuery)) + totalAssignmentCntSubQuery, + mapSheetAnalInferenceEntity.labelingClosedYn, + mapSheetAnalInferenceEntity.inspectionClosedYn)) .from(mapSheetAnalInferenceEntity) .innerJoin(mapSheetAnalDataInferenceEntity) .on(whereSubDataBuilder)