라벨링 추가할당 API 추가, 라벨링툴 목록 도엽순으로 소팅 #82

Merged
gina merged 1 commits from feat/infer_dev_260211 into develop 2026-02-20 18:25:18 +09:00
7 changed files with 200 additions and 2 deletions

View File

@@ -28,6 +28,7 @@ import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDate;
import java.util.List;
import java.util.UUID;
import lombok.RequiredArgsConstructor;
@@ -458,4 +459,43 @@ public class LabelAllocateApiController {
UUID uuid) {
return ApiResponseDto.ok(labelAllocateService.isDownloadable(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)
})
@PostMapping("/allocate-add-stblt")
public ApiResponseDto<ApiResponseDto.ResponseObj> labelAllocateAddStblt(
@RequestBody @Valid LabelAllocateDto.AllocateAddStbltDto dto) {
return ApiResponseDto.okObject(
labelAllocateService.allocateAddStbltYn(
dto.getTotalCnt(), dto.getUuid(), dto.getLabelers(), dto.getBaseDate()));
}
@Operation(summary = "라벨링 추가 할당 가능한 건수", description = "라벨링 추가 할당 가능한 건수 API")
@ApiResponses(
value = {
@ApiResponse(responseCode = "200", description = "조회 성공"),
@ApiResponse(responseCode = "404", description = "코드를 찾을 수 없음"),
@ApiResponse(responseCode = "500", description = "서버 오류")
})
@GetMapping("/allocate-add-cnt")
public ApiResponseDto<Long> allocateAddCnt(
@RequestParam UUID uuid, @RequestParam LocalDate baseDate) {
return ApiResponseDto.ok(labelAllocateService.findAllocateAddCnt(uuid, baseDate));
}
}

View File

@@ -3,6 +3,7 @@ package com.kamco.cd.kamcoback.label.dto;
import com.kamco.cd.kamcoback.common.utils.enums.CodeExpose;
import com.kamco.cd.kamcoback.common.utils.enums.EnumType;
import io.swagger.v3.oas.annotations.media.Schema;
import java.time.LocalDate;
import java.time.ZonedDateTime;
import java.util.List;
import java.util.UUID;
@@ -365,9 +366,35 @@ public class LabelAllocateDto {
@AllArgsConstructor
@NoArgsConstructor
public static class InferenceLearnDto {
private UUID analUuid;
private String learnUid;
private String analState;
private Long analId;
}
@Getter
@Setter
@AllArgsConstructor
public static class AllocateAddStbltDto {
@Schema(description = "총 잔여 건수", example = "179")
private Integer totalCnt;
@Schema(
description = "추가할당할 라벨러",
example =
"""
[
"123454", "654321", "222233", "777222"
]
""")
private List<String> labelers;
@Schema(description = "회차 마스터 key", example = "c0e77cc7-8c28-46ba-9ca4-11e90246ab44")
private UUID uuid;
@Schema(description = "기준일자", example = "2026-02-20")
private LocalDate baseDate;
}
}

View File

@@ -20,6 +20,7 @@ import com.kamco.cd.kamcoback.log.dto.AuditLogDto;
import com.kamco.cd.kamcoback.log.dto.AuditLogDto.DownloadReq;
import com.kamco.cd.kamcoback.postgres.core.AuditLogCoreService;
import com.kamco.cd.kamcoback.postgres.core.LabelAllocateCoreService;
import java.time.LocalDate;
import java.util.List;
import java.util.Objects;
import java.util.UUID;
@@ -302,4 +303,52 @@ public class LabelAllocateService {
public boolean isDownloadable(UUID uuid) {
return labelAllocateCoreService.isDownloadable(uuid);
}
/**
* 실태조사가 값 들어온 기간만큼 할당하는 로직 (최초 할당 이후 작업)
*
* @param uuid
* @param targetUsers
* @return
*/
@Transactional
public ApiResponseDto.ResponseObj allocateAddStbltYn(
Integer totalCnt, UUID uuid, List<String> targetUsers, LocalDate baseDate) {
int userCount = targetUsers.size();
if (userCount == 0) {
return new ApiResponseDto.ResponseObj(ApiResponseCode.BAD_REQUEST, "추가 할당할 라벨러를 선택해주세요.");
}
int base = totalCnt / userCount;
int remainder = totalCnt % userCount;
Long lastId = null;
List<AllocateInfoDto> allIds =
labelAllocateCoreService.fetchNextIdsAddStbltYn(
uuid, baseDate, lastId, totalCnt.longValue());
// MapSheetAnalInferenceEntity analUid 가져오기
Long analUid = labelAllocateCoreService.findMapSheetAnalInferenceUid(uuid);
int index = 0;
for (int i = 0; i < userCount; i++) {
int assignCount = base;
// 마지막 사람에게 나머지 몰아주기
if (i == userCount - 1) {
assignCount += remainder;
}
int end = index + assignCount;
List<AllocateInfoDto> sub = allIds.subList(index, end);
labelAllocateCoreService.assignOwner(sub, targetUsers.get(i), analUid);
index = end;
}
return new ApiResponseDto.ResponseObj(ApiResponseCode.OK, "추가 할당이 완료되었습니다.");
}
public Long findAllocateAddCnt(UUID uuid, LocalDate baseDate) {
return labelAllocateCoreService.findAllocateAddCnt(uuid, baseDate);
}
}

