102 Commits

Author SHA1 Message Date
d240e4349c 영상관리 스케줄러 true로 변경, 영상삭제 시 year도 업데이트 되게 추가 2026-05-18 15:29:44 +09:00
4801ae8b7d 라벨링 툴 영문명칭 수정 추가 2026-05-15 16:41:20 +09:00
8c33439bcd 작업현황 > 작업자목록 RoleType 영문명 추가 2026-05-15 11:13:25 +09:00
ec4fb46cea 적합여부 상태명 영문 추가 2026-05-15 10:57:06 +09:00
63b3b3c09d 변화지도 년도 목록 id desc 로 수정 2026-05-15 09:58:46 +09:00
cfd5963a15 추론 삭제여부 로직 추가 2026-05-15 09:55:31 +09:00
9116bf7b89 선택 모델목록 생성완료일 desc 로 수정 2026-05-12 16:58:29 +09:00
2d7413c8e1 영문버전 변환 누락된 부분 추가 2026-05-12 11:16:19 +09:00
df7a67ed41 추론 목록,class-count, geom-list 에 한글로 나오는 코드 영문버전 로직 추가 2026-05-12 10:49:39 +09:00
176d27c229 로그인 시, 접속 IP 대역 확인 2026-05-12 09:50:26 +09:00
8c70ec345c 추론 상세 geom-list 오래 걸리는 현상 수정 2026-05-11 17:52:37 +09:00
8fd92f5691 lang check 로직 수정 2026-05-11 16:24:11 +09:00
b69d09176e lang check 2026-05-11 16:14:25 +09:00
04af23b814 영문 Enum 항목값 내릴 수 있도록 수정 Accept-Language: en 으로 추가하여 호출해야 함. 2026-05-11 15:58:03 +09:00
a1fc62b3eb 메뉴 목록, 영상관리 목록 API 영문 헤더 추가, 추론 삭제기능 추가 2026-05-11 14:15:45 +09:00
ed3c901143 메뉴 API 영문명 로직 추가 2026-05-11 10:36:45 +09:00
b58b859470 주석 추가 2026-05-11 10:08:18 +09:00
b78fda151a 영상관리 삭제 API 추가, shp 생성 기존로직으로 수정 2026-05-11 10:02:48 +09:00
435a48afb9 test 2026-05-07 12:53:01 +09:00
af0866ff5b Merge branch 'develop' of https://kamco.git.gs.dabeeo.com/MVPTeam/kamco-cd-api into develop 2026-05-06 17:46:56 +09:00
d0132ac697 local 에서도 redis 포트 수정 2026-05-06 17:46:32 +09:00
dean
4272558f32 add log 2026-04-22 09:41:23 +09:00
d83fb01cbe 학습데이터 목록 쿼리 수정 2026-04-20 16:37:22 +09:00
0425a6486d Merge pull request 'shp 테스트' (#160) from feat/infer_dev_260211 into develop
Reviewed-on: #160
2026-03-25 14:07:21 +09:00
241c7222d1 shp 테스트 2026-03-25 14:07:06 +09:00
71e4ab14bd Merge pull request 'shp 테스트' (#159) from feat/infer_dev_260211 into develop
Reviewed-on: #159
2026-03-25 14:01:26 +09:00
52da4dafc3 shp 테스트 2026-03-25 14:01:02 +09:00
24d615174d Merge pull request '토근 정보 변경' (#158) from feat/infer_dev_260211 into develop
Reviewed-on: #158
2026-03-19 13:48:56 +09:00
12b0f0867d 토근 정보 변경 2026-03-19 13:48:14 +09:00
283d906da6 Merge pull request '토큰시간 변경' (#157) from feat/infer_dev_260211 into develop
Reviewed-on: #157
2026-03-19 10:31:05 +09:00
360b451c38 토큰시간 변경 2026-03-19 10:30:42 +09:00
80e281cb99 Merge pull request '개발서버 토큰 시간 변경' (#156) from feat/infer_dev_260211 into develop
Reviewed-on: #156
2026-03-18 14:02:29 +09:00
b07bc38ee8 개발서버 토큰 시간 변경 2026-03-18 14:01:56 +09:00
e4c1c76b2b Merge pull request '재할당 transactional 추가' (#155) from feat/infer_dev_260211 into develop
Reviewed-on: #155
2026-03-17 17:47:43 +09:00
01b64eeca7 재할당 transactional 추가 2026-03-17 17:47:15 +09:00
516f949a37 Merge pull request 'spotless 적용' (#154) from feat/infer_dev_260211 into develop
Reviewed-on: #154
2026-03-17 14:00:22 +09:00
b6ed3b57ef spotless 적용 2026-03-17 14:00:04 +09:00
9eebf23774 Merge pull request 'dev 토큰 유효시간 변경, 인증 Log 추가한거 삭제' (#153) from feat/infer_dev_260211 into develop
Reviewed-on: #153
2026-03-17 13:55:53 +09:00
4f742edf8b dev 토큰 유효시간 변경, 인증 Log 추가한거 삭제 2026-03-17 13:55:30 +09:00
0aa415cf3a Merge pull request '토큰 생성 로그 추가' (#152) from feat/infer_dev_260211 into develop
Reviewed-on: #152
2026-03-17 13:33:34 +09:00
884b635585 토큰 생성 로그 추가 2026-03-17 13:33:17 +09:00
6861f6b8b6 Merge pull request '메뉴 권한 Log 추가' (#151) from feat/infer_dev_260211 into develop
Reviewed-on: #151
2026-03-17 13:22:06 +09:00
a97af0d4dd 메뉴 권한 Log 추가 2026-03-17 13:21:46 +09:00
9297d19e24 Merge pull request 'refresh 토큰 인증 로그 추가' (#150) from feat/infer_dev_260211 into develop
Reviewed-on: #150
2026-03-17 12:46:12 +09:00
65c38b3083 refresh 토큰 인증 로그 추가 2026-03-17 12:45:56 +09:00
24dca652f0 Merge pull request 'refresh 토큰 재발급 로그 추가' (#149) from feat/infer_dev_260211 into develop
Reviewed-on: #149
2026-03-17 12:26:31 +09:00
193cd449a8 refresh 토큰 재발급 로그 추가 2026-03-17 12:23:45 +09:00
dean
0efde4e5bb access-token 2026-03-17 07:49:13 +09:00
dean
548d82da12 access-token 2026-03-17 07:20:08 +09:00
536ff8fc65 추론 종료 async 수정 2026-03-13 15:52:20 +09:00
1dc1ce741e 추론 종료 비동기 호출 2026-03-13 15:38:11 +09:00
d21ed61666 hello 2026-03-12 08:22:39 +09:00
c9a1007c21 hello 2026-03-12 08:08:59 +09:00
413afb0b7c shp파일 바꾸는로직정리 2026-03-12 08:08:11 +09:00
e69eccc82b shp파일 바꾸는로직정리 2026-03-12 07:57:14 +09:00
828a4c5dca shp파일 바꾸는로직정리 2026-03-12 07:31:54 +09:00
5d417d85ff Merge pull request '국유인연동 알럿 문구 변경' (#148) from feat/infer_dev_260211 into develop
Reviewed-on: #148
2026-03-09 10:52:25 +09:00
614d6da695 국유인연동 알럿 문구 변경 2026-03-09 10:51:57 +09:00
8d45e91982 Merge pull request '국유인연동 : 라벨링 진행중 회차 uuid 추가' (#147) from feat/infer_dev_260211 into develop
Reviewed-on: #147
2026-03-09 10:10:52 +09:00
e1febf5863 국유인연동 : 라벨링 진행중 회차 uuid 추가 2026-03-09 10:10:08 +09:00
97fb659f15 enum @notBlank제거 2026-03-07 01:43:07 +09:00
ebb48c3f57 enum @notBlank제거 2026-03-07 01:41:34 +09:00
a35b4b8f59 enum @notBlank제거 2026-03-07 01:24:55 +09:00
0a53e186dc enum @notBlank제거 2026-03-07 01:21:30 +09:00
f3c7c5e8e8 enum @notBlank제거 2026-03-07 01:13:28 +09:00
c505e9b740 @enumvalid제거 2026-03-07 01:11:56 +09:00
8c2f2eff1b enum @notBlank제거 2026-03-07 01:08:21 +09:00
ade8bfa76a Merge pull request 'feat/dean/polishing_2' (#146) from feat/dean/polishing_2 into develop
Reviewed-on: #146
2026-03-07 01:02:58 +09:00
3752b83292 인퍼런스실행 정리 2026-03-07 00:56:51 +09:00
9f31f661fc 원복 2026-03-06 22:12:54 +09:00
b477928261 Merge pull request 'testing 테이블 조회 조건 추가' (#145) from feat/infer_dev_260211 into develop
Reviewed-on: #145
2026-03-06 19:51:41 +09:00
f4f75f353c testing 테이블 조회 조건 추가 2026-03-06 19:51:08 +09:00
f977e4be7c Merge pull request 'shp 파일 생성중 오류 발생시 중지되게 수정' (#144) from feat/infer_dev_260211 into develop
Reviewed-on: #144
2026-03-06 19:29:27 +09:00
573da5b53a shp 파일 생성중 오류 발생시 중지되게 수정 2026-03-06 19:21:08 +09:00
bd72852556 Merge pull request '추론실행 오류 수정' (#143) from feat/infer_dev_260211 into develop
Reviewed-on: #143
2026-03-06 18:54:16 +09:00
e4b904606f 추론실행 오류 수정 2026-03-06 18:53:38 +09:00
0d14dafecc Merge pull request 'feat/infer_dev_260211' (#142) from feat/infer_dev_260211 into develop
Reviewed-on: #142
2026-03-06 18:05:00 +09:00
37f534abff spotless 2026-03-06 18:04:38 +09:00
3521a5fd3d trim, foundUnique.add 추가 2026-03-06 18:04:23 +09:00
cbae052338 Merge branch 'develop' into feat/infer_dev_260211
# Conflicts:
#	src/main/java/com/kamco/cd/kamcoback/common/inference/utils/GeoJsonValidator.java
2026-03-06 18:02:44 +09:00
b2c9c36d4c trim, log 추가 2026-03-06 17:58:23 +09:00
114088469e 추론실행 테스트 2026-03-06 17:53:01 +09:00
7d6dca8b24 Merge pull request '추론실행 테스트' (#138) from feat/infer_dev_260211 into develop
Reviewed-on: #138
2026-03-06 17:42:58 +09:00
2e7ad26528 추론실행 테스트 2026-03-06 17:42:40 +09:00
0353e172ed Merge pull request 'feat/infer_dev_260211' (#137) from feat/infer_dev_260211 into develop
Reviewed-on: #137
2026-03-06 17:15:20 +09:00
1d5b1343a9 file del 추가 2026-03-06 17:14:44 +09:00
65f9026922 학습서버배포정보 수정 2026-03-06 16:02:18 +09:00
9b79f31d7b 줄맞춤 2026-03-06 16:01:28 +09:00
de45bf47c5 review-to-geojson 로직 수정, 미사용 주석 추가 2026-03-06 13:16:08 +09:00
a413de4b93 학습데이터 상세 > 폴리곤 수 로직 수정 2026-03-06 13:12:33 +09:00
815675f112 Merge pull request 'inference_results 결과 저장 중복방지' (#136) from feat/infer_dev_260211 into develop
Reviewed-on: #136
2026-03-06 12:43:56 +09:00
b9f7e36175 inference_results 결과 저장 중복방지 2026-03-06 12:43:36 +09:00
855aca6e5a Merge pull request 'feat/infer_dev_260211' (#135) from feat/infer_dev_260211 into develop
Reviewed-on: #135
2026-03-06 12:25:04 +09:00
206dba6ff9 추론결과 저장 result 조회 조건 수정 2026-03-06 12:24:52 +09:00
5db9127f0c 추론결과 저장 result 조회 저장 테스트 2026-03-06 12:23:16 +09:00
132bad8c33 Merge pull request '추론결과 저장 result 조회 저장 테스트' (#134) from feat/infer_dev_260211 into develop
Reviewed-on: #134
2026-03-06 12:00:01 +09:00
6dde4cd891 추론결과 저장 result 조회 저장 테스트 2026-03-06 11:59:38 +09:00
ac248c2f30 Merge pull request '추론결과 저장 result 조회 조건 변경' (#133) from feat/infer_dev_260211 into develop
Reviewed-on: #133
2026-03-06 11:10:04 +09:00
15d082af0e 추론결과 저장 result 조회 조건 변경 2026-03-06 11:09:34 +09:00
3be536424a Merge pull request 'feat/infer_dev_260211' (#132) from feat/infer_dev_260211 into develop
Reviewed-on: #132
2026-03-05 11:33:24 +09:00
a3b2fd0c73 status 체크 후 pnu cnt update 로 수정 2026-03-05 11:32:43 +09:00
9b504396bc status 체크 후 pnu cnt update 추가 2026-03-05 09:39:00 +09:00
85 changed files with 1339 additions and 537 deletions

View File

@@ -37,7 +37,6 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter {
// JWT 토큰을 검증하고, 인증된 사용자로 SecurityContext에 등록 // JWT 토큰을 검증하고, 인증된 사용자로 SecurityContext에 등록
if (token != null && jwtTokenProvider.isValidToken(token)) { if (token != null && jwtTokenProvider.isValidToken(token)) {
String username = jwtTokenProvider.getSubject(token); String username = jwtTokenProvider.getSubject(token);
UserDetails userDetails = userDetailsService.loadUserByUsername(username); UserDetails userDetails = userDetailsService.loadUserByUsername(username);
UsernamePasswordAuthenticationToken authentication = UsernamePasswordAuthenticationToken authentication =
new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities()); new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());

View File

@@ -8,11 +8,13 @@ import jakarta.annotation.PostConstruct;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.Date; import java.util.Date;
import javax.crypto.SecretKey; import javax.crypto.SecretKey;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
/** 토큰 생성 */ /** 토큰 생성 */
@Component @Component
@Log4j2
public class JwtTokenProvider { public class JwtTokenProvider {
@Value("${jwt.secret}") @Value("${jwt.secret}")
@@ -34,11 +36,13 @@ public class JwtTokenProvider {
// Access Token 생성 // Access Token 생성
public String createAccessToken(String subject) { public String createAccessToken(String subject) {
log.info("TOKEN VALIDITY = {}", accessTokenValidityInMs);
return createToken(subject, accessTokenValidityInMs); return createToken(subject, accessTokenValidityInMs);
} }
// Refresh Token 생성 // Refresh Token 생성
public String createRefreshToken(String subject) { public String createRefreshToken(String subject) {
log.info("REFRESH TOKEN VALIDITY = {}", refreshTokenValidityInMs);
return createToken(subject, refreshTokenValidityInMs); return createToken(subject, refreshTokenValidityInMs);
} }

View File

@@ -58,6 +58,7 @@ public class MenuAuthorizationManager implements AuthorizationManager<RequestAut
for (MenuEntity menu : allowedMenus) { for (MenuEntity menu : allowedMenus) {
String baseUri = menu.getMenuUrl(); String baseUri = menu.getMenuUrl();
if (baseUri == null || baseUri.isBlank()) { if (baseUri == null || baseUri.isBlank()) {
continue; continue;
} }

View File

@@ -35,6 +35,11 @@ public class ChangeDetectionDto {
public String getText() { public String getText() {
return desc; return desc;
} }
@Override
public String getTextEn() {
return name();
}
} }
@CodeExpose @CodeExpose
@@ -55,6 +60,11 @@ public class ChangeDetectionDto {
public String getText() { public String getText() {
return desc; return desc;
} }
@Override
public String getTextEn() {
return desc;
}
} }
@Schema(name = "TestDto", description = "테스트용") @Schema(name = "TestDto", description = "테스트용")

View File

@@ -2,6 +2,7 @@ package com.kamco.cd.kamcoback.code.dto;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.kamco.cd.kamcoback.common.utils.HeaderUtil;
import com.kamco.cd.kamcoback.common.utils.html.HtmlEscapeDeserializer; import com.kamco.cd.kamcoback.common.utils.html.HtmlEscapeDeserializer;
import com.kamco.cd.kamcoback.common.utils.html.HtmlUnescapeSerializer; import com.kamco.cd.kamcoback.common.utils.html.HtmlUnescapeSerializer;
import com.kamco.cd.kamcoback.common.utils.interfaces.JsonFormatDttm; import com.kamco.cd.kamcoback.common.utils.interfaces.JsonFormatDttm;
@@ -119,10 +120,11 @@ public class CommonCodeDto {
String props2, String props2,
String props3, String props3,
ZonedDateTime deletedDttm) { ZonedDateTime deletedDttm) {
boolean english = HeaderUtil.isEnglishRequest();
this.id = id; this.id = id;
this.code = code; this.code = code;
this.description = description; this.description = description;
this.name = name; this.name = english ? code : name;
this.order = order; this.order = order;
this.used = used; this.used = used;
this.deleted = deleted; this.deleted = deleted;

View File

@@ -45,4 +45,9 @@ public enum CommonUseStatus implements EnumType {
public EnumDto<CommonUseStatus> getEnumDto() { public EnumDto<CommonUseStatus> getEnumDto() {
return new EnumDto<>(this, this.id, this.text); return new EnumDto<>(this, this.id, this.text);
} }
@Override
public String getTextEn() {
return id;
}
} }

View File

@@ -9,12 +9,13 @@ import lombok.Getter;
@Getter @Getter
@AllArgsConstructor @AllArgsConstructor
public enum CrsType implements EnumType { public enum CrsType implements EnumType {
EPSG_3857("Web Mercator, 웹지도 미터(EPSG:900913 동일)"), EPSG_3857("Web Mercator, 웹지도 미터(EPSG:900913 동일)", "Web Mercator"),
EPSG_4326("WGS84 위경도, GeoJSON/OSM 기본"), EPSG_4326("WGS84 위경도, GeoJSON/OSM 기본", "GeoJSON/OSM"),
EPSG_5186("5186::Korea 2000 중부 TM, 한국 SHP"), EPSG_5186("5186::Korea 2000 중부 TM, 한국 SHP", "5186::Korea 2000"),
EPSG_5179("5179::Korea 2000 중부 TM, 한국 SHP"); EPSG_5179("5179::Korea 2000 중부 TM, 한국 SHP", "5179::Korea 2000");
private final String desc; private final String desc;
private final String descEn;
@Override @Override
public String getId() { public String getId() {
@@ -25,4 +26,9 @@ public enum CrsType implements EnumType {
public String getText() { public String getText() {
return desc; return desc;
} }
@Override
public String getTextEn() {
return descEn;
}
} }

View File

@@ -1,5 +1,6 @@
package com.kamco.cd.kamcoback.common.enums; package com.kamco.cd.kamcoback.common.enums;
import com.kamco.cd.kamcoback.common.utils.HeaderUtil;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Getter; import lombok.Getter;
@@ -51,6 +52,6 @@ public enum DetectionClassification {
*/ */
public static String fromStrDesc(String text) { public static String fromStrDesc(String text) {
DetectionClassification dtf = fromString(text); DetectionClassification dtf = fromString(text);
return dtf.getDesc(); return HeaderUtil.isEnglishRequest() ? dtf.getId() : dtf.getDesc();
} }
} }

View File

@@ -9,13 +9,14 @@ import lombok.Getter;
@Getter @Getter
@AllArgsConstructor @AllArgsConstructor
public enum FileUploadStatus implements EnumType { public enum FileUploadStatus implements EnumType {
INIT("초기화"), INIT("초기화", "Init"),
UPLOADING("업로드중"), UPLOADING("업로드중", "Uploading"),
DONE("업로드완료"), DONE("업로드완료", "Upload Done"),
MERGED("병합완료"), MERGED("병합완료", "Merged Done"),
MERGE_FAIL("병합 실패"); MERGE_FAIL("병합 실패", "Merge Failed");
private final String desc; private final String desc;
private final String descEn;
@Override @Override
public String getId() { public String getId() {
@@ -26,4 +27,9 @@ public enum FileUploadStatus implements EnumType {
public String getText() { public String getText() {
return desc; return desc;
} }
@Override
public String getTextEn() {
return descEn;
}
} }

View File

@@ -1,5 +1,6 @@
package com.kamco.cd.kamcoback.common.enums; package com.kamco.cd.kamcoback.common.enums;
import com.kamco.cd.kamcoback.common.utils.HeaderUtil;
import com.kamco.cd.kamcoback.common.utils.enums.CodeExpose; import com.kamco.cd.kamcoback.common.utils.enums.CodeExpose;
import com.kamco.cd.kamcoback.common.utils.enums.EnumType; import com.kamco.cd.kamcoback.common.utils.enums.EnumType;
import java.util.Arrays; import java.util.Arrays;
@@ -25,6 +26,11 @@ public enum ImageryFitStatus implements EnumType {
return desc; return desc;
} }
@Override
public String getTextEn() {
return name();
}
public static ImageryFitStatus fromCode(String code) { public static ImageryFitStatus fromCode(String code) {
if (code == null) { if (code == null) {
return null; return null;
@@ -38,6 +44,8 @@ public enum ImageryFitStatus implements EnumType {
public static String getDescByCode(String code) { public static String getDescByCode(String code) {
ImageryFitStatus status = fromCode(code); ImageryFitStatus status = fromCode(code);
return status != null ? status.getDesc() : null; return status != null
? (HeaderUtil.isEnglishRequest() ? status.getTextEn() : status.getDesc())
: null;
} }
} }

View File

@@ -10,14 +10,15 @@ import lombok.Getter;
@Getter @Getter
@AllArgsConstructor @AllArgsConstructor
public enum LayerType implements EnumType { public enum LayerType implements EnumType {
TILE("배경지도"), TILE("배경지도", "Tile"),
GEOJSON("객체데이터"), GEOJSON("객체데이터", "GeoJSON"),
WMTS("타일레이어"), WMTS("타일레이어", "WMTS"),
WMS("지적도"), WMS("지적도", "WMS"),
KAMCO_WMS("국유인WMS"), KAMCO_WMS("국유인WMS", "KAMCO_WMS"),
KAMCO_WMTS("국유인WMTS"); KAMCO_WMTS("국유인WMTS", "KAMCO_WMTS");
private final String desc; private final String desc;
private final String descEn;
@Override @Override
public String getId() { public String getId() {
@@ -29,6 +30,11 @@ public enum LayerType implements EnumType {
return desc; return desc;
} }
@Override
public String getTextEn() {
return descEn;
}
public static Optional<LayerType> from(String type) { public static Optional<LayerType> from(String type) {
try { try {
return Optional.of(LayerType.valueOf(type)); return Optional.of(LayerType.valueOf(type));

View File

@@ -9,12 +9,13 @@ import lombok.Getter;
@Getter @Getter
@AllArgsConstructor @AllArgsConstructor
public enum MngStateType implements EnumType { public enum MngStateType implements EnumType {
NOTYET("동기화 시작"), NOTYET("동기화 시작", "Sync Started"),
PROCESSING("데이터 체크"), PROCESSING("데이터 체크", "Data Check"),
DONE("동기화 작업 종료"), DONE("동기화 작업 종료", "Sync Completed"),
TAKINGERROR("오류 데이터 처리중"); TAKINGERROR("오류 데이터 처리중", "Processing Error Data");
private final String desc; private final String desc;
private final String descEn;
@Override @Override
public String getId() { public String getId() {
@@ -25,4 +26,8 @@ public enum MngStateType implements EnumType {
public String getText() { public String getText() {
return desc; return desc;
} }
public String getTextEn() {
return descEn;
}
} }

View File

@@ -24,4 +24,9 @@ public enum RoleType implements EnumType {
public String getText() { public String getText() {
return desc; return desc;
} }
@Override
public String getTextEn() {
return name();
}
} }

View File

@@ -24,4 +24,9 @@ public enum StatusType implements EnumType {
public String getText() { public String getText() {
return desc; return desc;
} }
@Override
public String getTextEn() {
return name();
}
} }

View File

@@ -23,4 +23,9 @@ public enum SyncCheckStateType implements EnumType {
public String getText() { public String getText() {
return desc; return desc;
} }
@Override
public String getTextEn() {
return name();
}
} }

View File

@@ -30,4 +30,9 @@ public enum SyncStateType implements EnumType {
public String getText() { public String getText() {
return desc; return desc;
} }
@Override
public String getTextEn() {
return name();
}
} }

View File

@@ -5,7 +5,11 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.*; import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
@@ -170,7 +174,9 @@ public class GeoJsonValidator {
// properties가 있고 scene_id가 null이 아니면 텍스트로 읽음 // properties가 있고 scene_id가 null이 아니면 텍스트로 읽음
// 없으면 null 처리 // 없으면 null 처리
String sceneId = String sceneId =
(props != null && props.hasNonNull("scene_id")) ? props.get("scene_id").asText() : null; (props != null && props.hasNonNull("scene_id"))
? props.get("scene_id").asText().trim()
: null;
// scene_id가 없거나 빈값이면 "정상적으로 도엽번호가 들어오지 않은 feature"로 카운트 // scene_id가 없거나 빈값이면 "정상적으로 도엽번호가 들어오지 않은 feature"로 카운트
if (sceneId == null || sceneId.isBlank()) { if (sceneId == null || sceneId.isBlank()) {
@@ -209,17 +215,17 @@ public class GeoJsonValidator {
// ========================================================= // =========================================================
log.info( log.info(
""" """
===== GeoJSON Validation ===== ===== GeoJSON Validation =====
file: {} file: {}
features(total): {} features(total): {}
requested(unique): {} requested(unique): {}
found(unique scene_id): {} found(unique scene_id): {}
scene_id null/blank: {} scene_id null/blank: {}
duplicates(scene_id): {} duplicates(scene_id): {}
missing(requested - found): {} missing(requested - found): {}
extra(found - requested): {} extra(found - requested): {}
============================== ==============================
""", """,
geojsonPath, geojsonPath,
featureCount, // 중복 포함한 전체 feature 수 featureCount, // 중복 포함한 전체 feature 수
requested.size(), // 요청 도엽 유니크 수 requested.size(), // 요청 도엽 유니크 수
@@ -230,12 +236,16 @@ public class GeoJsonValidator {
extra.size()); // 요청하지 않았는데 들어온 도엽 수 extra.size()); // 요청하지 않았는데 들어온 도엽 수
// 중복/누락/추가 항목은 전체를 다 찍으면 로그 폭발하므로 샘플만 // 중복/누락/추가 항목은 전체를 다 찍으면 로그 폭발하므로 샘플만
if (!duplicates.isEmpty()) // if (!duplicates.isEmpty())
log.warn("duplicates sample: {}", duplicates.stream().limit(20).toList()); // log.warn("duplicates sample: {}", duplicates.stream().limit(20).toList());
if (!missing.isEmpty()) log.warn("missing sample: {}", missing.stream().limit(50).toList()); if (!missing.isEmpty()) {
log.warn("missing sample: {}", missing.stream().limit(50).toList());
}
if (!extra.isEmpty()) log.warn("extra sample: {}", extra.stream().limit(50).toList()); if (!extra.isEmpty()) {
log.warn("extra sample: {}", extra.stream().limit(50).toList());
}
// ========================================================= // =========================================================
// 6) 실패 조건 판정 // 6) 실패 조건 판정

View File

@@ -1,23 +1,45 @@
package com.kamco.cd.kamcoback.common.service; package com.kamco.cd.kamcoback.common.service;
import com.kamco.cd.kamcoback.config.InferenceProperties;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2; import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@Log4j2 @Log4j2
// 0312
@RequiredArgsConstructor
@Component @Component
public class ExternalJarRunner { public class ExternalJarRunner {
@Value("${spring.profiles.active}") @Value("${spring.profiles.active}")
private String profile; private String profile;
// 0312
private final InferenceProperties inferenceProperties;
private static final long TIMEOUT_MINUTES = TimeUnit.DAYS.toMinutes(3); private static final long TIMEOUT_MINUTES = TimeUnit.DAYS.toMinutes(3);
// java -jar build/libs/shp-exporter.jar --batch --geoserver.enabled=true
// --converter.inference-id=qq99999 --converter.batch-ids[0]=111
// 0312 shp 파일 배치를 통해 생성
public void run(String inferenceLearningId, List<Long> batchIds) {
// JAR 경로 (shape파일 생성용)
String jarPathV2 = inferenceProperties.getJarPathV2();
List<String> args = new ArrayList<>();
args.add(" --spring.profiles.active=" + profile);
args.add(" --batch");
args.add(" --geoserver.enabled=true");
args.add(" --converter.inference-id=" + inferenceLearningId);
batchIds.forEach(batchId -> args.add(" --converter.batch-ids[" + args.size() + "]=" + batchId));
execJar(jarPathV2, args);
}
/** /**
* shp 파일 생성 * shp 파일 생성
* *
@@ -28,7 +50,8 @@ public class ExternalJarRunner {
* @param mode * @param mode
* <p>MERGED - batch-ids 에 해당하는 **모든 데이터를 하나의 Shapefile로 병합 생성, * <p>MERGED - batch-ids 에 해당하는 **모든 데이터를 하나의 Shapefile로 병합 생성,
* <p>MAP_IDS - 명시적으로 전달한 map-ids만 대상으로 Shapefile 생성, * <p>MAP_IDS - 명시적으로 전달한 map-ids만 대상으로 Shapefile 생성,
* <p>RESOLVE - batch-ids 기준으로 **JAR 내부에서 map_ids를 조회**한 뒤 Shapefile 생성 * <p>RESOLVE - batch-ids 기준으로 **JAR 내부에서 map_ids를 조회**한 뒤 Shapefile 생성 java -jar
* build/libs/shp-exporter.jar --spring.profiles.active=prod
*/ */
public void run(String jarPath, String batchIds, String inferenceId, String mapIds, String mode) { public void run(String jarPath, String batchIds, String inferenceId, String mapIds, String mode) {
List<String> args = new ArrayList<>(); List<String> args = new ArrayList<>();
@@ -73,7 +96,8 @@ public class ExternalJarRunner {
cmd.add("-jar"); cmd.add("-jar");
cmd.add(jarPath); cmd.add(jarPath);
cmd.addAll(args); cmd.addAll(args);
// 0312
log.info("exec jar command: {}", cmd);
ProcessBuilder pb = new ProcessBuilder(cmd); ProcessBuilder pb = new ProcessBuilder(cmd);
pb.redirectErrorStream(true); pb.redirectErrorStream(true);
@@ -103,6 +127,7 @@ public class ExternalJarRunner {
} catch (Exception e) { } catch (Exception e) {
log.error("jar execution error. output=\n{}", out, e); log.error("jar execution error. output=\n{}", out, e);
throw new RuntimeException("jar execution error\n" + out, e);
} }
} }

View File

@@ -1,6 +1,9 @@
package com.kamco.cd.kamcoback.common.utils; package com.kamco.cd.kamcoback.common.utils;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
public final class HeaderUtil { public final class HeaderUtil {
@@ -20,4 +23,20 @@ public final class HeaderUtil {
public static String getRequired(HttpServletRequest request, String headerName) { public static String getRequired(HttpServletRequest request, String headerName) {
return get(request, headerName); return get(request, headerName);
} }
public static boolean isEnglishRequest() {
RequestAttributes attrs = RequestContextHolder.getRequestAttributes();
if (!(attrs instanceof ServletRequestAttributes servletAttrs)) {
return false;
}
String acceptLanguage = servletAttrs.getRequest().getHeader("Accept-Language");
if (acceptLanguage == null || acceptLanguage.isBlank()) {
return false;
}
return acceptLanguage.toLowerCase().startsWith("en");
}
} }

View File

@@ -5,4 +5,6 @@ public interface EnumType {
String getId(); String getId();
String getText(); String getText();
String getTextEn();
} }

View File

@@ -1,6 +1,7 @@
package com.kamco.cd.kamcoback.common.utils.enums; package com.kamco.cd.kamcoback.common.utils.enums;
import com.kamco.cd.kamcoback.code.dto.CommonCodeDto.CodeDto; import com.kamco.cd.kamcoback.code.dto.CommonCodeDto.CodeDto;
import com.kamco.cd.kamcoback.common.utils.HeaderUtil;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
@@ -32,11 +33,12 @@ public class Enums {
// enum -> CodeDto list // enum -> CodeDto list
public static List<CodeDto> toList(Class<? extends Enum<?>> enumClass) { public static List<CodeDto> toList(Class<? extends Enum<?>> enumClass) {
Object[] enums = enumClass.getEnumConstants(); Object[] enums = enumClass.getEnumConstants();
boolean english = HeaderUtil.isEnglishRequest();
return Arrays.stream(enums) return Arrays.stream(enums)
.map(e -> (EnumType) e) .map(e -> (EnumType) e)
.filter(e -> !isHidden(enumClass, (Enum<?>) e)) .filter(e -> !isHidden(enumClass, (Enum<?>) e))
.map(e -> new CodeDto(e.getId(), e.getText())) .map(e -> new CodeDto(e.getId(), english ? e.getTextEn() : e.getText()))
.toList(); .toList();
} }

View File

@@ -16,5 +16,7 @@ public class InferenceProperties {
private String batchUrl; private String batchUrl;
private String geojsonDir; private String geojsonDir;
private String jarPath; private String jarPath;
// 0312
private String jarPathV2;
private String inferenceServerName; private String inferenceServerName;
} }

View File

@@ -71,6 +71,10 @@ public class SecurityConfig {
.requestMatchers("/api/test/review") .requestMatchers("/api/test/review")
.hasAnyRole("ADMIN", "REVIEWER") .hasAnyRole("ADMIN", "REVIEWER")
// shapefile 생성 테스트 API - 인증 없이 접근 가능
.requestMatchers("/api/test/make-shapefile")
.permitAll()
// ASYNC/ERROR 재디스패치는 막지 않기 (다운로드/스트리밍에서 필수) // ASYNC/ERROR 재디스패치는 막지 않기 (다운로드/스트리밍에서 필수)
.dispatcherTypeMatchers(DispatcherType.ASYNC, DispatcherType.ERROR) .dispatcherTypeMatchers(DispatcherType.ASYNC, DispatcherType.ERROR)
.permitAll() .permitAll()

View File

@@ -39,10 +39,10 @@ public class ApiLogFunction {
public static String getXFowardedForIp(HttpServletRequest request) { public static String getXFowardedForIp(HttpServletRequest request) {
String ip = request.getHeader("X-Forwarded-For"); String ip = request.getHeader("X-Forwarded-For");
if (ip != null) { if (ip != null && !ip.isBlank()) {
ip = ip.split(",")[0].trim(); return ip.split(",")[0].trim();
} }
return ip; return request.getRemoteAddr();
} }
// 사용자 ID 추출 예시 (Spring Security 기준) // 사용자 ID 추출 예시 (Spring Security 기준)

View File

@@ -3,6 +3,7 @@ package com.kamco.cd.kamcoback.config.api;
import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude;
import com.kamco.cd.kamcoback.common.utils.enums.EnumType; import com.kamco.cd.kamcoback.common.utils.enums.EnumType;
import java.util.Arrays;
import lombok.Getter; import lombok.Getter;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.ToString; import lombok.ToString;
@@ -192,6 +193,11 @@ public class ApiResponseDto<T> {
return message; return message;
} }
@Override
public String getTextEn() {
return message;
}
public static ApiResponseCode getCode(String name) { public static ApiResponseCode getCode(String name) {
return ApiResponseCode.valueOf(name.toUpperCase()); return ApiResponseCode.valueOf(name.toUpperCase());
} }
@@ -220,5 +226,12 @@ public class ApiResponseDto<T> {
return INTERNAL_SERVER_ERROR; return INTERNAL_SERVER_ERROR;
} }
public static ApiResponseCode fromMessage(String message) {
return Arrays.stream(values())
.filter(code -> code.getMessage().equals(message))
.findFirst()
.orElse(null);
}
} }
} }

View File

@@ -14,9 +14,9 @@ public class GukYuinDto {
public enum GukYuinLinkFailCode implements EnumType { public enum GukYuinLinkFailCode implements EnumType {
OK("연동 가능"), OK("연동 가능"),
NOT_FOUND("대상 회차가 없습니다."), NOT_FOUND("대상 회차가 없습니다."),
SCOPE_PART_NOT_ALLOWED("부분 도엽은 연동 불가능 합니다."), SCOPE_PART_NOT_ALLOWED("부분 도엽 추론 결과는 연동 할 수 없습니다."),
HAS_RUNNING_INFERENCE("라벨링 진행 중 회차가 있습니다."), HAS_RUNNING_INFERENCE("라벨링 진행중 회차가 있습니다.\n진행중인 라벨링 작업을 종료하신 후 다시 연동해주세요."),
OTHER_GUKYUIN_IN_PROGRESS("국유in 연동 진행 중 회차가 있습니다."); OTHER_GUKYUIN_IN_PROGRESS("국유in 연동 진행중입니다. 선행 연동 작업이 종료된 후 진행할 수 있습니다.");
private final String desc; private final String desc;
@@ -29,6 +29,11 @@ public class GukYuinDto {
public String getText() { public String getText() {
return desc; return desc;
} }
@Override
public String getTextEn() {
return desc;
}
} }
@Getter @Getter
@@ -36,8 +41,9 @@ public class GukYuinDto {
public static class GukYuinLinkableRes { public static class GukYuinLinkableRes {
private boolean linkable; private boolean linkable;
// private GukYuinLinkFailCode code; private GukYuinLinkFailCode code;
private String message; private String message;
private UUID inferenceUuid;
} }
// Repository가 반환할 Fact(조회 결과) // Repository가 반환할 Fact(조회 결과)
@@ -45,7 +51,8 @@ public class GukYuinDto {
boolean existsLearn, boolean existsLearn,
boolean isPartScope, boolean isPartScope,
boolean hasRunningInference, boolean hasRunningInference,
boolean hasOtherUnfinishedGukYuin) {} boolean hasOtherUnfinishedGukYuin,
UUID inferenceUuid) {}
@Getter @Getter
@Setter @Setter

View File

@@ -26,4 +26,9 @@ public enum GukYuinStatus implements EnumType {
public String getText() { public String getText() {
return desc; return desc;
} }
@Override
public String getTextEn() {
return desc;
}
} }

View File

@@ -237,9 +237,12 @@ public class GukYuinApiService {
GukYuinLinkFailCode code = decideCode(f); GukYuinLinkFailCode code = decideCode(f);
GukYuinLinkableRes res = new GukYuinLinkableRes(); GukYuinLinkableRes res = new GukYuinLinkableRes();
// res.setCode(code); res.setCode(code);
res.setLinkable(code == GukYuinLinkFailCode.OK); res.setLinkable(code == GukYuinLinkFailCode.OK);
res.setMessage(code.getDesc()); res.setMessage(code.getDesc());
if (code == GukYuinLinkFailCode.HAS_RUNNING_INFERENCE) {
res.setInferenceUuid(f.inferenceUuid());
}
return res; return res;
} }

View File

@@ -8,6 +8,7 @@ import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto;
import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.InferenceServerStatusDto; import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.InferenceServerStatusDto;
import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.InferenceStatusDetailDto; import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.InferenceStatusDetailDto;
import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.ResultList; import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.ResultList;
import com.kamco.cd.kamcoback.inference.service.InferenceAsyncService;
import com.kamco.cd.kamcoback.inference.service.InferenceResultService; import com.kamco.cd.kamcoback.inference.service.InferenceResultService;
import com.kamco.cd.kamcoback.log.dto.AuditLogDto; import com.kamco.cd.kamcoback.log.dto.AuditLogDto;
import com.kamco.cd.kamcoback.log.dto.AuditLogDto.DownloadReq; import com.kamco.cd.kamcoback.log.dto.AuditLogDto.DownloadReq;
@@ -55,6 +56,7 @@ import org.springframework.web.bind.annotation.RestController;
public class InferenceResultApiController { public class InferenceResultApiController {
private final InferenceResultService inferenceResultService; private final InferenceResultService inferenceResultService;
private final InferenceAsyncService inferenceAsyncService;
private final MapSheetMngService mapSheetMngService; private final MapSheetMngService mapSheetMngService;
private final ModelMngService modelMngService; private final ModelMngService modelMngService;
private final RangeDownloadResponder rangeDownloadResponder; private final RangeDownloadResponder rangeDownloadResponder;
@@ -176,7 +178,8 @@ public class InferenceResultApiController {
}) })
@DeleteMapping("/end") @DeleteMapping("/end")
public ApiResponseDto<UUID> getInferenceGeomList() { public ApiResponseDto<UUID> getInferenceGeomList() {
UUID uuid = inferenceResultService.deleteInferenceEnd(); // UUID uuid = inferenceResultService.deleteInferenceEnd();
UUID uuid = inferenceAsyncService.asyncInferenceEnd();
return ApiResponseDto.ok(uuid); return ApiResponseDto.ok(uuid);
} }
@@ -376,7 +379,7 @@ public class InferenceResultApiController {
Path zipPath = Path.of(path); Path zipPath = Path.of(path);
if (!Files.isRegularFile(zipPath)) { if (!Files.isRegularFile(zipPath)) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body("추론이 완료되지 않아 파일이 생성되지 않았습니다."); return ResponseEntity.status(HttpStatus.NOT_FOUND).body("다운로드 받을 파일이 없습니다.");
} }
return rangeDownloadResponder.buildZipResponse(zipPath, uid + ".zip", request); return rangeDownloadResponder.buildZipResponse(zipPath, uid + ".zip", request);
@@ -445,4 +448,22 @@ public class InferenceResultApiController {
UUID uuid) { UUID uuid) {
return ApiResponseDto.ok(inferenceResultService.getInferenceRunMapId(uuid)); return ApiResponseDto.ok(inferenceResultService.getInferenceRunMapId(uuid));
} }
@Operation(summary = "추론 삭제", description = "추론관리 > 추론목록 > 추론 상세 > 추론 삭제")
@ApiResponses(
value = {
@ApiResponse(
responseCode = "201",
description = "종료 성공",
content =
@Content(
mediaType = "application/json",
schema = @Schema(implementation = Page.class))),
@ApiResponse(responseCode = "400", description = "잘못된 검색 조건", content = @Content),
@ApiResponse(responseCode = "500", description = "서버 오류", content = @Content)
})
@DeleteMapping("/delete-inference/{uuid}")
public ApiResponseDto<ApiResponseDto.ResponseObj> deleteInference(@PathVariable UUID uuid) {
return ApiResponseDto.ok(inferenceResultService.deleteInference(uuid));
}
} }

View File

@@ -8,6 +8,7 @@ import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.kamco.cd.kamcoback.common.enums.DetectionClassification; import com.kamco.cd.kamcoback.common.enums.DetectionClassification;
import com.kamco.cd.kamcoback.common.enums.ImageryFitStatus; import com.kamco.cd.kamcoback.common.enums.ImageryFitStatus;
import com.kamco.cd.kamcoback.common.utils.HeaderUtil;
import com.kamco.cd.kamcoback.common.utils.interfaces.JsonFormatDttm; import com.kamco.cd.kamcoback.common.utils.interfaces.JsonFormatDttm;
import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.DetectOption; import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.DetectOption;
import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.MapSheetScope; import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.MapSheetScope;
@@ -156,9 +157,14 @@ public class InferenceDetailDto {
String classAfterName; String classAfterName;
Long classAfterCnt; Long classAfterCnt;
boolean english = HeaderUtil.isEnglishRequest();
public Dashboard(String classAfterCd, Long classAfterCnt) { public Dashboard(String classAfterCd, Long classAfterCnt) {
this.classAfterCd = classAfterCd; this.classAfterCd = classAfterCd;
this.classAfterName = DetectionClassification.fromString(classAfterCd).getDesc(); this.classAfterName =
english
? DetectionClassification.fromString(classAfterCd).getId()
: DetectionClassification.fromString(classAfterCd).getDesc();
this.classAfterCnt = classAfterCnt; this.classAfterCnt = classAfterCnt;
} }
} }
@@ -294,8 +300,10 @@ public class InferenceDetailDto {
} }
@Getter @Getter
@Setter
public static class Geom { public static class Geom {
Long geoUid;
UUID uuid; UUID uuid;
String uid; String uid;
Integer compareYyyy; Integer compareYyyy;
@@ -313,7 +321,10 @@ public class InferenceDetailDto {
String pnu; String pnu;
String fitState; String fitState;
boolean english = HeaderUtil.isEnglishRequest();
public Geom( public Geom(
Long geoUid,
UUID uuid, UUID uuid,
String uid, String uid,
Integer compareYyyy, Integer compareYyyy,
@@ -328,16 +339,23 @@ public class InferenceDetailDto {
String subUid, String subUid,
String pnu, String pnu,
String fitState) { String fitState) {
this.geoUid = geoUid;
this.uuid = uuid; this.uuid = uuid;
this.uid = uid; this.uid = uid;
this.compareYyyy = compareYyyy; this.compareYyyy = compareYyyy;
this.targetYyyy = targetYyyy; this.targetYyyy = targetYyyy;
this.cdProb = cdProb; this.cdProb = cdProb;
this.classBeforeCd = classBeforeCd; this.classBeforeCd = classBeforeCd;
this.classBeforeName = DetectionClassification.fromString(classBeforeCd).getDesc(); this.classBeforeName =
english
? DetectionClassification.fromString(classBeforeCd).getId()
: DetectionClassification.fromString(classBeforeCd).getDesc();
this.classBeforeProb = classBeforeProb; this.classBeforeProb = classBeforeProb;
this.classAfterCd = classAfterCd; this.classAfterCd = classAfterCd;
this.classAfterName = DetectionClassification.fromString(classAfterCd).getDesc(); this.classAfterName =
english
? DetectionClassification.fromString(classAfterCd).getId()
: DetectionClassification.fromString(classAfterCd).getDesc();
this.classAfterProb = classAfterProb; this.classAfterProb = classAfterProb;
this.mapSheetNum = mapSheetNum; this.mapSheetNum = mapSheetNum;
this.mapSheetName = mapSheetName; this.mapSheetName = mapSheetName;

View File

@@ -2,6 +2,7 @@ package com.kamco.cd.kamcoback.inference.dto;
import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import com.kamco.cd.kamcoback.common.utils.HeaderUtil;
import com.kamco.cd.kamcoback.common.utils.enums.EnumType; import com.kamco.cd.kamcoback.common.utils.enums.EnumType;
import com.kamco.cd.kamcoback.common.utils.interfaces.EnumValid; import com.kamco.cd.kamcoback.common.utils.interfaces.EnumValid;
import com.kamco.cd.kamcoback.common.utils.interfaces.JsonFormatDttm; import com.kamco.cd.kamcoback.common.utils.interfaces.JsonFormatDttm;
@@ -14,10 +15,13 @@ import java.time.ZonedDateTime;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Getter; import lombok.Getter;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import lombok.Setter; import lombok.Setter;
import lombok.ToString;
import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
@@ -50,6 +54,11 @@ public class InferenceResultDto {
public String getText() { public String getText() {
return desc; return desc;
} }
@Override
public String getTextEn() {
return desc;
}
} }
/** 탐지 데이터 옵션 dto */ /** 탐지 데이터 옵션 dto */
@@ -78,6 +87,11 @@ public class InferenceResultDto {
public String getText() { public String getText() {
return desc; return desc;
} }
@Override
public String getTextEn() {
return desc;
}
} }
@Getter @Getter
@@ -86,6 +100,7 @@ public class InferenceResultDto {
READY("대기"), READY("대기"),
IN_PROGRESS("진행중"), IN_PROGRESS("진행중"),
END("완료"), END("완료"),
END_FAIL("종료실패"),
FORCED_END("강제종료"); FORCED_END("강제종료");
private final String desc; private final String desc;
@@ -94,7 +109,12 @@ public class InferenceResultDto {
} }
public static String getDescByCode(String code) { public static String getDescByCode(String code) {
return fromCode(code).getDesc(); boolean english = HeaderUtil.isEnglishRequest();
if (english) {
return fromCode(code).getTextEn();
} else {
return fromCode(code).getDesc();
}
} }
@Override @Override
@@ -106,6 +126,11 @@ public class InferenceResultDto {
public String getText() { public String getText() {
return desc; return desc;
} }
@Override
public String getTextEn() {
return name();
}
} }
@Getter @Getter
@@ -127,6 +152,11 @@ public class InferenceResultDto {
public String getText() { public String getText() {
return desc; return desc;
} }
@Override
public String getTextEn() {
return desc;
}
} }
/** 목록조회 dto */ /** 목록조회 dto */
@@ -209,7 +239,46 @@ public class InferenceResultDto {
long m = (s % 3600) / 60; long m = (s % 3600) / 60;
long sec = s % 60; long sec = s % 60;
return String.format("%d시간 %d분 %d초", h, m, sec); boolean english = HeaderUtil.isEnglishRequest();
if (english) {
return String.format("%dh %dm %ds", h, m, sec);
} else {
return String.format("%d시간 %d분 %d초", h, m, sec);
}
}
@JsonProperty("mapSheetCnt")
public String getMapSheetCnt() {
boolean english = HeaderUtil.isEnglishRequest();
String text = this.mapSheetCnt;
if (!english) {
return text;
}
// "창원 외 34건"
Pattern otherPattern = Pattern.compile("^(.+) 외 (\\d+)건$");
Matcher otherMatcher = otherPattern.matcher(text);
if (otherMatcher.find()) {
String name = otherMatcher.group(1);
int count = Integer.parseInt(otherMatcher.group(2));
return name + " and " + count + (count == 1 ? " other" : " others");
}
// "창원 1건"
Pattern itemPattern = Pattern.compile("^(.+) (\\d+)건$");
Matcher itemMatcher = itemPattern.matcher(text);
if (itemMatcher.find()) {
String name = itemMatcher.group(1);
int count = Integer.parseInt(itemMatcher.group(2));
return name + " (" + count + (count == 1 ? " item" : " items") + ")";
}
return text;
} }
} }
@@ -240,6 +309,7 @@ public class InferenceResultDto {
@Setter @Setter
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
@ToString
public static class RegReq { public static class RegReq {
@Schema(description = "제목", example = "2023-2024 변화탐지 테스트") @Schema(description = "제목", example = "2023-2024 변화탐지 테스트")
@@ -272,11 +342,10 @@ public class InferenceResultDto {
private String mapSheetScope; private String mapSheetScope;
@Schema(description = "탐지 데이터 옵션 - 추론제외(EXCL), 이전 년도 도엽 사용(PREV)", example = "EXCL") @Schema(description = "탐지 데이터 옵션 - 추론제외(EXCL), 이전 년도 도엽 사용(PREV)", example = "EXCL")
@NotBlank // @EnumValid(
@EnumValid( // enumClass = DetectOption.class,
enumClass = DetectOption.class, // message = "탐지 데이터 옵션은 '추론제외', '이전 년도 도엽 사용' 만 사용 가능합니다.")
message = "탐지 데이터 옵션은 '추론제외', '이전 년도 도엽 사용' 만 사용 가능합니다.") private DetectOption detectOption;
private String detectOption;
@Schema(description = "5k 도협 번호 목록", example = "[33605,33606, 33610, 34802, 35603, 35611]") @Schema(description = "5k 도협 번호 목록", example = "[33605,33606, 33610, 34802, 35603, 35611]")
@NotNull @NotNull
@@ -682,6 +751,7 @@ public class InferenceResultDto {
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
public static class MapSheetFallbackYearDto { public static class MapSheetFallbackYearDto {
private String mapSheetNum; private String mapSheetNum;
private Integer mngYyyy; private Integer mngYyyy;
} }

View File

@@ -0,0 +1,117 @@
package com.kamco.cd.kamcoback.inference.service;
import com.kamco.cd.kamcoback.common.exception.CustomApiException;
import com.kamco.cd.kamcoback.common.inference.service.InferenceCommonService;
import com.kamco.cd.kamcoback.common.utils.UserUtil;
import com.kamco.cd.kamcoback.config.resttemplate.ExternalHttpClient;
import com.kamco.cd.kamcoback.config.resttemplate.ExternalHttpClient.ExternalCallResult;
import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.SaveInferenceAiDto;
import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.Status;
import com.kamco.cd.kamcoback.postgres.core.AuditLogCoreService;
import com.kamco.cd.kamcoback.postgres.core.InferenceResultCoreService;
import com.kamco.cd.kamcoback.postgres.core.MapSheetMngCoreService;
import com.kamco.cd.kamcoback.postgres.core.ModelMngCoreService;
import java.time.ZonedDateTime;
import java.util.List;
import java.util.UUID;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
/** 추론 관리 */
@Service
@Slf4j
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class InferenceAsyncService {
private final InferenceResultCoreService inferenceResultCoreService;
private final MapSheetMngCoreService mapSheetMngCoreService;
private final ModelMngCoreService modelMngCoreService;
private final AuditLogCoreService auditLogCoreService;
private final InferenceCommonService inferenceCommonService;
private final ExternalHttpClient externalHttpClient;
private final UserUtil userUtil;
@Value("${inference.batch-url}")
private String batchUrl;
@Value("${inference.inference-server-name}")
private String inferenceServerName;
@Value("${file.dataset-dir}")
private String datasetDir;
@Value("${spring.profiles.active}")
private String activeEnv;
@Value("${inference.geojson-dir}")
private String inferenceDir;
// 0313
@Transactional
public UUID asyncInferenceEnd() {
SaveInferenceAiDto dto = inferenceResultCoreService.getProcessing();
if (dto == null) {
throw new CustomApiException("NOT_FOUND", HttpStatus.NOT_FOUND);
}
this.deleteInferenceEndAsync(dto); // 비동기 종료 호출
return dto.getUuid();
}
// 0313
@Async("inferenceEndExecutor")
@Transactional
public void deleteInferenceEndAsync(SaveInferenceAiDto dto) {
Long batchId = dto.getBatchId();
String url = batchUrl + "/" + batchId;
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.setAccept(List.of(MediaType.APPLICATION_JSON));
try {
log.info("[추론 종료 비동기 시작] uuid={}, batchId={}", dto.getUuid(), batchId);
ExternalCallResult<String> result =
externalHttpClient.callLong(url, HttpMethod.DELETE, dto, headers, String.class);
if (!result.success()) {
log.error("[추론 종료 실패] 외부 API 호출 실패. uuid={}, batchId={}", dto.getUuid(), batchId);
SaveInferenceAiDto failRequest = new SaveInferenceAiDto();
failRequest.setUuid(dto.getUuid());
failRequest.setStatus(Status.END_FAIL.getId()); // TODO: 종료실패 상태 추가하는 게 맞는지?
failRequest.setUpdateUid(userUtil.getId());
failRequest.setInferEndDttm(ZonedDateTime.now());
inferenceResultCoreService.update(failRequest);
return;
}
SaveInferenceAiDto request = new SaveInferenceAiDto();
request.setStatus(Status.FORCED_END.getId());
request.setUuid(dto.getUuid());
request.setUpdateUid(userUtil.getId());
request.setInferEndDttm(ZonedDateTime.now());
inferenceResultCoreService.update(request);
Long learnId = inferenceResultCoreService.getInferenceLearnIdByUuid(dto.getUuid());
inferenceResultCoreService.upsertGeomData(learnId);
log.info("[추론 종료 비동기 완료] uuid={}, batchId={}", dto.getUuid(), batchId);
} catch (Exception e) {
log.error("[추론 종료 비동기 예외] uuid={}, batchId={}", dto.getUuid(), batchId, e);
}
}
}

View File

@@ -7,24 +7,13 @@ import com.kamco.cd.kamcoback.common.geometry.GeoJsonFileWriter.Scene;
import com.kamco.cd.kamcoback.common.inference.service.InferenceCommonService; import com.kamco.cd.kamcoback.common.inference.service.InferenceCommonService;
import com.kamco.cd.kamcoback.common.inference.utils.GeoJsonValidator; import com.kamco.cd.kamcoback.common.inference.utils.GeoJsonValidator;
import com.kamco.cd.kamcoback.common.utils.UserUtil; import com.kamco.cd.kamcoback.common.utils.UserUtil;
import com.kamco.cd.kamcoback.config.api.ApiResponseDto;
import com.kamco.cd.kamcoback.config.resttemplate.ExternalHttpClient; import com.kamco.cd.kamcoback.config.resttemplate.ExternalHttpClient;
import com.kamco.cd.kamcoback.config.resttemplate.ExternalHttpClient.ExternalCallResult; import com.kamco.cd.kamcoback.config.resttemplate.ExternalHttpClient.ExternalCallResult;
import com.kamco.cd.kamcoback.inference.dto.InferenceDetailDto; import com.kamco.cd.kamcoback.inference.dto.InferenceDetailDto;
import com.kamco.cd.kamcoback.inference.dto.InferenceDetailDto.AnalResultInfo; import com.kamco.cd.kamcoback.inference.dto.InferenceDetailDto.*;
import com.kamco.cd.kamcoback.inference.dto.InferenceDetailDto.Dashboard;
import com.kamco.cd.kamcoback.inference.dto.InferenceDetailDto.Detail;
import com.kamco.cd.kamcoback.inference.dto.InferenceDetailDto.Geom;
import com.kamco.cd.kamcoback.inference.dto.InferenceDetailDto.MapSheet;
import com.kamco.cd.kamcoback.inference.dto.InferenceDetailDto.SearchGeoReq;
import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto; import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto;
import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.DetectOption; import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.*;
import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.InferenceLearnDto;
import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.InferenceServerStatusDto;
import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.InferenceStatusDetailDto;
import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.MapSheetScope;
import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.ResultList;
import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.SaveInferenceAiDto;
import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.Status;
import com.kamco.cd.kamcoback.inference.dto.InferenceSendDto; import com.kamco.cd.kamcoback.inference.dto.InferenceSendDto;
import com.kamco.cd.kamcoback.inference.dto.InferenceSendDto.pred_requests_areas; import com.kamco.cd.kamcoback.inference.dto.InferenceSendDto.pred_requests_areas;
import com.kamco.cd.kamcoback.log.dto.AuditLogDto; import com.kamco.cd.kamcoback.log.dto.AuditLogDto;
@@ -44,19 +33,10 @@ import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.time.ZonedDateTime; import java.time.ZonedDateTime;
import java.util.ArrayList; import java.util.*;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import org.springframework.http.HttpHeaders; import org.springframework.http.HttpHeaders;
@@ -68,9 +48,9 @@ import org.springframework.transaction.annotation.Transactional;
/** 추론 관리 */ /** 추론 관리 */
@Service @Service
@Log4j2 @Slf4j
@RequiredArgsConstructor @RequiredArgsConstructor
@Transactional(readOnly = true) @Transactional
public class InferenceResultService { public class InferenceResultService {
private final InferenceResultCoreService inferenceResultCoreService; private final InferenceResultCoreService inferenceResultCoreService;
@@ -128,47 +108,40 @@ public class InferenceResultService {
*/ */
@Transactional @Transactional
public UUID run(InferenceResultDto.RegReq req) { public UUID run(InferenceResultDto.RegReq req) {
if (req.getDetectOption().equals(DetectOption.EXCL.getId())) { log.info("inference start request = {}", req);
DetectOption detectOption = req.getDetectOption();
if (detectOption == DetectOption.EXCL) {
// 추론 제외 일때 EXCL // 추론 제외 일때 EXCL
return runExcl(req); return runExcl(req);
} }
// 이전연도 도엽 사용 일때 PREV // 이전연도 도엽 사용 일때 PREV
return runPrev(req); return runPrev(req);
} }
/** /**
* 변화탐지 옵션 추론제외 실행 * 변화탐지 [옵션 추론제외 실행]
* *
* @param req * @param req
* @return * @return
*/ */
public UUID runExcl(InferenceResultDto.RegReq req) { public UUID runExcl(InferenceResultDto.RegReq req) {
// TODO 쿼리로 한번에 할수 있게 수정해야하나..
// 기준연도 실행가능 도엽 조회 // 기준연도 실행가능 도엽 조회
List<MngListDto> targetMngList = List<MngListDto> targetMngList =
mapSheetMngCoreService.getMapSheetMngHst( mapSheetMngCoreService.getMapSheetMngHst(req.getTargetYyyy(), req.getMapSheetNum());
req.getTargetYyyy(), req.getMapSheetScope(), req.getMapSheetNum());
// List<MngListDto> mngList =
// mapSheetMngCoreService.findExecutableSheets(
// req.getCompareYyyy(),
// req.getTargetYyyy(),
// req.getMapSheetScope(),
// req.getMapSheetNum());
if (targetMngList == null || targetMngList.isEmpty()) { if (targetMngList == null || targetMngList.isEmpty()) {
throw new CustomApiException("NOT_FOUND_MAP_SHEET_NUM", HttpStatus.NOT_FOUND); throw new CustomApiException("NOT_FOUND_MAP_SHEET_NUM", HttpStatus.NOT_FOUND);
} }
log.info("targetMngList size = {}", targetMngList.size());
// 비교연도 실행가능 도엽 조회 // 비교연도 실행가능 도엽 조회
List<MngListDto> compareMngList = List<MngListDto> compareMngList =
mapSheetMngCoreService.getMapSheetMngHst( mapSheetMngCoreService.getMapSheetMngHst(req.getCompareYyyy(), req.getMapSheetNum());
req.getCompareYyyy(), req.getMapSheetScope(), req.getMapSheetNum());
if (compareMngList == null || compareMngList.isEmpty()) { if (compareMngList == null || compareMngList.isEmpty()) {
throw new CustomApiException("NOT_FOUND_COMPARE_YEAR", HttpStatus.NOT_FOUND); throw new CustomApiException("NOT_FOUND_COMPARE_YEAR", HttpStatus.NOT_FOUND);
} }
log.info("compareMngList size = {}", compareMngList.size());
// compare 도엽번호 Set 구성 // compare 도엽번호 Set 구성
Set<String> compareSet = Set<String> compareSet =
@@ -210,14 +183,14 @@ public class InferenceResultService {
log.info( log.info(
""" """
===== MapSheet Year Comparison ===== ===== MapSheet Year Comparison =====
target Total: {} target Total: {}
compare Total: {} compare Total: {}
Intersection: {} Intersection: {}
target Only (Excluded): {} target Only (Excluded): {}
compare Only: {} compare Only: {}
==================================== ====================================
""", """,
targetTotal, targetTotal,
compareTotal, compareTotal,
intersection, intersection,
@@ -268,28 +241,31 @@ public class InferenceResultService {
*/ */
@Transactional @Transactional
public UUID runPrev(InferenceResultDto.RegReq req) { public UUID runPrev(InferenceResultDto.RegReq req) {
// TODO 쿼리로 한번에 할수 있게 수정해야하나.. Integer targetYyyy = req.getTargetYyyy();
// 기준연도 실행가능 도엽 조회 Integer compareYyyy = req.getCompareYyyy();
List<MngListDto> targetMngList = String mapSheetScope = req.getMapSheetScope();
mapSheetMngCoreService.getMapSheetMngHst(
req.getTargetYyyy(), req.getMapSheetScope(), req.getMapSheetNum());
log.info("[{}|{}}] ,{}", compareYyyy, targetYyyy, mapSheetScope);
// 기준연도 실행가능 도엽 조회[AFTER]
List<MngListDto> targetMngList =
mapSheetMngCoreService.getMapSheetMngHst(targetYyyy, req.getMapSheetNum());
log.info("[runPrev] targetMngList size = {}", targetMngList.size());
if (targetMngList == null || targetMngList.isEmpty()) { if (targetMngList == null || targetMngList.isEmpty()) {
throw new CustomApiException("NOT_FOUND_TARGET_YEAR", HttpStatus.NOT_FOUND); throw new CustomApiException("NOT_FOUND_TARGET_YEAR", HttpStatus.NOT_FOUND);
} }
// 비교연도 실행가능 도엽 조회 // 비교연도 실행가능 도엽 조회
List<MngListDto> compareMngList = List<MngListDto> compareMngList =
mapSheetMngCoreService.getMapSheetMngHst( mapSheetMngCoreService.getMapSheetMngHst(compareYyyy, req.getMapSheetNum());
req.getCompareYyyy(), req.getMapSheetScope(), req.getMapSheetNum());
log.info("[runPrev] compareMngList size = {}", compareMngList.size());
if (compareMngList == null || compareMngList.isEmpty()) { if (compareMngList == null || compareMngList.isEmpty()) {
throw new CustomApiException("NOT_FOUND_COMPARE_YEAR", HttpStatus.NOT_FOUND); throw new CustomApiException("NOT_FOUND_COMPARE_YEAR", HttpStatus.NOT_FOUND);
} }
log.info("targetMngList size = {}", targetMngList.size()); log.info("[runPrev] Difference in count = {}", targetMngList.size() - compareMngList.size());
log.info("compareMngList size = {}", compareMngList.size());
log.info("Difference in count = {}", targetMngList.size() - compareMngList.size());
// 로그용 원본 카운트 (이전도엽 추가 전) // 로그용 원본 카운트 (이전도엽 추가 전)
int targetTotal = targetMngList.size(); int targetTotal = targetMngList.size();
@@ -310,14 +286,14 @@ public class InferenceResultService {
.filter(num -> !compareSet0.contains(num)) .filter(num -> !compareSet0.contains(num))
.toList(); .toList();
log.info("targetOnlyMapSheetNums in count = {}", targetOnlyMapSheetNums.size()); log.info("[runPrev] targetOnlyMapSheetNums in count = {}", targetOnlyMapSheetNums.size());
// 이전연도 초회 추가 // 이전연도 초회 추가
compareMngList.addAll( compareMngList.addAll(
mapSheetMngCoreService.findFallbackCompareYearByMapSheets( mapSheetMngCoreService.findFallbackCompareYearByMapSheets(
req.getCompareYyyy(), targetOnlyMapSheetNums)); compareYyyy, targetOnlyMapSheetNums));
log.info("fallback compare size= {}", compareMngList.size()); log.info("[runPrev] fallback compare size= {}", compareMngList.size());
// 이전연도 추가 후 compare 총 개수 // 이전연도 추가 후 compare 총 개수
int compareTotalAfterFallback = compareMngList.size(); int compareTotalAfterFallback = compareMngList.size();
@@ -361,15 +337,15 @@ public class InferenceResultService {
log.info( log.info(
""" """
===== MapSheet Year Comparison ===== ===== MapSheet Year Comparison =====
target Total: {} target Total: {}
compare Total(before fallback): {} compare Total(before fallback): {}
compare Total(after fallback): {} compare Total(after fallback): {}
Intersection: {} Intersection: {}
target Only (Excluded): {} target Only (Excluded): {}
compare Only: {} compare Only: {}
==================================== ====================================
""", """,
targetTotal, targetTotal,
compareTotalBeforeFallback, compareTotalBeforeFallback,
compareTotalAfterFallback, compareTotalAfterFallback,
@@ -384,18 +360,12 @@ public class InferenceResultService {
// compare 기준 geojson 생성 // compare 기준 geojson 생성
Scene compareScene = Scene compareScene =
getSceneInference( getSceneInference(
compareMngList, compareMngList, compareYyyy.toString(), mapSheetScope, req.getDetectOption());
req.getCompareYyyy().toString(),
req.getMapSheetScope(),
req.getDetectOption());
// target 기준 geojson 생성 // target 기준 geojson 생성
Scene targetScene = Scene targetScene =
getSceneInference( getSceneInference(
req.getTargetYyyy().toString(), targetYyyy.toString(), mapSheetNums, mapSheetScope, req.getDetectOption());
mapSheetNums,
req.getMapSheetScope(),
req.getDetectOption());
log.info("비교년도 geojson 파일 validation ===== {}", compareScene.getFilePath()); log.info("비교년도 geojson 파일 validation ===== {}", compareScene.getFilePath());
GeoJsonValidator.validateWithRequested(compareScene.getFilePath(), mapSheetNums); GeoJsonValidator.validateWithRequested(compareScene.getFilePath(), mapSheetNums);
@@ -671,7 +641,7 @@ public class InferenceResultService {
* @return * @return
*/ */
private Scene getSceneInference( private Scene getSceneInference(
String yyyy, List<String> mapSheetNums, String mapSheetScope, String detectOption) { String yyyy, List<String> mapSheetNums, String mapSheetScope, DetectOption detectOption) {
// geojson 생성시 필요한 영상파일 정보 조회 // geojson 생성시 필요한 영상파일 정보 조회
List<ImageFeature> features = List<ImageFeature> features =
@@ -697,7 +667,7 @@ public class InferenceResultService {
* @return * @return
*/ */
private Scene getSceneInference( private Scene getSceneInference(
List<MngListDto> yearDtos, String yyyy, String mapSheetScope, String detectOption) { List<MngListDto> yearDtos, String yyyy, String mapSheetScope, DetectOption detectOption) {
List<ImageFeature> features = List<ImageFeature> features =
mapSheetMngCoreService.loadSceneInferenceByFallbackYears(yearDtos); mapSheetMngCoreService.loadSceneInferenceByFallbackYears(yearDtos);
@@ -982,7 +952,10 @@ public class InferenceResultService {
* @return Scene * @return Scene
*/ */
private Scene writeSceneGeoJson( private Scene writeSceneGeoJson(
String yyyy, String mapSheetScope, String detectOption, List<ImageFeature> sceneInference) { String yyyy,
String mapSheetScope,
DetectOption detectOption,
List<ImageFeature> sceneInference) {
boolean isAll = MapSheetScope.ALL.getId().equals(mapSheetScope); boolean isAll = MapSheetScope.ALL.getId().equals(mapSheetScope);
String optionSuffix = buildOptionSuffix(detectOption); String optionSuffix = buildOptionSuffix(detectOption);
@@ -1031,9 +1004,23 @@ public class InferenceResultService {
* @param detectOption * @param detectOption
* @return * @return
*/ */
private String buildOptionSuffix(String detectOption) { private String buildOptionSuffix(DetectOption detectOption) {
if (DetectOption.EXCL.getId().equals(detectOption)) return "_EXCL"; if (DetectOption.EXCL == detectOption) {
if (DetectOption.PREV.getId().equals(detectOption)) return "_PREV"; return "_EXCL";
}
if (DetectOption.PREV == detectOption) {
return "_PREV";
}
return ""; return "";
} }
public ApiResponseDto.ResponseObj deleteInference(UUID uuid) {
long result = inferenceResultCoreService.deleteInference(uuid);
if (result > 0) {
return new ApiResponseDto.ResponseObj(ApiResponseDto.ApiResponseCode.OK, "삭제되었습니다.");
} else {
return new ApiResponseDto.ResponseObj(
ApiResponseDto.ApiResponseCode.INTERNAL_SERVER_ERROR, "삭제 실패하였습니다. 관리자에게 문의해주세요.");
}
}
} }

View File

@@ -37,6 +37,11 @@ public class LabelAllocateDto {
public String getText() { public String getText() {
return desc; return desc;
} }
@Override
public String getTextEn() {
return name();
}
} }
@CodeExpose @CodeExpose
@@ -59,6 +64,11 @@ public class LabelAllocateDto {
public String getText() { public String getText() {
return desc; return desc;
} }
@Override
public String getTextEn() {
return name();
}
} }
@CodeExpose @CodeExpose
@@ -80,6 +90,11 @@ public class LabelAllocateDto {
public String getText() { public String getText() {
return desc; return desc;
} }
@Override
public String getTextEn() {
return name();
}
} }
@Getter @Getter

View File

@@ -1,6 +1,8 @@
package com.kamco.cd.kamcoback.label.dto; package com.kamco.cd.kamcoback.label.dto;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import com.kamco.cd.kamcoback.common.enums.RoleType;
import com.kamco.cd.kamcoback.common.utils.HeaderUtil;
import com.kamco.cd.kamcoback.common.utils.enums.Enums; import com.kamco.cd.kamcoback.common.utils.enums.Enums;
import com.kamco.cd.kamcoback.common.utils.interfaces.JsonFormatDttm; 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.LabelAllocateDto.LabelMngState;
@@ -34,6 +36,7 @@ public class LabelWorkDto {
@AllArgsConstructor @AllArgsConstructor
public static class LabelWorkMng { public static class LabelWorkMng {
private Long analUid;
private UUID uuid; private UUID uuid;
private Integer compareYyyy; private Integer compareYyyy;
private Integer targetYyyy; private Integer targetYyyy;
@@ -108,7 +111,13 @@ public class LabelWorkDto {
if (type == null) { if (type == null) {
return enumId; return enumId;
} }
return type.getText();
boolean english = HeaderUtil.isEnglishRequest();
if (!english) {
return type.getText();
}
return type.getTextEn();
} }
/** /**
@@ -222,10 +231,8 @@ public class LabelWorkDto {
} }
public String getUserRoleName() { public String getUserRoleName() {
if (this.userRole.equals("LABELER")) { RoleType type = Enums.fromId(RoleType.class, this.userRole);
return "라벨러"; return HeaderUtil.isEnglishRequest() ? type.getTextEn() : type.getText();
}
return "검수자";
} }
} }
@@ -260,4 +267,18 @@ public class LabelWorkDto {
return PageRequest.of(page, size); return PageRequest.of(page, size);
} }
} }
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public static class LabelCntInfo {
private Long assignedCnt;
private Long skipCnt;
private Long doneCnt;
private Long unconfirmCnt;
private Long exceptCnt;
private Long completeCnt;
}
} }

View File

@@ -157,6 +157,7 @@ public class LabelAllocateService {
return labelAllocateCoreService.findInferenceDetail(uuid); return labelAllocateCoreService.findInferenceDetail(uuid);
} }
@Transactional
public ApiResponseDto.ResponseObj allocateMove( public ApiResponseDto.ResponseObj allocateMove(
Integer totalCnt, String uuid, List<String> targetUsers, String userId) { Integer totalCnt, String uuid, List<String> targetUsers, String userId) {

View File

@@ -1,7 +1,10 @@
package com.kamco.cd.kamcoback.log.dto; package com.kamco.cd.kamcoback.log.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.kamco.cd.kamcoback.common.utils.HeaderUtil;
import com.kamco.cd.kamcoback.common.utils.enums.CodeExpose; import com.kamco.cd.kamcoback.common.utils.enums.CodeExpose;
import com.kamco.cd.kamcoback.common.utils.enums.EnumType; import com.kamco.cd.kamcoback.common.utils.enums.EnumType;
import com.kamco.cd.kamcoback.config.api.ApiResponseDto;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import java.time.LocalDate; import java.time.LocalDate;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
@@ -36,6 +39,11 @@ public class ErrorLogDto {
public String getText() { public String getText() {
return desc; return desc;
} }
@Override
public String getTextEn() {
return desc;
}
} }
@Schema(name = "ErrorLogBasic", description = "에러로그 기본 정보") @Schema(name = "ErrorLogBasic", description = "에러로그 기본 정보")
@@ -56,6 +64,26 @@ public class ErrorLogDto {
private final String errorMessage; private final String errorMessage;
private final String errorDetail; private final String errorDetail;
private final String createDate; // to_char해서 가져옴 private final String createDate; // to_char해서 가져옴
@JsonProperty("errorMessage")
public String getErrorMessage() {
boolean english = HeaderUtil.isEnglishRequest();
if (!english) {
return this.errorMessage;
}
return ApiResponseDto.ApiResponseCode.fromMessage(this.errorMessage).toString();
}
@JsonProperty("errorName")
public String getErrorName() {
boolean english = HeaderUtil.isEnglishRequest();
if (!english) {
return this.errorName;
}
return ApiResponseDto.ApiResponseCode.fromMessage(this.errorName).toString();
}
} }
@Schema(name = "ErrorSearchReq", description = "에러로그 검색 요청") @Schema(name = "ErrorSearchReq", description = "에러로그 검색 요청")

View File

@@ -21,4 +21,9 @@ public enum EventStatus implements EnumType {
public String getText() { public String getText() {
return desc; return desc;
} }
@Override
public String getTextEn() {
return desc;
}
} }

View File

@@ -39,4 +39,9 @@ public enum EventType implements EnumType {
public String getText() { public String getText() {
return desc; return desc;
} }
@Override
public String getTextEn() {
return name();
}
} }

View File

@@ -22,14 +22,7 @@ import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
@Tag(name = "영상 관리", description = "영상 관리 API") @Tag(name = "영상 관리", description = "영상 관리 API")
@@ -256,4 +249,11 @@ public class MapSheetMngApiController {
return ApiResponseDto.ok( return ApiResponseDto.ok(
mapSheetMngService.uploadChunkMapSheetFile(hstUid, upAddReqDto, chunkFile)); mapSheetMngService.uploadChunkMapSheetFile(hstUid, upAddReqDto, chunkFile));
} }
@Operation(summary = "영상데이터관리 > 등록된 년도 데이터 전체 삭제", description = "영상데이터관리 > 등록된 년도 데이터 전체 삭제")
@DeleteMapping
public ApiResponseDto<Void> deleteByMngYyyyMngAll(@RequestParam Integer mngYyyy) {
mapSheetMngService.deleteByMngYyyyMngAll(mngYyyy);
return ApiResponseDto.ok(null);
}
} }

View File

@@ -3,6 +3,7 @@ package com.kamco.cd.kamcoback.mapsheet.dto;
import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnore;
import com.kamco.cd.kamcoback.common.enums.MngStateType; import com.kamco.cd.kamcoback.common.enums.MngStateType;
import com.kamco.cd.kamcoback.common.enums.SyncStateType; import com.kamco.cd.kamcoback.common.enums.SyncStateType;
import com.kamco.cd.kamcoback.common.utils.HeaderUtil;
import com.kamco.cd.kamcoback.common.utils.enums.EnumType; import com.kamco.cd.kamcoback.common.utils.enums.EnumType;
import com.kamco.cd.kamcoback.common.utils.enums.Enums; import com.kamco.cd.kamcoback.common.utils.enums.Enums;
import com.kamco.cd.kamcoback.common.utils.interfaces.JsonFormatDttm; import com.kamco.cd.kamcoback.common.utils.interfaces.JsonFormatDttm;
@@ -41,6 +42,11 @@ public class MapSheetMngDto {
public String getText() { public String getText() {
return message; return message;
} }
@Override
public String getTextEn() {
return message;
}
} }
@Schema(name = "MngSearchReq", description = "영상관리 검색 요청") @Schema(name = "MngSearchReq", description = "영상관리 검색 요청")
@@ -217,7 +223,11 @@ public class MapSheetMngDto {
enumId = "NOTYET"; enumId = "NOTYET";
} }
boolean english = HeaderUtil.isEnglishRequest();
MngStateType type = Enums.fromId(MngStateType.class, enumId); MngStateType type = Enums.fromId(MngStateType.class, enumId);
if (english) {
return type.getTextEn();
}
return type.getText(); return type.getText();
} }
} }
@@ -341,6 +351,10 @@ public class MapSheetMngDto {
} }
SyncStateType type = Enums.fromId(SyncStateType.class, enumId); SyncStateType type = Enums.fromId(SyncStateType.class, enumId);
boolean english = HeaderUtil.isEnglishRequest();
if (english) {
return type.getTextEn();
}
return type.getText(); return type.getText();
} }
} }

View File

@@ -40,7 +40,7 @@ import org.springframework.web.multipart.MultipartFile;
@Slf4j @Slf4j
@Service @Service
@RequiredArgsConstructor @RequiredArgsConstructor
@Transactional(readOnly = true) @Transactional
public class MapSheetMngService { public class MapSheetMngService {
private final MapSheetMngCoreService mapSheetMngCoreService; private final MapSheetMngCoreService mapSheetMngCoreService;
@@ -489,4 +489,8 @@ public class MapSheetMngService {
return modelUploadResDto; return modelUploadResDto;
} }
public void deleteByMngYyyyMngAll(Integer mngYyyy) {
mapSheetMngCoreService.deleteByMngYyyyMngAll(mngYyyy);
}
} }

View File

@@ -4,6 +4,7 @@ import com.kamco.cd.kamcoback.auth.CustomUserDetails;
import com.kamco.cd.kamcoback.auth.JwtTokenProvider; import com.kamco.cd.kamcoback.auth.JwtTokenProvider;
import com.kamco.cd.kamcoback.auth.RefreshTokenService; import com.kamco.cd.kamcoback.auth.RefreshTokenService;
import com.kamco.cd.kamcoback.common.enums.StatusType; import com.kamco.cd.kamcoback.common.enums.StatusType;
import com.kamco.cd.kamcoback.config.api.ApiLogFunction;
import com.kamco.cd.kamcoback.config.api.ApiResponseDto; import com.kamco.cd.kamcoback.config.api.ApiResponseDto;
import com.kamco.cd.kamcoback.members.dto.MembersDto; import com.kamco.cd.kamcoback.members.dto.MembersDto;
import com.kamco.cd.kamcoback.members.dto.SignInRequest; import com.kamco.cd.kamcoback.members.dto.SignInRequest;
@@ -16,11 +17,13 @@ import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
import java.nio.file.AccessDeniedException; import java.nio.file.AccessDeniedException;
import java.time.Duration; import java.time.Duration;
import java.util.UUID; import java.util.UUID;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpHeaders; import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseCookie; import org.springframework.http.ResponseCookie;
@@ -34,6 +37,7 @@ import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
@Slf4j
@Tag(name = "인증(Auth)", description = "로그인, 토큰 재발급, 로그아웃 API") @Tag(name = "인증(Auth)", description = "로그인, 토큰 재발급, 로그아웃 API")
@RestController @RestController
@RequestMapping("/api/auth") @RequestMapping("/api/auth")
@@ -103,8 +107,13 @@ public class AuthController {
required = true) required = true)
@RequestBody @RequestBody
SignInRequest request, SignInRequest request,
HttpServletRequest servletRequest,
HttpServletResponse response) { HttpServletResponse response) {
// TODO: 접속 가능한 IP 대역 조회
String clientIp = ApiLogFunction.getXFowardedForIp(servletRequest);
log.info("####### clientIp: {}", clientIp);
// 사용자 상태 조회 // 사용자 상태 조회
String status = authService.getUserStatus(request); String status = authService.getUserStatus(request);
Authentication authentication = null; Authentication authentication = null;
@@ -169,6 +178,7 @@ public class AuthController {
if (refreshToken == null || !jwtTokenProvider.isValidToken(refreshToken)) { if (refreshToken == null || !jwtTokenProvider.isValidToken(refreshToken)) {
throw new AccessDeniedException("만료되었거나 유효하지 않은 리프레시 토큰 입니다."); throw new AccessDeniedException("만료되었거나 유효하지 않은 리프레시 토큰 입니다.");
} }
String username = jwtTokenProvider.getSubject(refreshToken); String username = jwtTokenProvider.getSubject(refreshToken);
// Redis에 저장된 RefreshToken과 일치하는지 확인 // Redis에 저장된 RefreshToken과 일치하는지 확인

View File

@@ -3,6 +3,7 @@ package com.kamco.cd.kamcoback.members.dto;
import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnore;
import com.kamco.cd.kamcoback.common.enums.RoleType; import com.kamco.cd.kamcoback.common.enums.RoleType;
import com.kamco.cd.kamcoback.common.enums.StatusType; import com.kamco.cd.kamcoback.common.enums.StatusType;
import com.kamco.cd.kamcoback.common.utils.HeaderUtil;
import com.kamco.cd.kamcoback.common.utils.enums.Enums; import com.kamco.cd.kamcoback.common.utils.enums.Enums;
import com.kamco.cd.kamcoback.common.utils.interfaces.EnumValid; import com.kamco.cd.kamcoback.common.utils.interfaces.EnumValid;
import com.kamco.cd.kamcoback.common.utils.interfaces.JsonFormatDttm; import com.kamco.cd.kamcoback.common.utils.interfaces.JsonFormatDttm;
@@ -65,16 +66,18 @@ public class MembersDto {
private String getUserRoleName(String roleId) { private String getUserRoleName(String roleId) {
RoleType type = Enums.fromId(RoleType.class, roleId); RoleType type = Enums.fromId(RoleType.class, roleId);
return type.getText(); boolean english = HeaderUtil.isEnglishRequest();
return english ? type.getTextEn() : type.getText();
} }
private String getStatusName(String status, Boolean pwdResetYn) { private String getStatusName(String status, Boolean pwdResetYn) {
StatusType type = Enums.fromId(StatusType.class, status); StatusType type = Enums.fromId(StatusType.class, status);
boolean english = HeaderUtil.isEnglishRequest();
pwdResetYn = pwdResetYn != null && pwdResetYn; pwdResetYn = pwdResetYn != null && pwdResetYn;
if (type.equals(StatusType.PENDING) && pwdResetYn) { if (type.equals(StatusType.PENDING) && pwdResetYn) {
type = StatusType.ACTIVE; type = StatusType.ACTIVE;
} }
return type.getText(); return english ? type.getTextEn() : type.getText();
} }
} }

View File

@@ -31,7 +31,7 @@ public class MenuDto {
@JsonFormatDttm private ZonedDateTime updatedDttm; @JsonFormatDttm private ZonedDateTime updatedDttm;
private String menuApiUrl; private String menuNmEn;
public Basic( public Basic(
String menuUid, String menuUid,
@@ -45,7 +45,8 @@ public class MenuDto {
Long updatedUid, Long updatedUid,
List<MenuDto.Basic> children, List<MenuDto.Basic> children,
ZonedDateTime createdDttm, ZonedDateTime createdDttm,
ZonedDateTime updatedDttm) { ZonedDateTime updatedDttm,
String menuNmEn) {
this.menuUid = menuUid; this.menuUid = menuUid;
this.menuNm = menuNm; this.menuNm = menuNm;
this.menuUrl = menuUrl; this.menuUrl = menuUrl;
@@ -58,6 +59,7 @@ public class MenuDto {
this.children = children; this.children = children;
this.createdDttm = createdDttm; this.createdDttm = createdDttm;
this.updatedDttm = updatedDttm; this.updatedDttm = updatedDttm;
this.menuNmEn = menuNmEn;
} }
} }

View File

@@ -36,6 +36,11 @@ public class ModelMngDto {
public String getText() { public String getText() {
return desc; return desc;
} }
@Override
public String getTextEn() {
return desc;
}
} }
@Schema(name = "ModelMgmtDto Basic", description = "모델관리 엔티티 기본 정보") @Schema(name = "ModelMgmtDto Basic", description = "모델관리 엔티티 기본 정보")

View File

@@ -15,8 +15,8 @@ public class GukYuinPnuCntUpdateJobCoreService {
} }
@Transactional @Transactional
public int updateGukYuinContListPnuUpdateCnt(String uid) { public void updateGukYuinContListPnuUpdateCnt() {
return gukYuinPnuCntUpdateRepository.updateGukYuinContListPnuUpdateCnt(uid); gukYuinPnuCntUpdateRepository.updateGukYuinContListPnuUpdateCnt();
} }
@Transactional @Transactional

View File

@@ -47,7 +47,7 @@ import java.util.function.Consumer;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2; import lombok.extern.slf4j.Slf4j;
import org.springframework.dao.DataAccessException; import org.springframework.dao.DataAccessException;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
@@ -55,7 +55,7 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@Service @Service
@Log4j2 @Slf4j
@RequiredArgsConstructor @RequiredArgsConstructor
public class InferenceResultCoreService { public class InferenceResultCoreService {
@@ -120,7 +120,7 @@ public class InferenceResultCoreService {
mapSheetLearnEntity.setCompareYyyy(req.getCompareYyyy()); mapSheetLearnEntity.setCompareYyyy(req.getCompareYyyy());
mapSheetLearnEntity.setTargetYyyy(req.getTargetYyyy()); mapSheetLearnEntity.setTargetYyyy(req.getTargetYyyy());
mapSheetLearnEntity.setMapSheetScope(req.getMapSheetScope()); mapSheetLearnEntity.setMapSheetScope(req.getMapSheetScope());
mapSheetLearnEntity.setDetectOption(req.getDetectOption()); mapSheetLearnEntity.setDetectOption(req.getDetectOption().getId());
mapSheetLearnEntity.setCreatedUid(userUtil.getId()); mapSheetLearnEntity.setCreatedUid(userUtil.getId());
mapSheetLearnEntity.setMapSheetCnt(mapSheetName); mapSheetLearnEntity.setMapSheetCnt(mapSheetName);
mapSheetLearnEntity.setDetectingCnt(0L); mapSheetLearnEntity.setDetectingCnt(0L);
@@ -502,9 +502,13 @@ public class InferenceResultCoreService {
*/ */
@Transactional @Transactional
public void upsertGeomData(Long id) { public void upsertGeomData(Long id) {
// 추론 결과 목록 저장
Long analId = inferenceResultRepository.upsertGroupsFromMapSheetAnal(id); Long analId = inferenceResultRepository.upsertGroupsFromMapSheetAnal(id);
// 추론 결과 상세 저장
inferenceResultRepository.upsertGroupsFromInferenceResults(analId); inferenceResultRepository.upsertGroupsFromInferenceResults(analId);
// geom 목록 추론 결과 저장
inferenceResultRepository.upsertGeomsFromInferenceResults(analId); inferenceResultRepository.upsertGeomsFromInferenceResults(analId);
// 집계 추론 결과 저장
inferenceResultRepository.upsertSttcFromInferenceResults(analId); inferenceResultRepository.upsertSttcFromInferenceResults(analId);
} }
@@ -622,4 +626,8 @@ public class InferenceResultCoreService {
public List<InferenceResultsTestingDto.Basic> getInferenceResultGroupList(List<Long> batchIds) { public List<InferenceResultsTestingDto.Basic> getInferenceResultGroupList(List<Long> batchIds) {
return inferenceResultsTestingRepository.getInferenceResultGroupList(batchIds); return inferenceResultsTestingRepository.getInferenceResultGroupList(batchIds);
} }
public long deleteInference(UUID uuid) {
return inferenceResultRepository.deleteInference(uuid);
}
} }

View File

@@ -1,9 +1,12 @@
package com.kamco.cd.kamcoback.postgres.core; package com.kamco.cd.kamcoback.postgres.core;
import com.kamco.cd.kamcoback.common.exception.CustomApiException;
import com.kamco.cd.kamcoback.inference.dto.InferenceResultShpDto; import com.kamco.cd.kamcoback.inference.dto.InferenceResultShpDto;
import com.kamco.cd.kamcoback.postgres.entity.MapSheetAnalInferenceEntity;
import com.kamco.cd.kamcoback.postgres.repository.Inference.InferenceResultRepository; import com.kamco.cd.kamcoback.postgres.repository.Inference.InferenceResultRepository;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2; import lombok.extern.log4j.Log4j2;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@@ -24,6 +27,12 @@ public class InferenceResultShpCoreService {
@Transactional @Transactional
public InferenceResultShpDto.InferenceCntDto buildInferenceData(Long id) { public InferenceResultShpDto.InferenceCntDto buildInferenceData(Long id) {
MapSheetAnalInferenceEntity analInferenceEntity =
repo.getAnalInferenceDataByLearnId(id).orElse(null);
if (analInferenceEntity != null) {
throw new CustomApiException("CONFLICT", HttpStatus.CONFLICT);
}
Long analId = repo.upsertGroupsFromMapSheetAnal(id); Long analId = repo.upsertGroupsFromMapSheetAnal(id);
int analDataCnt = repo.upsertGroupsFromInferenceResults(analId); int analDataCnt = repo.upsertGroupsFromInferenceResults(analId);
int geomCnt = repo.upsertGeomsFromInferenceResults(analId); int geomCnt = repo.upsertGeomsFromInferenceResults(analId);

View File

@@ -345,16 +345,15 @@ public class MapSheetMngCoreService {
* 변화탐지 실행 가능 비교년도 조회 * 변화탐지 실행 가능 비교년도 조회
* *
* @param mngYyyy 비교년도 * @param mngYyyy 비교년도
* @param mapId 5k 도엽번호 * @param mapIds 5k 도엽번호
* @return List<MngListCompareDto> * @return List<MngListCompareDto>
*/ */
public List<MngListCompareDto> getByHstMapSheetCompareList(int mngYyyy, List<String> mapId) { public List<MngListCompareDto> getByHstMapSheetCompareList(int mngYyyy, List<String> mapIds) {
return mapSheetMngYearRepository.findByHstMapSheetCompareList(mngYyyy, mapId); return mapSheetMngYearRepository.findByHstMapSheetCompareList(mngYyyy, mapIds);
} }
public List<MngListDto> getMapSheetMngHst( public List<MngListDto> getMapSheetMngHst(Integer year, List<String> mapSheetNums50k) {
Integer year, String mapSheetScope, List<String> mapSheetNum) { return mapSheetMngRepository.getMapSheetMngHst(year, mapSheetNums50k);
return mapSheetMngRepository.getMapSheetMngHst(year, mapSheetScope, mapSheetNum);
} }
/** /**
@@ -380,4 +379,8 @@ public class MapSheetMngCoreService {
return result; return result;
} }
public void deleteByMngYyyyMngAll(Integer mngYyyy) {
mapSheetMngRepository.deleteByMngYyyyMngAll(mngYyyy);
}
} }

View File

@@ -1,5 +1,6 @@
package com.kamco.cd.kamcoback.postgres.core; package com.kamco.cd.kamcoback.postgres.core;
import com.kamco.cd.kamcoback.common.utils.HeaderUtil;
import com.kamco.cd.kamcoback.menu.dto.MenuDto; import com.kamco.cd.kamcoback.menu.dto.MenuDto;
import com.kamco.cd.kamcoback.menu.dto.MyMenuDto; import com.kamco.cd.kamcoback.menu.dto.MyMenuDto;
import com.kamco.cd.kamcoback.postgres.entity.MenuEntity; import com.kamco.cd.kamcoback.postgres.entity.MenuEntity;
@@ -7,8 +8,10 @@ import com.kamco.cd.kamcoback.postgres.repository.menu.MenuRepository;
import java.util.Comparator; import java.util.Comparator;
import java.util.List; import java.util.List;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@Slf4j
@Service @Service
@RequiredArgsConstructor @RequiredArgsConstructor
public class MenuCoreService { public class MenuCoreService {
@@ -27,6 +30,8 @@ public class MenuCoreService {
*/ */
public List<MyMenuDto.Basic> getFindByRole(String role) { public List<MyMenuDto.Basic> getFindByRole(String role) {
List<MenuEntity> entities = menuRepository.getFindByRole(role); List<MenuEntity> entities = menuRepository.getFindByRole(role);
boolean english = HeaderUtil.isEnglishRequest();
log.info("[LANG CHECK] english={}", english);
return entities.stream() return entities.stream()
.map( .map(
@@ -34,7 +39,7 @@ public class MenuCoreService {
MyMenuDto.Basic p = MyMenuDto.Basic p =
new MyMenuDto.Basic( new MyMenuDto.Basic(
parent.getMenuUid(), parent.getMenuUid(),
parent.getMenuNm(), english ? parent.getMenuNmEn() : parent.getMenuNm(),
parent.getMenuUrl(), parent.getMenuUrl(),
parent.getMenuOrder()); parent.getMenuOrder());
@@ -48,7 +53,10 @@ public class MenuCoreService {
.map( .map(
c -> c ->
new MyMenuDto.Basic( new MyMenuDto.Basic(
c.getMenuUid(), c.getMenuNm(), c.getMenuUrl(), c.getMenuOrder())) c.getMenuUid(),
english ? c.getMenuNmEn() : c.getMenuNm(),
c.getMenuUrl(),
c.getMenuOrder()))
.forEach(childDto -> p.getChildren().add(childDto)); .forEach(childDto -> p.getChildren().add(childDto));
return p; return p;

View File

@@ -229,6 +229,9 @@ public class MapSheetLearnEntity {
@Column(name = "shp_error_message") @Column(name = "shp_error_message")
private String shp_error_message; private String shp_error_message;
@Column(name = "is_deleted")
private Boolean isDeleted;
public InferenceResultDto.ResultList toDto() { public InferenceResultDto.ResultList toDto() {
return new InferenceResultDto.ResultList( return new InferenceResultDto.ResultList(
this.uuid, this.uuid,

View File

@@ -53,8 +53,6 @@ import lombok.NoArgsConstructor;
* system leveraging 1:5k map data. * system leveraging 1:5k map data.
*/ */
@Getter @Getter
// entity의 접근제어를 위해 @setter를 사용 x
// @Setter
@NoArgsConstructor(access = AccessLevel.PROTECTED) @NoArgsConstructor(access = AccessLevel.PROTECTED)
@Entity @Entity
// 영상관리이력 // 영상관리이력
@@ -92,7 +90,7 @@ public class MapSheetMngHstEntity extends CommonDateEntity {
private Integer scaleRatio; private Integer scaleRatio;
@Column(name = "data_state", length = 20) @Column(name = "data_state", length = 20)
private String dataState; private String dataState; // DONE,NOTYET 둘중하나임 같은연도는 같은값
@Column(name = "data_state_dttm") @Column(name = "data_state_dttm")
private ZonedDateTime dataStateDttm; private ZonedDateTime dataStateDttm;
@@ -165,13 +163,4 @@ public class MapSheetMngHstEntity extends CommonDateEntity {
@Column(name = "upload_id") @Column(name = "upload_id")
private String uploadId; private String uploadId;
// 파일정보 업데이트
public void updateFileInfos(Long tifSizeBytes, Long tfwSizeBytes) {
tifSizeBytes = tifSizeBytes == null ? 0L : tifSizeBytes;
tfwSizeBytes = tfwSizeBytes == null ? 0L : tfwSizeBytes;
this.tifSizeBytes = tifSizeBytes;
this.tfwSizeBytes = tfwSizeBytes;
this.totalSizeBytes = tifSizeBytes + tfwSizeBytes;
}
} }

View File

@@ -58,6 +58,9 @@ public class MenuEntity extends CommonDateEntity {
@OneToMany(mappedBy = "parent", fetch = FetchType.LAZY, cascade = CascadeType.ALL) @OneToMany(mappedBy = "parent", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private List<MenuEntity> children = new ArrayList<>(); private List<MenuEntity> children = new ArrayList<>();
@Column(name = "menu_nm_en")
private String menuNmEn; // 영문 메뉴명
public MenuDto.Basic toDto() { public MenuDto.Basic toDto() {
return new MenuDto.Basic( return new MenuDto.Basic(
this.menuUid, this.menuUid,
@@ -71,6 +74,7 @@ public class MenuEntity extends CommonDateEntity {
this.updatedUid, this.updatedUid,
this.children.stream().map(MenuEntity::toDto).toList(), this.children.stream().map(MenuEntity::toDto).toList(),
this.getCreatedDate(), this.getCreatedDate(),
this.getModifiedDate()); this.getModifiedDate(),
this.menuNmEn);
} }
} }

View File

@@ -1,19 +1,50 @@
package com.kamco.cd.kamcoback.postgres.repository.Inference; package com.kamco.cd.kamcoback.postgres.repository.Inference;
import com.kamco.cd.kamcoback.postgres.entity.MapSheetAnalInferenceEntity;
import com.kamco.cd.kamcoback.postgres.entity.MapSheetLearnEntity; import com.kamco.cd.kamcoback.postgres.entity.MapSheetLearnEntity;
import java.util.Optional; import java.util.Optional;
import java.util.UUID; import java.util.UUID;
public interface InferenceResultRepositoryCustom { public interface InferenceResultRepositoryCustom {
/**
* tb_map_sheet_anal_inference 추론 결과 목록 저장
*
* @param id learn 테이블 id
* @return
*/
Long upsertGroupsFromMapSheetAnal(Long id); Long upsertGroupsFromMapSheetAnal(Long id);
/**
* tb_map_sheet_anal_data_inference 추론 결과 상세 저장
*
* @param analId
* @return
*/
int upsertGroupsFromInferenceResults(Long analId); int upsertGroupsFromInferenceResults(Long analId);
/**
* tb_map_sheet_anal_data_inference_geom geom 목록 추론 결과 저장
*
* @param analId
* @return
*/
int upsertGeomsFromInferenceResults(Long analId); int upsertGeomsFromInferenceResults(Long analId);
/**
* tb_map_sheet_anal_sttc 집계 추론 결과 저장
*
* @param analId
* @return
*/
int upsertSttcFromInferenceResults(Long analId); int upsertSttcFromInferenceResults(Long analId);
/**
* 추론실행 목록 uuid 조회
*
* @param uuid 추론 uuid
* @return 추론 실행 정보
*/
Long getInferenceLearnIdByUuid(UUID uuid); Long getInferenceLearnIdByUuid(UUID uuid);
/** /**
@@ -23,4 +54,19 @@ public interface InferenceResultRepositoryCustom {
* @return 추론 정보 * @return 추론 정보
*/ */
Optional<MapSheetLearnEntity> getInferenceUid(UUID uuid); Optional<MapSheetLearnEntity> getInferenceUid(UUID uuid);
/**
* learn id 로 analInference 값 조회
*
* @param id 추론 id
* @return
*/
Optional<MapSheetAnalInferenceEntity> getAnalInferenceDataByLearnId(Long id);
/**
* 추론 삭제
*
* @param uuid
*/
long deleteInference(UUID uuid);
} }

View File

@@ -1,8 +1,10 @@
package com.kamco.cd.kamcoback.postgres.repository.Inference; package com.kamco.cd.kamcoback.postgres.repository.Inference;
import static com.kamco.cd.kamcoback.postgres.entity.QMapSheetAnalInferenceEntity.mapSheetAnalInferenceEntity;
import static com.kamco.cd.kamcoback.postgres.entity.QMapSheetLearnEntity.mapSheetLearnEntity; import static com.kamco.cd.kamcoback.postgres.entity.QMapSheetLearnEntity.mapSheetLearnEntity;
import com.kamco.cd.kamcoback.label.dto.LabelAllocateDto.LabelMngState; import com.kamco.cd.kamcoback.label.dto.LabelAllocateDto.LabelMngState;
import com.kamco.cd.kamcoback.postgres.entity.MapSheetAnalInferenceEntity;
import com.kamco.cd.kamcoback.postgres.entity.MapSheetLearnEntity; import com.kamco.cd.kamcoback.postgres.entity.MapSheetLearnEntity;
import com.querydsl.jpa.impl.JPAQueryFactory; import com.querydsl.jpa.impl.JPAQueryFactory;
import jakarta.persistence.EntityManager; import jakarta.persistence.EntityManager;
@@ -214,6 +216,9 @@ public class InferenceResultRepositoryImpl implements InferenceResultRepositoryC
WHERE msl.anal_uid = :analUid WHERE msl.anal_uid = :analUid
AND r.after_c is not null AND r.after_c is not null
AND r.after_p is not null AND r.after_p is not null
AND r.probability is not null
AND r.before_c is not null
AND r.before_p is not null
ORDER BY r.uid, r.created_date DESC NULLS LAST ORDER BY r.uid, r.created_date DESC NULLS LAST
) x ) x
ON CONFLICT (result_uid) ON CONFLICT (result_uid)
@@ -331,4 +336,23 @@ public class InferenceResultRepositoryImpl implements InferenceResultRepositoryC
.where(mapSheetLearnEntity.uuid.eq(uuid)) .where(mapSheetLearnEntity.uuid.eq(uuid))
.fetchOne()); .fetchOne());
} }
@Override
public Optional<MapSheetAnalInferenceEntity> getAnalInferenceDataByLearnId(Long id) {
return Optional.ofNullable(
queryFactory
.select(mapSheetAnalInferenceEntity)
.from(mapSheetAnalInferenceEntity)
.where(mapSheetAnalInferenceEntity.learnId.eq(id))
.fetchOne());
}
@Override
public long deleteInference(UUID uuid) {
return queryFactory
.update(mapSheetLearnEntity)
.set(mapSheetLearnEntity.isDeleted, true)
.where(mapSheetLearnEntity.uuid.eq(uuid))
.execute();
}
} }

View File

@@ -23,11 +23,12 @@ public class InferenceResultsTestingRepositoryImpl
.select(inferenceResultsTestingEntity) .select(inferenceResultsTestingEntity)
.from(inferenceResultsTestingEntity) .from(inferenceResultsTestingEntity)
.where( .where(
inferenceResultsTestingEntity inferenceResultsTestingEntity.batchId.in(batchIds),
.batchId inferenceResultsTestingEntity.afterC.isNotNull(),
.in(batchIds) inferenceResultsTestingEntity.afterP.isNotNull(),
.and(inferenceResultsTestingEntity.afterC.isNotNull()) inferenceResultsTestingEntity.beforeC.isNotNull(),
.and(inferenceResultsTestingEntity.afterP.isNotNull())) inferenceResultsTestingEntity.beforeP.isNotNull(),
inferenceResultsTestingEntity.probability.isNotNull())
.fetch(); .fetch();
} }
@@ -44,7 +45,10 @@ public class InferenceResultsTestingRepositoryImpl
.where( .where(
inferenceResultsTestingEntity.batchId.in(batchIds), inferenceResultsTestingEntity.batchId.in(batchIds),
inferenceResultsTestingEntity.afterC.isNotNull(), inferenceResultsTestingEntity.afterC.isNotNull(),
inferenceResultsTestingEntity.afterP.isNotNull()) inferenceResultsTestingEntity.afterP.isNotNull(),
inferenceResultsTestingEntity.beforeC.isNotNull(),
inferenceResultsTestingEntity.beforeP.isNotNull(),
inferenceResultsTestingEntity.probability.isNotNull())
.fetchOne(); .fetchOne();
return cnt == null ? 0L : cnt; return cnt == null ? 0L : cnt;
@@ -62,11 +66,12 @@ public class InferenceResultsTestingRepositoryImpl
inferenceResultsTestingEntity.afterYear.max())) inferenceResultsTestingEntity.afterYear.max()))
.from(inferenceResultsTestingEntity) .from(inferenceResultsTestingEntity)
.where( .where(
inferenceResultsTestingEntity inferenceResultsTestingEntity.batchId.in(batchIds),
.batchId inferenceResultsTestingEntity.afterC.isNotNull(),
.in(batchIds) inferenceResultsTestingEntity.afterP.isNotNull(),
.and(inferenceResultsTestingEntity.afterC.isNotNull()) inferenceResultsTestingEntity.beforeC.isNotNull(),
.and(inferenceResultsTestingEntity.afterP.isNotNull())) inferenceResultsTestingEntity.beforeP.isNotNull(),
inferenceResultsTestingEntity.probability.isNotNull())
.groupBy( .groupBy(
inferenceResultsTestingEntity.batchId, inferenceResultsTestingEntity.batchId,
inferenceResultsTestingEntity.modelVersion, inferenceResultsTestingEntity.modelVersion,

View File

@@ -30,6 +30,7 @@ import com.kamco.cd.kamcoback.postgres.entity.QMapSheetAnalInferenceEntity;
import com.kamco.cd.kamcoback.postgres.entity.QMapSheetLearnEntity; import com.kamco.cd.kamcoback.postgres.entity.QMapSheetLearnEntity;
import com.kamco.cd.kamcoback.postgres.entity.QModelMngEntity; import com.kamco.cd.kamcoback.postgres.entity.QModelMngEntity;
import com.querydsl.core.BooleanBuilder; import com.querydsl.core.BooleanBuilder;
import com.querydsl.core.Tuple;
import com.querydsl.core.types.Expression; import com.querydsl.core.types.Expression;
import com.querydsl.core.types.Projections; import com.querydsl.core.types.Projections;
import com.querydsl.core.types.dsl.BooleanExpression; import com.querydsl.core.types.dsl.BooleanExpression;
@@ -37,13 +38,10 @@ import com.querydsl.core.types.dsl.CaseBuilder;
import com.querydsl.core.types.dsl.Expressions; import com.querydsl.core.types.dsl.Expressions;
import com.querydsl.core.types.dsl.NumberExpression; import com.querydsl.core.types.dsl.NumberExpression;
import com.querydsl.core.types.dsl.StringExpression; import com.querydsl.core.types.dsl.StringExpression;
import com.querydsl.jpa.JPAExpressions;
import com.querydsl.jpa.impl.JPAQueryFactory; import com.querydsl.jpa.impl.JPAQueryFactory;
import java.time.OffsetDateTime; import java.time.OffsetDateTime;
import java.util.List; import java.util.*;
import java.util.Objects; import java.util.stream.Collectors;
import java.util.Optional;
import java.util.UUID;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
@@ -90,6 +88,9 @@ public class MapSheetLearnRepositoryImpl implements MapSheetLearnRepositoryCusto
builder.and(mapSheetLearnEntity.title.containsIgnoreCase(req.getTitle())); builder.and(mapSheetLearnEntity.title.containsIgnoreCase(req.getTitle()));
} }
// 삭제여부 값 추가
builder.and(mapSheetLearnEntity.isDeleted.eq(false).or(mapSheetLearnEntity.isDeleted.isNull()));
List<MapSheetLearnEntity> content = List<MapSheetLearnEntity> content =
queryFactory queryFactory
.select(mapSheetLearnEntity) .select(mapSheetLearnEntity)
@@ -445,12 +446,14 @@ public class MapSheetLearnRepositoryImpl implements MapSheetLearnRepositoryCusto
NumberExpression<Long> inkxNoAsLong = NumberExpression<Long> inkxNoAsLong =
Expressions.numberTemplate(Long.class, "cast({0} as long)", mapInkx5kEntity.mapidcdNo); Expressions.numberTemplate(Long.class, "cast({0} as long)", mapInkx5kEntity.mapidcdNo);
/* 미사용
StringExpression pnu = StringExpression pnu =
Expressions.stringTemplate( Expressions.stringTemplate(
"nullif(({0}), '')", "nullif(({0}), '')",
JPAExpressions.select(Expressions.stringTemplate("string_agg({0}, ',')", pnuEntity.pnu)) JPAExpressions.select(Expressions.stringTemplate("string_agg({0}, ',')", pnuEntity.pnu))
.from(pnuEntity) .from(pnuEntity)
.where(pnuEntity.geo.geoUid.eq(mapSheetAnalDataInferenceGeomEntity.geoUid))); .where(pnuEntity.geo.geoUid.eq(mapSheetAnalDataInferenceGeomEntity.geoUid)));
*/
// 4) content // 4) content
List<Geom> content = List<Geom> content =
@@ -458,6 +461,7 @@ public class MapSheetLearnRepositoryImpl implements MapSheetLearnRepositoryCusto
.select( .select(
Projections.constructor( Projections.constructor(
Geom.class, Geom.class,
mapSheetAnalDataInferenceGeomEntity.geoUid,
mapSheetAnalDataInferenceGeomEntity.uuid, mapSheetAnalDataInferenceGeomEntity.uuid,
mapSheetAnalDataInferenceGeomEntity.resultUid, mapSheetAnalDataInferenceGeomEntity.resultUid,
mapSheetAnalDataInferenceGeomEntity.compareYyyy, mapSheetAnalDataInferenceGeomEntity.compareYyyy,
@@ -472,7 +476,7 @@ public class MapSheetLearnRepositoryImpl implements MapSheetLearnRepositoryCusto
Expressions.stringTemplate( Expressions.stringTemplate(
"substring({0} from 1 for 8)", "substring({0} from 1 for 8)",
mapSheetAnalDataInferenceGeomEntity.resultUid), mapSheetAnalDataInferenceGeomEntity.resultUid),
pnu, Expressions.constant(""),
mapSheetAnalDataInferenceGeomEntity.fitState)) mapSheetAnalDataInferenceGeomEntity.fitState))
.from(mapSheetAnalInferenceEntity) .from(mapSheetAnalInferenceEntity)
.join(mapSheetAnalDataInferenceEntity) .join(mapSheetAnalDataInferenceEntity)
@@ -487,6 +491,33 @@ public class MapSheetLearnRepositoryImpl implements MapSheetLearnRepositoryCusto
.limit(pageable.getPageSize()) .limit(pageable.getPageSize())
.fetch(); .fetch();
// 4-1) pnu 를 geoUid 로 검색하기 -> AS-IS 에서는 전체 tb_pnu 를 조회해서 오래 걸렸음
List<Long> geoUids = content.stream().map(Geom::getGeoUid).filter(Objects::nonNull).toList();
Map<Long, String> pnuMap = new HashMap<>();
if (!geoUids.isEmpty()) {
StringExpression pnuAgg = Expressions.stringTemplate("string_agg({0}, ',')", pnuEntity.pnu);
List<Tuple> pnuRows =
queryFactory
.select(
pnuEntity.geo.geoUid,
Expressions.stringTemplate("string_agg({0}, ',')", pnuEntity.pnu))
.from(pnuEntity)
.where(pnuEntity.geo.geoUid.in(geoUids))
.groupBy(pnuEntity.geo.geoUid)
.fetch();
pnuMap =
pnuRows.stream()
.collect(
Collectors.toMap(row -> row.get(pnuEntity.geo.geoUid), row -> row.get(pnuAgg)));
}
for (Geom geom : content) {
geom.setPnu(pnuMap.get(geom.getGeoUid()));
}
// 5) total (조인 최소화 유지) // 5) total (조인 최소화 유지)
Long total = Long total =
queryFactory queryFactory
@@ -519,7 +550,7 @@ public class MapSheetLearnRepositoryImpl implements MapSheetLearnRepositoryCusto
.fetchOne(); .fetchOne();
if (learn == null) { if (learn == null) {
return new GukYuinLinkFacts(false, false, false, false); return new GukYuinLinkFacts(false, false, false, false, null);
} }
// 부분 도엽 실행인지 확인 // 부분 도엽 실행인지 확인
@@ -529,19 +560,21 @@ public class MapSheetLearnRepositoryImpl implements MapSheetLearnRepositoryCusto
QMapSheetLearnEntity learn2 = new QMapSheetLearnEntity("learn2"); QMapSheetLearnEntity learn2 = new QMapSheetLearnEntity("learn2");
QMapSheetLearnEntity learnQ = QMapSheetLearnEntity.mapSheetLearnEntity; QMapSheetLearnEntity learnQ = QMapSheetLearnEntity.mapSheetLearnEntity;
// 실행중인 추론 있는지 확인 // 현재 국유인 연동하려는 추론의 비교년도,기준년도와 같은 회차 중, 할당되거나 진행중인 학습데이터 uuid 조회
boolean hasRunningInference = // ex. 2022-2023년도 9회차 학습데이터 제작 진행중 -> 10회차 연동하려고 할 시, 먼저 9회차를 종료해야 함
UUID runningInferenceUuid =
queryFactory queryFactory
.selectOne() .select(inf.uuid)
.from(inf) .from(inf)
.join(learn2) .join(learn2)
.on(inf.learnId.eq(learn2.id)) .on(inf.learnId.eq(learn2.id))
.where( .where(
learn2.compareYyyy.eq(learn.getCompareYyyy()), learn2.compareYyyy.eq(learn.getCompareYyyy()),
learn2.targetYyyy.eq(learn.getTargetYyyy()), learn2.targetYyyy.eq(learn.getTargetYyyy()),
inf.analState.in("ASSIGNED", "ING")) inf.analState.in("ASSIGNED", "ING"))
.fetchFirst() .fetchFirst();
!= null;
boolean hasRunningInference = runningInferenceUuid != null;
// 국유인 작업 진행중 있는지 확인 // 국유인 작업 진행중 있는지 확인
boolean hasOtherUnfinishedGukYuin = boolean hasOtherUnfinishedGukYuin =
@@ -556,6 +589,7 @@ public class MapSheetLearnRepositoryImpl implements MapSheetLearnRepositoryCusto
.fetchFirst() .fetchFirst()
!= null; != null;
return new GukYuinLinkFacts(true, isPartScope, hasRunningInference, hasOtherUnfinishedGukYuin); return new GukYuinLinkFacts(
true, isPartScope, hasRunningInference, hasOtherUnfinishedGukYuin, runningInferenceUuid);
} }
} }

View File

@@ -211,8 +211,9 @@ public class ChangeDetectionRepositoryImpl extends QuerydslRepositorySupport
.where( .where(
l.status l.status
.eq(Status.END.getId()) .eq(Status.END.getId())
.and(JPAExpressions.selectOne().from(d).where(d.analUid.eq(a.id)).exists())) .and(JPAExpressions.selectOne().from(d).where(d.analUid.eq(a.id)).exists()),
.orderBy(l.id.asc()) l.isDeleted.eq(false).or(l.isDeleted.isNull()))
.orderBy(l.id.desc())
.fetch(); .fetch();
} }

View File

@@ -2,7 +2,7 @@ package com.kamco.cd.kamcoback.postgres.repository.gukyuin;
public interface GukYuinPnuCntUpdateJobRepositoryCustom { public interface GukYuinPnuCntUpdateJobRepositoryCustom {
int updateGukYuinContListPnuUpdateCnt(String uid); void updateGukYuinContListPnuUpdateCnt();
void updateGukYuinApplyStatus(String uid, String status); void updateGukYuinApplyStatus(String uid, String status);
} }

View File

@@ -20,30 +20,20 @@ public class GukYuinPnuCntUpdateJobRepositoryImpl
@PersistenceContext private EntityManager em; @PersistenceContext private EntityManager em;
@Override @Override
public int updateGukYuinContListPnuUpdateCnt(String uid) { public void updateGukYuinContListPnuUpdateCnt() {
String sql = String sql =
""" """
WITH target_geo AS ( update tb_map_sheet_anal_data_inference_geom p
SELECT DISTINCT g.geo_uid set pnu = c_count.actual_count
FROM tb_map_sheet_anal_data_inference_geom g from (
JOIN tb_map_sheet_anal_data_inference d ON d.data_uid = g.data_uid select geo_uid, count(*) actual_count
JOIN tb_map_sheet_anal_inference i ON i.anal_uid = d.anal_uid from tb_pnu
JOIN tb_map_sheet_learn l ON l.id = i.learn_id group by geo_uid
WHERE l.uid = ? ) c_count
), where p.geo_uid = c_count.geo_uid and p.pnu != c_count.actual_count;
pnu_cnt AS ( """;
SELECT p.geo_uid, COUNT(*)::int AS cnt
FROM tb_pnu p
JOIN target_geo t ON t.geo_uid = p.geo_uid
GROUP BY p.geo_uid
)
UPDATE tb_map_sheet_anal_data_inference_geom geo
SET pnu = pnu_cnt.cnt
FROM pnu_cnt
WHERE geo.geo_uid = pnu_cnt.geo_uid
""";
return jdbcTemplate.update(sql, uid); jdbcTemplate.update(sql);
} }
@Override @Override

View File

@@ -6,7 +6,6 @@ import static com.kamco.cd.kamcoback.postgres.entity.QLabelingLabelerEntity.labe
import static com.kamco.cd.kamcoback.postgres.entity.QMapSheetAnalDataInferenceEntity.mapSheetAnalDataInferenceEntity; 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.QMapSheetAnalDataInferenceGeomEntity.mapSheetAnalDataInferenceGeomEntity;
import static com.kamco.cd.kamcoback.postgres.entity.QMapSheetAnalInferenceEntity.mapSheetAnalInferenceEntity; import static com.kamco.cd.kamcoback.postgres.entity.QMapSheetAnalInferenceEntity.mapSheetAnalInferenceEntity;
import static com.kamco.cd.kamcoback.postgres.entity.QMapSheetLearnDataGeomEntity.mapSheetLearnDataGeomEntity;
import static com.kamco.cd.kamcoback.postgres.entity.QMapSheetLearnEntity.mapSheetLearnEntity; import static com.kamco.cd.kamcoback.postgres.entity.QMapSheetLearnEntity.mapSheetLearnEntity;
import static com.kamco.cd.kamcoback.postgres.entity.QMemberEntity.memberEntity; import static com.kamco.cd.kamcoback.postgres.entity.QMemberEntity.memberEntity;
@@ -490,17 +489,21 @@ public class LabelAllocateRepositoryImpl implements LabelAllocateRepositoryCusto
inspectionStatus = inspectionRemaining > 0 ? "진행중" : "완료"; inspectionStatus = inspectionRemaining > 0 ? "진행중" : "완료";
} }
ZoneId zoneId = ZoneId.of("Asia/Seoul");
LocalDate targetDate = LocalDate.now(zoneId);
ZonedDateTime end = targetDate.plusDays(1).atStartOfDay(zoneId);
Long downloadPolygonCnt = Long downloadPolygonCnt =
queryFactory queryFactory
.select(mapSheetLearnDataGeomEntity.geoUid.count()) .select(labelingAssignmentEntity.inferenceGeomUid.count())
.from(mapSheetLearnDataGeomEntity) .from(labelingAssignmentEntity)
.innerJoin(labelingAssignmentEntity)
.on(labelingAssignmentEntity.inferenceGeomUid.eq(mapSheetLearnDataGeomEntity.geoUid))
.innerJoin(mapSheetAnalInferenceEntity) .innerJoin(mapSheetAnalInferenceEntity)
.on( .on(
labelingAssignmentEntity.analUid.eq(mapSheetAnalInferenceEntity.id), labelingAssignmentEntity.analUid.eq(mapSheetAnalInferenceEntity.id),
mapSheetAnalInferenceEntity.id.eq(analUid)) mapSheetAnalInferenceEntity.id.eq(analUid))
.where(mapSheetLearnDataGeomEntity.fileCreateYn.isTrue()) .where(
labelingAssignmentEntity.inspectState.eq(InspectState.COMPLETE.getId()),
labelingAssignmentEntity.inspectStatDttm.lt(end))
.fetchOne(); .fetchOne();
return WorkProgressInfo.builder() return WorkProgressInfo.builder()
@@ -670,17 +673,21 @@ public class LabelAllocateRepositoryImpl implements LabelAllocateRepositoryCusto
inspectionStatus = inspectionRemaining > 0 ? "진행중" : "완료"; inspectionStatus = inspectionRemaining > 0 ? "진행중" : "완료";
} }
ZoneId zoneId = ZoneId.of("Asia/Seoul");
LocalDate targetDate = LocalDate.now(zoneId);
ZonedDateTime end = targetDate.plusDays(1).atStartOfDay(zoneId);
Long downloadPolygonCnt = Long downloadPolygonCnt =
queryFactory queryFactory
.select(mapSheetLearnDataGeomEntity.geoUid.count()) .select(labelingAssignmentEntity.inferenceGeomUid.count())
.from(mapSheetLearnDataGeomEntity) .from(labelingAssignmentEntity)
.innerJoin(labelingAssignmentEntity)
.on(labelingAssignmentEntity.inferenceGeomUid.eq(mapSheetLearnDataGeomEntity.geoUid))
.innerJoin(mapSheetAnalInferenceEntity) .innerJoin(mapSheetAnalInferenceEntity)
.on( .on(
labelingAssignmentEntity.analUid.eq(mapSheetAnalInferenceEntity.id), labelingAssignmentEntity.analUid.eq(mapSheetAnalInferenceEntity.id),
mapSheetAnalInferenceEntity.uuid.eq(targetUuid)) mapSheetAnalInferenceEntity.uuid.eq(targetUuid))
.where(mapSheetLearnDataGeomEntity.fileCreateYn.isTrue()) .where(
labelingAssignmentEntity.inspectState.eq(InspectState.COMPLETE.getId()),
labelingAssignmentEntity.inspectStatDttm.lt(end))
.fetchOne(); .fetchOne();
return WorkProgressInfo.builder() return WorkProgressInfo.builder()

View File

@@ -7,6 +7,7 @@ import com.kamco.cd.kamcoback.label.dto.LabelAllocateDto.InspectState;
import com.kamco.cd.kamcoback.label.dto.LabelAllocateDto.LabelMngState; import com.kamco.cd.kamcoback.label.dto.LabelAllocateDto.LabelMngState;
import com.kamco.cd.kamcoback.label.dto.LabelAllocateDto.LabelState; import com.kamco.cd.kamcoback.label.dto.LabelAllocateDto.LabelState;
import com.kamco.cd.kamcoback.label.dto.LabelWorkDto; import com.kamco.cd.kamcoback.label.dto.LabelWorkDto;
import com.kamco.cd.kamcoback.label.dto.LabelWorkDto.LabelCntInfo;
import com.kamco.cd.kamcoback.label.dto.LabelWorkDto.LabelWorkMng; import com.kamco.cd.kamcoback.label.dto.LabelWorkDto.LabelWorkMng;
import com.kamco.cd.kamcoback.label.dto.LabelWorkDto.LabelWorkMngDetail; import com.kamco.cd.kamcoback.label.dto.LabelWorkDto.LabelWorkMngDetail;
import com.kamco.cd.kamcoback.label.dto.LabelWorkDto.WorkerState; import com.kamco.cd.kamcoback.label.dto.LabelWorkDto.WorkerState;
@@ -18,7 +19,6 @@ import com.kamco.cd.kamcoback.postgres.entity.QMapSheetAnalDataInferenceGeomEnti
import com.kamco.cd.kamcoback.postgres.entity.QMapSheetAnalInferenceEntity; import com.kamco.cd.kamcoback.postgres.entity.QMapSheetAnalInferenceEntity;
import com.kamco.cd.kamcoback.postgres.entity.QMemberEntity; import com.kamco.cd.kamcoback.postgres.entity.QMemberEntity;
import com.querydsl.core.BooleanBuilder; import com.querydsl.core.BooleanBuilder;
import com.querydsl.core.types.Expression;
import com.querydsl.core.types.OrderSpecifier; import com.querydsl.core.types.OrderSpecifier;
import com.querydsl.core.types.Projections; import com.querydsl.core.types.Projections;
import com.querydsl.core.types.dsl.BooleanExpression; import com.querydsl.core.types.dsl.BooleanExpression;
@@ -86,7 +86,7 @@ public class LabelWorkRepositoryImpl implements LabelWorkRepositoryCustom {
} }
/** /**
* 라벨링 작업관리 목록 조회 (복잡한 집계 쿼리로 인해 DTO 직접 반환) * 라벨링 작업관리 목록 조회 :: 전체 geom 집계 -> inference 먼저 쿼리 하고 count 정보 따로 집계하도록 수정
* *
* @param searchReq 검색 조건 * @param searchReq 검색 조건
* @return 라벨링 작업관리 목록 페이지 * @return 라벨링 작업관리 목록 페이지
@@ -95,9 +95,8 @@ public class LabelWorkRepositoryImpl implements LabelWorkRepositoryCustom {
public Page<LabelWorkMng> labelWorkMngList(LabelWorkDto.LabelWorkMngSearchReq searchReq) { public Page<LabelWorkMng> labelWorkMngList(LabelWorkDto.LabelWorkMngSearchReq searchReq) {
Pageable pageable = PageRequest.of(searchReq.getPage(), searchReq.getSize()); Pageable pageable = PageRequest.of(searchReq.getPage(), searchReq.getSize());
BooleanBuilder whereBuilder = new BooleanBuilder();
BooleanBuilder whereSubDataBuilder = new BooleanBuilder(); BooleanBuilder baseWhereBuilder = new BooleanBuilder();
BooleanBuilder whereSubBuilder = new BooleanBuilder();
if (StringUtils.isNotBlank(searchReq.getDetectYear())) { if (StringUtils.isNotBlank(searchReq.getDetectYear())) {
String[] years = searchReq.getDetectYear().split("-"); String[] years = searchReq.getDetectYear().split("-");
@@ -106,67 +105,14 @@ public class LabelWorkRepositoryImpl implements LabelWorkRepositoryCustom {
Integer compareYear = Integer.valueOf(years[0]); Integer compareYear = Integer.valueOf(years[0]);
Integer targetYear = Integer.valueOf(years[1]); Integer targetYear = Integer.valueOf(years[1]);
whereBuilder.and( baseWhereBuilder.and(
mapSheetAnalDataInferenceEntity mapSheetAnalInferenceEntity
.compareYyyy .compareYyyy
.eq(compareYear) .eq(compareYear)
.and(mapSheetAnalDataInferenceEntity.targetYyyy.eq(targetYear))); .and(mapSheetAnalInferenceEntity.targetYyyy.eq(targetYear)));
} }
} }
whereSubDataBuilder.and(
mapSheetAnalInferenceEntity.id.eq(mapSheetAnalDataInferenceEntity.analUid));
whereSubBuilder.and(
mapSheetAnalDataInferenceGeomEntity.dataUid.eq(mapSheetAnalDataInferenceEntity.id));
if (searchReq.getStrtDttm() != null && searchReq.getEndDttm() != null) {
ZoneId zoneId = ZoneId.of("Asia/Seoul");
ZonedDateTime start = searchReq.getStrtDttm().atStartOfDay(zoneId);
ZonedDateTime end = searchReq.getEndDttm().plusDays(1).atStartOfDay(zoneId);
whereSubBuilder.and(
mapSheetAnalDataInferenceGeomEntity
.labelStateDttm
.goe(start)
.and(mapSheetAnalDataInferenceGeomEntity.labelStateDttm.lt(end)));
}
// labelTotCnt: pnu가 있고 fitState = UNFIT 인 건수만 라벨링 대상
NumberExpression<Long> labelTotCntExpr =
new CaseBuilder()
.when(
mapSheetAnalDataInferenceGeomEntity
.pnu
.gt(0)
.and(
mapSheetAnalDataInferenceGeomEntity.fitState.eq(
ImageryFitStatus.UNFIT.getId())))
.then(1L)
.otherwise(0L)
.sum();
// stagnation_yn = 'N'인 정상 진행 건수 (서브쿼리로 별도 집계)
Expression<Long> normalProgressCntSubQuery =
JPAExpressions.select(
new CaseBuilder()
.when(labelingAssignmentEntity.stagnationYn.eq('N'))
.then(1L)
.otherwise(0L)
.sum()
.coalesce(0L))
.from(labelingAssignmentEntity)
.where(labelingAssignmentEntity.analUid.eq(mapSheetAnalInferenceEntity.id));
// 총 배정 건수 (서브쿼리로 별도 집계)
Expression<Long> totalAssignmentCntSubQuery =
JPAExpressions.select(labelingAssignmentEntity.count().coalesce(0L))
.from(labelingAssignmentEntity)
.where(labelingAssignmentEntity.analUid.eq(mapSheetAnalInferenceEntity.id));
/** 순서 ING 진행중 ASSIGNED 작업할당 PENDING 작업대기 FINISH 종료 */ /** 순서 ING 진행중 ASSIGNED 작업할당 PENDING 작업대기 FINISH 종료 */
NumberExpression<Integer> stateOrder = NumberExpression<Integer> stateOrder =
new CaseBuilder() new CaseBuilder()
@@ -180,64 +126,31 @@ public class LabelWorkRepositoryImpl implements LabelWorkRepositoryCustom {
.then(3) .then(3)
.otherwise(99); .otherwise(99);
// 1단계: anal_inference 기준으로 상위 페이지 목록만 조회
List<LabelWorkMng> foundContent = List<LabelWorkMng> foundContent =
queryFactory queryFactory
.select( .select(
Projections.constructor( Projections.constructor(
LabelWorkMng.class, LabelWorkMng.class,
mapSheetAnalInferenceEntity.id, // analUid 추가
mapSheetAnalInferenceEntity.uuid, mapSheetAnalInferenceEntity.uuid,
mapSheetAnalInferenceEntity.compareYyyy, mapSheetAnalInferenceEntity.compareYyyy,
mapSheetAnalInferenceEntity.targetYyyy, mapSheetAnalInferenceEntity.targetYyyy,
mapSheetAnalInferenceEntity.stage, mapSheetAnalInferenceEntity.stage,
// 국유인 반영 컬럼 gukyuinApplyDttm
mapSheetAnalInferenceEntity.gukyuinApplyDttm, mapSheetAnalInferenceEntity.gukyuinApplyDttm,
mapSheetAnalDataInferenceGeomEntity.dataUid.count(), mapSheetAnalInferenceEntity.detectingCnt,
// labelTotCnt: pnu 있고 pass_yn = false인 건수 Expressions.constant(0L),
labelTotCntExpr, Expressions.constant(0L),
new CaseBuilder() Expressions.constant(0L),
.when( Expressions.constant(0L),
mapSheetAnalDataInferenceGeomEntity.labelState.eq( mapSheetAnalInferenceEntity.createdDttm,
LabelState.ASSIGNED.getId()))
.then(1L)
.otherwise(0L)
.sum(),
new CaseBuilder()
.when(
mapSheetAnalDataInferenceGeomEntity.labelState.eq(
LabelState.SKIP.getId())) // "STOP"?
.then(1L)
.otherwise(0L)
.sum(),
new CaseBuilder()
.when(
mapSheetAnalDataInferenceGeomEntity.labelState.eq(
LabelState.DONE.getId()))
.then(1L)
.otherwise(0L)
.sum(),
mapSheetAnalDataInferenceGeomEntity.labelStateDttm.min(),
// analState: tb_map_sheet_anal_inference.anal_state
mapSheetAnalInferenceEntity.analState, mapSheetAnalInferenceEntity.analState,
// normalProgressCnt: stagnation_yn = 'N'인 건수 (서브쿼리) Expressions.constant(0L),
normalProgressCntSubQuery, Expressions.constant(0L),
// totalAssignmentCnt: 총 배정 건수 (서브쿼리)
totalAssignmentCntSubQuery,
mapSheetAnalInferenceEntity.labelingClosedYn, mapSheetAnalInferenceEntity.labelingClosedYn,
mapSheetAnalInferenceEntity.inspectionClosedYn, mapSheetAnalInferenceEntity.inspectionClosedYn,
new CaseBuilder() Expressions.constant(0L),
.when( Expressions.constant(0L),
mapSheetAnalDataInferenceGeomEntity.testState.eq(
InspectState.COMPLETE.getId()))
.then(1L)
.otherwise(0L)
.sum(),
new CaseBuilder()
.when(
mapSheetAnalDataInferenceGeomEntity.testState.eq(
InspectState.UNCONFIRM.getId()))
.then(1L)
.otherwise(0L)
.sum(),
new CaseBuilder() new CaseBuilder()
.when(mapSheetAnalInferenceEntity.inspectionClosedYn.eq("Y")) .when(mapSheetAnalInferenceEntity.inspectionClosedYn.eq("Y"))
.then(mapSheetAnalInferenceEntity.updatedDttm) .then(mapSheetAnalInferenceEntity.updatedDttm)
@@ -247,23 +160,11 @@ public class LabelWorkRepositoryImpl implements LabelWorkRepositoryCustom {
"substring({0} from 1 for 8)", mapSheetLearnEntity.uid), "substring({0} from 1 for 8)", mapSheetLearnEntity.uid),
mapSheetLearnEntity.uuid)) mapSheetLearnEntity.uuid))
.from(mapSheetAnalInferenceEntity) .from(mapSheetAnalInferenceEntity)
.innerJoin(mapSheetAnalDataInferenceEntity) .innerJoin(mapSheetLearnEntity)
.on(whereSubDataBuilder) .on(
.innerJoin(mapSheetAnalDataInferenceGeomEntity) mapSheetAnalInferenceEntity.learnId.eq(mapSheetLearnEntity.id),
.on(whereSubBuilder) mapSheetLearnEntity.isDeleted.eq(false).or(mapSheetLearnEntity.isDeleted.isNull()))
.leftJoin(mapSheetLearnEntity) .where(baseWhereBuilder)
.on(mapSheetAnalInferenceEntity.learnId.eq(mapSheetLearnEntity.id))
.where(whereBuilder)
.groupBy(
mapSheetAnalInferenceEntity.uuid,
mapSheetAnalInferenceEntity.compareYyyy,
mapSheetAnalInferenceEntity.targetYyyy,
mapSheetAnalInferenceEntity.stage,
mapSheetAnalInferenceEntity.createdDttm,
mapSheetAnalInferenceEntity.analState,
mapSheetAnalInferenceEntity.id,
mapSheetLearnEntity.uid,
mapSheetLearnEntity.uuid)
.orderBy( .orderBy(
stateOrder.asc(), stateOrder.asc(),
mapSheetAnalInferenceEntity.targetYyyy.desc(), mapSheetAnalInferenceEntity.targetYyyy.desc(),
@@ -273,20 +174,122 @@ public class LabelWorkRepositoryImpl implements LabelWorkRepositoryCustom {
.limit(pageable.getPageSize()) .limit(pageable.getPageSize())
.fetch(); .fetch();
// Count 쿼리 별도 실행 (null safe handling) // total count
long total = long total =
Optional.ofNullable( Optional.ofNullable(
queryFactory queryFactory
.select(mapSheetAnalInferenceEntity.uuid.countDistinct()) .select(mapSheetAnalInferenceEntity.count())
.from(mapSheetAnalInferenceEntity) .from(mapSheetAnalInferenceEntity)
.innerJoin(mapSheetAnalDataInferenceEntity) .where(baseWhereBuilder)
.on(whereSubDataBuilder)
.innerJoin(mapSheetAnalDataInferenceGeomEntity)
.on(whereSubBuilder)
.where(whereBuilder)
.fetchOne()) .fetchOne())
.orElse(0L); .orElse(0L);
// 2단계: 각 analUid별 count 조회 후 세팅
for (LabelWorkMng item : foundContent) {
Long analUid = item.getAnalUid();
BooleanBuilder geomWhereBuilder = new BooleanBuilder();
geomWhereBuilder.and(mapSheetAnalDataInferenceEntity.analUid.eq(analUid));
geomWhereBuilder.and(
mapSheetAnalDataInferenceGeomEntity.dataUid.eq(mapSheetAnalDataInferenceEntity.id));
if (searchReq.getStrtDttm() != null && searchReq.getEndDttm() != null) {
ZoneId zoneId = ZoneId.of("Asia/Seoul");
ZonedDateTime start = searchReq.getStrtDttm().atStartOfDay(zoneId);
ZonedDateTime end = searchReq.getEndDttm().plusDays(1).atStartOfDay(zoneId);
geomWhereBuilder.and(
mapSheetAnalDataInferenceGeomEntity
.createdDttm
.goe(start)
.and(mapSheetAnalDataInferenceGeomEntity.createdDttm.lt(end)));
}
// labelTotCnt: pnu > 0 and fitState = UNFIT
Long labelTotCnt =
Optional.ofNullable(
queryFactory
.select(mapSheetAnalDataInferenceGeomEntity.count())
.from(mapSheetAnalDataInferenceEntity)
.innerJoin(mapSheetAnalDataInferenceGeomEntity)
.on(
mapSheetAnalDataInferenceGeomEntity.dataUid.eq(
mapSheetAnalDataInferenceEntity.id))
.where(
geomWhereBuilder,
mapSheetAnalDataInferenceGeomEntity.pnu.gt(0),
mapSheetAnalDataInferenceGeomEntity.fitState.eq(
ImageryFitStatus.UNFIT.getId()))
.fetchOne())
.orElse(0L);
LabelCntInfo cntInfo =
queryFactory
.select(
Projections.constructor(
LabelCntInfo.class,
new CaseBuilder()
.when(labelingAssignmentEntity.workState.eq(LabelState.ASSIGNED.getId()))
.then(1L)
.otherwise(0L)
.sum()
.coalesce(0L),
new CaseBuilder()
.when(labelingAssignmentEntity.workState.eq(LabelState.SKIP.getId()))
.then(1L)
.otherwise(0L)
.sum()
.coalesce(0L),
new CaseBuilder()
.when(labelingAssignmentEntity.workState.eq(LabelState.DONE.getId()))
.then(1L)
.otherwise(0L)
.sum()
.coalesce(0L),
new CaseBuilder()
.when(
labelingAssignmentEntity.inspectState.eq(
InspectState.UNCONFIRM.getId()))
.then(1L)
.otherwise(0L)
.sum()
.coalesce(0L),
new CaseBuilder()
.when(
labelingAssignmentEntity.inspectState.eq(InspectState.EXCEPT.getId()))
.then(1L)
.otherwise(0L)
.sum()
.coalesce(0L),
new CaseBuilder()
.when(
labelingAssignmentEntity.inspectState.eq(
InspectState.COMPLETE.getId()))
.then(1L)
.otherwise(0L)
.sum()
.coalesce(0L)))
.from(labelingAssignmentEntity)
.where(labelingAssignmentEntity.analUid.eq(analUid))
.fetchOne();
Long normalProgressCnt = 0L;
Long totalAssignmentCnt = 0L;
if (cntInfo == null) {
cntInfo = new LabelCntInfo(0L, 0L, 0L, 0L, 0L, 0L);
}
item.setLabelTotCnt(labelTotCnt);
item.setLabelAssignCnt(cntInfo.getAssignedCnt());
item.setLabelSkipTotCnt(cntInfo.getSkipCnt());
item.setLabelCompleteTotCnt(cntInfo.getDoneCnt());
item.setNormalProgressCnt(normalProgressCnt);
item.setTotalAssignmentCnt(totalAssignmentCnt);
item.setInspectorCompleteTotCnt(cntInfo.getCompleteCnt());
item.setInspectorRemainCnt(cntInfo.getUnconfirmCnt());
}
return new PageImpl<>(foundContent, pageable, total); return new PageImpl<>(foundContent, pageable, total);
} }

View File

@@ -5,6 +5,7 @@ import static com.kamco.cd.kamcoback.postgres.entity.QErrorLogEntity.errorLogEnt
import static com.kamco.cd.kamcoback.postgres.entity.QMemberEntity.memberEntity; import static com.kamco.cd.kamcoback.postgres.entity.QMemberEntity.memberEntity;
import static com.kamco.cd.kamcoback.postgres.entity.QMenuEntity.menuEntity; import static com.kamco.cd.kamcoback.postgres.entity.QMenuEntity.menuEntity;
import com.kamco.cd.kamcoback.common.utils.HeaderUtil;
import com.kamco.cd.kamcoback.log.dto.AuditLogDto; import com.kamco.cd.kamcoback.log.dto.AuditLogDto;
import com.kamco.cd.kamcoback.log.dto.AuditLogDto.DownloadReq; import com.kamco.cd.kamcoback.log.dto.AuditLogDto.DownloadReq;
import com.kamco.cd.kamcoback.log.dto.ErrorLogDto; import com.kamco.cd.kamcoback.log.dto.ErrorLogDto;
@@ -13,6 +14,7 @@ import com.kamco.cd.kamcoback.log.dto.EventType;
import com.kamco.cd.kamcoback.postgres.entity.AuditLogEntity; import com.kamco.cd.kamcoback.postgres.entity.AuditLogEntity;
import com.kamco.cd.kamcoback.postgres.entity.QMenuEntity; import com.kamco.cd.kamcoback.postgres.entity.QMenuEntity;
import com.querydsl.core.BooleanBuilder; import com.querydsl.core.BooleanBuilder;
import com.querydsl.core.types.Expression;
import com.querydsl.core.types.Projections; import com.querydsl.core.types.Projections;
import com.querydsl.core.types.dsl.BooleanExpression; import com.querydsl.core.types.dsl.BooleanExpression;
import com.querydsl.core.types.dsl.CaseBuilder; import com.querydsl.core.types.dsl.CaseBuilder;
@@ -83,13 +85,16 @@ public class AuditLogRepositoryImpl extends QuerydslRepositorySupport
public Page<AuditLogDto.MenuAuditList> findLogByMenu( public Page<AuditLogDto.MenuAuditList> findLogByMenu(
AuditLogDto.searchReq searchReq, String searchValue) { AuditLogDto.searchReq searchReq, String searchValue) {
Pageable pageable = searchReq.toPageable(); Pageable pageable = searchReq.toPageable();
boolean english = HeaderUtil.isEnglishRequest();
Expression<String> menuNameExpr = english ? menuEntity.menuNmEn.max() : menuEntity.menuNm.max();
List<AuditLogDto.MenuAuditList> foundContent = List<AuditLogDto.MenuAuditList> foundContent =
queryFactory queryFactory
.select( .select(
Projections.constructor( Projections.constructor(
AuditLogDto.MenuAuditList.class, AuditLogDto.MenuAuditList.class,
auditLogEntity.menuUid.as("menuId"), auditLogEntity.menuUid.as("menuId"),
menuEntity.menuNm.max().as("menuName"), menuNameExpr,
readCount().as("readCount"), readCount().as("readCount"),
cudCount().as("cudCount"), cudCount().as("cudCount"),
printCount().as("printCount"), printCount().as("printCount"),
@@ -217,21 +222,24 @@ public class AuditLogRepositoryImpl extends QuerydslRepositorySupport
@Override @Override
public Page<AuditLogDto.DailyDetail> findLogByDailyResult( public Page<AuditLogDto.DailyDetail> findLogByDailyResult(
AuditLogDto.searchReq searchReq, LocalDate logDate) { AuditLogDto.searchReq searchReq, LocalDate logDate) {
boolean english = HeaderUtil.isEnglishRequest();
Pageable pageable = searchReq.toPageable(); Pageable pageable = searchReq.toPageable();
QMenuEntity parent = new QMenuEntity("parent"); QMenuEntity parent = new QMenuEntity("parent");
// 1depth menu name // 1depth menu name
StringExpression parentMenuName = StringExpression parentMenuName =
new CaseBuilder() new CaseBuilder()
.when(parent.menuUid.isNull()) .when(parent.menuUid.isNull())
.then(menuEntity.menuNm) .then(english ? menuEntity.menuNmEn : menuEntity.menuNm)
.otherwise(parent.menuNm); .otherwise(english ? parent.menuNmEn : parent.menuNm);
// 2depth menu name // 2depth menu name
StringExpression menuName = StringExpression menuName =
new CaseBuilder() new CaseBuilder()
.when(parent.menuUid.isNull()) .when(parent.menuUid.isNull())
.then(NULL_STRING) .then(NULL_STRING)
.otherwise(menuEntity.menuNm); .otherwise(english ? menuEntity.menuNmEn : menuEntity.menuNm);
StringExpression menuNm = (english ? menuEntity.menuNmEn : menuEntity.menuNm);
List<AuditLogDto.DailyDetail> foundContent = List<AuditLogDto.DailyDetail> foundContent =
queryFactory queryFactory
@@ -241,20 +249,20 @@ public class AuditLogRepositoryImpl extends QuerydslRepositorySupport
auditLogEntity.id.as("logId"), auditLogEntity.id.as("logId"),
memberEntity.name.as("userName"), memberEntity.name.as("userName"),
memberEntity.employeeNo.as("loginId"), memberEntity.employeeNo.as("loginId"),
menuEntity.menuNm.as("menuName"), menuNm.as("menuName"),
auditLogEntity.eventType.as("eventType"), auditLogEntity.eventType.as("eventType"),
Expressions.stringTemplate( Expressions.stringTemplate(
"to_char({0}, 'YYYY-MM-DD HH24:MI')", auditLogEntity.createdDate) "to_char({0}, 'YYYY-MM-DD HH24:MI')", auditLogEntity.createdDate)
.as("logDateTime"), .as("logDateTime"),
Projections.constructor( Projections.constructor(
AuditLogDto.LogDetail.class, AuditLogDto.LogDetail.class,
Expressions.constant("한국자산관리공사"), // serviceName Expressions.constant(english ? "Kamco" : "한국자산관리공사"), // serviceName
parentMenuName.as("parentMenuName"), parentMenuName.as("parentMenuName"),
menuName, menuName,
menuEntity.menuUrl.as("menuUrl"), menuEntity.menuUrl.as("menuUrl"),
menuEntity.description.as("menuDescription"), menuEntity.description.as("menuDescription"),
menuEntity.menuOrder.as("sortOrder"), menuEntity.menuOrder.as("sortOrder"),
menuEntity.isUse.as("used")))) // TODO menuEntity.isUse.as("used"))))
.from(auditLogEntity) .from(auditLogEntity)
.leftJoin(menuEntity) .leftJoin(menuEntity)
.on(auditLogEntity.menuUid.eq(menuEntity.menuUid)) .on(auditLogEntity.menuUid.eq(menuEntity.menuUid))
@@ -285,21 +293,22 @@ public class AuditLogRepositoryImpl extends QuerydslRepositorySupport
@Override @Override
public Page<AuditLogDto.MenuDetail> findLogByMenuResult( public Page<AuditLogDto.MenuDetail> findLogByMenuResult(
AuditLogDto.searchReq searchReq, String menuUid) { AuditLogDto.searchReq searchReq, String menuUid) {
boolean english = HeaderUtil.isEnglishRequest();
Pageable pageable = searchReq.toPageable(); Pageable pageable = searchReq.toPageable();
QMenuEntity parent = new QMenuEntity("parent"); QMenuEntity parent = new QMenuEntity("parent");
// 1depth menu name // 1depth menu name
StringExpression parentMenuName = StringExpression parentMenuName =
new CaseBuilder() new CaseBuilder()
.when(parent.menuUid.isNull()) .when(parent.menuUid.isNull())
.then(menuEntity.menuNm) .then(english ? menuEntity.menuNmEn : menuEntity.menuNm)
.otherwise(parent.menuNm); .otherwise(english ? parent.menuNmEn : parent.menuNm);
// 2depth menu name // 2depth menu name
StringExpression menuName = StringExpression menuName =
new CaseBuilder() new CaseBuilder()
.when(parent.menuUid.isNull()) .when(parent.menuUid.isNull())
.then(NULL_STRING) .then(NULL_STRING)
.otherwise(menuEntity.menuNm); .otherwise(english ? menuEntity.menuNmEn : menuEntity.menuNm);
List<AuditLogDto.MenuDetail> foundContent = List<AuditLogDto.MenuDetail> foundContent =
queryFactory queryFactory
@@ -315,7 +324,7 @@ public class AuditLogRepositoryImpl extends QuerydslRepositorySupport
auditLogEntity.eventType.as("eventType"), auditLogEntity.eventType.as("eventType"),
Projections.constructor( Projections.constructor(
AuditLogDto.LogDetail.class, AuditLogDto.LogDetail.class,
Expressions.constant("한국자산관리공사"), // serviceName Expressions.constant(english ? "Kamco" : "한국자산관리공사"), // serviceName
parentMenuName.as("parentMenuName"), parentMenuName.as("parentMenuName"),
menuName, menuName,
menuEntity.menuUrl.as("menuUrl"), menuEntity.menuUrl.as("menuUrl"),
@@ -352,21 +361,24 @@ public class AuditLogRepositoryImpl extends QuerydslRepositorySupport
@Override @Override
public Page<AuditLogDto.UserDetail> findLogByAccountResult( public Page<AuditLogDto.UserDetail> findLogByAccountResult(
AuditLogDto.searchReq searchReq, Long userUid) { AuditLogDto.searchReq searchReq, Long userUid) {
boolean english = HeaderUtil.isEnglishRequest();
Pageable pageable = searchReq.toPageable(); Pageable pageable = searchReq.toPageable();
QMenuEntity parent = new QMenuEntity("parent"); QMenuEntity parent = new QMenuEntity("parent");
// 1depth menu name // 1depth menu name
StringExpression parentMenuName = StringExpression parentMenuName =
new CaseBuilder() new CaseBuilder()
.when(parent.menuUid.isNull()) .when(parent.menuUid.isNull())
.then(menuEntity.menuNm) .then(english ? menuEntity.menuNmEn : menuEntity.menuNm)
.otherwise(parent.menuNm); .otherwise(english ? parent.menuNmEn : parent.menuNm);
// 2depth menu name // 2depth menu name
StringExpression menuName = StringExpression menuName =
new CaseBuilder() new CaseBuilder()
.when(parent.menuUid.isNull()) .when(parent.menuUid.isNull())
.then(NULL_STRING) .then(NULL_STRING)
.otherwise(menuEntity.menuNm); .otherwise(english ? menuEntity.menuNmEn : menuEntity.menuNm);
StringExpression menuNm = (english ? menuEntity.menuNmEn : menuEntity.menuNm);
List<AuditLogDto.UserDetail> foundContent = List<AuditLogDto.UserDetail> foundContent =
queryFactory queryFactory
@@ -377,11 +389,11 @@ public class AuditLogRepositoryImpl extends QuerydslRepositorySupport
Expressions.stringTemplate( Expressions.stringTemplate(
"to_char({0}, 'YYYY-MM-DD HH24:MI')", auditLogEntity.createdDate) "to_char({0}, 'YYYY-MM-DD HH24:MI')", auditLogEntity.createdDate)
.as("logDateTime"), .as("logDateTime"),
menuEntity.menuNm.as("menuName"), menuNm.as("menuName"),
auditLogEntity.eventType.as("eventType"), auditLogEntity.eventType.as("eventType"),
Projections.constructor( Projections.constructor(
AuditLogDto.LogDetail.class, AuditLogDto.LogDetail.class,
Expressions.constant("한국자산관리공사"), // serviceName Expressions.constant(english ? "Kamco" : "한국자산관리공사"), // serviceName
parentMenuName.as("parentMenuName"), parentMenuName.as("parentMenuName"),
menuName, menuName,
menuEntity.menuUrl.as("menuUrl"), menuEntity.menuUrl.as("menuUrl"),
@@ -442,7 +454,7 @@ public class AuditLogRepositoryImpl extends QuerydslRepositorySupport
if (StringUtils.isBlank(searchValue)) { if (StringUtils.isBlank(searchValue)) {
return null; return null;
} }
return menuEntity.menuNm.contains(searchValue); return menuEntity.menuNm.contains(searchValue).or(menuEntity.menuNmEn.contains(searchValue));
} }
private BooleanExpression loginIdOrUsernameContains(String searchValue) { private BooleanExpression loginIdOrUsernameContains(String searchValue) {

View File

@@ -5,6 +5,7 @@ import static com.kamco.cd.kamcoback.postgres.entity.QErrorLogEntity.errorLogEnt
import static com.kamco.cd.kamcoback.postgres.entity.QMemberEntity.memberEntity; import static com.kamco.cd.kamcoback.postgres.entity.QMemberEntity.memberEntity;
import static com.kamco.cd.kamcoback.postgres.entity.QMenuEntity.menuEntity; import static com.kamco.cd.kamcoback.postgres.entity.QMenuEntity.menuEntity;
import com.kamco.cd.kamcoback.common.utils.HeaderUtil;
import com.kamco.cd.kamcoback.log.dto.ErrorLogDto; import com.kamco.cd.kamcoback.log.dto.ErrorLogDto;
import com.kamco.cd.kamcoback.log.dto.EventStatus; import com.kamco.cd.kamcoback.log.dto.EventStatus;
import com.kamco.cd.kamcoback.log.dto.EventType; import com.kamco.cd.kamcoback.log.dto.EventType;
@@ -38,14 +39,17 @@ public class ErrorLogRepositoryImpl extends QuerydslRepositorySupport
@Override @Override
public Page<ErrorLogDto.Basic> findLogByError(ErrorLogDto.ErrorSearchReq searchReq) { public Page<ErrorLogDto.Basic> findLogByError(ErrorLogDto.ErrorSearchReq searchReq) {
Pageable pageable = searchReq.toPageable(); Pageable pageable = searchReq.toPageable();
boolean english = HeaderUtil.isEnglishRequest();
StringExpression menuNm = (english ? menuEntity.menuNmEn : menuEntity.menuNm);
List<ErrorLogDto.Basic> foundContent = List<ErrorLogDto.Basic> foundContent =
queryFactory queryFactory
.select( .select(
Projections.constructor( Projections.constructor(
ErrorLogDto.Basic.class, ErrorLogDto.Basic.class,
errorLogEntity.id.as("logId"), errorLogEntity.id.as("logId"),
Expressions.stringTemplate("{0}", "한국자산관리공사"), // serviceName Expressions.constant(english ? "Kamco" : "한국자산관리공사"), // serviceName
menuEntity.menuNm.as("menuName"), menuNm.as("menuName"),
memberEntity.employeeNo.as("loginId"), memberEntity.employeeNo.as("loginId"),
memberEntity.name.as("userName"), memberEntity.name.as("userName"),
errorLogEntity.errorType.as("eventType"), errorLogEntity.errorType.as("eventType"),

View File

@@ -141,12 +141,13 @@ public interface MapSheetMngRepositoryCustom {
void insertMapSheetMngTile(@Valid AddReq addReq); void insertMapSheetMngTile(@Valid AddReq addReq);
/** /**
* 연도 조건으로 도엽번호 조회 * 연도별 도엽 목록 조회
* *
* @param year 연도 * @param year 관리연도
* @return 추론 가능한 도엽 정보 * @param mapSheetNums50k 50k 도엽번호 리스트 (null 또는 empty인 경우 전체 조회)
* @return 도엽 목록
*/ */
List<MngListDto> getMapSheetMngHst(Integer year, String mapSheetScope, List<String> mapSheetNum); List<MngListDto> getMapSheetMngHst(Integer year, List<String> mapSheetNums50k);
/** /**
* 비교연도 사용 가능한 이전도엽을 조회한다. * 비교연도 사용 가능한 이전도엽을 조회한다.

View File

@@ -1,5 +1,6 @@
package com.kamco.cd.kamcoback.postgres.repository.mapsheet; package com.kamco.cd.kamcoback.postgres.repository.mapsheet;
import static com.kamco.cd.kamcoback.postgres.entity.QMapInkx50kEntity.mapInkx50kEntity;
import static com.kamco.cd.kamcoback.postgres.entity.QMapInkx5kEntity.mapInkx5kEntity; import static com.kamco.cd.kamcoback.postgres.entity.QMapInkx5kEntity.mapInkx5kEntity;
import static com.kamco.cd.kamcoback.postgres.entity.QMapSheetMngEntity.mapSheetMngEntity; import static com.kamco.cd.kamcoback.postgres.entity.QMapSheetMngEntity.mapSheetMngEntity;
import static com.kamco.cd.kamcoback.postgres.entity.QMapSheetMngFileEntity.mapSheetMngFileEntity; import static com.kamco.cd.kamcoback.postgres.entity.QMapSheetMngFileEntity.mapSheetMngFileEntity;
@@ -10,12 +11,12 @@ import static com.querydsl.core.types.dsl.Expressions.nullExpression;
import com.kamco.cd.kamcoback.common.enums.CommonUseStatus; import com.kamco.cd.kamcoback.common.enums.CommonUseStatus;
import com.kamco.cd.kamcoback.common.geometry.GeoJsonFileWriter.ImageFeature; import com.kamco.cd.kamcoback.common.geometry.GeoJsonFileWriter.ImageFeature;
import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.MapSheetScope;
import com.kamco.cd.kamcoback.mapsheet.dto.MapSheetMngDto; import com.kamco.cd.kamcoback.mapsheet.dto.MapSheetMngDto;
import com.kamco.cd.kamcoback.mapsheet.dto.MapSheetMngDto.AddReq; import com.kamco.cd.kamcoback.mapsheet.dto.MapSheetMngDto.AddReq;
import com.kamco.cd.kamcoback.mapsheet.dto.MapSheetMngDto.MngListDto; import com.kamco.cd.kamcoback.mapsheet.dto.MapSheetMngDto.MngListDto;
import com.kamco.cd.kamcoback.mapsheet.dto.MapSheetMngDto.YearSearchReq; import com.kamco.cd.kamcoback.mapsheet.dto.MapSheetMngDto.YearSearchReq;
import com.kamco.cd.kamcoback.postgres.entity.MapSheetMngHstEntity; import com.kamco.cd.kamcoback.postgres.entity.MapSheetMngHstEntity;
import com.kamco.cd.kamcoback.postgres.entity.QMapInkx50kEntity;
import com.kamco.cd.kamcoback.postgres.entity.QMapInkx5kEntity; import com.kamco.cd.kamcoback.postgres.entity.QMapInkx5kEntity;
import com.kamco.cd.kamcoback.postgres.entity.QMapSheetMngFileEntity; import com.kamco.cd.kamcoback.postgres.entity.QMapSheetMngFileEntity;
import com.kamco.cd.kamcoback.postgres.entity.QMapSheetMngHstEntity; import com.kamco.cd.kamcoback.postgres.entity.QMapSheetMngHstEntity;
@@ -27,7 +28,6 @@ import com.querydsl.core.types.dsl.BooleanExpression;
import com.querydsl.core.types.dsl.CaseBuilder; import com.querydsl.core.types.dsl.CaseBuilder;
import com.querydsl.core.types.dsl.Expressions; import com.querydsl.core.types.dsl.Expressions;
import com.querydsl.core.types.dsl.NumberExpression; import com.querydsl.core.types.dsl.NumberExpression;
import com.querydsl.core.types.dsl.StringExpression;
import com.querydsl.jpa.JPAExpressions; import com.querydsl.jpa.JPAExpressions;
import com.querydsl.jpa.impl.JPAQueryFactory; import com.querydsl.jpa.impl.JPAQueryFactory;
import jakarta.persistence.EntityManager; import jakarta.persistence.EntityManager;
@@ -50,7 +50,6 @@ public class MapSheetMngRepositoryImpl extends QuerydslRepositorySupport
implements MapSheetMngRepositoryCustom { implements MapSheetMngRepositoryCustom {
private final JPAQueryFactory queryFactory; private final JPAQueryFactory queryFactory;
private final StringExpression NULL_STRING = Expressions.stringTemplate("cast(null as text)");
@PersistenceContext private EntityManager em; @PersistenceContext private EntityManager em;
@@ -749,6 +748,13 @@ public class MapSheetMngRepositoryImpl extends QuerydslRepositorySupport
.delete(mapSheetMngTileEntity) .delete(mapSheetMngTileEntity)
.where(mapSheetMngTileEntity.mngYyyy.eq(mngYyyy)) .where(mapSheetMngTileEntity.mngYyyy.eq(mngYyyy))
.execute(); .execute();
long updateNotYetCount =
queryFactory
.update(yearEntity)
.set(yearEntity.status, "NOTYET")
.where(yearEntity.yyyy.eq(mngYyyy))
.execute();
} }
@Override @Override
@@ -983,7 +989,8 @@ public class MapSheetMngRepositoryImpl extends QuerydslRepositorySupport
.or(mapSheetMngHstEntity.syncCheckState.eq("DONE"))); .or(mapSheetMngHstEntity.syncCheckState.eq("DONE")));
// file_ext = 'tif' // file_ext = 'tif'
whereBuilder.and(mapSheetMngFileEntity.fileExt.eq("tif")); whereBuilder.and(
mapSheetMngFileEntity.fileExt.eq("tif").and(mapSheetMngFileEntity.fileDel.isFalse()));
// mng_yyyy = '2023' // mng_yyyy = '2023'
if (yyyy != null && !yyyy.isEmpty()) { if (yyyy != null && !yyyy.isEmpty()) {
@@ -1083,11 +1090,27 @@ public class MapSheetMngRepositoryImpl extends QuerydslRepositorySupport
mapSheetMngHstEntity.mapSheetNum)); mapSheetMngHstEntity.mapSheetNum));
} }
/**
* 영상데이터관리 > 연도별 도엽 목록 조회
*
* @param year 관리연도
* @param mapSheetNums50k 50k 도엽번호 리스트 (null 또는 empty인 경우 전체 조회)
* @return 도엽 목록
*/
@Override @Override
public List<MngListDto> getMapSheetMngHst( public List<MngListDto> getMapSheetMngHst(Integer year, List<String> mapSheetNums50k) {
Integer year, String mapSheetScope, List<String> mapSheetNum) { /*
검색조건:
- ✅ 데이터 처리 완료(data_state='DONE')
- ✅ 동기화 완료(sync_state='DONE' OR sync_check_state='DONE')
- ✅ 추론 사용(use_inference='USE')
- ✅ 지정 연도(mng_yyyy=year)
- ✅ 완료된 TIF 파일 존재
- ✅ 사용 중인 도엽만(mapInkx5k.useInference='USE')
- ✅ 50k 도엽번호로 필터링 (mapSheetNums50k가 있는 경우)
*/
BooleanBuilder whereBuilder = new BooleanBuilder(); BooleanBuilder whereBuilder = new BooleanBuilder();
whereBuilder.and(mapSheetMngHstEntity.mngYyyy.eq(year));
whereBuilder.and(mapSheetMngHstEntity.dataState.eq("DONE")); whereBuilder.and(mapSheetMngHstEntity.dataState.eq("DONE"));
whereBuilder.and( whereBuilder.and(
mapSheetMngHstEntity mapSheetMngHstEntity
@@ -1096,39 +1119,20 @@ public class MapSheetMngRepositoryImpl extends QuerydslRepositorySupport
.or(mapSheetMngHstEntity.syncCheckState.eq("DONE"))); .or(mapSheetMngHstEntity.syncCheckState.eq("DONE")));
whereBuilder.and(mapSheetMngHstEntity.useInference.eq("USE")); whereBuilder.and(mapSheetMngHstEntity.useInference.eq("USE"));
whereBuilder.and(mapSheetMngHstEntity.mngYyyy.eq(year)); // TIF 파일 존재 여부 확인
whereBuilder.and( whereBuilder.and(
JPAExpressions.selectOne() JPAExpressions.selectOne()
.from(mapSheetMngFileEntity) .from(mapSheetMngFileEntity)
.where( .where(
mapSheetMngFileEntity mapSheetMngFileEntity
.hstUid .hstUid
.eq(mapSheetMngHstEntity.hstUid) // FK 관계에 맞게 유지 .eq(mapSheetMngHstEntity.hstUid)
.and(mapSheetMngHstEntity.mngYyyy.eq(year))
.and(mapSheetMngFileEntity.fileExt.eq("tif")) .and(mapSheetMngFileEntity.fileExt.eq("tif"))
.and(mapSheetMngFileEntity.fileState.eq("DONE"))) .and(mapSheetMngFileEntity.fileState.eq("DONE"))
.and(mapSheetMngFileEntity.fileDel.eq(false)))
.exists()); .exists());
BooleanBuilder likeBuilder = new BooleanBuilder();
if (MapSheetScope.PART.getId().equals(mapSheetScope)) {
List<String> list = mapSheetNum;
if (list == null || list.isEmpty()) {
return List.of();
}
for (String prefix : list) {
if (prefix == null || prefix.isBlank()) {
continue;
}
likeBuilder.or(mapSheetMngHstEntity.mapSheetNum.like(prefix.trim() + "%"));
}
}
if (likeBuilder.hasValue()) {
whereBuilder.and(likeBuilder);
}
return queryFactory return queryFactory
.select( .select(
Projections.constructor( Projections.constructor(
@@ -1145,7 +1149,8 @@ public class MapSheetMngRepositoryImpl extends QuerydslRepositorySupport
.mapidcdNo .mapidcdNo
.eq(mapSheetMngHstEntity.mapSheetNum) .eq(mapSheetMngHstEntity.mapSheetNum)
.and(mapInkx5kEntity.useInference.eq(CommonUseStatus.USE))) .and(mapInkx5kEntity.useInference.eq(CommonUseStatus.USE)))
.where(whereBuilder) .innerJoin(mapInkx5kEntity.mapInkx50k, mapInkx50kEntity)
.where(whereBuilder, inScenes50(mapInkx50kEntity, mapSheetNums50k))
.fetch(); .fetch();
} }
@@ -1177,7 +1182,12 @@ public class MapSheetMngRepositoryImpl extends QuerydslRepositorySupport
h.syncState.eq("DONE").or(h.syncCheckState.eq("DONE")), h.syncState.eq("DONE").or(h.syncCheckState.eq("DONE")),
JPAExpressions.selectOne() JPAExpressions.selectOne()
.from(f) .from(f)
.where(f.hstUid.eq(h.hstUid), f.fileExt.eq("tif"), f.fileState.eq("DONE")) .where(
f.hstUid.eq(h.hstUid),
f.mngYyyy.eq(year),
f.fileExt.eq("tif"),
f.fileState.eq("DONE"),
f.fileDel.eq(false))
.exists(), .exists(),
// mapSheetNum별 최대 mngYyyy인 행만 남김 // mapSheetNum별 최대 mngYyyy인 행만 남김
@@ -1194,9 +1204,19 @@ public class MapSheetMngRepositoryImpl extends QuerydslRepositorySupport
.from(f2) .from(f2)
.where( .where(
f2.hstUid.eq(h2.hstUid), f2.hstUid.eq(h2.hstUid),
f2.mngYyyy.eq(year),
f2.fileExt.eq("tif"), f2.fileExt.eq("tif"),
f2.fileState.eq("DONE")) f2.fileState.eq("DONE"),
f2.fileDel.eq(false))
.exists()))) .exists())))
.fetch(); .fetch();
} }
// 도엽번호(1:50k) IN 쿼리조건
private BooleanExpression inScenes50(QMapInkx50kEntity mapInkx50k, List<String> sceneIds) {
if (sceneIds == null || sceneIds.isEmpty()) {
return null;
}
return mapInkx50k.mapidcdNo.in(sceneIds);
}
} }

View File

@@ -88,17 +88,33 @@ public class MapSheetMngYearRepositoryImpl implements MapSheetMngYearRepositoryC
*/ */
@Override @Override
public List<MngListCompareDto> findByHstMapSheetCompareList(int mngYyyy, List<String> mapIds) { public List<MngListCompareDto> findByHstMapSheetCompareList(int mngYyyy, List<String> mapIds) {
QMapSheetMngYearYnEntity y = QMapSheetMngYearYnEntity.mapSheetMngYearYnEntity; QMapSheetMngYearYnEntity mapSheetMngYearYn = QMapSheetMngYearYnEntity.mapSheetMngYearYnEntity;
// SELECT
// concat(?, '') as col_0_0_, -- 파라미터 mngYyyy (문자열)
// m.map_sheet_num as col_1_0_, -- 도엽번호
// MAX(m.mng_yyyy) as col_2_0_ -- 최대 관리연도
// FROM tb_map_sheet_mng_year_yn m
// WHERE m.map_sheet_num IN (?, ?, ..., ?) -- mapIds 리스트
// AND m.yn = 'Y' -- 파일 존재 여부
// AND m.mng_yyyy <= ? -- 기준연도 이하만
// GROUP BY m.map_sheet_num
StringExpression mngYyyyStr = Expressions.stringTemplate("concat({0}, '')", mngYyyy); StringExpression mngYyyyStr = Expressions.stringTemplate("concat({0}, '')", mngYyyy);
return queryFactory return queryFactory
.select( .select(
Projections.constructor( Projections.constructor(
MngListCompareDto.class, mngYyyyStr, y.id.mapSheetNum, y.id.mngYyyy.max())) MngListCompareDto.class,
.from(y) mngYyyyStr,
.where(y.id.mapSheetNum.in(mapIds), y.yn.eq("Y"), y.id.mngYyyy.loe(mngYyyy)) mapSheetMngYearYn.id.mapSheetNum,
.groupBy(y.id.mapSheetNum) mapSheetMngYearYn.id.mngYyyy.max()))
.from(mapSheetMngYearYn)
.where(
mapSheetMngYearYn.id.mapSheetNum.in(mapIds),
mapSheetMngYearYn.yn.eq("Y"),
mapSheetMngYearYn.id.mngYyyy.loe(mngYyyy))
.groupBy(mapSheetMngYearYn.id.mapSheetNum)
.fetch(); .fetch();
} }

View File

@@ -4,7 +4,6 @@ import static com.kamco.cd.kamcoback.postgres.entity.QModelMngEntity.modelMngEnt
import static com.kamco.cd.kamcoback.postgres.entity.QModelResultMetricEntity.modelResultMetricEntity; import static com.kamco.cd.kamcoback.postgres.entity.QModelResultMetricEntity.modelResultMetricEntity;
import com.kamco.cd.kamcoback.model.dto.ModelMngDto; import com.kamco.cd.kamcoback.model.dto.ModelMngDto;
import com.kamco.cd.kamcoback.postgres.QuerydslOrderUtil;
import com.kamco.cd.kamcoback.postgres.entity.ModelMngEntity; import com.kamco.cd.kamcoback.postgres.entity.ModelMngEntity;
import com.querydsl.core.BooleanBuilder; import com.querydsl.core.BooleanBuilder;
import com.querydsl.core.types.Expression; import com.querydsl.core.types.Expression;
@@ -101,9 +100,10 @@ public class ModelMngRepositoryImpl extends QuerydslRepositorySupport
modelMngEntity.deleted.isFalse().or(modelMngEntity.deleted.isNull())) modelMngEntity.deleted.isFalse().or(modelMngEntity.deleted.isNull()))
.offset(pageable.getOffset()) .offset(pageable.getOffset())
.limit(pageable.getPageSize()) .limit(pageable.getPageSize())
.orderBy( .orderBy(modelMngEntity.createCompleteDttm.desc())
QuerydslOrderUtil.getOrderSpecifiers( // .orderBy(
pageable, ModelMngEntity.class, "modelMngEntity")) // QuerydslOrderUtil.getOrderSpecifiers(
// pageable, ModelMngEntity.class, "modelMngEntity"))
.fetch(); .fetch();
Long countQuery = Long countQuery =

View File

@@ -508,7 +508,7 @@ public class TrainingDataLabelRepositoryImpl extends QuerydslRepositorySupport
: "") : "")
.classificationName( .classificationName(
DetectionClassification.fromStrDesc( DetectionClassification.fromStrDesc(
mapSheetAnalDataInferenceGeomEntityEntity.getClassAfterCd())) mapSheetAnalDataInferenceGeomEntityEntity.getClassBeforeCd()))
.probability( .probability(
mapSheetAnalDataInferenceGeomEntityEntity.getClassBeforeProb() != null mapSheetAnalDataInferenceGeomEntityEntity.getClassBeforeProb() != null
? mapSheetAnalDataInferenceGeomEntityEntity.getClassBeforeProb() ? mapSheetAnalDataInferenceGeomEntityEntity.getClassBeforeProb()

View File

@@ -534,7 +534,7 @@ public class TrainingDataReviewRepositoryImpl extends QuerydslRepositorySupport
: "") : "")
.classificationName( .classificationName(
DetectionClassification.fromStrDesc( DetectionClassification.fromStrDesc(
mapSheetAnalDataInferenceGeomEntityEntity.getClassAfterCd())) mapSheetAnalDataInferenceGeomEntityEntity.getClassBeforeCd()))
.probability( .probability(
mapSheetAnalDataInferenceGeomEntityEntity.getClassBeforeProb() != null mapSheetAnalDataInferenceGeomEntityEntity.getClassBeforeProb() != null
? mapSheetAnalDataInferenceGeomEntityEntity.getClassBeforeProb() ? mapSheetAnalDataInferenceGeomEntityEntity.getClassBeforeProb()

View File

@@ -3,24 +3,30 @@ package com.kamco.cd.kamcoback.scheduler;
import com.kamco.cd.kamcoback.scheduler.service.MapSheetMngFileJobService; import com.kamco.cd.kamcoback.scheduler.service.MapSheetMngFileJobService;
import lombok.Getter; import lombok.Getter;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled; import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@Slf4j
@Component @Component
@RequiredArgsConstructor @RequiredArgsConstructor
public class MapSheetMngFileJobController { public class MapSheetMngFileJobController {
private final MapSheetMngFileJobService mapSheetMngFileJobService; private final MapSheetMngFileJobService mapSheetMngFileJobService;
@Value("${spring.profiles.active}")
private String profile;
// 현재 상태 확인용 Getter // 현재 상태 확인용 Getter
@Getter private boolean isSchedulerEnabled = false; @Getter private boolean isSchedulerEnabled = true;
@Getter private boolean isFileSyncSchedulerEnabled = false; @Getter private boolean isFileSyncSchedulerEnabled = false;
@Getter private int mngSyncPageSize = 20; @Getter private int mngSyncPageSize = 20;
// 파일싱크 진행여부 확인하기 // 파일싱크 진행여부 확인하기
@Scheduled(fixedDelay = 1000 * 10) @Scheduled(fixedDelay = 1000 * 10)
public void checkMngFileSync() { public void checkMngFileSync() {
if (!isSchedulerEnabled) { if ("local".equals(profile) || !isSchedulerEnabled) {
return; return;
} }

View File

@@ -6,14 +6,12 @@ import com.kamco.cd.kamcoback.gukyuin.service.GukYuinApiService;
import com.kamco.cd.kamcoback.inference.dto.InferenceResultShpDto; import com.kamco.cd.kamcoback.inference.dto.InferenceResultShpDto;
import com.kamco.cd.kamcoback.inference.service.InferenceResultShpService; import com.kamco.cd.kamcoback.inference.service.InferenceResultShpService;
import com.kamco.cd.kamcoback.scheduler.service.GukYuinApiLabelJobService; import com.kamco.cd.kamcoback.scheduler.service.GukYuinApiLabelJobService;
import com.kamco.cd.kamcoback.scheduler.service.GukYuinApiPnuCntUpdateJobService;
import com.kamco.cd.kamcoback.scheduler.service.GukYuinApiPnuJobService; import com.kamco.cd.kamcoback.scheduler.service.GukYuinApiPnuJobService;
import com.kamco.cd.kamcoback.scheduler.service.GukYuinApiStatusJobService; import com.kamco.cd.kamcoback.scheduler.service.GukYuinApiStatusJobService;
import com.kamco.cd.kamcoback.scheduler.service.GukYuinApiStbltJobService; import com.kamco.cd.kamcoback.scheduler.service.GukYuinApiStbltJobService;
import com.kamco.cd.kamcoback.scheduler.service.MemberInactiveJobService; import com.kamco.cd.kamcoback.scheduler.service.MemberInactiveJobService;
import com.kamco.cd.kamcoback.scheduler.service.TrainingDataLabelJobService; import com.kamco.cd.kamcoback.scheduler.service.TrainingDataLabelJobService;
import com.kamco.cd.kamcoback.scheduler.service.TrainingDataReviewJobService; import com.kamco.cd.kamcoback.scheduler.service.TrainingDataReviewJobService;
import io.swagger.v3.oas.annotations.Hidden;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Content;
@@ -50,7 +48,6 @@ public class SchedulerApiController {
private final MapSheetMngFileJobController mapSheetMngFileJobController; private final MapSheetMngFileJobController mapSheetMngFileJobController;
private final InferenceResultShpService inferenceResultShpService; private final InferenceResultShpService inferenceResultShpService;
private final GukYuinApiService gukYuinApiService; private final GukYuinApiService gukYuinApiService;
private final GukYuinApiPnuCntUpdateJobService gukYuinApiPnuCntUpdateJobService;
@Operation(summary = "국유인 탐지객체 조회 PNU 업데이트 스케줄링", description = "국유인 탐지객체 조회 PNU 업데이트 스케줄링") @Operation(summary = "국유인 탐지객체 조회 PNU 업데이트 스케줄링", description = "국유인 탐지객체 조회 PNU 업데이트 스케줄링")
@GetMapping("/gukyuin/pnu") @GetMapping("/gukyuin/pnu")
@@ -62,7 +59,7 @@ public class SchedulerApiController {
@Operation(summary = "국유인 등록 상태 체크 스케줄링", description = "국유인 등록 상태 체크 스케줄링") @Operation(summary = "국유인 등록 상태 체크 스케줄링", description = "국유인 등록 상태 체크 스케줄링")
@GetMapping("/gukyuin/status") @GetMapping("/gukyuin/status")
public ApiResponseDto<Void> findGukYuinMastCompleteYn() { public ApiResponseDto<Void> findGukYuinMastCompleteYn() {
gukYuinApiStatusJobService.findGukYuinMastCompleteYn(); gukYuinApiStatusJobService.findGukYuinPnuCntUpdate();
return ApiResponseDto.ok(null); return ApiResponseDto.ok(null);
} }
@@ -146,7 +143,6 @@ public class SchedulerApiController {
return ApiResponseDto.createOK("OK"); return ApiResponseDto.createOK("OK");
} }
@Hidden
@Operation(summary = "추론결과 데이터 저장", description = "추론결과 데이터 저장") @Operation(summary = "추론결과 데이터 저장", description = "추론결과 데이터 저장")
@ApiResponses( @ApiResponses(
value = { value = {
@@ -186,11 +182,4 @@ public class SchedulerApiController {
@PathVariable String uid, @PathVariable int updateCnt) { @PathVariable String uid, @PathVariable int updateCnt) {
return ApiResponseDto.ok(gukYuinApiService.updateStbltRandomData(uid, updateCnt)); return ApiResponseDto.ok(gukYuinApiService.updateStbltRandomData(uid, updateCnt));
} }
@Operation(summary = "pnu 연동 이후 pnu cnt, 상태 업데이트", description = "pnu 연동 이후 pnu cnt, 상태 업데이트")
@GetMapping("/gukyuin/pnu-cnt/{uid}")
public ApiResponseDto<Void> updateGukYuinContListPnuUpdateCnt(@PathVariable String uid) {
gukYuinApiPnuCntUpdateJobService.updateGukYuinContListPnuUpdateCnt(uid);
return ApiResponseDto.ok(null);
}
} }

View File

@@ -21,6 +21,17 @@ public class AsyncConfig {
return ex; return ex;
} }
@Bean(name = "makeShapeFile")
public Executor makeShapeFileExecutor() {
ThreadPoolTaskExecutor ex = new ThreadPoolTaskExecutor();
ex.setCorePoolSize(2);
ex.setMaxPoolSize(4);
ex.setQueueCapacity(50);
ex.setThreadNamePrefix("makeShapeFile-");
ex.initialize();
return ex;
}
@Bean(name = "auditLogExecutor") @Bean(name = "auditLogExecutor")
public Executor auditLogExecutor() { public Executor auditLogExecutor() {
ThreadPoolTaskExecutor exec = new ThreadPoolTaskExecutor(); ThreadPoolTaskExecutor exec = new ThreadPoolTaskExecutor();
@@ -31,4 +42,16 @@ public class AsyncConfig {
exec.initialize(); exec.initialize();
return exec; return exec;
} }
// 0313
@Bean(name = "inferenceEndExecutor")
public Executor inferenceEndExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(10);
executor.setQueueCapacity(100);
executor.setThreadNamePrefix("inference-async-");
executor.initialize();
return executor;
}
} }

View File

@@ -1,24 +0,0 @@
package com.kamco.cd.kamcoback.scheduler.service;
import com.kamco.cd.kamcoback.gukyuin.dto.GukYuinStatus;
import com.kamco.cd.kamcoback.postgres.core.GukYuinPnuCntUpdateJobCoreService;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.springframework.stereotype.Service;
@Log4j2
@Service
@RequiredArgsConstructor
public class GukYuinApiPnuCntUpdateJobService {
private final GukYuinPnuCntUpdateJobCoreService gukYuinPnuCntUpdateJobCoreService;
public void updateGukYuinContListPnuUpdateCnt(String uid) {
int execCnt = gukYuinPnuCntUpdateJobCoreService.updateGukYuinContListPnuUpdateCnt(uid);
if (execCnt > 0) {
gukYuinPnuCntUpdateJobCoreService.updateGukYuinApplyStatus(
uid, GukYuinStatus.PNU_COMPLETED.getId());
}
}
}

View File

@@ -5,6 +5,7 @@ import com.kamco.cd.kamcoback.gukyuin.dto.ChngDetectMastDto.LearnKeyDto;
import com.kamco.cd.kamcoback.gukyuin.dto.GukYuinStatus; import com.kamco.cd.kamcoback.gukyuin.dto.GukYuinStatus;
import com.kamco.cd.kamcoback.gukyuin.service.GukYuinApiService; import com.kamco.cd.kamcoback.gukyuin.service.GukYuinApiService;
import com.kamco.cd.kamcoback.postgres.core.GukYuinJobCoreService; import com.kamco.cd.kamcoback.postgres.core.GukYuinJobCoreService;
import com.kamco.cd.kamcoback.postgres.core.GukYuinPnuCntUpdateJobCoreService;
import java.util.List; import java.util.List;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2; import lombok.extern.log4j.Log4j2;
@@ -18,6 +19,7 @@ public class GukYuinApiStatusJobService {
private final GukYuinJobCoreService gukYuinJobCoreService; private final GukYuinJobCoreService gukYuinJobCoreService;
private final GukYuinApiService gukYuinApiService; private final GukYuinApiService gukYuinApiService;
private final GukYuinPnuCntUpdateJobCoreService gukYuinPnuCntUpdateJobCoreService;
@Value("${spring.profiles.active}") @Value("${spring.profiles.active}")
private String profile; private String profile;
@@ -31,12 +33,8 @@ public class GukYuinApiStatusJobService {
return "local".equalsIgnoreCase(profile); return "local".equalsIgnoreCase(profile);
} }
/** 국유인 연동 후, 100% 되었는지 확인하는 스케줄링 매 10분마다 호출 */ /** 매일 00시에 pnu cnt 업데이트 */
// @Scheduled(cron = "0 0/10 * * * *") public void findGukYuinPnuCntUpdate() {
public void findGukYuinMastCompleteYn() {
// if (isLocalProfile()) {
// return;
// }
List<LearnKeyDto> list = List<LearnKeyDto> list =
gukYuinJobCoreService.findGukyuinApplyStatusUidList( gukYuinJobCoreService.findGukyuinApplyStatusUidList(
@@ -59,8 +57,9 @@ public class GukYuinApiStatusJobService {
Integer progress = Integer progress =
basic.getExcnPgrt() == null ? null : Integer.parseInt(basic.getExcnPgrt().trim()); basic.getExcnPgrt() == null ? null : Integer.parseInt(basic.getExcnPgrt().trim());
if (progress != null && progress == 100) { if (progress != null && progress == 100) {
gukYuinJobCoreService.updateGukYuinApplyStateComplete( gukYuinPnuCntUpdateJobCoreService.updateGukYuinContListPnuUpdateCnt();
dto.getId(), GukYuinStatus.GUK_COMPLETED); gukYuinPnuCntUpdateJobCoreService.updateGukYuinApplyStatus(
dto.getUid(), GukYuinStatus.PNU_COMPLETED.getId());
} }
} catch (Exception e) { } catch (Exception e) {
log.error("[GUKYUIN] failed uid={}", dto.getChnDtctMstId(), e); log.error("[GUKYUIN] failed uid={}", dto.getChnDtctMstId(), e);

View File

@@ -255,7 +255,9 @@ public class MapSheetInferenceJobService {
// 추론 종료일때 shp 파일 생성 // 추론 종료일때 shp 파일 생성
String batchIdStr = batchIds.stream().map(String::valueOf).collect(Collectors.joining(",")); String batchIdStr = batchIds.stream().map(String::valueOf).collect(Collectors.joining(","));
// shp 파일 비동기 생성 // 0312 shp 파일 비동기 생성 (바꿔주세요)
// shpPipelineService.makeShapeFile(sheet.getUid(), batchIds);
// 0511 shp-exporter-v2.jar 에 에러가 있어 우선 기존 로직으로 변경함
shpPipelineService.runPipeline(jarPath, datasetDir, batchIdStr, sheet.getUid()); shpPipelineService.runPipeline(jarPath, datasetDir, batchIdStr, sheet.getUid());
} }

View File

@@ -3,7 +3,7 @@ package com.kamco.cd.kamcoback.scheduler.service;
import com.kamco.cd.kamcoback.common.service.ExternalJarRunner; import com.kamco.cd.kamcoback.common.service.ExternalJarRunner;
import com.kamco.cd.kamcoback.postgres.core.InferenceResultCoreService; import com.kamco.cd.kamcoback.postgres.core.InferenceResultCoreService;
import com.kamco.cd.kamcoback.scheduler.config.ShpKeyLock; import com.kamco.cd.kamcoback.scheduler.config.ShpKeyLock;
import java.nio.file.Paths; import java.util.List;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2; import lombok.extern.log4j.Log4j2;
import org.springframework.scheduling.annotation.Async; import org.springframework.scheduling.annotation.Async;
@@ -18,11 +18,40 @@ public class ShpPipelineService {
private final ExternalJarRunner externalJarRunner; private final ExternalJarRunner externalJarRunner;
private final ShpKeyLock shpKeyLock; private final ShpKeyLock shpKeyLock;
// 0312 shp 파일 비동기 생성
@Async("makeShapeFile")
public void makeShapeFile(String inferenceId, List<Long> batchIds) {
if (!shpKeyLock.tryLock(inferenceId)) {
log.info("");
log.info("============================================================");
log.info("SHP pipeline already running. inferenceId={}", inferenceId);
log.info("============================================================");
try {
log.info("SHP pipeline already start. inferenceId={}", inferenceId);
externalJarRunner.run(inferenceId, batchIds);
} catch (Exception e) {
log.error("SHP pipeline failed. inferenceId={}", inferenceId, e);
// TODO 실패 상태 업데이트 로직 추가
} finally {
log.info("============================================================");
log.info("SHP pipeline DONE. inferenceId={}", inferenceId);
log.info("============================================================");
log.info("");
shpKeyLock.unlock(inferenceId);
}
}
}
/**
* shp 파일 생성 1. merge 생성 2. 생성된 merge shp 파일로 geoserver 등록, 3.도엽별로 shp 생성
*
* @param jarPath 실행 jar 파일 경로
* @param datasetDir shp 파일이 생성될 경로
* @param batchIds 추론 batch id = 12,13,14
* @param inferenceId 추론 uid 32자
*/
@Async("shpExecutor") @Async("shpExecutor")
public void runPipeline(String jarPath, String datasetDir, String batchIds, String inferenceId) { public void runPipeline(String jarPath, String datasetDir, String batchIds, String inferenceId) {
//
// batchIds.split(",")
// inferenceResultCoreService.getInferenceResultCnt();
// inferenceId 기준 동시 실행 제한 // inferenceId 기준 동시 실행 제한
if (!shpKeyLock.tryLock(inferenceId)) { if (!shpKeyLock.tryLock(inferenceId)) {
@@ -31,24 +60,33 @@ public class ShpPipelineService {
} }
try { try {
log.info("");
log.info("============================================================");
log.info("SHP pipeline started. inferenceId={}", inferenceId);
log.info("============================================================");
// uid 기준 merge shp, geojson 파일 생성 // uid 기준 merge shp, geojson 파일 생성
externalJarRunner.run(jarPath, batchIds, inferenceId, "", "MERGED"); externalJarRunner.run(jarPath, batchIds, inferenceId, "", "MERGED");
// uid 기준 shp 파일 geoserver 등록 // uid 기준 shp 파일 geoserver 등록
String register = // String register =
Paths.get(datasetDir, inferenceId, "merge", inferenceId + ".shp").toString(); // Paths.get(datasetDir, inferenceId, "merge", inferenceId + ".shp").toString();
log.info("register={}", register); // log.info("register={}", register);
externalJarRunner.run(jarPath, register, inferenceId); // externalJarRunner.run(jarPath, register, inferenceId);
//
// uid 기준 도엽별 shp, geojson 파일 생성 // // uid 기준 도엽별 shp, geojson 파일 생성
externalJarRunner.run(jarPath, batchIds, inferenceId, "", "RESOLVE"); // externalJarRunner.run(jarPath, batchIds, inferenceId, "", "RESOLVE");
//
log.info("SHP pipeline finished. inferenceId={}", inferenceId); // log.info("SHP pipeline finished. inferenceId={}", inferenceId);
} catch (Exception e) { } catch (Exception e) {
log.error("SHP pipeline failed. inferenceId={}", inferenceId, e); log.error("SHP pipeline failed. inferenceId={}", inferenceId, e);
// TODO 실패 상태 업데이트 로직 추가 // TODO 실패 상태 업데이트 로직 추가
} finally { } finally {
log.info("============================================================");
log.info("SHP pipeline DONE. inferenceId={}", inferenceId);
log.info("============================================================");
shpKeyLock.unlock(inferenceId); shpKeyLock.unlock(inferenceId);
} }
} }

View File

@@ -15,7 +15,6 @@ import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.time.LocalDate; import java.time.LocalDate;
import java.util.List; import java.util.List;
import java.util.Objects;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2; import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
@@ -43,6 +42,11 @@ public class TrainingDataReviewJobService {
exportGeojsonLabelingGeom(null); exportGeojsonLabelingGeom(null);
} }
/**
* 미사용 -> kamco-cd-cron GIT에 kamco-make-dataset-generation jar 생성 로직에 포함되어 해당 로직은 미사용
*
* @param baseDate
*/
public void exportGeojsonLabelingGeom(LocalDate baseDate) { public void exportGeojsonLabelingGeom(LocalDate baseDate) {
// 1) 경로/파일명 결정 // 1) 경로/파일명 결정
@@ -51,7 +55,7 @@ public class TrainingDataReviewJobService {
log.info("[Step 1-1] geojson 파일 생성할 경로: {}", targetDir); log.info("[Step 1-1] geojson 파일 생성할 경로: {}", targetDir);
// 2) 진행중인 회차 중, complete_cnt 가 존재하는 회차 목록 가져오기 // 2) 진행중인 회차 중, complete_cnt 가 존재하는 회차 목록 가져오기
log.info("[Step 1-2] 진행중인 회차 중, complete_cnt 가 존재하는 회차 목록 가져오기"); log.info("[Step 1-2] 진행중(ING)인 회차 중, 검수완료한(complete_cnt) 갯수가 존재하는 회차 목록 가져오기");
List<AnalCntInfo> analList = trainingDataReviewJobCoreService.findAnalCntInfoList(); List<AnalCntInfo> analList = trainingDataReviewJobCoreService.findAnalCntInfoList();
log.info("[Step 1-3] 회차 리스트 건수: {}", analList == null ? 0 : analList.size()); log.info("[Step 1-3] 회차 리스트 건수: {}", analList == null ? 0 : analList.size());
@@ -61,19 +65,14 @@ public class TrainingDataReviewJobService {
} }
for (AnalCntInfo info : analList) { for (AnalCntInfo info : analList) {
log.info("[Step 2-1] 회차 폴리곤 전체 건수 == 파일 생성 건수 같은지 확인");
log.info("=== info.getAllCnt(): {}", info.getAllCnt());
log.info("=== info.getFileCnt(): {}", info.getFileCnt());
if (Objects.equals(info.getAllCnt(), info.getFileCnt())) {
log.info("[Step 2-2] 회차 폴리곤 전체 건수 == 파일 생성 건수 같아서 파일 생성 진행하지 않음 continue");
continue;
}
String resultUid = info.getResultUid(); // 회차의 대문자 uid (폴더명으로 사용) String resultUid = info.getResultUid(); // 회차의 대문자 uid (폴더명으로 사용)
// 3) 회차 + 어제까지 검수 완료된 총 데이터의 도엽별 목록 가져오기 // 3) 회차 + 어제까지 검수 완료된 총 데이터의 도엽별 목록 가져오기
log.info("[Step 3-1] 회차 + 어제까지 검수 완료된 총 데이터의 도엽별 목록 가져오기"); log.info("[Step 3-1] 회차 + 어제까지 검수 완료된 총 데이터의 도엽별 목록 가져오기");
log.info(" === 기준일자 baseDate : " + baseDate);
log.info(" === 검수완료일자 < 기준일자인 폴리곤의 도엽 목록을 조회");
List<AnalMapSheetList> analMapList = List<AnalMapSheetList> analMapList =
trainingDataReviewJobCoreService.findCompletedAnalMapSheetList( trainingDataReviewJobCoreService.findCompletedAnalMapSheetList(
info.getAnalUid(), baseDate); info.getAnalUid(), baseDate);
@@ -84,7 +83,7 @@ public class TrainingDataReviewJobService {
continue; continue;
} }
log.info("[Step 4-1] 도엽별 geom 데이터 가지고 와서 geojson 만들기 시작"); log.info("[Step 4-1] 도엽별 geom 데이터 가지고 와서 geojson 만들기 시작");
for (AnalMapSheetList mapSheet : analMapList) { for (AnalMapSheetList mapSheet : analMapList) {
// 4) 도엽별 geom 데이터 가지고 와서 geojson 만들기 // 4) 도엽별 geom 데이터 가지고 와서 geojson 만들기
log.info("[Step 4-2] 도엽별 검수완료된 폴리곤 데이터 목록 조회"); log.info("[Step 4-2] 도엽별 검수완료된 폴리곤 데이터 목록 조회");
@@ -123,9 +122,7 @@ public class TrainingDataReviewJobService {
objectMapper.enable(SerializationFeature.INDENT_OUTPUT); objectMapper.enable(SerializationFeature.INDENT_OUTPUT);
objectMapper.writeValue(outputPath.toFile(), collection); objectMapper.writeValue(outputPath.toFile(), collection);
// geoUids : file_create_yn = true 로 업데이트 log.info("[Step 6-3] geoJson 파일 생성 완료");
log.info("[Step 6-3] learn_data_geom 에 file_create_yn = true 로 업데이트");
trainingDataReviewJobCoreService.updateLearnDataGeomFileCreateYn(geoUids);
} catch (IOException e) { } catch (IOException e) {
log.error(e.getMessage()); log.error(e.getMessage());

View File

@@ -0,0 +1,83 @@
package com.kamco.cd.kamcoback.test;
import com.kamco.cd.kamcoback.config.api.ApiResponseDto;
import com.kamco.cd.kamcoback.scheduler.service.ShpPipelineService;
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 java.util.List;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.ErrorResponse;
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 = "test shape api", description = "test shape api")
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/test")
public class TestShapeApiController {
private final ShpPipelineService shpPipelineService;
@Value("${inference.jar-path}")
private String jarPath;
@Value("${file.dataset-dir}")
private String datasetDir;
@Operation(
summary = "shapefile 생성 테스트",
description = "지정된 inference ID와 batch ID 목록으로 shapefile을 생성합니다.")
@ApiResponses({
@ApiResponse(
responseCode = "200",
description = "shapefile 생성 요청 성공",
content = @Content(schema = @Schema(implementation = String.class))),
@ApiResponse(
responseCode = "400",
description = "잘못된 요청 데이터",
content = @Content(schema = @Schema(implementation = ErrorResponse.class))),
@ApiResponse(
responseCode = "500",
description = "서버 오류",
content = @Content(schema = @Schema(implementation = ErrorResponse.class)))
})
@GetMapping("/make-shapefile")
public ApiResponseDto<String> makeShapeFile(
@RequestParam String inferenceId, @RequestParam List<Long> batchIds) {
shpPipelineService.makeShapeFile(inferenceId, batchIds);
return ApiResponseDto.ok("Shapefile 생성이 시작되었습니다. inferenceId: " + inferenceId);
}
@Operation(
summary = "기존 run pipeline 테스트",
description = "지정된 inference ID와 batch ID 목록으로 shapefile을 생성합니다.")
@ApiResponses({
@ApiResponse(
responseCode = "200",
description = "shapefile 생성 요청 성공",
content = @Content(schema = @Schema(implementation = String.class))),
@ApiResponse(
responseCode = "400",
description = "잘못된 요청 데이터",
content = @Content(schema = @Schema(implementation = ErrorResponse.class))),
@ApiResponse(
responseCode = "500",
description = "서버 오류",
content = @Content(schema = @Schema(implementation = ErrorResponse.class)))
})
@GetMapping("/run-pipeline")
public ApiResponseDto<String> runPipeline(
@RequestParam String inferenceId, @RequestParam List<Long> batchIds) {
String batchIdStr = batchIds.stream().map(String::valueOf).collect(Collectors.joining(","));
shpPipelineService.runPipeline(jarPath, datasetDir, batchIdStr, inferenceId);
return ApiResponseDto.ok("runPipeline 생성이 시작되었습니다. inferenceId: " + inferenceId);
}
}

View File

@@ -7,6 +7,7 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.databind.node.ObjectNode;
import com.kamco.cd.kamcoback.common.enums.ImageryFitStatus; import com.kamco.cd.kamcoback.common.enums.ImageryFitStatus;
import com.kamco.cd.kamcoback.common.utils.HeaderUtil;
import com.kamco.cd.kamcoback.common.utils.geometry.GeometryDeserializer; import com.kamco.cd.kamcoback.common.utils.geometry.GeometryDeserializer;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import java.util.List; import java.util.List;
@@ -399,7 +400,10 @@ public class TrainingDataLabelDto {
private String memo; private String memo;
public InspectionResultInfo(String verificationResult, String inappropriateReason) { public InspectionResultInfo(String verificationResult, String inappropriateReason) {
this.verificationResult = ImageryFitStatus.fromCode(verificationResult).getText(); this.verificationResult =
HeaderUtil.isEnglishRequest()
? ImageryFitStatus.fromCode(verificationResult).getTextEn()
: ImageryFitStatus.fromCode(verificationResult).getText();
this.inappropriateReason = inappropriateReason; this.inappropriateReason = inappropriateReason;
} }
} }

View File

@@ -39,7 +39,7 @@ spring:
data: data:
redis: redis:
host: 192.168.2.109 host: 192.168.2.126
port: 6379 port: 6379
password: kamco password: kamco
@@ -59,8 +59,8 @@ jwt:
secret: "kamco_token_9b71e778-19a3-4c1d-97bf-2d687de17d5b" secret: "kamco_token_9b71e778-19a3-4c1d-97bf-2d687de17d5b"
access-token-validity-in-ms: 86400000 # 1일 access-token-validity-in-ms: 86400000 # 1일
refresh-token-validity-in-ms: 604800000 # 7일 refresh-token-validity-in-ms: 604800000 # 7일
#access-token-validity-in-ms: 60000 # 1 #access-token-validity-in-ms: 300000 # 5
#refresh-token-validity-in-ms: 300000 # 5 #refresh-token-validity-in-ms: 600000 # 10
token: token:
refresh-cookie-name: kamco-dev # 개발용 쿠키 이름 refresh-cookie-name: kamco-dev # 개발용 쿠키 이름
@@ -100,6 +100,7 @@ inference:
url: http://192.168.2.183:8000/jobs url: http://192.168.2.183:8000/jobs
batch-url: http://192.168.2.183:8000/batches batch-url: http://192.168.2.183:8000/batches
jar-path: ${inference.nfs}/repo/jar/shp-exporter.jar jar-path: ${inference.nfs}/repo/jar/shp-exporter.jar
jar-path-v2: ${inference.nfs}/repo/jar/shp-exporter-v2.jar
inference-server-name: server1,server2,server3,server4 inference-server-name: server1,server2,server3,server4
output-dir: ${inference.nfs}/model_output/export output-dir: ${inference.nfs}/model_output/export

View File

@@ -27,7 +27,7 @@ spring:
data: data:
redis: redis:
host: 192.168.2.109 host: 192.168.2.126
port: 6379 port: 6379
password: kamco password: kamco
@@ -78,6 +78,7 @@ inference:
url: http://10.100.0.11:8000/jobs url: http://10.100.0.11:8000/jobs
batch-url: http://10.100.0.11:8000/batches batch-url: http://10.100.0.11:8000/batches
jar-path: jar/shp-exporter.jar jar-path: jar/shp-exporter.jar
jar-path-v2: jar/shp-exporter-v2.jar
inference-server-name: server1,server2,server3,server4 inference-server-name: server1,server2,server3,server4
output-dir: ${inference.nfs}/model_output/export output-dir: ${inference.nfs}/model_output/export

View File

@@ -95,6 +95,8 @@ inference:
url: http://172.16.4.56:8000/jobs url: http://172.16.4.56:8000/jobs
batch-url: http://172.16.4.56:8000/batches batch-url: http://172.16.4.56:8000/batches
jar-path: ${inference.nfs}/repo/jar/shp-exporter.jar jar-path: ${inference.nfs}/repo/jar/shp-exporter.jar
# //0312
jar-path-v2: ${inference.nfs}/repo/jar/shp-exporter-v2.jar
inference-server-name: server1,server2,server3,server4 inference-server-name: server1,server2,server3,server4
output-dir: ${inference.nfs}/model_output/export output-dir: ${inference.nfs}/model_output/export

View File

@@ -88,3 +88,6 @@ inference:
nfs: /kamco-nfs nfs: /kamco-nfs
geojson-dir: ${inference.nfs}/requests/ # 추론실행을 위한 파일생성경로 geojson-dir: ${inference.nfs}/requests/ # 추론실행을 위한 파일생성경로
jar-path: ${inference.nfs}/repo/jar/shp-exporter.jar jar-path: ${inference.nfs}/repo/jar/shp-exporter.jar
# //0312
jar-path-v2: ${inference.nfs}/repo/jar/shp-exporter-v2.jar