모델 상세 API 커밋

This commit is contained in:
2026-02-04 19:46:57 +09:00
parent ce69bacb01
commit b2be43a76e
7 changed files with 424 additions and 0 deletions

View File

@@ -0,0 +1,43 @@
package com.kamco.cd.training.model;
import com.kamco.cd.training.config.api.ApiResponseDto;
import com.kamco.cd.training.model.dto.ModelTrainDetailDto;
import com.kamco.cd.training.model.dto.ModelTrainDetailDto.MappingDataset;
import com.kamco.cd.training.model.service.ModelTrainDetailService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import java.util.List;
import java.util.UUID;
import lombok.RequiredArgsConstructor;
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.RestController;
@RestController
@RequiredArgsConstructor
@Tag(name = "모델학습 관리", description = "어드민 홈 > 모델학습관리 > 모델관리 > 목록")
@RequestMapping("/api/models")
public class ModelTrainDetailApiController {
private final ModelTrainDetailService modelTrainDetailService;
@Operation(summary = "모델학습 상세 요약 정보", description = "모델학습 상세 요약 정보 API")
@GetMapping("/summary/{uuid}")
public ApiResponseDto<ModelTrainDetailDto.DetailSummary> getModelDetailSummary(
@PathVariable UUID uuid) {
return ApiResponseDto.ok(modelTrainDetailService.getModelDetailSummary(uuid));
}
@Operation(summary = "모델학습 상세 > 하이퍼파라미터 요약 정보", description = "모델학습 상세 하이퍼파라미터 요약 정보 API")
@GetMapping("/hyper-summary/{uuid}")
public ApiResponseDto<ModelTrainDetailDto.HyperSummary> getByModelHyperParamSummary(
@PathVariable UUID uuid) {
return ApiResponseDto.ok(modelTrainDetailService.getByModelHyperParamSummary(uuid));
}
@Operation(summary = "모델학습 상세 > 데이터셋 정보", description = "모델학습 상세 데이터셋 정보 API")
@GetMapping("/mapp-dataset/{uuid}")
public ApiResponseDto<List<MappingDataset>> getByModelMappingDataset(@PathVariable UUID uuid) {
return ApiResponseDto.ok(modelTrainDetailService.getByModelMappingDataset(uuid));
}
}

View File

@@ -0,0 +1,144 @@
package com.kamco.cd.training.model.dto;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.kamco.cd.training.common.enums.LearnDataType;
import com.kamco.cd.training.common.enums.TrainStatusType;
import com.kamco.cd.training.common.enums.TrainType;
import com.kamco.cd.training.common.utils.enums.Enums;
import com.kamco.cd.training.common.utils.interfaces.JsonFormatDttm;
import io.swagger.v3.oas.annotations.media.Schema;
import java.time.Duration;
import java.time.ZonedDateTime;
import java.util.UUID;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
public class ModelTrainDetailDto {
@Schema(name = "모델학습관리 목록", description = "모델학습관리 목록")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public static class DetailSummary {
private Long modelId;
private UUID uuid;
private String modelNo;
private String modelVer;
@JsonFormatDttm private ZonedDateTime step1StrtDttm;
@JsonFormatDttm private ZonedDateTime step2EndDttm;
private String statusCd;
private String trainType;
public String getStatusName() {
if (this.statusCd == null || this.statusCd.isBlank()) return null;
try {
return TrainStatusType.valueOf(this.statusCd).getText(); // 또는 getName()
} catch (IllegalArgumentException e) {
return this.statusCd; // 매핑 못하면 코드 그대로 반환(원하면 null 처리)
}
}
public String getTrainTypeName() {
if (this.trainType == null || this.trainType.isBlank()) return null;
try {
return TrainType.valueOf(this.trainType).getText(); // 또는 getName()
} catch (IllegalArgumentException e) {
return this.trainType; // 매핑 못하면 코드 그대로 반환(원하면 null 처리)
}
}
private String formatDuration(ZonedDateTime start, ZonedDateTime end) {
if (end == null) {
end = ZonedDateTime.now();
}
if (start == null) {
return null;
}
long totalSeconds = Math.abs(Duration.between(start, end).getSeconds());
long hours = totalSeconds / 3600;
long minutes = (totalSeconds % 3600) / 60;
long seconds = totalSeconds % 60;
return String.format("%d시간 %d분 %d초", hours, minutes, seconds);
}
public String getStepAllDuration() {
return formatDuration(this.step1StrtDttm, this.step2EndDttm);
}
}
@Schema(name = "모델학습관리 목록", description = "모델학습관리 목록")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public static class HyperSummary {
private UUID uuid;
private Long hyperParamId;
private String hyperVer;
private String backbone;
private String inputSize;
private Integer batchSize;
}
@Schema(name = "선택한 데이터셋 목록", description = "선택한 데이터셋 목록")
@Getter
@Setter
@NoArgsConstructor
public static class MappingDataset {
private Long modelId;
private String dataType;
private Integer compareYyyy;
private Integer targetYyyy;
private Long roundNo;
private String dataTypeName;
@JsonInclude(JsonInclude.Include.NON_NULL)
private Long buildingCnt;
@JsonInclude(JsonInclude.Include.NON_NULL)
private Long containerCnt;
@JsonInclude(JsonInclude.Include.NON_NULL)
private Long wasteCnt;
@JsonInclude(JsonInclude.Include.NON_NULL)
private Long landCoverCnt;
public MappingDataset(
Long modelId,
String dataType,
Integer compareYyyy,
Integer targetYyyy,
Long roundNo,
Long buildingCnt,
Long containerCnt,
Long wasteCnt,
Long landCoverCnt) {
this.modelId = modelId;
this.dataType = dataType;
this.compareYyyy = compareYyyy;
this.targetYyyy = targetYyyy;
this.roundNo = roundNo;
this.buildingCnt = buildingCnt;
this.containerCnt = containerCnt;
this.wasteCnt = wasteCnt;
this.landCoverCnt = landCoverCnt;
this.dataTypeName = getDataTypeName(this.dataType);
}
public String getDataTypeName(String groupTitleCd) {
LearnDataType type = Enums.fromId(LearnDataType.class, groupTitleCd);
return type == null ? null : type.getText();
}
}
}