View File

@@ -257,7 +257,9 @@ public class LabelAllocateCoreService {
// 파일이 있는지만 확인
Path path = Paths.get(responsePath).resolve(dto.getLearnUid() + ".zip");
if (!Files.isRegularFile(path)) return false; // exists 포함
if (!Files.isRegularFile(path)) {
return false; // exists 포함
}
String state = dto.getAnalState();
boolean isLabelingIng =
@@ -265,7 +267,9 @@ public class LabelAllocateCoreService {
if (isLabelingIng) {
Long analId = dto.getAnalId();
if (analId == null) return false;
if (analId == null) {
return false;
}
return batchStepHistoryRepository.isDownloadable(analId);
}
@@ -277,4 +281,13 @@ public class LabelAllocateCoreService {
.findLearnUid(uuid)
.orElseThrow(() -> new CustomApiException("NOT_FOUND_DATA", HttpStatus.NOT_FOUND));
}
public List<AllocateInfoDto> fetchNextIdsAddStbltYn(
UUID uuid, LocalDate baseDate, Long lastId, Long totalCnt) {
return labelAllocateRepository.fetchNextIdsAddStbltYn(uuid, baseDate, lastId, totalCnt);
}
public Long findAllocateAddCnt(UUID uuid, LocalDate baseDate) {
return labelAllocateRepository.findAllocateAddCnt(uuid, baseDate);
}
}

View File

@@ -110,4 +110,9 @@ public interface LabelAllocateRepositoryCustom {
InferenceLearnDto findLabelingIngProcessId(UUID uuid);
Optional<String> findLearnUid(UUID uuid);
List<AllocateInfoDto> fetchNextIdsAddStbltYn(
UUID uuid, LocalDate baseDate, Long lastId, Long totalCnt);
Long findAllocateAddCnt(UUID uuid, LocalDate baseDate);
}

View File

@@ -1856,4 +1856,56 @@ public class LabelAllocateRepositoryImpl implements LabelAllocateRepositoryCusto
.where(mapSheetAnalInferenceEntity.uuid.eq(uuid))
.fetchOne());
}
@Override
public List<AllocateInfoDto> fetchNextIdsAddStbltYn(
UUID uuid, LocalDate baseDate, Long lastId, Long totalCnt) {
ZoneId zone = ZoneId.of("Asia/Seoul"); // 기준 타임존 명확히
ZonedDateTime nextDayStart = baseDate.plusDays(1).atStartOfDay(zone);
return queryFactory
.select(
Projections.constructor(
AllocateInfoDto.class,
mapSheetAnalDataInferenceGeomEntity.geoUid,
mapSheetAnalDataInferenceGeomEntity.mapSheetNum,
mapSheetAnalDataInferenceGeomEntity.pnu))
.from(mapSheetAnalInferenceEntity)
.innerJoin(mapSheetAnalDataInferenceEntity)
.on(mapSheetAnalInferenceEntity.id.eq(mapSheetAnalDataInferenceEntity.analUid))
.innerJoin(mapSheetAnalDataInferenceGeomEntity)
.on(
mapSheetAnalDataInferenceEntity.id.eq(mapSheetAnalDataInferenceGeomEntity.dataUid),
mapSheetAnalDataInferenceGeomEntity.pnu.gt(0),
mapSheetAnalDataInferenceGeomEntity.fitState.eq(ImageryFitStatus.UNFIT.getId()),
mapSheetAnalDataInferenceGeomEntity.fitStateDttm.lt(nextDayStart),
mapSheetAnalDataInferenceGeomEntity.labelState.isNull())
.where(
mapSheetAnalInferenceEntity.uuid.eq(uuid),
lastId == null ? null : mapSheetAnalDataInferenceGeomEntity.geoUid.gt(lastId))
.orderBy(mapSheetAnalDataInferenceGeomEntity.mapSheetNum.asc())
.limit(totalCnt)
.fetch();
}
@Override
public Long findAllocateAddCnt(UUID uuid, LocalDate baseDate) {
ZoneId zone = ZoneId.of("Asia/Seoul"); // 기준 타임존 명확히
ZonedDateTime nextDayStart = baseDate.plusDays(1).atStartOfDay(zone);
return queryFactory
.select(mapSheetAnalDataInferenceGeomEntity.geoUid.count())
.from(mapSheetAnalInferenceEntity)
.innerJoin(mapSheetAnalDataInferenceEntity)
.on(mapSheetAnalInferenceEntity.id.eq(mapSheetAnalDataInferenceEntity.analUid))
.innerJoin(mapSheetAnalDataInferenceGeomEntity)
.on(
mapSheetAnalDataInferenceEntity.id.eq(mapSheetAnalDataInferenceGeomEntity.dataUid),
mapSheetAnalDataInferenceGeomEntity.pnu.gt(0),
mapSheetAnalDataInferenceGeomEntity.fitState.eq(ImageryFitStatus.UNFIT.getId()),
mapSheetAnalDataInferenceGeomEntity.fitStateDttm.lt(nextDayStart),
mapSheetAnalDataInferenceGeomEntity.labelState.isNull())
.where(mapSheetAnalInferenceEntity.uuid.eq(uuid))
.fetchOne();
}
}

