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..0337123c 100644 --- a/src/main/java/com/kamco/cd/kamcoback/label/LabelWorkerApiController.java +++ b/src/main/java/com/kamco/cd/kamcoback/label/LabelWorkerApiController.java @@ -5,6 +5,9 @@ import com.kamco.cd.kamcoback.config.api.ApiResponseDto; 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.LabelWorkMngSearchReq; +import com.kamco.cd.kamcoback.label.dto.LabelWorkDto.WorkerState; +import com.kamco.cd.kamcoback.label.dto.LabelWorkDto.WorkerStateSearchReq; +import com.kamco.cd.kamcoback.label.dto.WorkerStatsDto.WorkerStatistics; import com.kamco.cd.kamcoback.label.service.LabelWorkService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; @@ -61,4 +64,34 @@ public class LabelWorkerApiController { 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 = CommonCodeDto.Basic.class))), + @ApiResponse(responseCode = "404", description = "코드를 찾을 수 없음", content = @Content), + @ApiResponse(responseCode = "500", description = "서버 오류", content = @Content) + }) + @GetMapping("/work-state-list") + public ApiResponseDto> findWorkStateList( + @Parameter(description = "유형", example = "LABELER") @RequestParam(required = false) String userRole, + @Parameter(description = "검색어", example = "20261201") @RequestParam(required = false) String searchVal, + @Parameter(description = "페이지 번호 (0부터 시작)", example = "0") @RequestParam(defaultValue = "0") + int page, + @Parameter(description = "페이지 크기", example = "20") @RequestParam(defaultValue = "20") + int size) { + + LabelWorkDto.WorkerStateSearchReq searchReq = new WorkerStateSearchReq(); + searchReq.setUserRole(userRole); + searchReq.setSearchVal(searchVal); + searchReq.setPage(page); + searchReq.setSize(size); + return ApiResponseDto.ok(labelWorkService.findlabelWorkStateList(searchReq)); + } } 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..db5f5cb5 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 @@ -3,9 +3,11 @@ package com.kamco.cd.kamcoback.label.dto; 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 com.kamco.cd.kamcoback.label.dto.WorkerStatsDto.DailyHistory; import io.swagger.v3.oas.annotations.media.Schema; import java.time.ZonedDateTime; import lombok.AllArgsConstructor; +import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; @@ -93,4 +95,86 @@ public class LabelWorkDto { return PageRequest.of(page, size); } } + + + @Getter + @Setter + @Builder + @NoArgsConstructor + @AllArgsConstructor + @Schema(description = "작업자 통계 응답") + public static class WorkerState { + + @Schema(description = "작업자 유형 (LABELER/INSPECTOR)") + private String userRole; + + @Schema(description = "작업자 ID (사번)") + private String name; + + @Schema(description = "작업자 이름") + private String userId; + + @Schema(description = "배정개수") + private Long assignedCnt; + + @Schema(description = "완료개수") + private Long doneCnt; + + @Schema(description = "Skip개수") + private Long skipCnt; + + @Schema(description = "Skip개수") + private Long day3AgoDoneCnt; + + @Schema(description = "Skip개수") + private Long day2AgoDoneCnt; + + @Schema(description = "Skip개수") + private Long day1AgoDoneCnt; + + public Long getremindCnt() { + return this.assignedCnt - this.doneCnt; + } + + public double getDoneRate() { + if (this.doneCnt == null || this.assignedCnt == 0) { + return 0.0; + } + return (double) this.doneCnt / this.assignedCnt * 100.0; + } + + public String getUserRoleName() { + if (this.userRole.equals("LABELER")) { + return "라벨러"; + } + return "검수자"; + } + + } + + @Schema(name = "WorkerStateSearchReq", description = "라벨작업관리 검색 요청") + @Getter + @Setter + @NoArgsConstructor + @AllArgsConstructor + public static class WorkerStateSearchReq { + + // 페이징 파라미터 + @Schema(description = "페이지 번호 (0부터 시작) ", example = "0") + private int page = 0; + + @Schema(description = "페이지 크기", example = "20") + private int size = 20; + + @Schema(description = "유형", example = "LABELER") + private String userRole; + + @Schema(description = "종료일", example = "20261201") + private String searchVal; + + public Pageable toPageable() { + + return PageRequest.of(page, size); + } + } } 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..6681d1e4 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 @@ -135,6 +135,8 @@ public class LabelAllocateService { // 작업 진행 현황 조회 var progressInfo = labelAllocateCoreService.findWorkProgressInfo(analUid); + + // 작업자 통계 조회 List workers = labelAllocateCoreService.findWorkerStatistics( @@ -155,20 +157,9 @@ public class LabelAllocateService { long average = (day1Count + day2Count + day3Count) / 3; - DailyHistory history = - DailyHistory.builder() - .day1Ago(day1Count) - .day2Ago(day2Count) - .day3Ago(day3Count) - .average(average) - .build(); - worker.setHistory(history); - // 정체 여부 판단 (3일 평균이 STAGNATION_THRESHOLD 미만일 때) - if (average < STAGNATION_THRESHOLD) { - worker.setIsStagnated(true); - } + } // 페이징 처리 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..ceff3603 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 @@ -2,6 +2,8 @@ package com.kamco.cd.kamcoback.label.service; 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.WorkerState; +import com.kamco.cd.kamcoback.label.dto.WorkerStatsDto.WorkerStatistics; import com.kamco.cd.kamcoback.postgres.core.LabelWorkCoreService; import lombok.extern.slf4j.Slf4j; import org.springframework.data.domain.Page; @@ -21,4 +23,10 @@ public class LabelWorkService { return labelWorkCoreService.labelWorkMngList(searchReq); } + + + public Page findlabelWorkStateList(LabelWorkDto.WorkerStateSearchReq searchReq) { + + return labelWorkCoreService.findlabelWorkStateList(searchReq); + } } 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..3336c07d 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 @@ -2,6 +2,8 @@ package com.kamco.cd.kamcoback.postgres.core; 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.WorkerState; +import com.kamco.cd.kamcoback.label.dto.WorkerStatsDto.WorkerStatistics; import com.kamco.cd.kamcoback.postgres.repository.label.LabelWorkRepository; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Page; @@ -16,4 +18,9 @@ public class LabelWorkCoreService { public Page labelWorkMngList(LabelWorkDto.LabelWorkMngSearchReq searchReq) { return labelWorkRepository.labelWorkMngList(searchReq); } + + public Page findlabelWorkStateList(LabelWorkDto.WorkerStateSearchReq searchReq) + { + return labelWorkRepository.findlabelWorkStateList(searchReq); + }; } 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..c123cf9f 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,14 @@ 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.WorkerState; +import com.kamco.cd.kamcoback.label.dto.WorkerStatsDto.WorkerStatistics; import org.springframework.data.domain.Page; public interface LabelWorkRepositoryCustom { public Page labelWorkMngList(LabelWorkDto.LabelWorkMngSearchReq searchReq); + + Page findlabelWorkStateList(LabelWorkDto.WorkerStateSearchReq searchReq); + } 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..8d3e3154 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,10 +1,18 @@ 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.QMapInkx5kEntity.mapInkx5kEntity; 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.QMapSheetMngEntity.mapSheetMngEntity; +import static com.kamco.cd.kamcoback.postgres.entity.QMapSheetMngHstEntity.mapSheetMngHstEntity; +import static com.kamco.cd.kamcoback.postgres.entity.QMemberEntity.memberEntity; 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.WorkerState; +import com.kamco.cd.kamcoback.label.dto.WorkerStatsDto.WorkerStatistics; +import com.kamco.cd.kamcoback.mapsheet.dto.MapSheetMngDto; import com.kamco.cd.kamcoback.postgres.entity.MapSheetAnalDataGeomEntity; import com.querydsl.core.BooleanBuilder; import com.querydsl.core.types.Projections; @@ -14,6 +22,7 @@ import com.querydsl.core.types.dsl.StringExpression; import com.querydsl.jpa.impl.JPAQueryFactory; import jakarta.persistence.EntityManager; import jakarta.persistence.PersistenceContext; +import java.time.LocalDate; import java.time.format.DateTimeFormatter; import java.util.List; import lombok.extern.slf4j.Slf4j; @@ -135,4 +144,120 @@ public class LabelWorkRepositoryImpl extends QuerydslRepositorySupport return new PageImpl<>(foundContent, pageable, countQuery); } + + + @Override + public Page findlabelWorkStateList(LabelWorkDto.WorkerStateSearchReq searchReq){ + Pageable pageable = PageRequest.of(searchReq.getPage(), searchReq.getSize()); + BooleanBuilder whereBuilder = new BooleanBuilder(); + BooleanBuilder whereSubBuilder = new BooleanBuilder(); + + LocalDate threeDaysAgo = LocalDate.now().minusDays(3); + String s3 = threeDaysAgo.format(DateTimeFormatter.ofPattern("YYYY-MM-DD")); + + LocalDate twoDaysAgo = LocalDate.now().minusDays(2); + String s2 = twoDaysAgo.format(DateTimeFormatter.ofPattern("YYYY-MM-DD")); + + LocalDate oneDaysAgo = LocalDate.now().minusDays(1); + String s1 = oneDaysAgo.format(DateTimeFormatter.ofPattern("YYYY-MM-DD")); + + if (searchReq.getUserRole() != null && ! searchReq.getUserRole().isEmpty()) { + whereSubBuilder.and(memberEntity.userRole.eq(searchReq.getUserRole())); + } + + if (searchReq.getSearchVal() != null && ! searchReq.getSearchVal().isEmpty()) { + whereSubBuilder.and( + Expressions.stringTemplate( + "{0}",memberEntity.userId) + .likeIgnoreCase("%" + searchReq.getSearchVal() + "%") + .or( + Expressions.stringTemplate( + "{0}",memberEntity.name) + .likeIgnoreCase("%" + searchReq.getSearchVal() + "%") + ) + ); + } + + whereSubBuilder.and( + labelingAssignmentEntity.workerUid.eq(memberEntity.userId)); + + List foundContent = + queryFactory + .select( + Projections.constructor( + WorkerState.class, + memberEntity.userRole, + memberEntity.name, + memberEntity.userId, + labelingAssignmentEntity.workerUid.count().as("assignedCnt"), + new CaseBuilder() + .when(labelingAssignmentEntity.workState.eq("DONE")) + .then(1L) + .otherwise(0L) + .sum() + .as("doneCnt"), + new CaseBuilder() + .when(labelingAssignmentEntity.workState.eq("SKIP")) + .then(1L) + .otherwise(0L) + .sum() + .as("skipCnt"), + new CaseBuilder() + .when(labelingAssignmentEntity.workState.eq("DONE") + .and( + Expressions.stringTemplate( + "to_char({0}, 'YYYY-MM-DD')", labelingAssignmentEntity.modifiedDate).eq(s3) ) + ) + .then(1L) + .otherwise(0L) + .sum() + .as("day3AgoDoneCnt"), + new CaseBuilder() + .when(labelingAssignmentEntity.workState.eq("DONE") + .and( + Expressions.stringTemplate( + "to_char({0}, 'YYYY-MM-DD')", labelingAssignmentEntity.modifiedDate).eq(s2) ) + ) + .then(1L) + .otherwise(0L) + .sum() + .as("day2AgoDoneCnt"), + new CaseBuilder() + .when(labelingAssignmentEntity.workState.eq("DONE") + .and( + Expressions.stringTemplate( + "to_char({0}, 'YYYY-MM-DD')", labelingAssignmentEntity.modifiedDate).eq(s1) ) + ) + .then(1L) + .otherwise(0L) + .sum() + .as("day1AgoDoneCnt") + )) + .from(labelingAssignmentEntity) + .innerJoin(memberEntity) + .on(whereSubBuilder) + .where(whereBuilder) + .groupBy( + memberEntity.userRole, + memberEntity.name, + memberEntity.userId) + .offset(pageable.getOffset()) + .limit(pageable.getPageSize()) + .fetch(); + + /* + Long countQuery = + queryFactory + .select(labelingAssignmentEntity.workerUid.count()) + .from(labelingAssignmentEntity) + .where(whereBuilder) + .fetchOne(); + */ + + Long countQuery = foundContent.stream().count(); + + return new PageImpl<>(foundContent, pageable, countQuery); + } + + }