View File

@@ -0,0 +1,45 @@
package com.kamco.cd.training.model.service;
import com.kamco.cd.training.model.dto.ModelTrainDetailDto.DetailSummary;
import com.kamco.cd.training.model.dto.ModelTrainDetailDto.HyperSummary;
import com.kamco.cd.training.model.dto.ModelTrainDetailDto.MappingDataset;
import com.kamco.cd.training.postgres.core.ModelTrainDetailCoreService;
import java.util.List;
import java.util.UUID;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
@Slf4j
public class ModelTrainDetailService {
private final ModelTrainDetailCoreService modelTrainDetailCoreService;
/**
* 모델 상세정보 요약
*
* @param uuid
* @return
*/
public DetailSummary getModelDetailSummary(UUID uuid) {
return modelTrainDetailCoreService.getModelDetailSummary(uuid);
}
/**
* 모델 하이퍼파라미터 요약
*
* @param uuid
* @return
*/
public HyperSummary getByModelHyperParamSummary(UUID uuid) {
return modelTrainDetailCoreService.getByModelHyperParamSummary(uuid);
}
public List<MappingDataset> getByModelMappingDataset(UUID uuid) {
return modelTrainDetailCoreService.getByModelMappingDataset(uuid);
}
}

View File

@@ -0,0 +1,56 @@
package com.kamco.cd.training.postgres.core;
import com.kamco.cd.training.common.exception.BadRequestException;
import com.kamco.cd.training.common.exception.NotFoundException;
import com.kamco.cd.training.common.utils.UserUtil;
import com.kamco.cd.training.model.dto.ModelTrainDetailDto.DetailSummary;
import com.kamco.cd.training.model.dto.ModelTrainDetailDto.HyperSummary;
import com.kamco.cd.training.model.dto.ModelTrainDetailDto.MappingDataset;
import com.kamco.cd.training.postgres.entity.ModelMasterEntity;
import com.kamco.cd.training.postgres.repository.model.ModelDetailRepository;
import java.util.List;
import java.util.UUID;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
@Service
@RequiredArgsConstructor
public class ModelTrainDetailCoreService {
private final ModelDetailRepository modelDetailRepository;
private final UserUtil userUtil;
/**
* UUID로 모델 조회
*
* @param uuid UUID
* @return 모델 Entity
*/
public ModelMasterEntity findByUuid(String uuid) {
try {
UUID uuidObj = UUID.fromString(uuid);
return modelDetailRepository
.findByUuid(uuidObj)
.orElseThrow(() -> new NotFoundException("모델을 찾을 수 없습니다. UUID: " + uuid));
} catch (IllegalArgumentException e) {
throw new BadRequestException("잘못된 UUID 형식입니다: " + uuid);
}
}
/**
* 상세정보 페이지 > 요약정보
*
* @param uuid
* @return
*/
public DetailSummary getModelDetailSummary(UUID uuid) {
return modelDetailRepository.getModelDetailSummary(uuid);
}
public HyperSummary getByModelHyperParamSummary(UUID uuid) {
return modelDetailRepository.getByModelHyperParamSummary(uuid);
}
public List<MappingDataset> getByModelMappingDataset(UUID uuid) {
return modelDetailRepository.getByModelMappingDataset(uuid);
}
}

View File

@@ -0,0 +1,7 @@
package com.kamco.cd.training.postgres.repository.model;
import com.kamco.cd.training.postgres.entity.ModelMasterEntity;
import org.springframework.data.jpa.repository.JpaRepository;
public interface ModelDetailRepository
extends JpaRepository<ModelMasterEntity, Long>, ModelDetailRepositoryCustom {}

