Merge pull request 'feat/dev_251201' (#79) from feat/dev_251201 into develop

Reviewed-on: https://kamco.gitea.gs.dabeeo.com/dabeeo/kamco-dabeeo-backoffice/pulls/79
This commit is contained in:
2025-12-19 11:55:17 +09:00
18 changed files with 210 additions and 768 deletions

View File

@@ -1,43 +0,0 @@
package com.kamco.cd.kamcoback.model;
import com.kamco.cd.kamcoback.config.api.ApiResponseDto;
import com.kamco.cd.kamcoback.model.dto.ModelMgmtDto;
import com.kamco.cd.kamcoback.model.service.ModelMgmtService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.transaction.Transactional;
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.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@Tag(name = "모델 관리", description = "모델 관리 API")
@RequiredArgsConstructor
@RestController
@RequestMapping("/api/model")
@Transactional
public class ModelMgmtApiController {
private final ModelMgmtService modelMgmtService;
@Operation(summary = "모델관리 목록")
@GetMapping
public ApiResponseDto<Page<ModelMgmtDto.ModelList>> findModelMgmtList(
@RequestParam(required = false) LocalDate startDate,
@RequestParam(required = false) LocalDate endDate,
@RequestParam(required = false, defaultValue = "createCompleteDttm") String sortColumn,
@RequestParam(required = false) String modelType,
@RequestParam(required = false) String searchVal,
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "20") int size) {
ModelMgmtDto.searchReq searchReq = new ModelMgmtDto.searchReq(page, size, sortColumn + ",desc");
Page<ModelMgmtDto.ModelList> result =
modelMgmtService.findModelMgmtList(searchReq, startDate, endDate, modelType, searchVal);
return ApiResponseDto.ok(result);
}
}

View File

@@ -1,104 +1,42 @@
package com.kamco.cd.kamcoback.model;
import com.kamco.cd.kamcoback.code.dto.CommonCodeDto;
import com.kamco.cd.kamcoback.config.api.ApiResponseDto;
import com.kamco.cd.kamcoback.model.dto.ModelMngDto;
import com.kamco.cd.kamcoback.model.dto.ModelVerDto;
import com.kamco.cd.kamcoback.model.service.ModelMngService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Content;
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.transaction.Transactional;
import java.time.LocalDate;
import java.util.List;
import java.util.Optional;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@Tag(name = "모델 관리", description = "모델 관리 API")
@RequiredArgsConstructor
@RestController
@RequestMapping("/api/model/deprecated")
@RequestMapping("/api/model")
@Transactional
@Deprecated
public class ModelMngApiController {
private final ModelMngService modelMngService;
@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)
})
@Operation(summary = "모델관리 목록")
@GetMapping
public ApiResponseDto<List<ModelMngDto.Basic>> getFindAll() {
return ApiResponseDto.ok(modelMngService.findModelMngAll());
}
/**
* 최종 등록 모델 정보
*
* @return ModelMngDto.FinalModelDto
*/
@Operation(summary = "최종 등록 모델 조회", description = "최종 등록 모델 조회")
@GetMapping("/final-model-info")
public ApiResponseDto<Optional<ModelMngDto.FinalModelDto>> getFinalModelInfo() {
return ApiResponseDto.ok(modelMngService.getFinalModelInfo());
}
/**
* 모델 등록 => 모델, 버전 동시 등록 (UI 상 따로 등록하는 곳 없음)
*
* @param addReq 모델 입력 값
* @return ModelVerDto.Basic
*/
@Operation(summary = "모델 등록", description = "모델 등록")
@PostMapping
public ApiResponseDto<ModelVerDto.Basic> save(@RequestBody ModelMngDto.AddReq addReq) {
return ApiResponseDto.createOK(modelMngService.save(addReq));
}
@Operation(summary = "모델 수정", description = "모델 수정")
@PutMapping("/{id}")
public ApiResponseDto<Long> update(
@PathVariable Long id, @RequestBody ModelMngDto.AddReq addReq) {
return ApiResponseDto.ok(modelMngService.update(id, addReq));
}
@Operation(summary = "모델 삭제", description = "모델 삭제")
@DeleteMapping("/{id}")
public ApiResponseDto<Long> delete(@PathVariable Long id) {
return ApiResponseDto.deleteOk(modelMngService.delete(id));
}
@Operation(summary = "모델 등록 이력", description = "모델 등록 이력")
@GetMapping("/reg-history")
public ApiResponseDto<Page<ModelMngDto.ModelRegHistory>> getRegHistoryList(
public ApiResponseDto<Page<ModelMngDto.ModelList>> findModelMgmtList(
@RequestParam(required = false) LocalDate startDate,
@RequestParam(required = false) LocalDate endDate,
@RequestParam int page,
@RequestParam(defaultValue = "20") int size,
@RequestParam(required = false, defaultValue = "createCompleteDttm") String sortColumn,
@RequestParam(required = false) String modelType,
@RequestParam(required = false) String searchVal,
@RequestParam(required = false) String searchColumn) {
ModelMngDto.searchReq searchReq =
new ModelMngDto.searchReq(
page, size, Optional.ofNullable(searchColumn).orElse("createdDate") + ",desc");
// searchColumn:: Entity 컬럼명칭으로 -> 기본값 = 등록일 createdDate, (선택) 배포일 deployDttm
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "20") int size) {
ModelMngDto.searchReq searchReq = new ModelMngDto.searchReq(page, size, sortColumn + ",desc");
Page<ModelMngDto.ModelRegHistory> result =
modelMngService.getRegHistoryList(searchReq, startDate, endDate, searchVal);
Page<ModelMngDto.ModelList> result =
modelMngService.findModelMgmtList(searchReq, startDate, endDate, modelType, searchVal);
return ApiResponseDto.ok(result);
}

