Merge pull request 'feat/infer_dev_260107' (#243) from feat/infer_dev_260107 into develop

Reviewed-on: https://kamco.gitea.gs.dabeeo.com/dabeeo/kamco-dabeeo-backoffice/pulls/243
This commit is contained in:
2026-01-15 15:12:54 +09:00
7 changed files with 116 additions and 66 deletions

View File

@@ -28,6 +28,7 @@ import com.querydsl.core.types.dsl.Expressions;
import com.querydsl.core.types.dsl.NumberExpression; import com.querydsl.core.types.dsl.NumberExpression;
import com.querydsl.jpa.impl.JPAQueryFactory; import com.querydsl.jpa.impl.JPAQueryFactory;
import jakarta.persistence.EntityNotFoundException; import jakarta.persistence.EntityNotFoundException;
import java.time.OffsetDateTime;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.Optional; import java.util.Optional;
@@ -118,9 +119,9 @@ public class MapSheetLearnRepositoryImpl implements MapSheetLearnRepositoryCusto
.groupBy(systemMetricEntity.serverName) .groupBy(systemMetricEntity.serverName)
.fetch(); .fetch();
List<Integer> latestGpuIds = List<OffsetDateTime> latestGpuIds =
queryFactory queryFactory
.select(gpuMetricEntity.id1.max()) .select(gpuMetricEntity.timestamp.max())
.from(gpuMetricEntity) .from(gpuMetricEntity)
.groupBy(gpuMetricEntity.serverName) .groupBy(gpuMetricEntity.serverName)
.fetch(); .fetch();
@@ -135,12 +136,15 @@ public class MapSheetLearnRepositoryImpl implements MapSheetLearnRepositoryCusto
systemMetricEntity.cpuSystem, systemMetricEntity.cpuSystem,
systemMetricEntity.memused, systemMetricEntity.memused,
systemMetricEntity.kbmemused, systemMetricEntity.kbmemused,
gpuMetricEntity.gpuUtil)) gpuMetricEntity.gpuUtil
// gpuMetricEntity.gpuMemUsed,
// gpuMetricEntity.gpuMemTotal
))
.from(systemMetricEntity) .from(systemMetricEntity)
.leftJoin(gpuMetricEntity) .leftJoin(gpuMetricEntity)
.on( .on(
gpuMetricEntity gpuMetricEntity
.id1 .timestamp
.in(latestGpuIds) .in(latestGpuIds)
.and(gpuMetricEntity.serverName.eq(systemMetricEntity.serverName))) .and(gpuMetricEntity.serverName.eq(systemMetricEntity.serverName)))
.where(systemMetricEntity.id1.in(latestIds)) // In 절 사용 .where(systemMetricEntity.id1.in(latestIds)) // In 절 사용

View File