View File

@@ -0,0 +1,20 @@
package com.kamco.cd.training.postgres.repository.model;
import com.kamco.cd.training.model.dto.ModelTrainDetailDto.DetailSummary;
import com.kamco.cd.training.model.dto.ModelTrainDetailDto.HyperSummary;
import com.kamco.cd.training.model.dto.ModelTrainDetailDto.MappingDataset;
import com.kamco.cd.training.postgres.entity.ModelMasterEntity;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
public interface ModelDetailRepositoryCustom {
Optional<ModelMasterEntity> findByUuid(UUID uuid);
DetailSummary getModelDetailSummary(UUID uuid);
HyperSummary getByModelHyperParamSummary(UUID uuid);
List<MappingDataset> getByModelMappingDataset(UUID uuid);
}

View File

@@ -0,0 +1,109 @@
package com.kamco.cd.training.postgres.repository.model;
import static com.kamco.cd.training.postgres.entity.QDatasetEntity.datasetEntity;
import static com.kamco.cd.training.postgres.entity.QModelDatasetEntity.modelDatasetEntity;
import static com.kamco.cd.training.postgres.entity.QModelDatasetMappEntity.modelDatasetMappEntity;
import static com.kamco.cd.training.postgres.entity.QModelHyperParamEntity.modelHyperParamEntity;
import static com.kamco.cd.training.postgres.entity.QModelMasterEntity.modelMasterEntity;
import com.kamco.cd.training.model.dto.ModelTrainDetailDto.DetailSummary;
import com.kamco.cd.training.model.dto.ModelTrainDetailDto.HyperSummary;
import com.kamco.cd.training.model.dto.ModelTrainDetailDto.MappingDataset;
import com.kamco.cd.training.postgres.entity.ModelMasterEntity;
import com.querydsl.core.types.Projections;
import com.querydsl.jpa.JPAExpressions;
import com.querydsl.jpa.impl.JPAQueryFactory;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Repository;
@Repository
@RequiredArgsConstructor
public class ModelDetailRepositoryImpl implements ModelDetailRepositoryCustom {
private final JPAQueryFactory queryFactory;
/**
* 모델 조회
*
* @param uuid
* @return
*/
@Override
public Optional<ModelMasterEntity> findByUuid(UUID uuid) {
return Optional.ofNullable(
queryFactory
.select(modelMasterEntity)
.from(modelMasterEntity)
.where(modelMasterEntity.uuid.eq(uuid))
.fetchOne());
}
@Override
public DetailSummary getModelDetailSummary(UUID uuid) {
return queryFactory
.select(
Projections.constructor(
DetailSummary.class,
modelMasterEntity.id,
modelMasterEntity.uuid,
modelMasterEntity.modelNo,
modelMasterEntity.modelVer,
modelMasterEntity.step1StrtDttm,
modelMasterEntity.step2EndDttm,
modelMasterEntity.statusCd,
modelMasterEntity.trainType))
.from(modelMasterEntity)
.where(modelMasterEntity.uuid.eq(uuid))
.fetchOne();
}
@Override
public HyperSummary getByModelHyperParamSummary(UUID uuid) {
return queryFactory
.select(
Projections.constructor(
HyperSummary.class,
modelHyperParamEntity.uuid,
modelHyperParamEntity.id,
modelHyperParamEntity.hyperVer,
modelHyperParamEntity.backbone,
modelHyperParamEntity.inputSize,
modelHyperParamEntity.batchSize))
.from(modelHyperParamEntity)
.where(
modelHyperParamEntity.id.eq(
JPAExpressions.select(modelMasterEntity.hyperParamId)
.from(modelMasterEntity)
.where(modelMasterEntity.uuid.eq(uuid))))
.fetchOne();
}
@Override
public List<MappingDataset> getByModelMappingDataset(UUID uuid) {
return queryFactory
.select(
Projections.constructor(
MappingDataset.class,
modelMasterEntity.id,
datasetEntity.dataType,
datasetEntity.compareYyyy,
datasetEntity.targetYyyy,
datasetEntity.roundNo,
modelDatasetEntity.buildingCnt,
modelDatasetEntity.containerCnt,
modelDatasetEntity.wasteCnt,
modelDatasetEntity.landCoverCnt))
.from(modelMasterEntity)
.innerJoin(modelDatasetEntity)
.on(modelMasterEntity.id.eq(modelDatasetEntity.model.id))
.innerJoin(modelDatasetMappEntity)
.on(modelMasterEntity.id.eq(modelDatasetMappEntity.modelUid))
.innerJoin(datasetEntity)
.on(modelDatasetMappEntity.datasetUid.eq(datasetEntity.id))
.where(modelMasterEntity.uuid.eq(uuid))
.fetch();
}
}