View File

@@ -1,119 +0,0 @@
package com.kamco.cd.kamcoback.model.dto;
import com.kamco.cd.kamcoback.common.utils.interfaces.JsonFormatDttm;
import io.swagger.v3.oas.annotations.media.Schema;
import java.math.BigDecimal;
import java.time.ZonedDateTime;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
public class ModelMgmtDto {
@Schema(name = "ModelMgmtDto Basic", description = "모델관리 엔티티 기본 정보")
@Getter
@NoArgsConstructor
public static class Basic {
private String modelVer;
private String hyperVer;
private String epochVer;
private String dockerFileNm;
@JsonFormatDttm private ZonedDateTime createCompleteDttm;
@JsonFormatDttm private ZonedDateTime recentUseDttm;
private Boolean deleted;
@JsonFormatDttm private ZonedDateTime createdDttm;
private Long createdUid;
@JsonFormatDttm private ZonedDateTime updatedDttm;
private Long updatedUid;
public Basic(
String modelVer,
String hyperVer,
String epochVer,
String dockerFileNm,
ZonedDateTime createCompleteDttm,
ZonedDateTime recentUseDttm,
Boolean deleted,
ZonedDateTime createdDttm,
Long createdUid,
ZonedDateTime updatedDttm,
Long updatedUid) {
this.modelVer = modelVer;
this.hyperVer = hyperVer;
this.epochVer = epochVer;
this.dockerFileNm = dockerFileNm;
this.createCompleteDttm = createCompleteDttm;
this.recentUseDttm = recentUseDttm;
this.deleted = deleted;
this.createdDttm = createdDttm;
this.createdUid = createdUid;
this.updatedDttm = updatedDttm;
this.updatedUid = updatedUid;
}
}
@Schema(name = "ModelList", description = "모델관리 목록")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public static class ModelList {
private Integer rowNum;
private String modelVer;
private String dockerFileNm;
private String modelType;
private String createCompleteDttm;
private String recentUseDttm; // TODO: 추론관리 테이블 나오면 분석진행중일 때 사용중으로 리턴하기
private BigDecimal f1Score;
private BigDecimal precision;
private BigDecimal recall;
private BigDecimal accuracy;
private BigDecimal iou;
private Boolean deleted;
}
@Schema(name = "ModelAddReq", description = "모델 등록 req")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public static class AddReq {
private String modelType;
private String dockerFileNm;
private String modelVer;
private String hyperVer;
private String epochVer;
}
@Schema(name = "searchReq", description = "검색 요청")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public static class searchReq {
// 페이징 파라미터
private int page = 0;
private int size = 20;
private String sort;
public Pageable toPageable() {
if (sort != null && !sort.isEmpty()) {
String[] sortParams = sort.split(",");
String property = sortParams[0];
Sort.Direction direction =
sortParams.length > 1 ? Sort.Direction.fromString(sortParams[1]) : Sort.Direction.ASC;
return PageRequest.of(page, size, Sort.by(direction, property));
}
return PageRequest.of(page, size);
}
}
}

View File

@@ -0,0 +1,21 @@
package com.kamco.cd.kamcoback.model.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
public class ModelMngBakDto {
@Schema(name = "AddReq", description = "등록 req")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public static class AddReq {
private String modelCate;
private String modelVer;
private String modelPath;
}
}

View File