@@ -772,34 +772,49 @@ public class TrainingDataLabelRepositoryImpl extends QuerydslRepositorySupport
public TrainingDataLabelDto.CogImageResponse getCogImageUrl( public TrainingDataLabelDto.CogImageResponse getCogImageUrl(
String mapSheetNum, Integer beforeYear, Integer afterYear) { String mapSheetNum, Integer beforeYear, Integer afterYear) {
try { try {
// beforeYear COG URL 조회 // 최소 하나의 년도는 제공되어야 함
String beforeCogUrl = if (beforeYear == null && afterYear == null) {
queryFactory throw new IllegalArgumentException(
.select( "At least one year parameter (beforeYear or afterYear) must be provided");
Expressions.stringTemplate( }
"{0} || {1}", imageryEntity.cogMiddlePath, imageryEntity.cogFilename))
.from(imageryEntity)
.where(imageryEntity.scene5k.eq(mapSheetNum), imageryEntity.year.eq(beforeYear))
.fetchFirst();
// afterYear COG URL 조회 String beforeCogUrl = null;
String afterCogUrl = String afterCogUrl = null;
queryFactory
.select(
Expressions.stringTemplate(
"{0} || {1}", imageryEntity.cogMiddlePath, imageryEntity.cogFilename))
.from(imageryEntity)
.where(imageryEntity.scene5k.eq(mapSheetNum), imageryEntity.year.eq(afterYear))
.fetchFirst();
if (beforeCogUrl == null && afterCogUrl == null) { // beforeYear가 제공된 경우 COG URL 조회
if (beforeYear != null) {
beforeCogUrl =
queryFactory
.select(
Expressions.stringTemplate(
"{0} || {1}", imageryEntity.cogMiddlePath, imageryEntity.cogFilename))
.from(imageryEntity)
.where(imageryEntity.scene5k.eq(mapSheetNum), imageryEntity.year.eq(beforeYear))
.fetchFirst();
}
// afterYear가 제공된 경우 COG URL 조회
if (afterYear != null) {
afterCogUrl =
queryFactory
.select(
Expressions.stringTemplate(
"{0} || {1}", imageryEntity.cogMiddlePath, imageryEntity.cogFilename))
.from(imageryEntity)
.where(imageryEntity.scene5k.eq(mapSheetNum), imageryEntity.year.eq(afterYear))
.fetchFirst();
}
// 제공된 년도에 대해 하나도 찾지 못한 경우에만 예외 발생
if ((beforeYear != null && beforeCogUrl == null)
&& (afterYear != null && afterCogUrl == null)) {
throw new RuntimeException( throw new RuntimeException(
"COG images not found for mapSheetNum: " "COG images not found for mapSheetNum: "
+ mapSheetNum + mapSheetNum
+ ", years: " + ", years: "
+ beforeYear + (beforeYear != null ? beforeYear : "")
+ ", " + (beforeYear != null && afterYear != null ? ", " : "")
+ afterYear); + (afterYear != null ? afterYear : ""));
} }
return TrainingDataLabelDto.CogImageResponse.builder() return TrainingDataLabelDto.CogImageResponse.builder()
@@ -817,9 +832,9 @@ public class TrainingDataLabelRepositoryImpl extends QuerydslRepositorySupport
"Failed to get COG image URLs for mapSheetNum: " "Failed to get COG image URLs for mapSheetNum: "
+ mapSheetNum + mapSheetNum
+ ", years: " + ", years: "
+ beforeYear + (beforeYear != null ? beforeYear : "")
+ ", " + (beforeYear != null && afterYear != null ? ", " : "")
+ afterYear, + (afterYear != null ? afterYear : ""),
e); e);
} }
} }

View File

@@ -776,34 +776,49 @@ public class TrainingDataReviewRepositoryImpl extends QuerydslRepositorySupport
public TrainingDataReviewDto.CogImageResponse getCogImageUrl( public TrainingDataReviewDto.CogImageResponse getCogImageUrl(
String mapSheetNum, Integer beforeYear, Integer afterYear) { String mapSheetNum, Integer beforeYear, Integer afterYear) {
try { try {
// beforeYear COG URL 조회 // 최소 하나의 년도는 제공되어야 함
String beforeCogUrl = if (beforeYear == null && afterYear == null) {
queryFactory throw new IllegalArgumentException(
.select( "At least one year parameter (beforeYear or afterYear) must be provided");
Expressions.stringTemplate( }
"{0} || {1}", imageryEntity.cogMiddlePath, imageryEntity.cogFilename))
.from(imageryEntity)
.where(imageryEntity.scene5k.eq(mapSheetNum), imageryEntity.year.eq(beforeYear))
.fetchFirst();
// afterYear COG URL 조회 String beforeCogUrl = null;
String afterCogUrl = String afterCogUrl = null;
queryFactory
.select(
Expressions.stringTemplate(
"{0} || {1}", imageryEntity.cogMiddlePath, imageryEntity.cogFilename))
.from(imageryEntity)
.where(imageryEntity.scene5k.eq(mapSheetNum), imageryEntity.year.eq(afterYear))
.fetchFirst();
if (beforeCogUrl == null && afterCogUrl == null) { // beforeYear가 제공된 경우 COG URL 조회
if (beforeYear != null) {
beforeCogUrl =
queryFactory
.select(
Expressions.stringTemplate(
"{0} || {1}", imageryEntity.cogMiddlePath, imageryEntity.cogFilename))
.from(imageryEntity)
.where(imageryEntity.scene5k.eq(mapSheetNum), imageryEntity.year.eq(beforeYear))
.fetchFirst();
}
// afterYear가 제공된 경우 COG URL 조회
if (afterYear != null) {
afterCogUrl =
queryFactory
.select(
Expressions.stringTemplate(
"{0} || {1}", imageryEntity.cogMiddlePath, imageryEntity.cogFilename))
.from(imageryEntity)
.where(imageryEntity.scene5k.eq(mapSheetNum), imageryEntity.year.eq(afterYear))
.fetchFirst();
}
// 제공된 년도에 대해 하나도 찾지 못한 경우에만 예외 발생
if ((beforeYear != null && beforeCogUrl == null)
&& (afterYear != null && afterCogUrl == null)) {
throw new RuntimeException( throw new RuntimeException(
"COG images not found for mapSheetNum: " "COG images not found for mapSheetNum: "
+ mapSheetNum + mapSheetNum
+ ", years: " + ", years: "
+ beforeYear + (beforeYear != null ? beforeYear : "")
+ ", " + (beforeYear != null && afterYear != null ? ", " : "")
+ afterYear); + (afterYear != null ? afterYear : ""));
} }
return TrainingDataReviewDto.CogImageResponse.builder() return TrainingDataReviewDto.CogImageResponse.builder()
@@ -821,9 +836,9 @@ public class TrainingDataReviewRepositoryImpl extends QuerydslRepositorySupport
"Failed to get COG image URLs for mapSheetNum: " "Failed to get COG image URLs for mapSheetNum: "
+ mapSheetNum + mapSheetNum
+ ", years: " + ", years: "
+ beforeYear + (beforeYear != null ? beforeYear : "")
+ ", " + (beforeYear != null && afterYear != null ? ", " : "")
+ afterYear, + (afterYear != null ? afterYear : ""),
e); e);
} }
} }