View File

@@ -120,6 +120,7 @@ public class TrainingDataLabelRepositoryImpl extends QuerydslRepositorySupport
.orderBy(
mapSheetAnalInferenceEntity.targetYyyy.asc(),
mapSheetAnalInferenceEntity.compareYyyy.asc(),
labelingAssignmentEntity.assignGroupId.asc(),
labelingAssignmentEntity.createdDate.asc(),
labelingAssignmentEntity.inferenceGeomUid.asc())
.fetch();
@@ -641,6 +642,7 @@ public class TrainingDataLabelRepositoryImpl extends QuerydslRepositorySupport
queryFactory
.select(
labelingAssignmentEntity.assignmentUid,
labelingAssignmentEntity.assignGroupId,
labelingAssignmentEntity.createdDate,
labelingAssignmentEntity.inferenceGeomUid,
mapSheetAnalInferenceEntity.targetYyyy,
@@ -659,6 +661,7 @@ public class TrainingDataLabelRepositoryImpl extends QuerydslRepositorySupport
.orderBy(
mapSheetAnalInferenceEntity.targetYyyy.asc(),
mapSheetAnalInferenceEntity.compareYyyy.asc(),
labelingAssignmentEntity.assignGroupId.asc(),
labelingAssignmentEntity.createdDate.asc(),
labelingAssignmentEntity.inferenceGeomUid.asc())
.limit(1)
@@ -673,6 +676,7 @@ public class TrainingDataLabelRepositoryImpl extends QuerydslRepositorySupport
Long inferenceGeomUid = firstAssigned.get(labelingAssignmentEntity.inferenceGeomUid);
Integer targetYyyy = firstAssigned.get(mapSheetAnalInferenceEntity.targetYyyy);
Integer compareYyyy = firstAssigned.get(mapSheetAnalInferenceEntity.compareYyyy);
String assignGroupId = firstAssigned.get(labelingAssignmentEntity.assignGroupId);
BooleanExpression beforeCondition =
mapSheetAnalInferenceEntity
@@ -688,12 +692,20 @@ public class TrainingDataLabelRepositoryImpl extends QuerydslRepositorySupport
.targetYyyy
.eq(targetYyyy)
.and(mapSheetAnalInferenceEntity.compareYyyy.eq(compareYyyy))
.and(labelingAssignmentEntity.assignGroupId.lt(assignGroupId)))
.or(
mapSheetAnalInferenceEntity
.targetYyyy
.eq(targetYyyy)
.and(mapSheetAnalInferenceEntity.compareYyyy.eq(compareYyyy))
.and(labelingAssignmentEntity.assignGroupId.eq(assignGroupId))
.and(labelingAssignmentEntity.createdDate.lt(createdDttm)))
.or(
mapSheetAnalInferenceEntity
.targetYyyy
.eq(targetYyyy)
.and(mapSheetAnalInferenceEntity.compareYyyy.eq(compareYyyy))
.and(labelingAssignmentEntity.assignGroupId.eq(assignGroupId))
.and(labelingAssignmentEntity.createdDate.eq(createdDttm))
.and(labelingAssignmentEntity.inferenceGeomUid.lt(inferenceGeomUid)));