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 cb723a69..e23a4104 100644 --- a/src/main/java/com/kamco/cd/kamcoback/label/LabelAllocateApiController.java +++ b/src/main/java/com/kamco/cd/kamcoback/label/LabelAllocateApiController.java @@ -93,19 +93,16 @@ public class LabelAllocateApiController { defaultValue = "NAME_ASC")) @RequestParam(required = false) String sort, - @Parameter(description = "페이지 번호 (0부터 시작)", example = "0") - @RequestParam(defaultValue = "0") + @Parameter(description = "페이지 번호 (0부터 시작)", example = "0") @RequestParam(defaultValue = "0") Integer page, - @Parameter(description = "페이지 크기", example = "20") - @RequestParam(defaultValue = "20") + @Parameter(description = "페이지 크기", example = "20") @RequestParam(defaultValue = "20") Integer size) { // type이 null이면 기본값으로 LABELER 설정 String workerType = (type == null || type.isEmpty()) ? RoleType.LABELER.name() : type; return ApiResponseDto.ok( - labelAllocateService.getWorkerStatistics( - null, workerType, search, sort, page, size)); + labelAllocateService.getWorkerStatistics(null, workerType, search, sort, page, size)); } @Operation(summary = "라벨링작업 관리 > 작업 배정", description = "라벨링작업 관리 > 작업 배정") 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 d07083e1..eca1c7f0 100644 --- a/src/main/java/com/kamco/cd/kamcoback/label/LabelWorkerApiController.java +++ b/src/main/java/com/kamco/cd/kamcoback/label/LabelWorkerApiController.java @@ -1,9 +1,10 @@ package com.kamco.cd.kamcoback.label; -import com.kamco.cd.kamcoback.code.dto.CommonCodeDto; import com.kamco.cd.kamcoback.config.api.ApiResponseDto; import com.kamco.cd.kamcoback.label.dto.LabelWorkDto; +import com.kamco.cd.kamcoback.label.dto.LabelWorkDto.ChangeDetectYear; import com.kamco.cd.kamcoback.label.dto.LabelWorkDto.LabelWorkMng; +import com.kamco.cd.kamcoback.label.dto.LabelWorkDto.LabelWorkMngDetail; import com.kamco.cd.kamcoback.label.dto.LabelWorkDto.LabelWorkMngSearchReq; import com.kamco.cd.kamcoback.label.service.LabelWorkService; import io.swagger.v3.oas.annotations.Operation; @@ -13,10 +14,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 java.util.List; +import java.util.UUID; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.data.domain.Page; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @@ -30,6 +34,24 @@ public class LabelWorkerApiController { private final LabelWorkService labelWorkService; + @Operation(summary = "변화탐지 년도 셀렉트박스 조회", description = "라벨링작업 관리 > 목록 조회 변화탐지 년도 셀렉트박스 조회") + @ApiResponses( + value = { + @ApiResponse( + responseCode = "200", + description = "조회 성공", + content = + @Content( + mediaType = "application/json", + schema = @Schema(implementation = ChangeDetectYear.class))), + @ApiResponse(responseCode = "404", description = "코드를 찾을 수 없음", content = @Content), + @ApiResponse(responseCode = "500", description = "서버 오류", content = @Content) + }) + @GetMapping("/change-detect-year") + public ApiResponseDto> getChangeDetectYear() { + return ApiResponseDto.ok(labelWorkService.getChangeDetectYear()); + } + @Operation(summary = "라벨링작업 관리 > 목록 조회", description = "라벨링작업 관리 > 목록 조회") @ApiResponses( value = { @@ -39,26 +61,45 @@ public class LabelWorkerApiController { content = @Content( mediaType = "application/json", - schema = @Schema(implementation = CommonCodeDto.Basic.class))), + schema = @Schema(implementation = Page.class))), @ApiResponse(responseCode = "404", description = "코드를 찾을 수 없음", content = @Content), @ApiResponse(responseCode = "500", description = "서버 오류", content = @Content) }) @GetMapping("/label-work-mng-list") public ApiResponseDto> labelWorkMngList( - @Parameter(description = "변화탐지년도", example = "2024") @RequestParam(required = false) - Integer detectYyyy, - @Parameter(description = "시작일", example = "20260101") @RequestParam String strtDttm, + @Parameter(description = "변화탐지년도", example = "2022-2024") @RequestParam(required = false) + String detectYear, + @Parameter(description = "시작일", example = "20220101") @RequestParam String strtDttm, @Parameter(description = "종료일", example = "20261201") @RequestParam String endDttm, @Parameter(description = "페이지 번호 (0부터 시작)", example = "0") @RequestParam(defaultValue = "0") int page, @Parameter(description = "페이지 크기", example = "20") @RequestParam(defaultValue = "20") int size) { LabelWorkDto.LabelWorkMngSearchReq searchReq = new LabelWorkMngSearchReq(); - searchReq.setDetectYyyy(detectYyyy); + searchReq.setDetectYear(detectYear); searchReq.setStrtDttm(strtDttm); searchReq.setEndDttm(endDttm); searchReq.setPage(page); searchReq.setSize(size); return ApiResponseDto.ok(labelWorkService.labelWorkMngList(searchReq)); } + + @Operation(summary = "라벨링작업 관리 > 작업 배정 정보조회", description = "작업 배정 정보조회") + @ApiResponses( + value = { + @ApiResponse( + responseCode = "200", + description = "조회 성공", + content = + @Content( + mediaType = "application/json", + schema = @Schema(implementation = LabelWorkMngDetail.class))), + @ApiResponse(responseCode = "404", description = "코드를 찾을 수 없음", content = @Content), + @ApiResponse(responseCode = "500", description = "서버 오류", content = @Content) + }) + @GetMapping("/label-work-mng-detail/{uuid}") + public ApiResponseDto labelWorkMngDetail( + @Parameter(description = "uuid") @PathVariable UUID uuid) { + return ApiResponseDto.ok(labelWorkService.findLabelWorkMngDetail(uuid)); + } } 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 024f89b5..55272f9f 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 @@ -1,10 +1,12 @@ package com.kamco.cd.kamcoback.label.dto; +import com.fasterxml.jackson.annotation.JsonProperty; import com.kamco.cd.kamcoback.common.utils.enums.Enums; import com.kamco.cd.kamcoback.common.utils.interfaces.JsonFormatDttm; import com.kamco.cd.kamcoback.label.dto.LabelAllocateDto.LabelMngState; import io.swagger.v3.oas.annotations.media.Schema; import java.time.ZonedDateTime; +import java.util.UUID; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; @@ -14,6 +16,15 @@ import org.springframework.data.domain.Pageable; public class LabelWorkDto { + @Getter + @Setter + @AllArgsConstructor + public static class ChangeDetectYear { + + private String code; + private String name; + } + @Schema(name = "LabelWorkMng", description = "라벨작업관리") @Getter @Setter @@ -21,8 +32,9 @@ public class LabelWorkDto { @AllArgsConstructor public static class LabelWorkMng { - private int compareYyyy; - private int targetYyyy; + private UUID uuid; + private Integer compareYyyy; + private Integer targetYyyy; private int stage; @JsonFormatDttm private ZonedDateTime createdDttm; private Long detectionTotCnt; @@ -32,6 +44,14 @@ public class LabelWorkDto { private Long labelCompleteTotCnt; @JsonFormatDttm private ZonedDateTime labelStartDttm; + @JsonProperty("detectYear") + public String getDetectYear() { + if (compareYyyy == null || targetYyyy == null) { + return null; + } + return compareYyyy + "-" + targetYyyy; + } + public String getLabelState() { String mngState = "PENDING"; @@ -65,6 +85,20 @@ public class LabelWorkDto { } } + @Getter + @Setter + @AllArgsConstructor + @NoArgsConstructor + public static class LabelWorkMngDetail { + + private String detectionYear; + private Integer stage; + @JsonFormatDttm private ZonedDateTime createdDttm; + private Long labelTotCnt; + private Long labeler; + private Long reviewer; + } + @Schema(name = "LabelWorkMngSearchReq", description = "라벨작업관리 검색 요청") @Getter @Setter @@ -80,7 +114,7 @@ public class LabelWorkDto { private int size = 20; @Schema(description = "변화탐지년도", example = "2024") - private Integer detectYyyy; + private String detectYear; @Schema(description = "시작일", example = "20260101") private String strtDttm; 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 3501da8a..435c2060 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 @@ -67,23 +67,17 @@ public class WorkerStatsDto { private Boolean isStagnated; // 레거시 필드 (기존 호환성 유지) - @Deprecated - private Long doneCnt; // completed로 대체 + @Deprecated private Long doneCnt; // completed로 대체 - @Deprecated - private Long skipCnt; // skipped로 대체 + @Deprecated private Long skipCnt; // skipped로 대체 - @Deprecated - private Long remainingCnt; // remaining으로 대체 + @Deprecated private Long remainingCnt; // remaining으로 대체 - @Deprecated - private Long day3AgoDoneCnt; // history.day3Ago로 대체 + @Deprecated private Long day3AgoDoneCnt; // history.day3Ago로 대체 - @Deprecated - private Long day2AgoDoneCnt; // history.day2Ago로 대체 + @Deprecated private Long day2AgoDoneCnt; // history.day2Ago로 대체 - @Deprecated - private Long day1AgoDoneCnt; // history.day1Ago로 대체 + @Deprecated private Long day1AgoDoneCnt; // history.day1Ago로 대체 } @Getter @@ -107,7 +101,6 @@ public class WorkerStatsDto { private Long average; } - @Getter @Setter @Builder 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 ae0ada1d..bf551dfb 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 @@ -122,12 +122,7 @@ public class LabelAllocateService { * @return 작업자 목록 및 통계 */ public WorkerListResponse getWorkerStatistics( - Long analUid, - String workerType, - String search, - String sortType, - Integer page, - Integer size) { + Long analUid, String workerType, String search, String sortType, Integer page, Integer size) { // 프로젝트 정보 조회 (analUid가 있을 때만) var projectInfo = labelAllocateCoreService.findProjectInfo(analUid); @@ -137,8 +132,7 @@ public class LabelAllocateService { // 작업자 통계 조회 List workers = - labelAllocateCoreService.findWorkerStatistics( - analUid, workerType, search, sortType); + labelAllocateCoreService.findWorkerStatistics(analUid, workerType, search, sortType); // 각 작업자별 3일치 처리량 조회 LocalDate today = LocalDate.now(); diff --git a/src/main/java/com/kamco/cd/kamcoback/label/service/LabelWorkService.java b/src/main/java/com/kamco/cd/kamcoback/label/service/LabelWorkService.java index 3a8d6553..c597c5db 100644 --- a/src/main/java/com/kamco/cd/kamcoback/label/service/LabelWorkService.java +++ b/src/main/java/com/kamco/cd/kamcoback/label/service/LabelWorkService.java @@ -1,24 +1,52 @@ package com.kamco.cd.kamcoback.label.service; import com.kamco.cd.kamcoback.label.dto.LabelWorkDto; +import com.kamco.cd.kamcoback.label.dto.LabelWorkDto.ChangeDetectYear; import com.kamco.cd.kamcoback.label.dto.LabelWorkDto.LabelWorkMng; +import com.kamco.cd.kamcoback.label.dto.LabelWorkDto.LabelWorkMngDetail; import com.kamco.cd.kamcoback.postgres.core.LabelWorkCoreService; +import java.util.List; +import java.util.UUID; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.data.domain.Page; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; @Slf4j @Service +@RequiredArgsConstructor +@Transactional(rollbackFor = Exception.class) public class LabelWorkService { private final LabelWorkCoreService labelWorkCoreService; - public LabelWorkService(LabelWorkCoreService labelWorkCoreService) { - this.labelWorkCoreService = labelWorkCoreService; - } - + /** + * 라벨링작업 관리 목록조회 + * + * @param searchReq + * @return + */ public Page labelWorkMngList(LabelWorkDto.LabelWorkMngSearchReq searchReq) { - return labelWorkCoreService.labelWorkMngList(searchReq); } + + /** + * 작업배정 정보 조회 + * + * @param uuid + * @return + */ + public LabelWorkMngDetail findLabelWorkMngDetail(UUID uuid) { + return labelWorkCoreService.findLabelWorkMngDetail(uuid); + } + + /** + * 변화탐지 셀렉트박스 조회 + * + * @return + */ + public List getChangeDetectYear() { + return labelWorkCoreService.getChangeDetectYear(); + } } 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 a16252fa..2ad6ba67 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 @@ -54,12 +54,8 @@ public class LabelAllocateCoreService { } public List findWorkerStatistics( - Long analUid, - String workerType, - String search, - String sortType) { - return labelAllocateRepository.findWorkerStatistics( - analUid, workerType, search, sortType); + Long analUid, String workerType, String search, String sortType) { + return labelAllocateRepository.findWorkerStatistics(analUid, workerType, search, sortType); } public WorkProgressInfo findWorkProgressInfo(Long analUid) { diff --git a/src/main/java/com/kamco/cd/kamcoback/postgres/core/LabelWorkCoreService.java b/src/main/java/com/kamco/cd/kamcoback/postgres/core/LabelWorkCoreService.java index a1be92a5..cc3c3754 100644 --- a/src/main/java/com/kamco/cd/kamcoback/postgres/core/LabelWorkCoreService.java +++ b/src/main/java/com/kamco/cd/kamcoback/postgres/core/LabelWorkCoreService.java @@ -1,8 +1,13 @@ package com.kamco.cd.kamcoback.postgres.core; import com.kamco.cd.kamcoback.label.dto.LabelWorkDto; +import com.kamco.cd.kamcoback.label.dto.LabelWorkDto.ChangeDetectYear; import com.kamco.cd.kamcoback.label.dto.LabelWorkDto.LabelWorkMng; +import com.kamco.cd.kamcoback.label.dto.LabelWorkDto.LabelWorkMngDetail; import com.kamco.cd.kamcoback.postgres.repository.label.LabelWorkRepository; +import com.kamco.cd.kamcoback.postgres.repository.members.MembersRepository; +import java.util.List; +import java.util.UUID; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Page; import org.springframework.stereotype.Service; @@ -12,8 +17,40 @@ import org.springframework.stereotype.Service; public class LabelWorkCoreService { private final LabelWorkRepository labelWorkRepository; + private final MembersRepository membersRepository; + /** + * 변화탐지 년도 셀렉트박스 조회 + * + * @return + */ + public List getChangeDetectYear() { + return labelWorkRepository.findChangeDetectYearList().stream() + .map( + e -> + new ChangeDetectYear( + e.getCompareYyyy() + "-" + e.getTargetYyyy(), + e.getCompareYyyy() + "-" + e.getTargetYyyy())) + .toList(); + } + + /** + * 라벨링작업 관리 목록 조회 + * + * @param searchReq + * @return + */ public Page labelWorkMngList(LabelWorkDto.LabelWorkMngSearchReq searchReq) { return labelWorkRepository.labelWorkMngList(searchReq); } + + /** + * 작업배정 정보 조회 + * + * @param uuid + * @return + */ + public LabelWorkMngDetail findLabelWorkMngDetail(UUID uuid) { + return labelWorkRepository.findLabelWorkMngDetail(uuid); + } } diff --git a/src/main/java/com/kamco/cd/kamcoback/postgres/repository/Inference/InferenceResultRepositoryImpl.java b/src/main/java/com/kamco/cd/kamcoback/postgres/repository/Inference/InferenceResultRepositoryImpl.java index 8e1804b9..37f0c8a6 100644 --- a/src/main/java/com/kamco/cd/kamcoback/postgres/repository/Inference/InferenceResultRepositoryImpl.java +++ b/src/main/java/com/kamco/cd/kamcoback/postgres/repository/Inference/InferenceResultRepositoryImpl.java @@ -36,20 +36,29 @@ public class InferenceResultRepositoryImpl implements InferenceResultRepositoryC String sql = """ INSERT INTO tb_map_sheet_anal_inference ( - compare_yyyy, - target_yyyy, - stage, - anal_title, - detecting_cnt - ) - SELECT - r.input1 AS compare_yyyy, - r.input2 AS target_yyyy, - r.stage, - CONCAT(r.stage ,'_', r.input1 ,'_', r.input2) AS anal_title, - count(*) - FROM inference_results r - GROUP BY r.stage, r.input1, r.input2; + stage, + compare_yyyy, + target_yyyy, + anal_title, + detecting_cnt, + created_dttm, + updated_dttm + ) + SELECT + r.stage, + r.input1 AS compare_yyyy, + r.input2 AS target_yyyy, + CONCAT(r.stage, '_', r.input1, '_', r.input2) AS anal_title, + COUNT(*) AS detecting_cnt, + now(), + now() + FROM inference_results r + GROUP BY r.stage, r.input1, r.input2 + ON CONFLICT (stage, compare_yyyy, target_yyyy) + DO UPDATE SET + detecting_cnt = EXCLUDED.detecting_cnt, + anal_title = EXCLUDED.anal_title, + updated_dttm = now() """; return em.createNativeQuery(sql).executeUpdate(); @@ -69,30 +78,42 @@ public class InferenceResultRepositoryImpl implements InferenceResultRepositoryC String sql = """ INSERT INTO tb_map_sheet_anal_data_inference ( + anal_uid, stage, compare_yyyy, target_yyyy, map_sheet_num, - created_dttm, - updated_dttm, + detecting_cnt, file_created_yn, - detecting_cnt + created_dttm, + updated_dttm ) SELECT + ai.id AS anal_uid, r.stage, r.input1 AS compare_yyyy, r.input2 AS target_yyyy, r.map_id AS map_sheet_num, - now() AS created_dttm, - now() AS updated_dttm, - false AS file_created_yn, - count(*) AS detecting_cnt + COUNT(*) AS detecting_cnt, + false AS file_created_yn, + now(), + now() FROM inference_results r - GROUP BY r.stage, r.input1, r.input2, r.map_id + JOIN tb_map_sheet_anal_inference ai + ON ai.stage = r.stage + AND ai.compare_yyyy = r.input1 + AND ai.target_yyyy = r.input2 + GROUP BY + ai.id, + r.stage, + r.input1, + r.input2, + r.map_id ON CONFLICT (stage, compare_yyyy, target_yyyy, map_sheet_num) DO UPDATE SET - updated_dttm = now(), - detecting_cnt = EXCLUDED.detecting_cnt + anal_uid = EXCLUDED.anal_uid, + detecting_cnt = EXCLUDED.detecting_cnt, + updated_dttm = now() """; return em.createNativeQuery(sql).executeUpdate(); @@ -111,46 +132,70 @@ public class InferenceResultRepositoryImpl implements InferenceResultRepositoryC String sql = """ - INSERT INTO tb_map_sheet_anal_data_inference_geom ( - uuid, stage, cd_prob, compare_yyyy, target_yyyy, map_sheet_num, - class_before_cd, class_before_prob, class_after_cd, class_after_prob, - geom, area, data_uid, created_dttm, updated_dttm, - file_created_yn + INSERT INTO tb_map_sheet_anal_data_inference_geom ( + uuid, + stage, + cd_prob, + compare_yyyy, + target_yyyy, + map_sheet_num, + class_before_cd, + class_before_prob, + class_after_cd, + class_after_prob, + geom, + area, + data_uid, + file_created_yn, + created_dttm, + updated_dttm ) SELECT - x.uuid, x.stage, x.cd_prob, x.compare_yyyy, x.target_yyyy, x.map_sheet_num, - x.class_before_cd, x.class_before_prob, x.class_after_cd, x.class_after_prob, - x.geom, x.area, x.data_uid, x.created_dttm, x.updated_dttm, - false AS file_created_yn + x.uuid, + x.stage, + x.cd_prob, + x.compare_yyyy, + x.target_yyyy, + x.map_sheet_num, + x.class_before_cd, + x.class_before_prob, + x.class_after_cd, + x.class_after_prob, + x.geom, + x.area, + x.data_uid, + false, + x.created_dttm, + x.updated_dttm FROM ( - SELECT DISTINCT ON (r.uuid) - r.uuid, - r.stage, - r.cd_prob, - r.input1 AS compare_yyyy, - r.input2 AS target_yyyy, - r.map_id AS map_sheet_num, - r.before_class AS class_before_cd, - r.before_probability AS class_before_prob, - r.after_class AS class_after_cd, - r.after_probability AS class_after_prob, - CASE - WHEN r.geometry IS NULL THEN NULL - WHEN left(r.geometry, 2) = '01' - THEN ST_SetSRID(ST_GeomFromWKB(decode(r.geometry, 'hex')), 5186) - ELSE ST_GeomFromText(r.geometry, 5186) - END AS geom, - r.area, - di.data_uid, - r.created_dttm, - r.updated_dttm - FROM inference_results r - JOIN tb_map_sheet_anal_data_inference di - ON di.stage = r.stage - AND di.compare_yyyy = r.input1 - AND di.target_yyyy = r.input2 - AND di.map_sheet_num = r.map_id - ORDER BY r.uuid, r.updated_dttm DESC NULLS LAST, r.uid DESC + SELECT DISTINCT ON (r.uuid) + r.uuid, + r.stage, + r.cd_prob, + r.input1 AS compare_yyyy, + r.input2 AS target_yyyy, + r.map_id AS map_sheet_num, + r.before_class AS class_before_cd, + r.before_probability AS class_before_prob, + r.after_class AS class_after_cd, + r.after_probability AS class_after_prob, + CASE + WHEN r.geometry IS NULL THEN NULL + WHEN LEFT(r.geometry, 2) = '01' + THEN ST_SetSRID(ST_GeomFromWKB(decode(r.geometry, 'hex')), 5186) + ELSE ST_GeomFromText(r.geometry, 5186) + END AS geom, + r.area, + di.data_uid, + r.created_dttm, + r.updated_dttm + FROM inference_results r + JOIN tb_map_sheet_anal_data_inference di + ON di.stage = r.stage + AND di.compare_yyyy = r.input1 + AND di.target_yyyy = r.input2 + AND di.map_sheet_num = r.map_id + ORDER BY r.uuid, r.updated_dttm DESC NULLS LAST, r.uid DESC ) x ON CONFLICT (uuid) DO UPDATE SET 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 141bace8..b08617b8 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 @@ -192,10 +192,7 @@ public class LabelAllocateRepositoryImpl implements LabelAllocateRepositoryCusto @Override public List findWorkerStatistics( - Long analUid, - String workerType, - String search, - String sortType) { + Long analUid, String workerType, String search, String sortType) { // 작업자 유형에 따른 필드 선택 StringExpression workerIdField = @@ -211,8 +208,8 @@ public class LabelAllocateRepositoryImpl implements LabelAllocateRepositoryCusto // 검색 조건 (이름 또는 사번으로 검색) BooleanExpression searchCondition = null; if (search != null && !search.isEmpty()) { - searchCondition = memberEntity.name.contains(search) - .or(memberEntity.employeeNo.contains(search)); + searchCondition = + memberEntity.name.contains(search).or(memberEntity.employeeNo.contains(search)); } // 완료, 스킵, 남은 작업 계산 @@ -242,7 +239,8 @@ public class LabelAllocateRepositoryImpl implements LabelAllocateRepositoryCusto .sum(); // 기본 통계 조회 쿼리 - BooleanExpression analUidCondition = analUid != null ? labelingAssignmentEntity.analUid.eq(analUid) : null; + BooleanExpression analUidCondition = + analUid != null ? labelingAssignmentEntity.analUid.eq(analUid) : null; var baseQuery = queryFactory @@ -313,7 +311,8 @@ public class LabelAllocateRepositoryImpl implements LabelAllocateRepositoryCusto @Override public WorkProgressInfo findWorkProgressInfo(Long analUid) { - BooleanExpression analUidCondition = analUid != null ? labelingAssignmentEntity.analUid.eq(analUid) : null; + BooleanExpression analUidCondition = + analUid != null ? labelingAssignmentEntity.analUid.eq(analUid) : null; // 전체 배정 건수 Long totalAssigned = @@ -339,9 +338,7 @@ public class LabelAllocateRepositoryImpl implements LabelAllocateRepositoryCusto queryFactory .select(labelingAssignmentEntity.count()) .from(labelingAssignmentEntity) - .where( - analUidCondition, - labelingAssignmentEntity.workState.eq("SKIP")) + .where(analUidCondition, labelingAssignmentEntity.workState.eq("SKIP")) .fetchOne(); // 투입된 라벨러 수 @@ -349,9 +346,7 @@ public class LabelAllocateRepositoryImpl implements LabelAllocateRepositoryCusto queryFactory .select(labelingAssignmentEntity.workerUid.countDistinct()) .from(labelingAssignmentEntity) - .where( - analUidCondition, - labelingAssignmentEntity.workerUid.isNotNull()) + .where(analUidCondition, labelingAssignmentEntity.workerUid.isNotNull()) .fetchOne(); // === 검수 통계 === @@ -360,9 +355,7 @@ public class LabelAllocateRepositoryImpl implements LabelAllocateRepositoryCusto queryFactory .select(labelingAssignmentEntity.count()) .from(labelingAssignmentEntity) - .where( - analUidCondition, - labelingAssignmentEntity.workState.eq("DONE")) + .where(analUidCondition, labelingAssignmentEntity.workState.eq("DONE")) .fetchOne(); // 투입된 검수자 수 @@ -370,9 +363,7 @@ public class LabelAllocateRepositoryImpl implements LabelAllocateRepositoryCusto queryFactory .select(labelingAssignmentEntity.inspectorUid.countDistinct()) .from(labelingAssignmentEntity) - .where( - analUidCondition, - labelingAssignmentEntity.inspectorUid.isNotNull()) + .where(analUidCondition, labelingAssignmentEntity.inspectorUid.isNotNull()) .fetchOne(); // 남은 작업 건수 계산 @@ -432,7 +423,8 @@ public class LabelAllocateRepositoryImpl implements LabelAllocateRepositoryCusto ? labelingAssignmentEntity.inspectorUid.eq(workerId) : labelingAssignmentEntity.workerUid.eq(workerId); - BooleanExpression analUidCondition = analUid != null ? labelingAssignmentEntity.analUid.eq(analUid) : null; + BooleanExpression analUidCondition = + analUid != null ? labelingAssignmentEntity.analUid.eq(analUid) : null; Long count = queryFactory @@ -633,17 +625,17 @@ public class LabelAllocateRepositoryImpl implements LabelAllocateRepositoryCusto return null; } - var result = queryFactory - .select( - mapSheetAnalEntity.compareYyyy, - mapSheetAnalEntity.targetYyyy, - mapSheetAnalEntity.analTitle, - mapSheetAnalEntity.gukyuinApplyDttm, - mapSheetAnalEntity.analStrtDttm - ) - .from(mapSheetAnalEntity) - .where(mapSheetAnalEntity.id.eq(analUid)) - .fetchOne(); + var result = + queryFactory + .select( + mapSheetAnalEntity.compareYyyy, + mapSheetAnalEntity.targetYyyy, + mapSheetAnalEntity.analTitle, + mapSheetAnalEntity.gukyuinApplyDttm, + mapSheetAnalEntity.analStrtDttm) + .from(mapSheetAnalEntity) + .where(mapSheetAnalEntity.id.eq(analUid)) + .fetchOne(); if (result == null) { return null; @@ -656,9 +648,8 @@ public class LabelAllocateRepositoryImpl implements LabelAllocateRepositoryCusto ZonedDateTime analStrtDttm = result.get(mapSheetAnalEntity.analStrtDttm); // 변화탐지년도 생성 - String detectionYear = (compareYyyy != null && targetYyyy != null) - ? compareYyyy + "-" + targetYyyy - : null; + String detectionYear = + (compareYyyy != null && targetYyyy != null) ? compareYyyy + "-" + targetYyyy : null; // 회차 추출 (예: "8회차" → "8") String round = extractRoundFromTitle(analTitle); @@ -671,10 +662,7 @@ public class LabelAllocateRepositoryImpl implements LabelAllocateRepositoryCusto .build(); } - /** - * 제목에서 회차 숫자 추출 - * 예: "8회차", "제8회차" → "8" - */ + /** 제목에서 회차 숫자 추출 예: "8회차", "제8회차" → "8" */ private String extractRoundFromTitle(String title) { if (title == null || title.isEmpty()) { return null; @@ -686,9 +674,7 @@ public class LabelAllocateRepositoryImpl implements LabelAllocateRepositoryCusto return matcher.find() ? matcher.group(1) : null; } - /** - * ZonedDateTime을 "yyyy-MM-dd" 형식으로 변환 - */ + /** ZonedDateTime을 "yyyy-MM-dd" 형식으로 변환 */ private String formatDate(ZonedDateTime dateTime) { if (dateTime == null) { return null; diff --git a/src/main/java/com/kamco/cd/kamcoback/postgres/repository/label/LabelWorkRepositoryCustom.java b/src/main/java/com/kamco/cd/kamcoback/postgres/repository/label/LabelWorkRepositoryCustom.java index 6a27f5d0..9c0a55cb 100644 --- a/src/main/java/com/kamco/cd/kamcoback/postgres/repository/label/LabelWorkRepositoryCustom.java +++ b/src/main/java/com/kamco/cd/kamcoback/postgres/repository/label/LabelWorkRepositoryCustom.java @@ -2,9 +2,17 @@ package com.kamco.cd.kamcoback.postgres.repository.label; import com.kamco.cd.kamcoback.label.dto.LabelWorkDto; import com.kamco.cd.kamcoback.label.dto.LabelWorkDto.LabelWorkMng; +import com.kamco.cd.kamcoback.label.dto.LabelWorkDto.LabelWorkMngDetail; +import com.kamco.cd.kamcoback.postgres.entity.MapSheetAnalInferenceEntity; +import java.util.List; +import java.util.UUID; import org.springframework.data.domain.Page; public interface LabelWorkRepositoryCustom { + List findChangeDetectYearList(); + public Page labelWorkMngList(LabelWorkDto.LabelWorkMngSearchReq searchReq); + + LabelWorkMngDetail findLabelWorkMngDetail(UUID uuid); } 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 e17da42c..761d9f12 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 @@ -1,22 +1,29 @@ package com.kamco.cd.kamcoback.postgres.repository.label; +import static com.kamco.cd.kamcoback.postgres.entity.QLabelingAssignmentEntity.labelingAssignmentEntity; import static com.kamco.cd.kamcoback.postgres.entity.QMapSheetAnalDataInferenceEntity.mapSheetAnalDataInferenceEntity; import static com.kamco.cd.kamcoback.postgres.entity.QMapSheetAnalDataInferenceGeomEntity.mapSheetAnalDataInferenceGeomEntity; +import static com.kamco.cd.kamcoback.postgres.entity.QMapSheetAnalInferenceEntity.mapSheetAnalInferenceEntity; import com.kamco.cd.kamcoback.label.dto.LabelWorkDto; import com.kamco.cd.kamcoback.label.dto.LabelWorkDto.LabelWorkMng; +import com.kamco.cd.kamcoback.label.dto.LabelWorkDto.LabelWorkMngDetail; import com.kamco.cd.kamcoback.postgres.entity.MapSheetAnalDataGeomEntity; +import com.kamco.cd.kamcoback.postgres.entity.MapSheetAnalInferenceEntity; import com.querydsl.core.BooleanBuilder; import com.querydsl.core.types.Projections; import com.querydsl.core.types.dsl.CaseBuilder; import com.querydsl.core.types.dsl.Expressions; +import com.querydsl.core.types.dsl.NumberExpression; import com.querydsl.core.types.dsl.StringExpression; +import com.querydsl.jpa.JPAExpressions; import com.querydsl.jpa.impl.JPAQueryFactory; import jakarta.persistence.EntityManager; import jakarta.persistence.PersistenceContext; -import java.time.format.DateTimeFormatter; import java.util.List; +import java.util.UUID; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.PageRequest; @@ -39,20 +46,59 @@ public class LabelWorkRepositoryImpl extends QuerydslRepositorySupport this.queryFactory = queryFactory; } + /** + * 변화탐지 년도 셀렉트박스 조회 + * + * @return + */ + @Override + public List findChangeDetectYearList() { + return queryFactory + .selectFrom(mapSheetAnalInferenceEntity) + .where( + mapSheetAnalInferenceEntity.id.in( + JPAExpressions.select(mapSheetAnalInferenceEntity.id.min()) + .from(mapSheetAnalInferenceEntity) + .groupBy( + mapSheetAnalInferenceEntity.compareYyyy, + mapSheetAnalInferenceEntity.targetYyyy))) + .orderBy( + mapSheetAnalInferenceEntity.compareYyyy.asc(), + mapSheetAnalInferenceEntity.targetYyyy.asc()) + .fetch(); + } + + /** + * 라벨링 작업관리 목록 조회 + * + * @param searchReq + * @return + */ @Override public Page labelWorkMngList(LabelWorkDto.LabelWorkMngSearchReq searchReq) { Pageable pageable = PageRequest.of(searchReq.getPage(), searchReq.getSize()); BooleanBuilder whereBuilder = new BooleanBuilder(); + BooleanBuilder whereSubDataBuilder = new BooleanBuilder(); BooleanBuilder whereSubBuilder = new BooleanBuilder(); - DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd"); + if (StringUtils.isNotBlank(searchReq.getDetectYear())) { + String[] years = searchReq.getDetectYear().split("-"); - if (searchReq.getDetectYyyy() != null) { - whereBuilder.and(mapSheetAnalDataInferenceEntity.targetYyyy.eq(searchReq.getDetectYyyy())); + if (years.length == 2) { + Integer compareYear = Integer.valueOf(years[0]); + Integer targetYear = Integer.valueOf(years[1]); + + whereBuilder.and( + mapSheetAnalDataInferenceEntity + .compareYyyy + .eq(compareYear) + .and(mapSheetAnalDataInferenceEntity.targetYyyy.eq(targetYear))); + } } - // mapSheetAnalDataInferenceGeomEntity.dataUid.eq(mapSheetAnalDataInferenceEntity.id) + whereSubDataBuilder.and( + mapSheetAnalInferenceEntity.id.eq(mapSheetAnalDataInferenceEntity.analUid)); whereSubBuilder.and( mapSheetAnalDataInferenceGeomEntity.dataUid.eq(mapSheetAnalDataInferenceEntity.id)); @@ -61,15 +107,10 @@ public class LabelWorkRepositoryImpl extends QuerydslRepositorySupport && !searchReq.getStrtDttm().isEmpty() && searchReq.getEndDttm() != null && !searchReq.getEndDttm().isEmpty()) { - - // whereSubBuilder.and(mapSheetAnalDataInferenceGeomEntity.labelStateDttm.isNotNull()); whereSubBuilder.and( Expressions.stringTemplate( "to_char({0}, 'YYYYMMDD')", mapSheetAnalDataInferenceGeomEntity.labelStateDttm) .between(searchReq.getStrtDttm(), searchReq.getEndDttm())); - - // whereBuilder.and(mapSheetAnalDataInferenceGeomEntity.labelStateDttm.min().isNotNull()); - } List foundContent = @@ -77,9 +118,10 @@ public class LabelWorkRepositoryImpl extends QuerydslRepositorySupport .select( Projections.constructor( LabelWorkMng.class, - mapSheetAnalDataInferenceEntity.compareYyyy, - mapSheetAnalDataInferenceEntity.targetYyyy, - mapSheetAnalDataInferenceEntity.stage, + mapSheetAnalInferenceEntity.uuid, + mapSheetAnalInferenceEntity.compareYyyy, + mapSheetAnalInferenceEntity.targetYyyy, + mapSheetAnalInferenceEntity.stage, mapSheetAnalDataInferenceEntity.createdDttm.min(), mapSheetAnalDataInferenceGeomEntity.dataUid.count(), mapSheetAnalDataInferenceGeomEntity.dataUid.count(), @@ -99,17 +141,21 @@ public class LabelWorkRepositoryImpl extends QuerydslRepositorySupport .otherwise(0L) .sum(), mapSheetAnalDataInferenceGeomEntity.labelStateDttm.min())) - .from(mapSheetAnalDataInferenceEntity) + .from(mapSheetAnalInferenceEntity) + .innerJoin(mapSheetAnalDataInferenceEntity) + .on(whereSubDataBuilder) .innerJoin(mapSheetAnalDataInferenceGeomEntity) .on(whereSubBuilder) .where(whereBuilder) .groupBy( - mapSheetAnalDataInferenceEntity.compareYyyy, - mapSheetAnalDataInferenceEntity.targetYyyy, - mapSheetAnalDataInferenceEntity.stage) + mapSheetAnalInferenceEntity.uuid, + mapSheetAnalInferenceEntity.compareYyyy, + mapSheetAnalInferenceEntity.targetYyyy, + mapSheetAnalInferenceEntity.stage) .orderBy( - mapSheetAnalDataInferenceEntity.targetYyyy.desc(), - mapSheetAnalDataInferenceEntity.stage.desc()) + mapSheetAnalInferenceEntity.targetYyyy.desc(), + mapSheetAnalInferenceEntity.compareYyyy.desc(), + mapSheetAnalInferenceEntity.stage.desc()) .offset(pageable.getOffset()) .limit(pageable.getPageSize()) .fetch(); @@ -131,8 +177,62 @@ public class LabelWorkRepositoryImpl extends QuerydslRepositorySupport */ - Long countQuery = foundContent.stream().count(); + Long total = + queryFactory + .select(mapSheetAnalInferenceEntity.uuid.countDistinct()) + .from(mapSheetAnalInferenceEntity) + .innerJoin(mapSheetAnalDataInferenceEntity) + .on(whereSubDataBuilder) + .innerJoin(mapSheetAnalDataInferenceGeomEntity) + .on(whereSubBuilder) + .where(whereBuilder) + .fetchOne(); - return new PageImpl<>(foundContent, pageable, countQuery); + return new PageImpl<>(foundContent, pageable, total); + } + + /** + * 작업배정 상세조회 + * + * @param uuid + * @return + */ + @Override + public LabelWorkMngDetail findLabelWorkMngDetail(UUID uuid) { + + NumberExpression labelTotCnt = mapSheetAnalDataInferenceGeomEntity.geoUid.count(); + NumberExpression labelerCnt = labelingAssignmentEntity.workerUid.count(); + NumberExpression reviewerCnt = labelingAssignmentEntity.inspectorUid.count(); + + return queryFactory + .select( + Projections.constructor( + LabelWorkMngDetail.class, + mapSheetAnalInferenceEntity + .compareYyyy + .stringValue() + .concat("-") + .concat(mapSheetAnalInferenceEntity.targetYyyy.stringValue()), + mapSheetAnalInferenceEntity.stage, + mapSheetAnalInferenceEntity.createdDttm, + labelTotCnt, + labelerCnt, + reviewerCnt)) + .from(mapSheetAnalInferenceEntity) + .leftJoin(mapSheetAnalDataInferenceEntity) + .on(mapSheetAnalInferenceEntity.id.eq(mapSheetAnalDataInferenceEntity.analUid)) + .leftJoin(mapSheetAnalDataInferenceGeomEntity) + .on(mapSheetAnalDataInferenceEntity.id.eq(mapSheetAnalDataInferenceGeomEntity.dataUid)) + .leftJoin(labelingAssignmentEntity) + .on( + mapSheetAnalDataInferenceGeomEntity.geoUid.eq( + labelingAssignmentEntity.inferenceGeomUid)) + .where(mapSheetAnalInferenceEntity.uuid.eq(uuid)) + .groupBy( + mapSheetAnalInferenceEntity.compareYyyy, + mapSheetAnalInferenceEntity.targetYyyy, + mapSheetAnalInferenceEntity.stage, + mapSheetAnalInferenceEntity.createdDttm) + .fetchOne(); } }