@@ -2,7 +2,7 @@ package com.kamco.cd.kamcoback.model.dto;
import com.kamco.cd.kamcoback.common.utils.interfaces.JsonFormatDttm;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotEmpty;
import java.math.BigDecimal;
import java.time.ZonedDateTime;
import lombok.AllArgsConstructor;
import lombok.Getter;
@@ -14,98 +14,86 @@ import org.springframework.data.domain.Sort;
public class ModelMngDto {
@Schema(name = "ModelMng Basic", description = "모델관리 엔티티 기본 정보")
@Schema(name = "ModelMgmtDto Basic", description = "모델관리 엔티티 기본 정보")
@Getter
@NoArgsConstructor
public static class Basic {
private final Long id;
private final String modelNm;
private final String modelCate;
private final String modelPath;
private String modelVer;
private String hyperVer;
private String epochVer;
private String dockerFileNm;
@JsonFormatDttm private final ZonedDateTime createdDttm;
private final Long createdUid;
@JsonFormatDttm private ZonedDateTime createCompleteDttm;
@JsonFormatDttm private ZonedDateTime recentUseDttm;
private Boolean deleted;
@JsonFormatDttm private ZonedDateTime createdDttm;
private Long createdUid;
@JsonFormatDttm private final ZonedDateTime updatedDttm;
private final Long updatedUid;
private final String modelCntnt;
@JsonFormatDttm private ZonedDateTime updatedDttm;
private Long updatedUid;
public Basic(
Long id,
String modelNm,
String modelCate,
String modelPath,
String modelVer,
String hyperVer,
String epochVer,
String dockerFileNm,
ZonedDateTime createCompleteDttm,
ZonedDateTime recentUseDttm,
Boolean deleted,
ZonedDateTime createdDttm,
Long createdUid,
ZonedDateTime updatedDttm,
Long updatedUid,
String modelCntnt) {
this.id = id;
this.modelNm = modelNm;
this.modelCate = modelCate;
this.modelPath = modelPath;
Long updatedUid) {
this.modelVer = modelVer;
this.hyperVer = hyperVer;
this.epochVer = epochVer;
this.dockerFileNm = dockerFileNm;
this.createCompleteDttm = createCompleteDttm;
this.recentUseDttm = recentUseDttm;
this.deleted = deleted;
this.createdDttm = createdDttm;
this.createdUid = createdUid;
this.updatedDttm = updatedDttm;
this.updatedUid = updatedUid;
this.modelCntnt = modelCntnt;
}
}
@Schema(name = "FinalModelDto", description = "최종 등록 모델")
@Schema(name = "ModelList", description = "모델관리 목록")
@Getter
public static class FinalModelDto {
private final Long modelUid;
private final String modelNm;
private final String modelCate;
private final Long modelVerUid;
private final String modelVer;
private final String usedState;
private final String modelState;
private final Double qualityProb;
private final String deployState;
private final String modelPath;
public FinalModelDto(
Long modelUid,
String modelNm,
String modelCate,
Long modelVerUid,
String modelVer,
String usedState,
String modelState,
Double qualityProb,
String deployState,
String modelPath) {
this.modelUid = modelUid;
this.modelNm = modelNm;
this.modelCate = modelCate;
this.modelVerUid = modelVerUid;
this.modelVer = modelVer;
this.usedState = usedState;
this.deployState = deployState;
this.modelState = modelState;
this.qualityProb = qualityProb;
this.modelPath = modelPath;
}
@Setter
@NoArgsConstructor
@AllArgsConstructor
public static class ModelList {
private Integer rowNum;
private String modelVer;
private String dockerFileNm;
private String modelType;
private String createCompleteDttm;
private String recentUseDttm; // TODO: 추론관리 테이블 나오면 분석진행중일 때 사용중으로 리턴하기
private BigDecimal f1Score;
private BigDecimal precision;
private BigDecimal recall;
private BigDecimal accuracy;
private BigDecimal iou;
private Boolean deleted;
}
@Schema(name = "ModelAddReq", description = "모델 버전 등록 req")
@Schema(name = "ModelAddReq", description = "모델 등록 req")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public static class AddReq {
private String modelNm;
private String modelCate;
private String modelPath;
@NotEmpty private String modelVer;
private String modelCntnt;
private String modelType;
private String dockerFileNm;
private String modelVer;
private String hyperVer;
private String epochVer;
}
@Schema(name = "searchReq", description = "등록이력보기 검색 요청")
@Schema(name = "searchReq", description = "검색 요청")
@Getter
@Setter
@NoArgsConstructor
@@ -128,29 +116,4 @@ public class ModelMngDto {
return PageRequest.of(page, size);
}
}
@Schema(name = "ModelRegHistory", description = "모델 등록 이력")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public static class ModelRegHistory {
private String modelNm;
private String modelCate;
private String modelVer;
private String strCreatedDttm;
private String usedState;
private String deployState;
private String strDeployDttm;
}
@Schema(name = "ModelDmlReturn", description = "모델 등록/수정/삭제 리턴")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public static class ModelDmlReturn {
private String execStatus;
private String message;
}
}

View File

@@ -1,25 +0,0 @@
package com.kamco.cd.kamcoback.model.service;
import com.kamco.cd.kamcoback.model.dto.ModelMgmtDto;
import com.kamco.cd.kamcoback.postgres.core.ModelMgmtCoreService;
import java.time.LocalDate;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.stereotype.Service;
@Service
@RequiredArgsConstructor
public class ModelMgmtService {
private final ModelMgmtCoreService modelMgmtCoreService;
public Page<ModelMgmtDto.ModelList> findModelMgmtList(
ModelMgmtDto.searchReq searchReq,
LocalDate startDate,
LocalDate endDate,
String modelType,
String searchVal) {
return modelMgmtCoreService.findModelMgmtList(
searchReq, startDate, endDate, modelType, searchVal);
}
}

View File