View File

@@ -465,7 +465,9 @@ public class TrainingDataLabelApiController {
return ApiResponseDto.okObject(trainingDataLabelService.saveNewPolygon(request)); return ApiResponseDto.okObject(trainingDataLabelService.saveNewPolygon(request));
} }
@Operation(summary = "COG 이미지 URL 조회", description = "변화 전/후 COG 이미지 URL을 함께 조회합니다") @Operation(
summary = "COG 이미지 URL 조회",
description = "변화 전/후 COG 이미지 URL을 조회합니다. beforeYear와 afterYear 중 최소 하나는 필수입니다.")
@ApiResponses( @ApiResponses(
value = { value = {
@ApiResponse( @ApiResponse(
@@ -476,6 +478,10 @@ public class TrainingDataLabelApiController {
mediaType = "application/json", mediaType = "application/json",
schema = schema =
@Schema(implementation = TrainingDataLabelDto.CogImageResponse.class))), @Schema(implementation = TrainingDataLabelDto.CogImageResponse.class))),
@ApiResponse(
responseCode = "400",
description = "년도 파라미터가 하나도 제공되지 않음",
content = @Content),
@ApiResponse(responseCode = "404", description = "이미지를 찾을 수 없음", content = @Content), @ApiResponse(responseCode = "404", description = "이미지를 찾을 수 없음", content = @Content),
@ApiResponse(responseCode = "500", description = "서버 오류", content = @Content) @ApiResponse(responseCode = "500", description = "서버 오류", content = @Content)
}) })
@@ -483,9 +489,11 @@ public class TrainingDataLabelApiController {
public ApiResponseDto<TrainingDataLabelDto.CogImageResponse> getCogImageUrl( public ApiResponseDto<TrainingDataLabelDto.CogImageResponse> getCogImageUrl(
@Parameter(description = "도엽번호", required = true, example = "35905086") @RequestParam @Parameter(description = "도엽번호", required = true, example = "35905086") @RequestParam
String mapSheetNum, String mapSheetNum,
@Parameter(description = "변화 전 년도", required = true, example = "2023") @RequestParam @Parameter(description = "변화 전 년도", required = false, example = "2023")
@RequestParam(required = false)
Integer beforeYear, Integer beforeYear,
@Parameter(description = "변화 후 년도", required = true, example = "2024") @RequestParam @Parameter(description = "변화 후 년도", required = false, example = "2024")
@RequestParam(required = false)
Integer afterYear) { Integer afterYear) {
return ApiResponseDto.ok( return ApiResponseDto.ok(
trainingDataLabelService.getCogImageUrl(mapSheetNum, beforeYear, afterYear)); trainingDataLabelService.getCogImageUrl(mapSheetNum, beforeYear, afterYear));

View File

@@ -465,7 +465,9 @@ public class TrainingDataReviewApiController {
return ApiResponseDto.okObject(trainingDataReviewService.saveNewPolygon(request)); return ApiResponseDto.okObject(trainingDataReviewService.saveNewPolygon(request));
} }
@Operation(summary = "COG 이미지 URL 조회", description = "변화 전/후 COG 이미지 URL을 함께 조회합니다") @Operation(
summary = "COG 이미지 URL 조회",
description = "변화 전/후 COG 이미지 URL을 조회합니다. beforeYear와 afterYear 중 최소 하나는 필수입니다.")
@ApiResponses( @ApiResponses(
value = { value = {
@ApiResponse( @ApiResponse(
@@ -476,6 +478,10 @@ public class TrainingDataReviewApiController {
mediaType = "application/json", mediaType = "application/json",
schema = schema =
@Schema(implementation = TrainingDataReviewDto.CogImageResponse.class))), @Schema(implementation = TrainingDataReviewDto.CogImageResponse.class))),
@ApiResponse(
responseCode = "400",
description = "년도 파라미터가 하나도 제공되지 않음",
content = @Content),
@ApiResponse(responseCode = "404", description = "이미지를 찾을 수 없음", content = @Content), @ApiResponse(responseCode = "404", description = "이미지를 찾을 수 없음", content = @Content),
@ApiResponse(responseCode = "500", description = "서버 오류", content = @Content) @ApiResponse(responseCode = "500", description = "서버 오류", content = @Content)
}) })
@@ -483,9 +489,11 @@ public class TrainingDataReviewApiController {
public ApiResponseDto<TrainingDataReviewDto.CogImageResponse> getCogImageUrl( public ApiResponseDto<TrainingDataReviewDto.CogImageResponse> getCogImageUrl(
@Parameter(description = "도엽번호", required = true, example = "35905086") @RequestParam @Parameter(description = "도엽번호", required = true, example = "35905086") @RequestParam
String mapSheetNum, String mapSheetNum,
@Parameter(description = "변화 전 년도", required = true, example = "2023") @RequestParam @Parameter(description = "변화 전 년도", required = false, example = "2023")
@RequestParam(required = false)
Integer beforeYear, Integer beforeYear,
@Parameter(description = "변화 후 년도", required = true, example = "2024") @RequestParam @Parameter(description = "변화 후 년도", required = false, example = "2024")
@RequestParam(required = false)
Integer afterYear) { Integer afterYear) {
return ApiResponseDto.ok( return ApiResponseDto.ok(
trainingDataReviewService.getCogImageUrl(mapSheetNum, beforeYear, afterYear)); trainingDataReviewService.getCogImageUrl(mapSheetNum, beforeYear, afterYear));

View File

@@ -106,9 +106,9 @@ public class TrainingDataLabelService {
* COG 이미지 URL 조회 (변화 전/후) * COG 이미지 URL 조회 (변화 전/후)
* *
* @param mapSheetNum 도엽번호 * @param mapSheetNum 도엽번호
* @param beforeYear 변화 전 년도 * @param beforeYear 변화 전 년도 (선택적)
* @param afterYear 변화 후 년도 * @param afterYear 변화 후 년도 (선택적)
* @return 변화 전/후 COG 이미지 URL * @return 변화 전/후 COG 이미지 URL (최소 하나의 년도는 제공되어야 함)
*/ */
public TrainingDataLabelDto.CogImageResponse getCogImageUrl( public TrainingDataLabelDto.CogImageResponse getCogImageUrl(
String mapSheetNum, Integer beforeYear, Integer afterYear) { String mapSheetNum, Integer beforeYear, Integer afterYear) {

View File

@@ -108,9 +108,9 @@ public class TrainingDataReviewService {
* COG 이미지 URL 조회 (변화 전/후) * COG 이미지 URL 조회 (변화 전/후)
* *
* @param mapSheetNum 도엽번호 * @param mapSheetNum 도엽번호
* @param beforeYear 변화 전 년도 * @param beforeYear 변화 전 년도 (선택적)
* @param afterYear 변화 후 년도 * @param afterYear 변화 후 년도 (선택적)
* @return 변화 전/후 COG 이미지 URL * @return 변화 전/후 COG 이미지 URL (최소 하나의 년도는 제공되어야 함)
*/ */
public TrainingDataReviewDto.CogImageResponse getCogImageUrl( public TrainingDataReviewDto.CogImageResponse getCogImageUrl(
String mapSheetNum, Integer beforeYear, Integer afterYear) { String mapSheetNum, Integer beforeYear, Integer afterYear) {