@@ -1,45 +1,25 @@
package com.kamco.cd.kamcoback.model.service;
import com.kamco.cd.kamcoback.model.dto.ModelMngDto;
import com.kamco.cd.kamcoback.model.dto.ModelVerDto;
import com.kamco.cd.kamcoback.postgres.core.ModelMngCoreService;
import java.time.LocalDate;
import java.util.List;
import java.util.Optional;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class ModelMngService {
private final ModelMngCoreService modelMngCoreService;
public List<ModelMngDto.Basic> findModelMngAll() {
return modelMngCoreService.findModelMngAll();
}
public Optional<ModelMngDto.FinalModelDto> getFinalModelInfo() {
return modelMngCoreService.getFinalModelInfo();
}
public ModelVerDto.Basic save(ModelMngDto.AddReq addReq) {
return modelMngCoreService.save(addReq);
}
public Long update(Long id, ModelMngDto.AddReq addReq) {
return modelMngCoreService.update(id, addReq);
}
public Long delete(Long id) {
return modelMngCoreService.delete(id);
}
public Page<ModelMngDto.ModelRegHistory> getRegHistoryList(
ModelMngDto.searchReq searchReq, LocalDate startDate, LocalDate endDate, String searchVal) {
return modelMngCoreService.getRegHistoryList(searchReq, startDate, endDate, searchVal);
public Page<ModelMngDto.ModelList> findModelMgmtList(
ModelMngDto.searchReq searchReq,
LocalDate startDate,
LocalDate endDate,
String modelType,
String searchVal) {
return modelMngCoreService.findModelMgmtList(
searchReq, startDate, endDate, modelType, searchVal);
}
}

View File

@@ -1,25 +0,0 @@
package com.kamco.cd.kamcoback.postgres.core;
import com.kamco.cd.kamcoback.model.dto.ModelMgmtDto;
import com.kamco.cd.kamcoback.postgres.repository.model.ModelMgmtRepository;
import java.time.LocalDate;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.stereotype.Service;
@Service
@RequiredArgsConstructor
public class ModelMgmtCoreService {
private final ModelMgmtRepository modelMgmtRepository;
public Page<ModelMgmtDto.ModelList> findModelMgmtList(
ModelMgmtDto.searchReq searchReq,
LocalDate startDate,
LocalDate endDate,
String modelType,
String searchVal) {
return modelMgmtRepository.findModelMgmtList(
searchReq, startDate, endDate, modelType, searchVal);
}
}

View File

@@ -1,15 +1,8 @@
package com.kamco.cd.kamcoback.postgres.core;
import com.kamco.cd.kamcoback.model.dto.ModelMngDto;
import com.kamco.cd.kamcoback.model.dto.ModelVerDto;
import com.kamco.cd.kamcoback.postgres.entity.ModelMngBakEntity;
import com.kamco.cd.kamcoback.postgres.entity.ModelVerEntity;
import com.kamco.cd.kamcoback.postgres.repository.model.ModelMngRepository;
import com.kamco.cd.kamcoback.postgres.repository.model.ModelVerRepository;
import jakarta.persistence.EntityNotFoundException;
import java.time.LocalDate;
import java.util.List;
import java.util.Optional;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.stereotype.Service;
@@ -19,72 +12,14 @@ import org.springframework.stereotype.Service;
public class ModelMngCoreService {
private final ModelMngRepository modelMngRepository;
private final ModelVerRepository modelVerRepository;
public List<ModelMngDto.Basic> findModelMngAll() {
return modelMngRepository.findModelMngAll().stream().map(ModelMngBakEntity::toDto).toList();
}
public Optional<ModelMngDto.FinalModelDto> getFinalModelInfo() {
return modelMngRepository.getFinalModelInfo();
}
public ModelVerDto.Basic save(ModelMngDto.AddReq addReq) {
ModelMngBakEntity modelMngBakEntity =
new ModelMngBakEntity(
addReq.getModelNm(),
addReq.getModelCate(),
addReq.getModelPath(),
1L,
1L,
addReq.getModelCntnt()); // TODO: 로그인 기능 붙이면 Uid 넣어야 함
ModelMngBakEntity saved = modelMngRepository.save(modelMngBakEntity);
ModelVerEntity modelVerEntity =
new ModelVerEntity(
saved.getId(),
addReq.getModelCate(),
addReq.getModelVer(),
"NONE",
"NONE",
0.0,
"NONE",
addReq.getModelPath(),
1L,
1L);
return modelVerRepository.save(modelVerEntity).toDto();
}
public Long update(Long id, ModelMngDto.AddReq addReq) {
// 조회
ModelVerEntity existData =
modelVerRepository
.findModelVerById(id)
.orElseThrow(EntityNotFoundException::new); // 데이터 없는 경우 exception
existData.update(addReq);
// TODO: 추후 수정 단계에서 도커파일 업로드하면 버전 업데이트 하는 로직 필요
return existData.getId();
}
public Long delete(Long id) {
// 조회
ModelVerEntity verEntity =
modelVerRepository
.findModelVerById(id)
.orElseThrow(() -> new EntityNotFoundException("버전 id 에 대한 정보를 찾을 수 없습니다. id : " + id));
// usedState가 USED 이거나 이미 삭제된 상태이면 삭제 불가
if (verEntity.getUsedState().equals("USED") || verEntity.isDeleted().equals(true)) {
throw new IllegalStateException("해당 모델이 사용중이라 삭제 불가"); // TODO: 추후 규칙 정의되면 수정 필요
}
// id 코드 deleted = true 업데이트
verEntity.deleted();
return verEntity.getId();
}
public Page<ModelMngDto.ModelRegHistory> getRegHistoryList(
ModelMngDto.searchReq searchReq, LocalDate startDate, LocalDate endDate, String searchVal) {
return modelMngRepository.getRegHistoryList(searchReq, startDate, endDate, searchVal);
public Page<ModelMngDto.ModelList> findModelMgmtList(
ModelMngDto.searchReq searchReq,
LocalDate startDate,
LocalDate endDate,
String modelType,
String searchVal) {
return modelMngRepository.findModelMgmtList(
searchReq, startDate, endDate, modelType, searchVal);
}
}

View File

@@ -1,6 +1,5 @@
package com.kamco.cd.kamcoback.postgres.entity;
import com.kamco.cd.kamcoback.model.dto.ModelMngDto;
import com.kamco.cd.kamcoback.postgres.CommonDateEntity;
import jakarta.persistence.*;
import jakarta.validation.constraints.Size;
@@ -63,17 +62,4 @@ public class ModelMngBakEntity extends CommonDateEntity {
this.createdUid = createdUid;
this.updatedUid = updatedUid;
}
public ModelMngDto.Basic toDto() {
return new ModelMngDto.Basic(
this.id,
this.modelNm,
this.modelCate,
this.modelPath,
super.getCreatedDate(),
this.createdUid,
super.getModifiedDate(),
this.updatedUid,
this.modelCntnt);
}
}

View File

@@ -10,7 +10,7 @@ import lombok.Setter;
@Setter
@Entity
@Table(name = "tb_model_mgmt")
public class ModelMgmtEntity extends CommonDateEntity {
public class ModelMngEntity extends CommonDateEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "model_uid")

View File

@@ -1,6 +1,6 @@
package com.kamco.cd.kamcoback.postgres.entity;
import com.kamco.cd.kamcoback.model.dto.ModelMngDto;
import com.kamco.cd.kamcoback.model.dto.ModelMngBakDto;
import com.kamco.cd.kamcoback.model.dto.ModelVerDto;
import com.kamco.cd.kamcoback.postgres.CommonDateEntity;
import jakarta.persistence.*;
@@ -161,7 +161,7 @@ public class ModelVerEntity extends CommonDateEntity {
this.updatedUid);
}
public void update(ModelMngDto.AddReq addReq) {
public void update(ModelMngBakDto.AddReq addReq) {
this.modelCate = addReq.getModelCate();
this.modelVer = addReq.getModelVer();
this.modelPath = addReq.getModelPath();

View File

@@ -1,7 +0,0 @@
package com.kamco.cd.kamcoback.postgres.repository.model;
import com.kamco.cd.kamcoback.postgres.entity.ModelMgmtEntity;
import org.springframework.data.jpa.repository.JpaRepository;
public interface ModelMgmtRepository
extends JpaRepository<ModelMgmtEntity, Integer>, ModelMgmtRepositoryCustom {}

View File

@@ -1,15 +0,0 @@
package com.kamco.cd.kamcoback.postgres.repository.model;
import com.kamco.cd.kamcoback.model.dto.ModelMgmtDto;
import java.time.LocalDate;
import org.springframework.data.domain.Page;
public interface ModelMgmtRepositoryCustom {
Page<ModelMgmtDto.ModelList> findModelMgmtList(
ModelMgmtDto.searchReq searchReq,
LocalDate startDate,
LocalDate endDate,
String modelType,
String searchVal);
}

View File

@@ -1,155 +0,0 @@
package com.kamco.cd.kamcoback.postgres.repository.model;
import static com.kamco.cd.kamcoback.postgres.entity.QModelMgmtEntity.modelMgmtEntity;
import static com.kamco.cd.kamcoback.postgres.entity.QModelResultMetricEntity.modelResultMetricEntity;
import com.kamco.cd.kamcoback.model.dto.ModelMgmtDto;
import com.kamco.cd.kamcoback.postgres.QuerydslOrderUtil;
import com.kamco.cd.kamcoback.postgres.entity.ModelMgmtEntity;
import com.querydsl.core.BooleanBuilder;
import com.querydsl.core.types.Expression;
import com.querydsl.core.types.Projections;
import com.querydsl.core.types.dsl.BooleanExpression;
import com.querydsl.core.types.dsl.Expressions;
import com.querydsl.core.types.dsl.NumberPath;
import com.querydsl.core.types.dsl.StringExpression;
import com.querydsl.jpa.impl.JPAQueryFactory;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.repository.support.QuerydslRepositorySupport;
public class ModelMgmtRepositoryImpl extends QuerydslRepositorySupport
implements ModelMgmtRepositoryCustom {
private final JPAQueryFactory queryFactory;
private final StringExpression NULL_STRING = Expressions.stringTemplate("cast(null as text)");
public ModelMgmtRepositoryImpl(JPAQueryFactory queryFactory) {
super(ModelMgmtEntity.class);
this.queryFactory = queryFactory;
}
@Override
public Page<ModelMgmtDto.ModelList> findModelMgmtList(
ModelMgmtDto.searchReq searchReq,
LocalDate startDate,
LocalDate endDate,
String modelType,
String searchVal) {
Pageable pageable = searchReq.toPageable();
Sort sort = pageable.getSort();
String property = "createCompleteDttm"; // 기본으로 생성완료일 기준
Map<String, Expression<?>> sortColumnMap =
Map.of(
"createCompleteDttm", modelMgmtEntity.createCompleteDttm,
"recentUseDttm", modelMgmtEntity.recentUseDttm);
if (sort.isSorted()) {
Sort.Order order = sort.iterator().next();
property = order.getProperty();
}
Expression<?> sortColumn = sortColumnMap.get(property);
List<ModelMgmtDto.ModelList> foundContent =
queryFactory
.select(
Projections.constructor(
ModelMgmtDto.ModelList.class,
Expressions.numberTemplate(
Integer.class, "row_number() over(order by {0} desc)", sortColumn),
Expressions.stringTemplate(
"concat({0}, {1}, {2}, {3}, {4})",
modelMgmtEntity.modelVer,
".",
modelMgmtEntity.hyperVer,
".",
modelMgmtEntity.epochVer)
.as("modelVer"),
modelMgmtEntity.dockerFileNm,
modelMgmtEntity.modelVer.as("modelType"),
Expressions.stringTemplate(
"to_char({0}, 'YYYY-MM-DD')", modelMgmtEntity.createCompleteDttm),
Expressions.stringTemplate(
"to_char({0}, 'YYYY-MM-DD')", modelMgmtEntity.recentUseDttm),
roundNumericToPercent(modelResultMetricEntity.f1Score),
roundNumericToPercent(modelResultMetricEntity.precision),
roundNumericToPercent(modelResultMetricEntity.recall),
roundNumericToPercent(modelResultMetricEntity.loss),
roundNumericToPercent(modelResultMetricEntity.iou),
modelMgmtEntity.deleted))
.from(modelMgmtEntity)
.innerJoin(modelResultMetricEntity)
.on(modelMgmtEntity.modelUid.longValue().eq(modelResultMetricEntity.modelUid))
.where(
eventEndedAtBetween(startDate, endDate, property),
searchModelVersion(modelType, searchVal))
.offset(pageable.getOffset())
.limit(pageable.getPageSize())
.orderBy(
QuerydslOrderUtil.getOrderSpecifiers(
pageable, ModelMgmtEntity.class, "modelMgmtEntity"))
.fetch();
Long countQuery =
queryFactory
.select(modelMgmtEntity.modelUid.count())
.from(modelMgmtEntity)
.where(
eventEndedAtBetween(startDate, endDate, property),
searchModelVersion(modelType, searchVal))
.fetchOne();
return new PageImpl<>(foundContent, pageable, countQuery);
}
private BooleanExpression eventEndedAtBetween(
LocalDate startDate, LocalDate endDate, String sortColumn) {
if (Objects.isNull(startDate) || Objects.isNull(endDate)) {
return null;
}
ZoneId zone = ZoneId.systemDefault();
LocalDateTime startDateTime = startDate.atStartOfDay();
LocalDateTime endDateTime = endDate.plusDays(1).atStartOfDay();
if (sortColumn.equals("createCompleteDttm")) {
return modelMgmtEntity
.createCompleteDttm
.goe(startDateTime.atZone(zone))
.and(modelMgmtEntity.createCompleteDttm.lt(endDateTime.atZone(zone)));
} else {
return modelMgmtEntity
.recentUseDttm
.goe(startDateTime.atZone(zone))
.and(modelMgmtEntity.recentUseDttm.lt(endDateTime.atZone(zone)));
}
}
private BooleanBuilder searchModelVersion(String modelType, String searchVal) {
BooleanBuilder builder = new BooleanBuilder();
if (Objects.nonNull(modelType)) {
builder.and(modelMgmtEntity.modelVer.eq(modelType));
}
if (Objects.nonNull(searchVal)) {
builder.and(modelMgmtEntity.dockerFileNm.likeIgnoreCase("%" + searchVal + "%"));
}
return builder;
}
private Expression<BigDecimal> roundNumericToPercent(NumberPath<Double> ratio) {
return Expressions.numberTemplate(BigDecimal.class, "function('round', {0} * 100, 2)", ratio);
}
}

View File

@@ -1,7 +1,7 @@
package com.kamco.cd.kamcoback.postgres.repository.model;
import com.kamco.cd.kamcoback.postgres.entity.ModelMngBakEntity;
import com.kamco.cd.kamcoback.postgres.entity.ModelMngEntity;
import org.springframework.data.jpa.repository.JpaRepository;
public interface ModelMngRepository
extends JpaRepository<ModelMngBakEntity, Long>, ModelMngRepositoryCustom {}
extends JpaRepository<ModelMngEntity, Integer>, ModelMngRepositoryCustom {}

View File

@@ -1,18 +1,15 @@
package com.kamco.cd.kamcoback.postgres.repository.model;
import com.kamco.cd.kamcoback.model.dto.ModelMngDto;
import com.kamco.cd.kamcoback.postgres.entity.ModelMngBakEntity;
import java.time.LocalDate;
import java.util.List;
import java.util.Optional;
import org.springframework.data.domain.Page;
public interface ModelMngRepositoryCustom {
List<ModelMngBakEntity> findModelMngAll();
Optional<ModelMngDto.FinalModelDto> getFinalModelInfo();
Page<ModelMngDto.ModelRegHistory> getRegHistoryList(
ModelMngDto.searchReq searchReq, LocalDate startDate, LocalDate endDate, String searchVal);
Page<ModelMngDto.ModelList> findModelMgmtList(
ModelMngDto.searchReq searchReq,
LocalDate startDate,
LocalDate endDate,
String modelType,
String searchVal);
}

View File

@@ -1,28 +1,30 @@
package com.kamco.cd.kamcoback.postgres.repository.model;
import static com.kamco.cd.kamcoback.postgres.entity.QModelDeployHstEntity.modelDeployHstEntity;
import static com.kamco.cd.kamcoback.postgres.entity.QModelMngBakEntity.modelMngBakEntity;
import static com.kamco.cd.kamcoback.postgres.entity.QModelVerEntity.modelVerEntity;
import static com.kamco.cd.kamcoback.postgres.entity.QModelMngEntity.modelMngEntity;
import static com.kamco.cd.kamcoback.postgres.entity.QModelResultMetricEntity.modelResultMetricEntity;
import com.kamco.cd.kamcoback.model.dto.ModelMngDto;
import com.kamco.cd.kamcoback.postgres.QuerydslOrderUtil;
import com.kamco.cd.kamcoback.postgres.entity.ModelMngBakEntity;
import com.kamco.cd.kamcoback.postgres.entity.ModelVerEntity;
import com.kamco.cd.kamcoback.postgres.entity.ModelMngEntity;
import com.querydsl.core.BooleanBuilder;
import com.querydsl.core.types.Expression;
import com.querydsl.core.types.Projections;
import com.querydsl.core.types.dsl.BooleanExpression;
import com.querydsl.core.types.dsl.Expressions;
import com.querydsl.core.types.dsl.NumberPath;
import com.querydsl.core.types.dsl.StringExpression;
import com.querydsl.jpa.impl.JPAQueryFactory;
import io.micrometer.common.util.StringUtils;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZonedDateTime;
import java.time.ZoneId;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.repository.support.QuerydslRepositorySupport;
public class ModelMngRepositoryImpl extends QuerydslRepositorySupport
@@ -32,113 +34,122 @@ public class ModelMngRepositoryImpl extends QuerydslRepositorySupport
private final StringExpression NULL_STRING = Expressions.stringTemplate("cast(null as text)");
public ModelMngRepositoryImpl(JPAQueryFactory queryFactory) {
super(ModelMngBakEntity.class);
super(ModelMngEntity.class);
this.queryFactory = queryFactory;
}
@Override
public List<ModelMngBakEntity> findModelMngAll() {
return queryFactory.selectFrom(modelMngBakEntity).orderBy(modelMngBakEntity.id.desc()).fetch();
}
@Override
public Optional<ModelMngDto.FinalModelDto> getFinalModelInfo() {
return queryFactory
.select(
Projections.constructor(
ModelMngDto.FinalModelDto.class,
modelMngBakEntity.id.as("modelUid"),
modelMngBakEntity.modelNm,
modelMngBakEntity.modelCate,
modelVerEntity.id.as("modelVerUid"),
modelVerEntity.modelVer,
Expressions.stringTemplate(
"fn_codenm_to_misc({0}, {1})", 51, modelVerEntity.usedState), // 사용여부 한글 명칭
Expressions.stringTemplate(
"fn_codenm_to_misc({0}, {1})", 51, modelVerEntity.modelState), // 모델상태 한글 명칭
modelVerEntity.qualityProb,
Expressions.stringTemplate(
"fn_codenm_to_misc({0}, {1})", 52, modelVerEntity.deployState), // 배포상태 한글 명칭
modelVerEntity.modelPath))
.from(modelMngBakEntity)
.innerJoin(modelVerEntity)
.on(modelMngBakEntity.id.eq(modelVerEntity.modelUid))
.where(modelVerEntity.usedState.eq("USED")) // USED 인 것 중에
.orderBy(modelVerEntity.modelVer.desc()) // Version 높은 것 기준
.stream()
.findFirst();
}
@Override
public Page<ModelMngDto.ModelRegHistory> getRegHistoryList(
ModelMngDto.searchReq searchReq, LocalDate startDate, LocalDate endDate, String searchVal) {
public Page<ModelMngDto.ModelList> findModelMgmtList(
ModelMngDto.searchReq searchReq,
LocalDate startDate,
LocalDate endDate,
String modelType,
String searchVal) {
Pageable pageable = searchReq.toPageable();
List<ModelMngDto.ModelRegHistory> foundContent =
Sort sort = pageable.getSort();
String property = "createCompleteDttm"; // 기본으로 생성완료일 기준
Map<String, Expression<?>> sortColumnMap =
Map.of(
"createCompleteDttm", modelMngEntity.createCompleteDttm,
"recentUseDttm", modelMngEntity.recentUseDttm);
if (sort.isSorted()) {
Sort.Order order = sort.iterator().next();
property = order.getProperty();
}
Expression<?> sortColumn = sortColumnMap.get(property);
List<ModelMngDto.ModelList> foundContent =
queryFactory
.select(
Projections.constructor(
ModelMngDto.ModelRegHistory.class,
modelMngBakEntity.modelNm,
modelMngBakEntity.modelCate,
modelVerEntity.modelVer,
ModelMngDto.ModelList.class,
Expressions.numberTemplate(
Integer.class, "row_number() over(order by {0} desc)", sortColumn),
Expressions.stringTemplate(
"to_char({0}, 'YYYY-MM-DD')", modelVerEntity.createdDate)
.as("createdDttm"),
"concat({0}, {1}, {2}, {3}, {4})",
modelMngEntity.modelVer,
".",
modelMngEntity.hyperVer,
".",
modelMngEntity.epochVer)
.as("modelVer"),
modelMngEntity.dockerFileNm,
modelMngEntity.modelVer.as("modelType"),
Expressions.stringTemplate(
"fn_codenm_to_misc({0}, {1})", 51, modelVerEntity.usedState), // 사용여부 한글 명칭
"to_char({0}, 'YYYY-MM-DD')", modelMngEntity.createCompleteDttm),
Expressions.stringTemplate(
"fn_codenm_to_misc({0}, {1})",
52, modelVerEntity.deployState), // 배포상태 한글 명칭
Expressions.stringTemplate(
"to_char({0}, 'YYYY-MM-DD')", modelDeployHstEntity.deployDttm)
.as("deployDttm")))
.from(modelMngBakEntity)
.innerJoin(modelVerEntity)
.on(modelMngBakEntity.id.eq(modelVerEntity.modelUid))
.leftJoin(modelDeployHstEntity)
.on(
modelVerEntity
.id
.eq(modelDeployHstEntity.modelVerUid)
.and(modelDeployHstEntity.serverId.eq(1L)) // 1건만 조회해야 하기에 1번 서버만 조회하기
)
.where(eventEndedAtBetween(startDate, endDate), searchModelVerLike(searchVal))
"to_char({0}, 'YYYY-MM-DD')", modelMngEntity.recentUseDttm),
roundNumericToPercent(modelResultMetricEntity.f1Score),
roundNumericToPercent(modelResultMetricEntity.precision),
roundNumericToPercent(modelResultMetricEntity.recall),
roundNumericToPercent(modelResultMetricEntity.loss),
roundNumericToPercent(modelResultMetricEntity.iou),
modelMngEntity.deleted))
.from(modelMngEntity)
.innerJoin(modelResultMetricEntity)
.on(modelMngEntity.modelUid.longValue().eq(modelResultMetricEntity.modelUid))
.where(
eventEndedAtBetween(startDate, endDate, property),
searchModelVersion(modelType, searchVal))
.offset(pageable.getOffset())
.limit(pageable.getPageSize())
.orderBy(
QuerydslOrderUtil.getOrderSpecifiers(
pageable, ModelVerEntity.class, "modelVerEntity"))
pageable, ModelMngEntity.class, "modelMngEntity"))
.fetch();
Long countQuery =
queryFactory
.select(modelVerEntity.id.count())
.from(modelMngBakEntity)
.innerJoin(modelVerEntity)
.on(modelMngBakEntity.id.eq(modelVerEntity.modelUid))
.where(eventEndedAtBetween(startDate, endDate), searchModelVerLike(searchVal))
.select(modelMngEntity.modelUid.count())
.from(modelMngEntity)
.where(
eventEndedAtBetween(startDate, endDate, property),
searchModelVersion(modelType, searchVal))
.fetchOne();
return new PageImpl<>(foundContent, pageable, countQuery);
}
private BooleanExpression eventEndedAtBetween(LocalDate startDate, LocalDate endDate) {
private BooleanExpression eventEndedAtBetween(
LocalDate startDate, LocalDate endDate, String sortColumn) {
if (Objects.isNull(startDate) || Objects.isNull(endDate)) {
return null;
}
ZoneId zone = ZoneId.systemDefault();
LocalDateTime startDateTime = startDate.atStartOfDay();
LocalDateTime endDateTime = endDate.plusDays(1).atStartOfDay();
return modelMngBakEntity
.createdDate
.goe(ZonedDateTime.from(startDateTime))
.and(modelMngBakEntity.modifiedDate.lt(ZonedDateTime.from(endDateTime)));
if (sortColumn.equals("createCompleteDttm")) {
return modelMngEntity
.createCompleteDttm
.goe(startDateTime.atZone(zone))
.and(modelMngEntity.createCompleteDttm.lt(endDateTime.atZone(zone)));
} else {
return modelMngEntity
.recentUseDttm
.goe(startDateTime.atZone(zone))
.and(modelMngEntity.recentUseDttm.lt(endDateTime.atZone(zone)));
}
}
private BooleanExpression searchModelVerLike(String searchVal) {
if (StringUtils.isBlank(searchVal)) {
return null;
private BooleanBuilder searchModelVersion(String modelType, String searchVal) {
BooleanBuilder builder = new BooleanBuilder();
if (Objects.nonNull(modelType)) {
builder.and(modelMngEntity.modelVer.eq(modelType));
}
return modelVerEntity.modelVer.contains(searchVal);
if (Objects.nonNull(searchVal)) {
builder.and(modelMngEntity.dockerFileNm.likeIgnoreCase("%" + searchVal + "%"));
}
return builder;
}
private Expression<BigDecimal> roundNumericToPercent(NumberPath<Double> ratio) {
return Expressions.numberTemplate(BigDecimal.class, "function('round', {0} * 100, 2)", ratio);
}
}