134 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
2cc490012e Merge pull request 'feat/infer_dev_260211' (#131) from feat/infer_dev_260211 into develop
Reviewed-on: #131
2026-03-04 23:01:14 +09:00
f99144eccc pnu cnt update 수동호출 추가 2026-03-04 23:00:46 +09:00
66b78022a9 변화탐지 selected polygon, point 조건 수정 2026-03-04 22:59:59 +09:00
199e302068 Merge pull request '실태조사 적합여부 값 수정' (#130) from feat/infer_dev_260211 into develop
Reviewed-on: #130
2026-03-04 21:01:52 +09:00
5a3370d5fb 실태조사 적합여부 값 수정 2026-03-04 21:01:21 +09:00
5d7cb18fb8 줄맞춤 2026-03-04 20:05:00 +09:00
57baf4d911 국유인의타입추가 2026-03-04 19:58:02 +09:00
2fee2143ba Merge pull request 'pnu 로 polygon, point 조회 API 수정' (#129) from feat/infer_dev_260211 into develop
Reviewed-on: #129
2026-03-04 17:34:46 +09:00
f4a410ba49 pnu 로 polygon, point 조회 API 수정 2026-03-04 17:34:12 +09:00
deba2b84f0 Merge pull request '추론결과 상세 조회 api 시간 log 추가' (#128) from feat/infer_dev_260211 into develop
Reviewed-on: #128
2026-03-04 17:04:25 +09:00
85d8efc493 추론결과 상세 조회 api 시간 log 추가
- 추론결과 기본정보
- 분류별 탐지건수 조회
- 추론관리 분석결과 geom 상세 목록
2026-03-04 17:03:50 +09:00
5a82a9507a Merge pull request 'unfit 업데이트 수정' (#127) from feat/infer_dev_260211 into develop
Reviewed-on: #127
2026-03-04 13:21:37 +09:00
b9a268d43a unfit 업데이트 수정 2026-03-04 13:20:55 +09:00
4d1ea53d8f 변화탐지 pnu로도 조회 가능하게 하기 2026-03-04 08:04:04 +09:00
278d5d20e8 restTemplate read timeout, connect timeout 늘리기 2026-03-03 17:07:19 +09:00
4d9fa2d6a5 좌표계 5179 추가 2026-03-03 15:03:44 +09:00
e28e5ba371 국유인 반영 파일 경로 수정 2026-03-03 14:23:24 +09:00
22cc996399 Merge pull request 'feat/infer_dev_260211' (#126) from feat/infer_dev_260211 into develop
Reviewed-on: #126
2026-02-28 00:34:35 +09:00
e32f5e33d9 추론 기본 정보에 상태 추가 2026-02-28 00:34:03 +09:00
f95b82bf7f 스케줄링에 log 추가 완료 2026-02-28 00:23:48 +09:00
060a0db126 Merge pull request 'feat/infer_dev_260211' (#125) from feat/infer_dev_260211 into develop
Reviewed-on: #125
2026-02-27 22:53:07 +09:00
0692456b5b 학습데이터제작 > 작업현황 > 폴리곤 수 추가 2026-02-27 22:52:45 +09:00
8fb9f89c8f 국유인 실태조사 적합여부 임의로 업데이트 API 2026-02-27 22:15:31 +09:00
1cfe6e923f Merge remote-tracking branch 'origin/feat/infer_dev_260211' into feat/infer_dev_260211 2026-02-27 21:04:24 +09:00
af2721949c shp 생성 컨트롤러 이동, 주석추가 2026-02-27 21:04:18 +09:00
e9b9075c72 국유인 실태조사 job 에 log 찍기 추가 2026-02-27 20:37:56 +09:00
2b28241110 Merge pull request '국유인 연동 전 파일 경로 확인 log 추가' (#124) from feat/infer_dev_260211 into develop
Reviewed-on: #124
2026-02-27 19:41:26 +09:00
8e002f5673 국유인 연동 전 파일 경로 확인 log 추가 2026-02-27 19:40:10 +09:00
972cf808a9 Merge pull request '학습데이터 관리 목록 순서 변경, 주석추가' (#123) from feat/infer_dev_260211 into develop
Reviewed-on: #123
2026-02-27 18:54:29 +09:00
086eb20e8d 학습데이터 관리 목록 순서 변경, 주석추가 2026-02-27 18:54:03 +09:00
c3c194c067 Merge pull request '국유인 실태조사 bulk 확인' (#122) from feat/infer_dev_260211 into develop
Reviewed-on: #122
2026-02-27 17:02:22 +09:00
0acaeaac09 국유인 실태조사 bulk 확인 2026-02-27 17:01:54 +09:00
116 changed files with 2719 additions and 665 deletions

6
.gitignore vendored
View File

@@ -60,6 +60,7 @@ Thumbs.db
.env.*.local .env.*.local
application-local.yml application-local.yml
application-secret.yml application-secret.yml
metrics-collector/.env
### Docker (local testing) ### ### Docker (local testing) ###
.dockerignore .dockerignore
@@ -72,3 +73,8 @@ docker-compose.override.yml
*.swo *.swo
*~ *~
!/CLAUDE.md !/CLAUDE.md
### Metrics Collector ###
metrics-collector/venv/
metrics-collector/*.pid
metrics-collector/wheels/

47
inference-table-index.sh Normal file
View File

@@ -0,0 +1,47 @@
#!/bin/bash
#############################################
# PostgreSQL INDEX CREATE SCRIPT
# 에러 발생해도 계속 진행
#############################################
# set -e 제거 (중단 안함)
# ===== 환경변수 체크 =====
if [ -z "$DB_HOST" ] || [ -z "$DB_PORT" ] || [ -z "$DB_NAME" ] || [ -z "$DB_USER" ]; then
echo "DB 환경변수가 설정되지 않았습니다."
exit 1
fi
echo "========================================"
echo "START INDEX CREATE"
echo "TIME: $(date)"
echo "========================================"
run_index() {
echo "----------------------------------------"
echo "Running: $1"
psql -h $DB_HOST -p $DB_PORT -U $DB_USER -d $DB_NAME -c "$1"
echo "----------------------------------------"
}
run_index "CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_files_uid_ext_state
ON tb_map_sheet_mng_files (hst_uid, file_ext, file_state);"
run_index "CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_mng_files_hstuid_ext
ON tb_map_sheet_mng_files (hst_uid, file_ext);"
run_index "CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_inkx_no_use
ON tb_map_inkx_5k (mapidcd_no, use_inference);"
run_index "CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_inkx5k_mapidcd
ON tb_map_inkx_5k (mapidcd_no);"
run_index "CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_hst_exec_sheetnum_yyyy_desc
ON tb_map_sheet_mng_hst (map_sheet_num, mng_yyyy DESC);"
echo "========================================"
echo "END INDEX CREATE"
echo "TIME: $(date)"
echo "========================================"
echo "모든 인덱스 시도 완료"

View File

@@ -19,6 +19,7 @@ import org.springframework.web.filter.OncePerRequestFilter;
public class JwtAuthenticationFilter extends OncePerRequestFilter { public class JwtAuthenticationFilter extends OncePerRequestFilter {
private static final AntPathMatcher PATH_MATCHER = new AntPathMatcher(); private static final AntPathMatcher PATH_MATCHER = new AntPathMatcher();
// JWT 필터를 타지 않게 할 URL 패턴들
private static final String[] EXCLUDE_PATHS = { private static final String[] EXCLUDE_PATHS = {
"/api/auth/signin", "/api/auth/refresh", "/api/auth/logout", "/api/members/*/password" "/api/auth/signin", "/api/auth/refresh", "/api/auth/logout", "/api/members/*/password"
}; };
@@ -30,11 +31,12 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter {
HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException { throws ServletException, IOException {
// HTTP 요청 헤더에서 JWT 토큰 꺼내기
String token = resolveToken(request); String token = resolveToken(request);
// 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());
@@ -57,7 +59,7 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter {
return false; return false;
} }
// /api/members/{memberId}/password // HTTP 요청 헤더에서 JWT 토큰 꺼내기
private String resolveToken(HttpServletRequest request) { private String resolveToken(HttpServletRequest request) {
String bearer = request.getHeader("Authorization"); String bearer = request.getHeader("Authorization");
if (bearer != null && bearer.startsWith("Bearer ")) { if (bearer != null && bearer.startsWith("Bearer ")) {

View File

@@ -8,10 +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}")
@@ -31,11 +34,15 @@ public class JwtTokenProvider {
this.key = Keys.hmacShaKeyFor(secret.getBytes(StandardCharsets.UTF_8)); this.key = Keys.hmacShaKeyFor(secret.getBytes(StandardCharsets.UTF_8));
} }
// 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 생성
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

@@ -6,6 +6,7 @@ import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations; import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
/** redis token handler */
@Service @Service
@RequiredArgsConstructor @RequiredArgsConstructor
public class RefreshTokenService { public class RefreshTokenService {
@@ -13,16 +14,33 @@ public class RefreshTokenService {
private final StringRedisTemplate redisTemplate; private final StringRedisTemplate redisTemplate;
private static final String PREFIX = "RT:"; private static final String PREFIX = "RT:";
/**
* Refresh Token 저장
*
* @param username 사용자 식별값 (보통 username or userId)
* @param refreshToken 발급된 Refresh Token
* @param ttlMillis 토큰 만료 시간 (밀리초 단위)
*/
public void save(String username, String refreshToken, long ttlMillis) { public void save(String username, String refreshToken, long ttlMillis) {
ValueOperations<String, String> ops = redisTemplate.opsForValue(); ValueOperations<String, String> ops = redisTemplate.opsForValue();
ops.set(PREFIX + username, refreshToken, Duration.ofMillis(ttlMillis)); ops.set(PREFIX + username, refreshToken, Duration.ofMillis(ttlMillis));
} }
/**
* Refresh Token 검증
*
* <p>1. Redis에 저장된 값 조회 2. 클라이언트가 보낸 refreshToken과 비교 3. 동일하면 true
*/
public boolean validate(String username, String refreshToken) { public boolean validate(String username, String refreshToken) {
String stored = redisTemplate.opsForValue().get(PREFIX + username); String stored = redisTemplate.opsForValue().get(PREFIX + username);
return stored != null && stored.equals(refreshToken); return stored != null && stored.equals(refreshToken);
} }
/**
* Refresh Token 삭제
*
* <p>로그아웃 시 호출 Redis에서 해당 사용자 토큰 제거
*/
public void delete(String username) { public void delete(String username) {
redisTemplate.delete(PREFIX + username); redisTemplate.delete(PREFIX + username);
} }

View File

@@ -117,16 +117,18 @@ public class ChangeDetectionApiController {
@Parameter(description = "도곽/일반(MAPSHEET/ADDRESS) 검색 타입", required = true) @Parameter(description = "도곽/일반(MAPSHEET/ADDRESS) 검색 타입", required = true)
@RequestParam(defaultValue = "MAPSHEET") @RequestParam(defaultValue = "MAPSHEET")
DetectSearchType type, DetectSearchType type,
@Parameter(description = "5k/50k 구분(SCALE_5K/SCALE_50K))", required = true) @Parameter(description = "5k/50k 구분(SCALE_5K/SCALE_50K))")
@RequestParam(defaultValue = "SCALE_50K") @RequestParam(defaultValue = "SCALE_50K", required = false)
MapScaleType scale, MapScaleType scale,
@Parameter( @Parameter(
description = "변화탐지 년도(차수) /year-list 의 uuid", description = "변화탐지 년도(차수) /year-list 의 uuid",
example = "8584e8d4-53b3-4582-bde2-28a81495a626") example = "8584e8d4-53b3-4582-bde2-28a81495a626")
UUID uuid, UUID uuid,
@Parameter(description = "도엽번호", example = "34607") @RequestParam String mapSheetNum) { @Parameter(description = "도엽번호", example = "34607") @RequestParam(required = false)
String mapSheetNum,
@Parameter(description = "pnu") @RequestParam(required = false) String pnu) {
return ApiResponseDto.ok( return ApiResponseDto.ok(
changeDetectionService.getChangeDetectionPolygonList(type, scale, uuid, mapSheetNum)); changeDetectionService.getChangeDetectionPolygonList(type, scale, uuid, mapSheetNum, pnu));
} }
@Operation(summary = "변화탐지 결과 Point", description = "변화탐지 결과 Point") @Operation(summary = "변화탐지 결과 Point", description = "변화탐지 결과 Point")
@@ -135,16 +137,18 @@ public class ChangeDetectionApiController {
@Parameter(description = "도곽/일반(MAPSHEET/ADDRESS) 검색 타입", required = true) @Parameter(description = "도곽/일반(MAPSHEET/ADDRESS) 검색 타입", required = true)
@RequestParam(defaultValue = "MAPSHEET") @RequestParam(defaultValue = "MAPSHEET")
DetectSearchType type, DetectSearchType type,
@Parameter(description = "5k/50k 구분(SCALE_5K/SCALE_50K))", required = true) @Parameter(description = "5k/50k 구분(SCALE_5K/SCALE_50K))")
@RequestParam(defaultValue = "SCALE_50K") @RequestParam(defaultValue = "SCALE_50K", required = false)
MapScaleType scale, MapScaleType scale,
@Parameter( @Parameter(
description = "변화탐지 년도(차수) /year-list 의 uuid", description = "변화탐지 년도(차수) /year-list 의 uuid",
example = "8584e8d4-53b3-4582-bde2-28a81495a626") example = "8584e8d4-53b3-4582-bde2-28a81495a626")
UUID uuid, UUID uuid,
@Parameter(description = "도엽번호", example = "34607") @RequestParam String mapSheetNum) { @Parameter(description = "도엽번호", example = "34607") @RequestParam(required = false)
String mapSheetNum,
@Parameter(description = "pnu") @RequestParam(required = false) String pnu) {
return ApiResponseDto.ok( return ApiResponseDto.ok(
changeDetectionService.getChangeDetectionPointList(type, scale, uuid, mapSheetNum)); changeDetectionService.getChangeDetectionPointList(type, scale, uuid, mapSheetNum, pnu));
} }
@Operation(summary = "선택 변화탐지 결과 uuid 조회", description = "선택 변화탐지 결과 uuid 조회") @Operation(summary = "선택 변화탐지 결과 uuid 조회", description = "선택 변화탐지 결과 uuid 조회")
@@ -163,13 +167,13 @@ public class ChangeDetectionApiController {
@RequestParam @RequestParam
String chnDtctId, String chnDtctId,
@Parameter(description = "polygon 32자 uid", example = "3B1A7E5F895A4D9698489540EE1BBE1E") @Parameter(description = "polygon 32자 uid", example = "3B1A7E5F895A4D9698489540EE1BBE1E")
@RequestParam @RequestParam(required = false)
String cdObjectId, String cdObjectId,
@Parameter( @Parameter(
description = "polygon 32자 uids", description = "polygon 32자 uids",
example = example =
"3B1A7E5F895A4D9698489540EE1BBE1E,3B221A2AF9614647A0903A972D56C574,3B22686A7ACE44FC9CB20F1B4FA6DEFD,3B376D94A183479BB5FBE3D7166E6E1A") "3B1A7E5F895A4D9698489540EE1BBE1E,3B221A2AF9614647A0903A972D56C574,3B22686A7ACE44FC9CB20F1B4FA6DEFD,3B376D94A183479BB5FBE3D7166E6E1A")
@RequestParam @RequestParam(required = false)
List<String> cdObjectIds, List<String> cdObjectIds,
@Parameter(description = "pnu") @RequestParam(required = false) String pnu) { @Parameter(description = "pnu") @RequestParam(required = false) String pnu) {
return ApiResponseDto.ok( return ApiResponseDto.ok(
@@ -183,13 +187,13 @@ public class ChangeDetectionApiController {
@RequestParam @RequestParam
String chnDtctId, String chnDtctId,
@Parameter(description = "polygon 32자 uid", example = "3B1A7E5F895A4D9698489540EE1BBE1E") @Parameter(description = "polygon 32자 uid", example = "3B1A7E5F895A4D9698489540EE1BBE1E")
@RequestParam @RequestParam(required = false)
String cdObjectId, String cdObjectId,
@Parameter( @Parameter(
description = "polygon 32자 uids", description = "polygon 32자 uids",
example = example =
"3B1A7E5F895A4D9698489540EE1BBE1E,3B221A2AF9614647A0903A972D56C574,3B22686A7ACE44FC9CB20F1B4FA6DEFD,3B376D94A183479BB5FBE3D7166E6E1A") "3B1A7E5F895A4D9698489540EE1BBE1E,3B221A2AF9614647A0903A972D56C574,3B22686A7ACE44FC9CB20F1B4FA6DEFD,3B376D94A183479BB5FBE3D7166E6E1A")
@RequestParam @RequestParam(required = false)
List<String> cdObjectIds, List<String> cdObjectIds,
@Parameter(description = "pnu") @RequestParam(required = false) String pnu) { @Parameter(description = "pnu") @RequestParam(required = false) String pnu) {
return ApiResponseDto.ok( return ApiResponseDto.ok(

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

@@ -46,13 +46,13 @@ public class ChangeDetectionService {
} }
public ChangeDetectionDto.PolygonFeatureList getChangeDetectionPolygonList( public ChangeDetectionDto.PolygonFeatureList getChangeDetectionPolygonList(
DetectSearchType type, MapScaleType scale, UUID uuid, String mapSheetNum) { DetectSearchType type, MapScaleType scale, UUID uuid, String mapSheetNum, String pnu) {
switch (type) { switch (type) {
case MAPSHEET -> { case MAPSHEET -> {
return changeDetectionCoreService.getChangeDetectionPolygonList(scale, uuid, mapSheetNum); return changeDetectionCoreService.getChangeDetectionPolygonList(scale, uuid, mapSheetNum);
} }
case ADDRESS -> { case ADDRESS -> {
return new ChangeDetectionDto.PolygonFeatureList(); // TODO: 일반 주소 검색 로직 확인 후 작업 필요 return changeDetectionCoreService.getChangeDetectionPnuPolygonList(uuid, pnu);
} }
default -> throw new IllegalArgumentException("Unsupported type: " + type); default -> throw new IllegalArgumentException("Unsupported type: " + type);
} }
@@ -60,14 +60,14 @@ public class ChangeDetectionService {
// Geometry 객체 순환 참조 문제로 캐싱 불가 // Geometry 객체 순환 참조 문제로 캐싱 불가
public ChangeDetectionDto.PointFeatureList getChangeDetectionPointList( public ChangeDetectionDto.PointFeatureList getChangeDetectionPointList(
DetectSearchType type, MapScaleType scale, UUID uuid, String mapSheetNum) { DetectSearchType type, MapScaleType scale, UUID uuid, String mapSheetNum, String pnu) {
switch (type) { switch (type) {
case MAPSHEET -> { case MAPSHEET -> {
return changeDetectionCoreService.getChangeDetectionPointList(scale, uuid, mapSheetNum); return changeDetectionCoreService.getChangeDetectionPointList(scale, uuid, mapSheetNum);
} }
case ADDRESS -> { case ADDRESS -> {
return new ChangeDetectionDto.PointFeatureList(); // TODO: 일반 주소 검색 로직 확인 후 작업 필요 return changeDetectionCoreService.getChangeDetectionPnuPointList(uuid, pnu);
} }
default -> throw new IllegalArgumentException("Unsupported type: " + type); default -> throw new IllegalArgumentException("Unsupported type: " + type);
} }
@@ -101,7 +101,7 @@ public class ChangeDetectionService {
*/ */
public ChangeDetectionDto.PolygonFeatureList getPolygonListByCd( public ChangeDetectionDto.PolygonFeatureList getPolygonListByCd(
String chnDtctId, String cdObjectId, List<String> cdObjectIds, String pnu) { String chnDtctId, String cdObjectId, List<String> cdObjectIds, String pnu) {
return changeDetectionCoreService.getPolygonListByCd(chnDtctId, cdObjectId, cdObjectIds); return changeDetectionCoreService.getPolygonListByCd(chnDtctId, cdObjectId, cdObjectIds, pnu);
} }
/** /**
@@ -115,7 +115,7 @@ public class ChangeDetectionService {
*/ */
public ChangeDetectionDto.PointFeatureList getPointListByCd( public ChangeDetectionDto.PointFeatureList getPointListByCd(
String chnDtctId, String cdObjectId, List<String> cdObjectIds, String pnu) { String chnDtctId, String cdObjectId, List<String> cdObjectIds, String pnu) {
return changeDetectionCoreService.getPointListByCd(chnDtctId, cdObjectId, cdObjectIds); return changeDetectionCoreService.getPointListByCd(chnDtctId, cdObjectId, cdObjectIds, pnu);
} }
/** /**

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,11 +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("Korea 2000 중부 TM, 한국 SHP"); EPSG_5186("5186::Korea 2000 중부 TM, 한국 SHP", "5186::Korea 2000"),
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() {
@@ -24,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,12 +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"),
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() {
@@ -27,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()) {
@@ -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

@@ -16,12 +16,12 @@ public class RestTemplateConfig {
@Bean @Bean
@Primary @Primary
public RestTemplate restTemplate(RestTemplateBuilder builder) { public RestTemplate restTemplate(RestTemplateBuilder builder) {
return build(builder, 2000, 3000); return build(builder, 20000, 60000);
} }
@Bean("restTemplateLong") @Bean("restTemplateLong")
public RestTemplate restTemplateLong(RestTemplateBuilder builder) { public RestTemplate restTemplateLong(RestTemplateBuilder builder) {
return build(builder, 2000, 60000); return build(builder, 20000, 60000);
} }
private RestTemplate build(RestTemplateBuilder builder, int connectTimeoutMs, int readTimeoutMs) { private RestTemplate build(RestTemplateBuilder builder, int connectTimeoutMs, int readTimeoutMs) {

View File

@@ -322,4 +322,23 @@ public class GukYuinApiController {
@PathVariable String chnDtctObjtId) { @PathVariable String chnDtctObjtId) {
return ApiResponseDto.ok(gukYuinApiService.findRlbDtctObject(chnDtctObjtId)); return ApiResponseDto.ok(gukYuinApiService.findRlbDtctObject(chnDtctObjtId));
} }
@Operation(summary = "실태조사 적합여부 업데이트", description = "실태조사 적합여부 업데이트")
@ApiResponses(
value = {
@ApiResponse(
responseCode = "201",
description = "등록 성공",
content =
@Content(
mediaType = "application/json",
schema = @Schema(implementation = DetectMastReq.class))),
@ApiResponse(responseCode = "404", description = "코드를 찾을 수 없음", content = @Content),
@ApiResponse(responseCode = "500", description = "서버 오류", content = @Content)
})
@PostMapping("/rlb/objt/{chnDtctObjtId}/stblt/{stbltYn}")
public ApiResponseDto<ChngDetectContDto.ResultLabelDto> updateStbltObjtYn(
@PathVariable String chnDtctObjtId, @PathVariable String stbltYn) {
return ApiResponseDto.ok(gukYuinApiService.updateStbltObjtYn(chnDtctObjtId, stbltYn, ""));
}
} }

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

@@ -69,7 +69,7 @@ public class GukYuinApiService {
@Value("${file.nfs}") @Value("${file.nfs}")
private String nfs; private String nfs;
@Value("${file.output-dir}") @Value("${file.output-dir}") // 국유인 반영 파일 경로
private String outputDir; private String outputDir;
@Value("${file.dataset-dir}") @Value("${file.dataset-dir}")
@@ -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;
} }
@@ -281,6 +284,7 @@ public class GukYuinApiService {
+ "&reqEpno=" + "&reqEpno="
+ ("Y".equals(batchYn) ? "BATCH" : userUtil.getEmployeeNo()); + ("Y".equals(batchYn) ? "BATCH" : userUtil.getEmployeeNo());
log.info("##### API 호출 URL : {}", url);
ExternalCallResult<ChngDetectContDto.ResultContDto> result = ExternalCallResult<ChngDetectContDto.ResultContDto> result =
externalHttpClient.call( externalHttpClient.call(
url, url,
@@ -289,6 +293,7 @@ public class GukYuinApiService {
netUtils.jsonHeaders(), netUtils.jsonHeaders(),
ChngDetectContDto.ResultContDto.class); ChngDetectContDto.ResultContDto.class);
log.info("##### API 호출 완료 : {}", result.toString());
List<ContBasic> contList = result.body().getResult(); List<ContBasic> contList = result.body().getResult();
if (contList == null || contList.isEmpty()) { if (contList == null || contList.isEmpty()) {
return new ResultContDto( return new ResultContDto(
@@ -348,6 +353,7 @@ public class GukYuinApiService {
info.setReqIp(myip); info.setReqIp(myip);
info.setReqEpno("Y".equals(batchYn) ? "BATCH" : userUtil.getEmployeeNo()); info.setReqEpno("Y".equals(batchYn) ? "BATCH" : userUtil.getEmployeeNo());
log.info("##### API 호출 URL : {}", url);
ExternalCallResult<ChngDetectContDto.ResultLabelDto> result = ExternalCallResult<ChngDetectContDto.ResultLabelDto> result =
externalHttpClient.call( externalHttpClient.call(
url, url,
@@ -355,6 +361,7 @@ public class GukYuinApiService {
info, info,
netUtils.jsonHeaders(), netUtils.jsonHeaders(),
ChngDetectContDto.ResultLabelDto.class); ChngDetectContDto.ResultLabelDto.class);
log.info("##### API 호출 완료 : {}", result.toString());
this.insertGukyuinAuditLog( this.insertGukyuinAuditLog(
EventType.MODIFIED.getId(), EventType.MODIFIED.getId(),
@@ -408,10 +415,12 @@ public class GukYuinApiService {
+ "&reqEpno=" + "&reqEpno="
+ ("Y".equals(batchYn) ? "BATCH" : userUtil.getEmployeeNo()); + ("Y".equals(batchYn) ? "BATCH" : userUtil.getEmployeeNo());
log.info("##### API 호출 URL : {}", url);
ExternalCallResult<ChngDetectMastDto.ResultDto> result = ExternalCallResult<ChngDetectMastDto.ResultDto> result =
externalHttpClient.call( externalHttpClient.call(
url, HttpMethod.GET, null, netUtils.jsonHeaders(), ChngDetectMastDto.ResultDto.class); url, HttpMethod.GET, null, netUtils.jsonHeaders(), ChngDetectMastDto.ResultDto.class);
log.info("##### API 호출 완료 : {}", result.toString());
this.insertGukyuinAuditLog( this.insertGukyuinAuditLog(
EventType.DETAIL.getId(), EventType.DETAIL.getId(),
netUtils.getLocalIP(), netUtils.getLocalIP(),
@@ -460,7 +469,11 @@ public class GukYuinApiService {
} }
// 추론 shp 파일 생성되는 위치 // 추론 shp 파일 생성되는 위치
log.info(
"═════════════════════════════════[s] datasetDir : 추론 shp파일 생성되는 위치 ══════════════════════════════════");
log.info("datasetDir path : " + datasetDir + info.getUid()); log.info("datasetDir path : " + datasetDir + info.getUid());
log.info(
"═════════════════════════════════[e] datasetDir : 추론 shp파일 생성되는 위치 ════════════════════════════");
if (!Files.isDirectory(Path.of(datasetDir + info.getUid()))) { if (!Files.isDirectory(Path.of(datasetDir + info.getUid()))) {
return new ResponseObj( return new ResponseObj(
ApiResponseCode.NOT_FOUND_DATA, "파일 경로에 회차 실행 파일이 생성되지 않았습니다. 확인 부탁드립니다."); ApiResponseCode.NOT_FOUND_DATA, "파일 경로에 회차 실행 파일이 생성되지 않았습니다. 확인 부탁드립니다.");
@@ -472,7 +485,11 @@ public class GukYuinApiService {
// reqDto 셋팅 // reqDto 셋팅
// 마운트된 추론 shp 파일 생성되는 위치 // 마운트된 추론 shp 파일 생성되는 위치
log.info(
"═════════════════════════════════[s] outputDir : 마운트된 추론 shp 파일 생성되는 위치 ══════════════════════════════════");
log.info("outputDir path : " + outputDir + info.getUid()); log.info("outputDir path : " + outputDir + info.getUid());
log.info(
"═════════════════════════════════[e] outputDir : 마운트된 추론 shp 파일 생성되는 위치 ════════════════════════════");
String kamconfsDatasetExportPathfsDatasetExportPath = outputDir; String kamconfsDatasetExportPathfsDatasetExportPath = outputDir;
ChnDetectMastReqDto reqDto = new ChnDetectMastReqDto(); ChnDetectMastReqDto reqDto = new ChnDetectMastReqDto();
reqDto.setCprsYr(String.valueOf(info.getCompareYyyy())); reqDto.setCprsYr(String.valueOf(info.getCompareYyyy()));
@@ -554,10 +571,12 @@ public class GukYuinApiService {
+ "&yyyymmdd=" + "&yyyymmdd="
+ yyyymmdd; + yyyymmdd;
log.info("##### API 호출 URL : {}", url);
ExternalCallResult<ChngDetectMastDto.RlbDtctDto> result = ExternalCallResult<ChngDetectMastDto.RlbDtctDto> result =
externalHttpClient.call( externalHttpClient.call(
url, HttpMethod.GET, null, netUtils.jsonHeaders(), ChngDetectMastDto.RlbDtctDto.class); url, HttpMethod.GET, null, netUtils.jsonHeaders(), ChngDetectMastDto.RlbDtctDto.class);
log.info("##### API 호출 완료 : {}", result.toString());
this.insertGukyuinAuditLog( this.insertGukyuinAuditLog(
EventType.LIST.getId(), EventType.LIST.getId(),
netUtils.getLocalIP(), netUtils.getLocalIP(),
@@ -591,4 +610,45 @@ public class GukYuinApiService {
result.body() != null && result.body().getSuccess()); result.body() != null && result.body().getSuccess());
return result.body(); return result.body();
} }
public ChngDetectContDto.ResultLabelDto updateStbltObjtYn(
String chnDtctObjtId, String stbltYn, String batchYn) {
String url = gukyuinCdiUrl + "/rlb/objt/" + chnDtctObjtId + "/stblt/" + stbltYn;
ReqInfo info = new ReqInfo();
info.setReqIp(myip);
info.setReqEpno("Y".equals(batchYn) ? "BATCH" : userUtil.getEmployeeNo());
ExternalCallResult<ChngDetectContDto.ResultLabelDto> result =
externalHttpClient.call(
url,
HttpMethod.POST,
info,
netUtils.jsonHeaders(),
ChngDetectContDto.ResultLabelDto.class);
this.insertGukyuinAuditLog(
EventType.MODIFIED.getId(),
netUtils.getLocalIP(),
userUtil.getId(),
url.replace(gukyuinUrl, ""),
null,
result.body().getSuccess());
return result.body();
}
public void stbltBulkUpdate(List<String> objectIds) {
for (String objectId : objectIds) {
this.updateStbltObjtYn(objectId, "Y", "");
}
}
public List<String> findStbltObjectIds(String uid, String mapSheetNum) {
return gukyuinCoreService.findStbltObjectIds(uid, mapSheetNum);
}
public Integer updateStbltRandomData(String uid, int updateCnt) {
return gukyuinCoreService.updateStbltRandomData(uid, updateCnt);
}
} }

View File

@@ -1,10 +1,43 @@
package com.kamco.cd.kamcoback.inference; package com.kamco.cd.kamcoback.inference;
import com.kamco.cd.kamcoback.inference.dto.InferenceResultShpDto;
import com.kamco.cd.kamcoback.inference.service.InferenceManualService;
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 lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.PostMapping;
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;
@Tag(name = "추론결과 데이터 생성", description = "추론결과 데이터 생성 API")
@RestController @RestController
@RequiredArgsConstructor @RequiredArgsConstructor
@RequestMapping("/api/inference/manual") @RequestMapping("/api/inference/manual")
public class InferenceManualApiController {} public class InferenceManualApiController {
private final InferenceManualService inferenceManualService;
@Operation(summary = "추론 결과로 추론 목록 및 shp 생성", description = "추론 결과로 추론 목록 및 shp 생성")
@ApiResponses(
value = {
@ApiResponse(
responseCode = "200",
description = "데이터 저장 성공",
content =
@Content(
mediaType = "application/json",
schema =
@Schema(implementation = InferenceResultShpDto.InferenceCntDto.class))),
@ApiResponse(responseCode = "400", description = "잘못된 검색 조건", content = @Content),
@ApiResponse(responseCode = "500", description = "서버 오류", content = @Content)
})
@PostMapping("/save")
public void saveTesting(List<Long> batchIds) {
inferenceManualService.saveResultsTesting(batchIds);
}
}

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;
@@ -27,6 +28,7 @@ import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.Valid; import jakarta.validation.Valid;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.time.LocalDate; import java.time.LocalDate;
import java.util.List; import java.util.List;
@@ -35,6 +37,7 @@ import java.util.UUID;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2; import lombok.extern.log4j.Log4j2;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
@@ -53,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;
@@ -174,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);
} }
@@ -373,6 +378,9 @@ public class InferenceResultApiController {
} }
Path zipPath = Path.of(path); Path zipPath = Path.of(path);
if (!Files.isRegularFile(zipPath)) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body("다운로드 받을 파일이 없습니다.");
}
return rangeDownloadResponder.buildZipResponse(zipPath, uid + ".zip", request); return rangeDownloadResponder.buildZipResponse(zipPath, uid + ".zip", request);
} }
@@ -440,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

@@ -1,59 +0,0 @@
package com.kamco.cd.kamcoback.inference;
import com.kamco.cd.kamcoback.config.api.ApiResponseDto;
import com.kamco.cd.kamcoback.inference.dto.InferenceResultShpDto;
import com.kamco.cd.kamcoback.inference.service.InferenceResultShpService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
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.UUID;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@Tag(name = "추론결과 데이터 생성", description = "추론결과 데이터 생성 API")
@Log4j2
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/inference/shp")
public class InferenceResultShpApiController {
private final InferenceResultShpService inferenceResultShpService;
public static final String MAP_ID =
"{ \"mapIds\": [\"37716096\",\"37716095\",\"37716094\",\"37716091\",\"37716086\",\"37716085\",\"37716084\",\"37716083\",\"37716076\",\"37716066\",\"37716065\",\"37716064\",\"37716063\",\"37716061\",\"37716051\",\"37716011\"] }";
@Operation(summary = "추론결과 데이터 저장", description = "추론결과 데이터 저장")
@ApiResponses(
value = {
@ApiResponse(
responseCode = "201",
description = "데이터 저장 성공",
content =
@Content(
mediaType = "application/json",
schema =
@Schema(implementation = InferenceResultShpDto.InferenceCntDto.class))),
@ApiResponse(responseCode = "400", description = "잘못된 검색 조건", content = @Content),
@ApiResponse(responseCode = "500", description = "서버 오류", content = @Content)
})
@PostMapping("/save/{learnId}")
public ApiResponseDto<InferenceResultShpDto.InferenceCntDto> saveInferenceData(
@PathVariable Long learnId) {
return ApiResponseDto.createOK(inferenceResultShpService.saveInferenceResultData(learnId));
}
@Operation(summary = "추론결과 shp 생성", description = "추론결과 shp 생성")
@PostMapping("/shp/{uuid}")
public ApiResponseDto<Void> createShp(
@Parameter(example = "feb2ec0b-a0f7-49ca-95e4-98b2231bdaae") @PathVariable UUID uuid) {
inferenceResultShpService.createShp(uuid);
return ApiResponseDto.createOK(null);
}
}

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;
@@ -458,6 +476,7 @@ public class InferenceDetailDto {
private String bboxGeom; private String bboxGeom;
private String bboxCenterPoint; private String bboxCenterPoint;
private UUID inferenceUuid; private UUID inferenceUuid;
private String status;
public AnalResultInfo( public AnalResultInfo(
String analTitle, String analTitle,
@@ -474,7 +493,8 @@ public class InferenceDetailDto {
String subUid, String subUid,
Boolean applyYn, Boolean applyYn,
ZonedDateTime applyDttm, ZonedDateTime applyDttm,
UUID inferenceUuid) { UUID inferenceUuid,
String status) {
this.analTitle = analTitle; this.analTitle = analTitle;
this.modelVer1 = modelVer1; this.modelVer1 = modelVer1;
this.modelVer2 = modelVer2; this.modelVer2 = modelVer2;
@@ -489,6 +509,7 @@ public class InferenceDetailDto {
this.subUid = subUid; this.subUid = subUid;
this.applyYn = applyYn; this.applyYn = applyYn;
this.applyDttm = applyDttm; this.applyDttm = applyDttm;
this.status = status;
Duration elapsed = Duration elapsed =
(inferStartDttm != null && inferEndDttm != null) (inferStartDttm != null && inferEndDttm != null)
? Duration.between(inferStartDttm, inferEndDttm) ? Duration.between(inferStartDttm, inferEndDttm)
@@ -538,6 +559,10 @@ public class InferenceDetailDto {
public Boolean getApplyYn() { public Boolean getApplyYn() {
return this.applyYn != null && this.applyYn; return this.applyYn != null && this.applyYn;
} }
public String getStatusNm() {
return InferenceResultDto.Status.getDescByCode(this.status);
}
} }
@Getter @Getter

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,8 +109,13 @@ public class InferenceResultDto {
} }
public static String getDescByCode(String code) { public static String getDescByCode(String code) {
boolean english = HeaderUtil.isEnglishRequest();
if (english) {
return fromCode(code).getTextEn();
} else {
return fromCode(code).getDesc(); return fromCode(code).getDesc();
} }
}
@Override @Override
public String getId() { public String getId() {
@@ -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,10 +239,49 @@ public class InferenceResultDto {
long m = (s % 3600) / 60; long m = (s % 3600) / 60;
long sec = s % 60; long sec = s % 60;
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); 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;
}
}
/** 목록조회 검색 조건 dto */ /** 목록조회 검색 조건 dto */
@Getter @Getter
@Setter @Setter
@@ -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

@@ -71,14 +71,16 @@ public class InferenceResultShpDto {
@NoArgsConstructor @NoArgsConstructor
public static class InferenceCntDto { public static class InferenceCntDto {
@Schema(description = "추론 결과(inference_results)를 기준으로 신규 목록 저장 터이터 건수", example = "120") @Schema(
description = "추론 결과(inference_results_testing)를 기준으로 데이터 목록 저장 터이터 건수",
example = "120")
int sheetAnalDataCnt; int sheetAnalDataCnt;
@Schema(description = "추론 결과(inference_results)를 기준으로 신규 저장 데이터 건수", example = "120") @Schema(description = "추론 결과(inference_results_testing)를 기준으로 저장 Geom 데이터 건수", example = "120")
int inferenceCnt;
@Schema(description = "추론 결과(inference_results)를 기준으로 신규 저장 Geom 데이터 건수", example = "120")
int inferenceGeomCnt; int inferenceGeomCnt;
@Schema(description = "추론 결과(inference_results_testing)를 기준으로 저장 집계 데이터 건수", example = "120")
int inferenceSttcnt;
} }
@Setter @Setter

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

@@ -1,24 +1,61 @@
package com.kamco.cd.kamcoback.inference.service; package com.kamco.cd.kamcoback.inference.service;
import com.kamco.cd.kamcoback.common.exception.CustomApiException;
import com.kamco.cd.kamcoback.inference.dto.InferenceResultsTestingDto; import com.kamco.cd.kamcoback.inference.dto.InferenceResultsTestingDto;
import com.kamco.cd.kamcoback.postgres.core.InferenceResultCoreService; import com.kamco.cd.kamcoback.postgres.core.InferenceResultCoreService;
import com.kamco.cd.kamcoback.postgres.core.ModelMngCoreService;
import java.util.List; import java.util.List;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@Service @Service
@RequiredArgsConstructor @RequiredArgsConstructor
public class InferenceManualService { public class InferenceManualService {
private final InferenceResultCoreService inferenceResultCoreService; private final InferenceResultCoreService inferenceResultCoreService;
private final ModelMngCoreService modelMngCoreService;
public void getResultsTesting(List<Long> batchIds) { public void saveResultsTesting(List<Long> batchIds) {
List<InferenceResultsTestingDto.Basic> resultList = // 배치 id로 추론 결과 testing 테이블에서 조회
inferenceResultCoreService.getInferenceResults(batchIds); List<InferenceResultsTestingDto.Basic> resultInfoList =
inferenceResultCoreService.getInferenceResultGroupList(batchIds);
if (resultList.isEmpty()) {} if (resultInfoList.isEmpty()) {
throw new CustomApiException("NOT_FOUND", HttpStatus.NOT_FOUND);
}
// Long compareYear = resultInfoList.getFirst().getBeforeYear();
// Long targetYear = resultInfoList.getFirst().getAfterYear();
// String title = compareYear + "-" + targetYear + "변화탐지";
//
// InferenceResultDto.RegReq inferenceDto = new InferenceResultDto.RegReq();
// inferenceDto.setTitle(title);
// inferenceDto.setCompareYyyy(Integer.valueOf(compareYear));
// inferenceDto.setTargetYyyy(targetYear);
// // 추론 기본정보 저장
// for (InferenceResultsTestingDto.Basic result : resultInfoList) {
//
// if (result.getModelVersion().startsWith(ModelType.G1.getId()) ||
// result.getModelVersion().startsWith("M1")) {
// ModelMngDto.Basic model =
// modelMngCoreService.findByModelVer(result.getModelVersion());
// inferenceDto.setModel1Uuid(model.getUuid());
//
// } else if (result.getModelVersion().startsWith("G2") ||
// result.getModelVersion().startsWith("M2")) {
// ModelMngDto.Basic model =
// modelMngCoreService.findByModelVer(result.getModelVersion());
// inferenceDto.setModel2Uuid(model.getUuid());
//
// } else if (result.getModelVersion().startsWith("G3") ||
// result.getModelVersion().startsWith("M3")) {
// ModelMngDto.Basic model =
// modelMngCoreService.findByModelVer(result.getModelVersion());
// inferenceDto.setModel3Uuid(model.getUuid());
// }
//
// System.out.println(result);
// }
for (InferenceResultsTestingDto.Basic result : resultList) {
System.out.println(result);
}
} }
} }

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 =
@@ -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();
@@ -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,10 +37,16 @@ public class InferenceResultShpService {
@Value("${file.dataset-dir}") @Value("${file.dataset-dir}")
private String datasetDir; private String datasetDir;
/** inference_results 테이블을 기준으로 분석 결과 테이블과 도형 테이블을 최신 상태로 반영한다. */ /**
* 추론 결과 inference 테이블 upsert
*
* @param uuid learn uuid
* @return
*/
@Transactional @Transactional
public InferenceResultShpDto.InferenceCntDto saveInferenceResultData(Long id) { public InferenceResultShpDto.InferenceCntDto saveInferenceResultData(UUID uuid) {
return coreService.buildInferenceData(id); Long learnId = inferenceResultCoreService.getInferenceLearnIdByUuid(uuid);
return coreService.buildInferenceData(learnId);
} }
/** /**
@@ -55,13 +61,13 @@ public class InferenceResultShpService {
return; return;
} }
String batchId = String batchIds =
Stream.of(dto.getM1ModelBatchId(), dto.getM2ModelBatchId(), dto.getM3ModelBatchId()) Stream.of(dto.getM1ModelBatchId(), dto.getM2ModelBatchId(), dto.getM3ModelBatchId())
.filter(Objects::nonNull) .filter(Objects::nonNull)
.map(String::valueOf) .map(String::valueOf)
.collect(Collectors.joining(",")); .collect(Collectors.joining(","));
// shp 파일 비동기 생성 // shp 파일 비동기 생성
shpPipelineService.runPipeline(jarPath, datasetDir, batchId, dto.getUid()); shpPipelineService.runPipeline(jarPath, datasetDir, batchIds, dto.getUid());
} }
} }

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,9 +111,15 @@ public class LabelWorkDto {
if (type == null) { if (type == null) {
return enumId; return enumId;
} }
boolean english = HeaderUtil.isEnglishRequest();
if (!english) {
return type.getText(); return type.getText();
} }
return type.getTextEn();
}
/** /**
* 작업 진행률 반환 (tb_labeling_assignment.stagnation_yn = 'N'인 정상 진행율 기준) 계산식: (정상 진행 건수 / 총 배정 건수) * * 작업 진행률 반환 (tb_labeling_assignment.stagnation_yn = 'N'인 정상 진행율 기준) 계산식: (정상 진행 건수 / 총 배정 건수) *
* 100 * 100
@@ -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

@@ -219,6 +219,9 @@ public class WorkerStatsDto {
@Deprecated @Deprecated
@Schema(description = "[Deprecated] inspectionRemainingCount 사용 권장") @Schema(description = "[Deprecated] inspectionRemainingCount 사용 권장")
private Long remainingInspectCount; private Long remainingInspectCount;
@Schema(description = "파일 다운로드 가능한 폴리곤 수")
private Long downloadPolygonCnt;
} }
@Getter @Getter

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

@@ -62,7 +62,7 @@ public class LayerService {
.orElseThrow(() -> new CustomApiException("BAD_REQUEST", HttpStatus.BAD_REQUEST)); .orElseThrow(() -> new CustomApiException("BAD_REQUEST", HttpStatus.BAD_REQUEST));
switch (layerType) { switch (layerType) {
case TILE -> { case TILE, KAMCO_WMS, KAMCO_WMTS -> {
return mapLayerCoreService.saveTile(dto); return mapLayerCoreService.saveTile(dto);
} }

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;
@@ -59,10 +59,20 @@ public class MapSheetMngService {
@Value("${file.sync-file-extention}") @Value("${file.sync-file-extention}")
private String syncFileExtention; private String syncFileExtention;
/**
* 영상데이터관리 > 목록 조회
*
* @return
*/
public List<MngDto> findMapSheetMngList() { public List<MngDto> findMapSheetMngList() {
return mapSheetMngCoreService.findMapSheetMngList(); return mapSheetMngCoreService.findMapSheetMngList();
} }
/**
* 영상데이터관리 > 데이터 등록 > 연도 선택 목록
*
* @return
*/
public List<Integer> findMapSheetMngYyyyList() { public List<Integer> findMapSheetMngYyyyList() {
return mapSheetMngCoreService.findMapSheetMngYyyyList(); return mapSheetMngCoreService.findMapSheetMngYyyyList();
} }
@@ -77,6 +87,12 @@ public class MapSheetMngService {
.toList(); .toList();
} }
/**
* 영상데이터관리 > 상세 조회
*
* @param mngYyyy
* @return
*/
public MngDto findMapSheetMng(int mngYyyy) { public MngDto findMapSheetMng(int mngYyyy) {
return mapSheetMngCoreService.findMapSheetMng(mngYyyy); return mapSheetMngCoreService.findMapSheetMng(mngYyyy);
} }
@@ -89,6 +105,12 @@ public class MapSheetMngService {
return new DmlReturn("success", "작업완료 처리되었습니다."); return new DmlReturn("success", "작업완료 처리되었습니다.");
} }
/**
* 영상데이터관리 > 상세 > 오류 처리 내역
*
* @param searchReq
* @return
*/
public Page<ErrorDataDto> findMapSheetErrorList(@Valid ErrorSearchReq searchReq) { public Page<ErrorDataDto> findMapSheetErrorList(@Valid ErrorSearchReq searchReq) {
return mapSheetMngCoreService.findMapSheetErrorList(searchReq); return mapSheetMngCoreService.findMapSheetErrorList(searchReq);
} }
@@ -97,12 +119,20 @@ public class MapSheetMngService {
return mapSheetMngCoreService.findMapSheetError(hstUid); return mapSheetMngCoreService.findMapSheetError(hstUid);
} }
/**
* 영상데이터관리 > 데이터 등록
*
* @param addReq
* @return
*/
@Transactional @Transactional
public DmlReturn mngDataSave(AddReq addReq) { public DmlReturn mngDataSave(AddReq addReq) {
addReq.setCreatedUid(userUtil.getId()); addReq.setCreatedUid(userUtil.getId());
int execCnt = mapSheetMngCoreService.mngDataSave(addReq); int execCnt =
mapSheetMngCoreService.mngDataSave(addReq); // tb_map_sheet_mng_hst 테이블에 insert된 개수 리턴됨
return new MapSheetMngDto.DmlReturn("success", addReq.getMngYyyy() + "년, " + execCnt + "건 생성"); return new MapSheetMngDto.DmlReturn("success", addReq.getMngYyyy() + "년, " + execCnt + "건 생성");
} }
@@ -110,6 +140,15 @@ public class MapSheetMngService {
return mapSheetMngCoreService.updateExceptUseInference(hstUidList); return mapSheetMngCoreService.updateExceptUseInference(hstUidList);
} }
/**
* 영상데이터관리 > 상세 > 오류 처리 내역 > 업로드 (페어 파일 저장)
*
* @param tfwFile
* @param tifFile
* @param hstUid
* @param tifFileSize
* @return
*/
@Transactional @Transactional
public DmlReturn uploadPair( public DmlReturn uploadPair(
MultipartFile tfwFile, String tifFile, Long hstUid, Long tifFileSize) { MultipartFile tfwFile, String tifFile, Long hstUid, Long tifFileSize) {
@@ -118,6 +157,7 @@ public class MapSheetMngService {
DmlReturn dmlReturn = new DmlReturn("success", "UPLOAD COMPLETE"); DmlReturn dmlReturn = new DmlReturn("success", "UPLOAD COMPLETE");
// hst 테이블에 선택한 hstUid row 정보 조회
ErrorDataDto errDto = mapSheetMngCoreService.findMapSheetError(hstUid); ErrorDataDto errDto = mapSheetMngCoreService.findMapSheetError(hstUid);
if (errDto == null) { if (errDto == null) {
@@ -135,33 +175,14 @@ public class MapSheetMngService {
return dmlReturn; return dmlReturn;
} }
// TODO 삭제? // ★ tif 파일은 대용량 업로드에서 아래 정보들을 체크하기 때문에 체크 로직에서 제외됨
MngDto mngDto = mapSheetMngCoreService.findMapSheetMng(errDto.getMngYyyy());
// 중복체크 -> 도엽50k/uuid 경로에 업로드 할 거라 overwrite 되지 않음
// if (!overwrite) {
// dmlReturn =
// this.duplicateFile(
// errDto.getMngYyyy(), tfwFile.getOriginalFilename(), tifFile.getOriginalFilename());
// if (dmlReturn.getFlag().equals("duplicate")) {
// return dmlReturn;
// }
// }
// 멀티파트 파일 tmp폴더 저장(파일형식 체크를 위해) // 멀티파트 파일 tmp폴더 저장(파일형식 체크를 위해)
String tfwTmpPath = tmpPath + tfwFile.getOriginalFilename(); String tfwTmpPath = tmpPath + tfwFile.getOriginalFilename();
// String tifTmpPath = tmpPath + tifFile;
if (!FIleChecker.multipartSaveTo(tfwFile, tfwTmpPath)) { if (!FIleChecker.multipartSaveTo(tfwFile, tfwTmpPath)) {
return new DmlReturn("fail", "UPLOAD ERROR"); return new DmlReturn("fail", "UPLOAD ERROR");
} }
// if (!FIleChecker.multipartSaveTo(tifFile, tifTmpPath)) {
// return new DmlReturn("fail", "UPLOAD ERROR");
// }
// if (!FIleChecker.cmmndGdalInfo(tifTmpPath)) {
// return new DmlReturn("fail", "TIF TYPE ERROR");
// }
if (!FIleChecker.checkTfw(tfwTmpPath)) { if (!FIleChecker.checkTfw(tfwTmpPath)) {
return new DmlReturn("fail", "TFW TYPE ERROR"); return new DmlReturn("fail", "TFW TYPE ERROR");
} }
@@ -175,7 +196,6 @@ public class MapSheetMngService {
} }
Path tfwTmpSavePath = Paths.get(tfwTmpPath); Path tfwTmpSavePath = Paths.get(tfwTmpPath);
// Path tifTmpSavePath = Paths.get(tifTmpPath);
Path tfwTargetPath = null; Path tfwTargetPath = null;
Path tifTargetPath = null; Path tifTargetPath = null;
@@ -222,7 +242,7 @@ public class MapSheetMngService {
try { try {
Files.move(tfwTmpSavePath, tfwTargetPath, StandardCopyOption.REPLACE_EXISTING); Files.move(tfwTmpSavePath, tfwTargetPath, StandardCopyOption.REPLACE_EXISTING);
// Files.move(tifTmpSavePath, tifTargetPath, StandardCopyOption.REPLACE_EXISTING); // tif파일은 대용량 업로드에서 이미 업로드 했기 때문에 완료된 상태
} catch (IOException e) { } catch (IOException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
@@ -234,7 +254,7 @@ public class MapSheetMngService {
updReqSyncCheckState.setFilePath(uploadPath); updReqSyncCheckState.setFilePath(uploadPath);
updReqSyncCheckState.setSyncCheckTfwFileName(tfwFile.getOriginalFilename()); updReqSyncCheckState.setSyncCheckTfwFileName(tfwFile.getOriginalFilename());
updReqSyncCheckState.setSyncCheckTifFileName(tifFile); updReqSyncCheckState.setSyncCheckTifFileName(tifFile);
updReqSyncCheckState.setSyncCheckState("DONE"); updReqSyncCheckState.setSyncCheckState("DONE"); // 오류처리 완료
mapSheetMngCoreService.updateMapSheetMngHstSyncCheckState(updReqSyncCheckState); mapSheetMngCoreService.updateMapSheetMngHstSyncCheckState(updReqSyncCheckState);
// 파일정보 업데이트 // 파일정보 업데이트
mapSheetMngCoreService.deleteByHstUidMngFile(hstUid); mapSheetMngCoreService.deleteByHstUidMngFile(hstUid);
@@ -469,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 = "모델관리 엔티티 기본 정보")
@@ -67,6 +72,7 @@ public class ModelMngDto {
private String clsModelVersion; private String clsModelVersion;
private Double priority; private Double priority;
private String memo; private String memo;
private UUID uuid;
public Basic( public Basic(
Long modelUid, Long modelUid,
@@ -89,7 +95,8 @@ public class ModelMngDto {
String clsModelFileName, String clsModelFileName,
String clsModelVersion, String clsModelVersion,
double priority, double priority,
String memo) { String memo,
UUID uuid) {
this.modelUid = modelUid; this.modelUid = modelUid;
this.modelVer = modelVer; this.modelVer = modelVer;
this.createCompleteDttm = createCompleteDttm; this.createCompleteDttm = createCompleteDttm;
@@ -111,6 +118,7 @@ public class ModelMngDto {
this.clsModelVersion = clsModelVersion; this.clsModelVersion = clsModelVersion;
this.priority = priority; this.priority = priority;
this.memo = memo; this.memo = memo;
this.uuid = uuid;
} }
} }

View File

@@ -6,6 +6,8 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import com.kamco.cd.kamcoback.changedetection.dto.ChangeDetectionDto; import com.kamco.cd.kamcoback.changedetection.dto.ChangeDetectionDto;
import com.kamco.cd.kamcoback.changedetection.dto.ChangeDetectionDto.MapScaleType; import com.kamco.cd.kamcoback.changedetection.dto.ChangeDetectionDto.MapScaleType;
import com.kamco.cd.kamcoback.changedetection.dto.ChangeDetectionDto.MapSheetList; import com.kamco.cd.kamcoback.changedetection.dto.ChangeDetectionDto.MapSheetList;
import com.kamco.cd.kamcoback.changedetection.dto.ChangeDetectionDto.PointFeatureList;
import com.kamco.cd.kamcoback.changedetection.dto.ChangeDetectionDto.PolygonFeatureList;
import com.kamco.cd.kamcoback.common.enums.DetectionClassification; import com.kamco.cd.kamcoback.common.enums.DetectionClassification;
import com.kamco.cd.kamcoback.common.exception.CustomApiException; import com.kamco.cd.kamcoback.common.exception.CustomApiException;
import com.kamco.cd.kamcoback.postgres.entity.MapSheetAnalDataInferenceGeomEntity; import com.kamco.cd.kamcoback.postgres.entity.MapSheetAnalDataInferenceGeomEntity;
@@ -108,8 +110,8 @@ public class ChangeDetectionCoreService {
* @return * @return
*/ */
public ChangeDetectionDto.PolygonFeatureList getPolygonListByCd( public ChangeDetectionDto.PolygonFeatureList getPolygonListByCd(
String chnDtctId, String cdObjectId, List<String> cdObjectIds) { String chnDtctId, String cdObjectId, List<String> cdObjectIds, String pnu) {
return changeDetectionRepository.getPolygonListByCd(chnDtctId, cdObjectId, cdObjectIds); return changeDetectionRepository.getPolygonListByCd(chnDtctId, cdObjectId, cdObjectIds, pnu);
} }
/** /**
@@ -121,8 +123,8 @@ public class ChangeDetectionCoreService {
* @return * @return
*/ */
public ChangeDetectionDto.PointFeatureList getPointListByCd( public ChangeDetectionDto.PointFeatureList getPointListByCd(
String chnDtctId, String cdObjectId, List<String> cdObjectIds) { String chnDtctId, String cdObjectId, List<String> cdObjectIds, String pnu) {
return changeDetectionRepository.getPointListByCd(chnDtctId, cdObjectId, cdObjectIds); return changeDetectionRepository.getPointListByCd(chnDtctId, cdObjectId, cdObjectIds, pnu);
} }
/** /**
@@ -136,4 +138,12 @@ public class ChangeDetectionCoreService {
.getLearnUuid(chnDtctId) .getLearnUuid(chnDtctId)
.orElseThrow(() -> new CustomApiException("NOT_FOUND_DATA", HttpStatus.NOT_FOUND)); .orElseThrow(() -> new CustomApiException("NOT_FOUND_DATA", HttpStatus.NOT_FOUND));
} }
public PolygonFeatureList getChangeDetectionPnuPolygonList(UUID uuid, String pnu) {
return changeDetectionRepository.getChangeDetectionPnuPolygonList(uuid, pnu);
}
public PointFeatureList getChangeDetectionPnuPointList(UUID uuid, String pnu) {
return changeDetectionRepository.getChangeDetectionPnuPointList(uuid, pnu);
}
} }

View File

@@ -77,4 +77,12 @@ public class GukYuinCoreService {
public void updateMapSheetInferenceLabelEndStatus(Long learnId) { public void updateMapSheetInferenceLabelEndStatus(Long learnId) {
gukYuinRepository.updateMapSheetInferenceLabelEndStatus(learnId); gukYuinRepository.updateMapSheetInferenceLabelEndStatus(learnId);
} }
public List<String> findStbltObjectIds(String uid, String mapSheetNum) {
return gukYuinRepository.findStbltObjectIds(uid, mapSheetNum);
}
public Integer updateStbltRandomData(String uid, int updateCnt) {
return gukYuinRepository.updateStbltRandomData(uid, updateCnt);
}
} }

View File

@@ -0,0 +1,26 @@
package com.kamco.cd.kamcoback.postgres.core;
import com.kamco.cd.kamcoback.postgres.repository.gukyuin.GukYuinPnuCntUpdateJobRepository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class GukYuinPnuCntUpdateJobCoreService {
private final GukYuinPnuCntUpdateJobRepository gukYuinPnuCntUpdateRepository;
public GukYuinPnuCntUpdateJobCoreService(
GukYuinPnuCntUpdateJobRepository gukYuinPnuCntUpdateRepository) {
this.gukYuinPnuCntUpdateRepository = gukYuinPnuCntUpdateRepository;
}
@Transactional
public void updateGukYuinContListPnuUpdateCnt() {
gukYuinPnuCntUpdateRepository.updateGukYuinContListPnuUpdateCnt();
}
@Transactional
public void updateGukYuinApplyStatus(String uid, String status) {
gukYuinPnuCntUpdateRepository.updateGukYuinApplyStatus(uid, status);
}
}

View File

@@ -36,6 +36,7 @@ import com.kamco.cd.kamcoback.scheduler.dto.JobStatusDto;
import jakarta.persistence.EntityManager; import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityNotFoundException; import jakarta.persistence.EntityNotFoundException;
import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.NotNull;
import java.time.LocalDateTime;
import java.time.ZonedDateTime; import java.time.ZonedDateTime;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
@@ -46,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;
@@ -54,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 {
@@ -119,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);
@@ -271,7 +272,7 @@ public class InferenceResultCoreService {
.getInferenceResultByUuid(request.getUuid()) .getInferenceResultByUuid(request.getUuid())
.orElseThrow(EntityNotFoundException::new); .orElseThrow(EntityNotFoundException::new);
// M1/M2/M3 영역 업데이트 // G1/G2/G3 영역 업데이트
if (request.getType() != null) { if (request.getType() != null) {
applyModelUpdate(entity, request); applyModelUpdate(entity, request);
} }
@@ -453,12 +454,18 @@ public class InferenceResultCoreService {
* @return * @return
*/ */
public AnalResultInfo getInferenceResultInfo(UUID uuid) { public AnalResultInfo getInferenceResultInfo(UUID uuid) {
// 추론 결과 정보조회 // 추론 결과 정보조회
log.info("get inference result info start time = {}", LocalDateTime.now());
AnalResultInfo resultInfo = mapSheetLearnRepository.getInferenceResultInfo(uuid); AnalResultInfo resultInfo = mapSheetLearnRepository.getInferenceResultInfo(uuid);
log.info("get inference result info end time = {}", LocalDateTime.now());
// bbox, point 조회 // bbox, point 조회
log.info("get inference result info bbox start time = {}", LocalDateTime.now());
BboxPointDto bboxPointDto = mapSheetLearnRepository.getBboxPoint(uuid); BboxPointDto bboxPointDto = mapSheetLearnRepository.getBboxPoint(uuid);
log.info("get inference result info bbox end time = {}", LocalDateTime.now());
resultInfo.setBboxGeom(bboxPointDto.getBboxGeom()); resultInfo.setBboxGeom(bboxPointDto.getBboxGeom());
resultInfo.setBboxCenterPoint(bboxPointDto.getBboxCenterPoint()); resultInfo.setBboxCenterPoint(bboxPointDto.getBboxCenterPoint());
return resultInfo; return resultInfo;
} }
@@ -469,7 +476,10 @@ public class InferenceResultCoreService {
* @return 분류별 탐지건수 정보 * @return 분류별 탐지건수 정보
*/ */
public List<Dashboard> getInferenceClassCountList(UUID uuid) { public List<Dashboard> getInferenceClassCountList(UUID uuid) {
return mapSheetLearnRepository.getInferenceClassCountList(uuid); log.info("get inference class count list start time = {}", LocalDateTime.now());
List<Dashboard> classCountList = mapSheetLearnRepository.getInferenceClassCountList(uuid);
log.info("get inference class count list end time = {}", LocalDateTime.now());
return classCountList;
} }
/** /**
@@ -478,7 +488,11 @@ public class InferenceResultCoreService {
* @return geom 목록 정보 * @return geom 목록 정보
*/ */
public Page<Geom> getInferenceGeomList(UUID uuid, SearchGeoReq searchGeoReq) { public Page<Geom> getInferenceGeomList(UUID uuid, SearchGeoReq searchGeoReq) {
return mapSheetLearnRepository.getInferenceGeomList(uuid, searchGeoReq); log.info("get Inference Geom List start time = {}", LocalDateTime.now());
Page<Geom> geom = mapSheetLearnRepository.getInferenceGeomList(uuid, searchGeoReq);
log.info("get Inference Geom List end time = {}", LocalDateTime.now());
return geom;
} }
/** /**
@@ -488,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);
} }
@@ -575,11 +593,41 @@ public class InferenceResultCoreService {
return mapSheetLearn5kRepository.getInferenceRunMapId(uuid); return mapSheetLearn5kRepository.getInferenceRunMapId(uuid);
} }
/**
* 실패 처리되어야 할 목록 중에 아직 실패로 표시되지 않은 ID 조회
*
* @param uuid 추론 uuid
* @param failMapIds AI API 연결하여 조회한 실패 job id
* @param type 모델 타입
* @return job id
*/
public List<Long> findFail5kList(UUID uuid, List<Long> failMapIds, String type) { public List<Long> findFail5kList(UUID uuid, List<Long> failMapIds, String type) {
return mapSheetLearn5kRepository.findFail5kList(uuid, failMapIds, type); return mapSheetLearn5kRepository.findFail5kList(uuid, failMapIds, type);
} }
/**
* 완료된 것으로 들어온 목록 중 실제로 존재하는 5k jobId 조회
*
* @param uuid 추론 uuid
* @param completedIds AI API 연결하여 조회한 성공 job id
* @param type 모델 타입
* @return job id
*/
public List<Long> findCompleted5kList(UUID uuid, List<Long> completedIds, String type) { public List<Long> findCompleted5kList(UUID uuid, List<Long> completedIds, String type) {
return mapSheetLearn5kRepository.findCompleted5kList(uuid, completedIds, type); return mapSheetLearn5kRepository.findCompleted5kList(uuid, completedIds, type);
} }
/**
* testing 테이블 결과로 기본정보 조회
*
* @param batchIds batch id
* @return batch id, model ver, year 정보
*/
public List<InferenceResultsTestingDto.Basic> getInferenceResultGroupList(List<Long> batchIds) {
return inferenceResultsTestingRepository.getInferenceResultGroupList(batchIds);
}
public long deleteInference(UUID uuid) {
return inferenceResultRepository.deleteInference(uuid);
}
} }

View File

@@ -1,12 +1,17 @@
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 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;
@Service @Service
@Log4j2
@RequiredArgsConstructor @RequiredArgsConstructor
public class InferenceResultShpCoreService { public class InferenceResultShpCoreService {
@@ -15,15 +20,33 @@ public class InferenceResultShpCoreService {
/** /**
* inference_results 기준으로 - tb_map_sheet_anal_data_inference - * inference_results 기준으로 - tb_map_sheet_anal_data_inference -
* tb_map_sheet_anal_data_inference_geom 테이블을 최신 상태로 구성한다. * tb_map_sheet_anal_data_inference_geom 테이블을 최신 상태로 구성한다.
*
* @param id learn id
* @return
*/ */
@Transactional @Transactional
public InferenceResultShpDto.InferenceCntDto buildInferenceData(Long id) { public InferenceResultShpDto.InferenceCntDto buildInferenceData(Long id) {
Long analId = repo.upsertGroupsFromMapSheetAnal(id);
repo.upsertGroupsFromInferenceResults(analId);
repo.upsertGeomsFromInferenceResults(analId);
repo.upsertSttcFromInferenceResults(analId);
InferenceResultShpDto.InferenceCntDto cntDto = new InferenceResultShpDto.InferenceCntDto();
MapSheetAnalInferenceEntity analInferenceEntity =
repo.getAnalInferenceDataByLearnId(id).orElse(null);
if (analInferenceEntity != null) {
throw new CustomApiException("CONFLICT", HttpStatus.CONFLICT);
}
Long analId = repo.upsertGroupsFromMapSheetAnal(id);
int analDataCnt = repo.upsertGroupsFromInferenceResults(analId);
int geomCnt = repo.upsertGeomsFromInferenceResults(analId);
int sttcCnt = repo.upsertSttcFromInferenceResults(analId);
InferenceResultShpDto.InferenceCntDto cntDto = new InferenceResultShpDto.InferenceCntDto();
cntDto.setSheetAnalDataCnt(analDataCnt);
cntDto.setInferenceGeomCnt(geomCnt);
cntDto.setInferenceSttcnt(sttcCnt);
log.info(
"[ANAL SAVE] analId={}, tb_map_sheet_anal_data_inference={}, tb_map_sheet_anal_data_inference_geom={}, tb_map_sheet_anal_sttc={}",
analId,
analDataCnt,
geomCnt,
sttcCnt);
return cntDto; return cntDto;
} }
} }

View File

@@ -48,10 +48,20 @@ public class MapSheetMngCoreService {
@Value("${file.sync-root-dir}") @Value("${file.sync-root-dir}")
private String syncRootDir; private String syncRootDir;
/**
* 영상데이터관리 > 목록 조회
*
* @return
*/
public List<MapSheetMngDto.MngDto> findMapSheetMngList() { public List<MapSheetMngDto.MngDto> findMapSheetMngList() {
return mapSheetMngRepository.findMapSheetMngList(); return mapSheetMngRepository.findMapSheetMngList();
} }
/**
* 영상데이터관리 > 데이터 등록 > 연도 선택 목록
*
* @return
*/
public List<Integer> findMapSheetMngYyyyList() { public List<Integer> findMapSheetMngYyyyList() {
return mapSheetMngRepository.findMapSheetMngYyyyList(); return mapSheetMngRepository.findMapSheetMngYyyyList();
} }
@@ -68,6 +78,12 @@ public class MapSheetMngCoreService {
return mapSheetMngRepository.getYears(req); return mapSheetMngRepository.getYears(req);
} }
/**
* 영상데이터관리 > 상세 조회
*
* @param mngYyyy
* @return
*/
public MapSheetMngDto.MngDto findMapSheetMng(int mngYyyy) { public MapSheetMngDto.MngDto findMapSheetMng(int mngYyyy) {
return mapSheetMngRepository.findMapSheetMng(mngYyyy); return mapSheetMngRepository.findMapSheetMng(mngYyyy);
} }
@@ -95,15 +111,33 @@ public class MapSheetMngCoreService {
mapSheetMngRepository.updateMapSheetMngHstSyncCheckState(reqDto); mapSheetMngRepository.updateMapSheetMngHstSyncCheckState(reqDto);
} }
/**
* 영상데이터관리 > 상세 > 오류 처리 내역
*
* @param searchReq 오류 검색 조건
* @return
*/
public Page<MapSheetMngDto.ErrorDataDto> findMapSheetErrorList( public Page<MapSheetMngDto.ErrorDataDto> findMapSheetErrorList(
MapSheetMngDto.@Valid ErrorSearchReq searchReq) { MapSheetMngDto.@Valid ErrorSearchReq searchReq) {
return mapSheetMngRepository.findMapSheetErrorList(searchReq); return mapSheetMngRepository.findMapSheetErrorList(searchReq);
} }
/**
* hst 테이블에 선택한 hstUid row 정보 조회
*
* @param hstUid
* @return
*/
public MapSheetMngDto.ErrorDataDto findMapSheetError(Long hstUid) { public MapSheetMngDto.ErrorDataDto findMapSheetError(Long hstUid) {
return mapSheetMngRepository.findMapSheetError(hstUid); return mapSheetMngRepository.findMapSheetError(hstUid);
} }
/**
* 파일 목록으로 업로드 경로 확인
*
* @param hstUid
* @return
*/
public List<MapSheetMngDto.MngFilesDto> findByHstUidMapSheetFileList(Long hstUid) { public List<MapSheetMngDto.MngFilesDto> findByHstUidMapSheetFileList(Long hstUid) {
return mapSheetMngRepository.findByHstUidMapSheetFileList(hstUid); return mapSheetMngRepository.findByHstUidMapSheetFileList(hstUid);
} }
@@ -146,12 +180,19 @@ public class MapSheetMngCoreService {
entity.setCreatedUid(addReq.getCreatedUid()); entity.setCreatedUid(addReq.getCreatedUid());
entity.setUpdatedUid(addReq.getCreatedUid()); entity.setUpdatedUid(addReq.getCreatedUid());
// 같은 년도로 저장된 데이터가 있다면 삭제
// tb_map_sheet_mng, tb_map_sheet_hst, tb_map_sheet_mng_files, tb_map_sheet_mng_tile
mapSheetMngRepository.deleteByMngYyyyMngAll(addReq.getMngYyyy()); mapSheetMngRepository.deleteByMngYyyyMngAll(addReq.getMngYyyy());
// tb_map_sheet_mng 엔티티 저장
MapSheetMngEntity saved = mapSheetMngRepository.save(entity); MapSheetMngEntity saved = mapSheetMngRepository.save(entity);
// 5k 도엽 기준으로 tb_map_sheet_hst 테이블에 먼저 insert 하기
int hstCnt = int hstCnt =
mapSheetMngRepository.insertMapSheetOrgDataToMapSheetMngHst( mapSheetMngRepository.insertMapSheetOrgDataToMapSheetMngHst(
saved.getMngYyyy(), saved.getMngPath()); saved.getMngYyyy(), saved.getMngPath());
// tb_year 에 해당 년도 완료로 업데이트
mapSheetMngRepository.updateYearState(saved.getMngYyyy(), "DONE"); mapSheetMngRepository.updateYearState(saved.getMngYyyy(), "DONE");
// 년도별 Tile 정보 등록 // 년도별 Tile 정보 등록
@@ -304,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);
} }
/** /**
@@ -339,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

@@ -109,4 +109,18 @@ public class ModelMngCoreService {
.orElseThrow(() -> new EntityNotFoundException("모델 정보가 없습니다.")); .orElseThrow(() -> new EntityNotFoundException("모델 정보가 없습니다."));
return entity.toDto(); return entity.toDto();
} }
/**
* 모델 버전명으로 조회
*
* @param ver 모델버전
* @return 모델정보
*/
public ModelMngDto.Basic findByModelVer(String ver) {
ModelMngEntity entity =
modelMngRepository
.findByModelVer(ver)
.orElseThrow(() -> new EntityNotFoundException("모델 정보가 없습니다."));
return entity.toDto();
}
} }

View File

@@ -11,37 +11,94 @@ import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import org.hibernate.annotations.ColumnDefault; import org.hibernate.annotations.ColumnDefault;
/**
* GPU 메트릭 엔티티
*
* <p>서버의 GPU 성능 및 자원 사용량 메트릭 데이터를 저장하는 JPA 엔티티입니다. GPU 연산 사용률 및 메모리 사용량 등 GPU 리소스 모니터링 데이터를 관리합니다.
*
* <p>데이터 소스: nvidia-smi 명령어 또는 NVML (NVIDIA Management Library)
*
* <p>활용 사례:
*
* <ul>
* <li>AI/ML 학습 모니터링: 딥러닝 작업 중 GPU 활용도 추적
* <li>리소스 최적화: GPU 메모리 부족 또는 유휴 상태 감지
* <li>용량 계획: GPU 추가 필요 시점 예측
* <li>알림 설정: gpuUtil > 95% 또는 gpuMemUsed/gpuMemTotal > 90% 시 경고
* </ul>
*/
@Getter @Getter
@Setter @Setter
@Entity @Entity
@Table(name = "gpu_metrics") @Table(name = "gpu_metrics")
public class GpuMetricEntity { public class GpuMetricEntity {
/** 기본 키 (UUID, 자동 생성) */
@Id @Id
@ColumnDefault("gen_random_uuid()") @ColumnDefault("gen_random_uuid()")
@Column(name = "uuid", nullable = false) @Column(name = "uuid", nullable = false)
private UUID id; private UUID id;
/** 시퀀스 기반 보조 ID */
@NotNull @NotNull
@ColumnDefault("nextval('gpu_metrics_id_seq')") @ColumnDefault("nextval('gpu_metrics_id_seq')")
@Column(name = "id", nullable = false) @Column(name = "id", nullable = false)
private Integer id1; private Integer id1;
/** 메트릭 수집 시각 (시간대 포함, 기본값: 현재 시각) */
@NotNull @NotNull
@ColumnDefault("now()") @ColumnDefault("now()")
@Column(name = "\"timestamp\"", nullable = false) @Column(name = "\"timestamp\"", nullable = false)
private OffsetDateTime timestamp; private OffsetDateTime timestamp;
/** 모니터링 대상 서버 이름 */
@NotNull @NotNull
@Column(name = "server_name", nullable = false, length = Integer.MAX_VALUE) @Column(name = "server_name", nullable = false, length = Integer.MAX_VALUE)
private String serverName; private String serverName;
/**
* GPU 연산 사용률 (백분율)
*
* <p>GPU 코어의 연산 처리 활용도를 나타냅니다.
*
* <p>범위: 0.0 ~ 100.0
*
* <p>예시: 85.5 = GPU가 85.5% 활용되어 연산 중
*
* <p>데이터 소스: nvidia-smi의 'utilization.gpu' 또는 NVML의 nvmlDeviceGetUtilizationRates
*
* <p>참고: 높은 사용률(>90%)은 GPU가 충분히 활용되고 있음을 의미하며, 낮은 사용률은 병목 지점이 다른 곳(CPU, I/O)에 있을 수 있음
*/
@Column(name = "gpu_util") @Column(name = "gpu_util")
private Float gpuUtil; private Float gpuUtil;
/**
* GPU 메모리 사용량 (MB 단위)
*
* <p>현재 GPU에 할당되어 사용 중인 메모리 양
*
* <p>예시: 10240.0 = 약 10GB의 GPU 메모리 사용 중
*
* <p>데이터 소스: nvidia-smi의 'memory.used' 또는 NVML의 nvmlDeviceGetMemoryInfo
*
* <p>용도: 딥러닝 모델 크기, 배치 사이즈 최적화, OOM(Out Of Memory) 에러 예측
*/
@Column(name = "gpu_mem_used") @Column(name = "gpu_mem_used")
private Float gpuMemUsed; private Float gpuMemUsed;
/**
* GPU 총 메모리 용량 (MB 단위)
*
* <p>GPU에 장착된 전체 메모리 용량
*
* <p>예시: 16384.0 = 16GB VRAM 장착
*
* <p>데이터 소스: nvidia-smi의 'memory.total' 또는 NVML의 nvmlDeviceGetMemoryInfo
*
* <p>계산식: 메모리 사용률(%) = (gpuMemUsed / gpuMemTotal) × 100
*
* <p>활용: 여유 메모리 = gpuMemTotal - gpuMemUsed
*/
@Column(name = "gpu_mem_total") @Column(name = "gpu_mem_total")
private Float gpuMemTotal; private Float gpuMemTotal;
} }

View File

@@ -202,6 +202,36 @@ public class MapSheetLearnEntity {
@Column(name = "chn_dtct_mst_id") @Column(name = "chn_dtct_mst_id")
private String chnDtctMstId; private String chnDtctMstId;
@Column(name = "shp_create_status")
private String shp_create_status;
@Column(name = "shp_create_message")
private String shp_create_message;
@Column(name = "shp_create_status_dttm")
private ZonedDateTime shp_create_status_dttm;
@Column(name = "shp_status")
private String shp_status;
@Column(name = "shp_stage")
private String shp_stage;
@Column(name = "shp_started_dttm")
private ZonedDateTime shp_started_dttm;
@Column(name = "shp_ended_dttm")
private ZonedDateTime shp_ended_dttm;
@Column(name = "shp_last_message")
private String shp_last_message;
@Column(name = "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

@@ -117,6 +117,7 @@ public class ModelMngEntity extends CommonDateEntity {
this.clsModelFileName, this.clsModelFileName,
this.clsModelVersion, this.clsModelVersion,
this.priority, this.priority,
this.memo); this.memo,
this.uuid);
} }
} }

View File

@@ -11,48 +11,101 @@ import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import org.hibernate.annotations.ColumnDefault; import org.hibernate.annotations.ColumnDefault;
/**
* 시스템 메트릭 엔티티
*
* <p>서버 시스템의 성능 메트릭 데이터를 저장하는 JPA 엔티티입니다. CPU 및 메모리 사용량 등 시스템 리소스 모니터링 데이터를 관리합니다.
*
* <p>데이터 소스: Linux sar 명령어 또는 /proc/meminfo 파일
*
* <p>활용 사례:
*
* <ul>
* <li>용량 계획: 메모리 추가 필요 시점 예측
* <li>성능 모니터링: 메모리 부족 상황 감지
* <li>트렌드 분석: 시간대별 메모리 사용 패턴 파악
* <li>알림 설정: memused > 90% 시 경고
* </ul>
*/
@Getter @Getter
@Setter @Setter
@Entity @Entity
@Table(name = "system_metrics") @Table(name = "system_metrics")
public class SystemMetricEntity { public class SystemMetricEntity {
/** 기본 키 (UUID, 자동 생성) */
@Id @Id
@ColumnDefault("gen_random_uuid()") @ColumnDefault("gen_random_uuid()")
@Column(name = "uuid", nullable = false) @Column(name = "uuid", nullable = false)
private UUID id; private UUID id;
/** 시퀀스 기반 보조 ID */
@NotNull @NotNull
@ColumnDefault("nextval('system_metrics_id_seq')") @ColumnDefault("nextval('system_metrics_id_seq')")
@Column(name = "id", nullable = false) @Column(name = "id", nullable = false)
private Integer id1; private Integer id1;
/** 메트릭 수집 시각 (시간대 포함) */
@NotNull @NotNull
@Column(name = "\"timestamp\"", nullable = false) @Column(name = "\"timestamp\"", nullable = false)
private OffsetDateTime timestamp; private OffsetDateTime timestamp;
/** 모니터링 대상 서버 이름 */
@NotNull @NotNull
@Column(name = "server_name", nullable = false, length = Integer.MAX_VALUE) @Column(name = "server_name", nullable = false, length = Integer.MAX_VALUE)
private String serverName; private String serverName;
/** 사용자 프로세스가 사용한 CPU 사용률 (%) - 응용 프로그램 실행 */
@Column(name = "cpu_user") @Column(name = "cpu_user")
private Float cpuUser; private Float cpuUser;
/** 시스템 프로세스가 사용한 CPU 사용률 (%) - 커널 작업 */
@Column(name = "cpu_system") @Column(name = "cpu_system")
private Float cpuSystem; private Float cpuSystem;
/** I/O 대기로 소모된 CPU 사용률 (%) - 디스크/네트워크 대기 */
@Column(name = "cpu_iowait") @Column(name = "cpu_iowait")
private Float cpuIowait; private Float cpuIowait;
/** 유휴 상태 CPU 사용률 (%) - 사용 가능한 여유 CPU */
@Column(name = "cpu_idle") @Column(name = "cpu_idle")
private Float cpuIdle; private Float cpuIdle;
/**
* 사용 가능한 여유 메모리 (KB 단위)
*
* <p>시스템에서 즉시 사용 가능한 물리 메모리 양
*
* <p>예시: 4194304 = 약 4GB의 여유 메모리
*
* <p>데이터 소스: /proc/meminfo의 MemFree
*/
@Column(name = "kbmemfree") @Column(name = "kbmemfree")
private Long kbmemfree; private Long kbmemfree;
/**
* 현재 사용 중인 메모리 (KB 단위)
*
* <p>시스템이 현재 할당하여 사용 중인 물리 메모리 양
*
* <p>예시: 8388608 = 약 8GB의 사용 중인 메모리
*
* <p>계산: MemTotal - MemFree
*/
@Column(name = "kbmemused") @Column(name = "kbmemused")
private Long kbmemused; private Long kbmemused;
/**
* 메모리 사용률 (백분율)
*
* <p>전체 메모리 대비 사용 중인 메모리 비율
*
* <p>계산식: (kbmemused / (kbmemused + kbmemfree)) × 100
*
* <p>예시: 66.7 = 전체 메모리의 66.7% 사용 중
*
* <p>관계식: 총 메모리 = kbmemused + kbmemfree
*/
@Column(name = "memused") @Column(name = "memused")
private Float memused; private Float memused;
} }

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);
void upsertGroupsFromInferenceResults(Long analId); /**
* tb_map_sheet_anal_data_inference 추론 결과 상세 저장
*
* @param analId
* @return
*/
int upsertGroupsFromInferenceResults(Long analId);
void upsertGeomsFromInferenceResults(Long analId); /**
* tb_map_sheet_anal_data_inference_geom geom 목록 추론 결과 저장
*
* @param analId
* @return
*/
int upsertGeomsFromInferenceResults(Long analId);
void upsertSttcFromInferenceResults(Long analId); /**
* tb_map_sheet_anal_sttc 집계 추론 결과 저장
*
* @param analId
* @return
*/
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;
@@ -80,7 +82,7 @@ public class InferenceResultRepositoryImpl implements InferenceResultRepositoryC
* @return 반영된 행 수 * @return 반영된 행 수
*/ */
@Override @Override
public void upsertGroupsFromInferenceResults(Long analId) { public int upsertGroupsFromInferenceResults(Long analId) {
String sql = String sql =
""" """
@@ -124,7 +126,7 @@ public class InferenceResultRepositoryImpl implements InferenceResultRepositoryC
updated_dttm = now() updated_dttm = now()
"""; """;
em.createNativeQuery(sql).setParameter("analId", analId).executeUpdate(); return em.createNativeQuery(sql).setParameter("analId", analId).executeUpdate();
} }
/** /**
@@ -136,7 +138,7 @@ public class InferenceResultRepositoryImpl implements InferenceResultRepositoryC
* @return 반영된 행 수 * @return 반영된 행 수
*/ */
@Override @Override
public void upsertGeomsFromInferenceResults(Long analUid) { public int upsertGeomsFromInferenceResults(Long analUid) {
String sql = String sql =
""" """
@@ -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)
@@ -229,11 +234,11 @@ public class InferenceResultRepositoryImpl implements InferenceResultRepositoryC
ref_map_sheet_num = EXCLUDED.ref_map_sheet_num ref_map_sheet_num = EXCLUDED.ref_map_sheet_num
"""; """;
em.createNativeQuery(sql).setParameter("analUid", analUid).executeUpdate(); return em.createNativeQuery(sql).setParameter("analUid", analUid).executeUpdate();
} }
@Override @Override
public void upsertSttcFromInferenceResults(Long analUid) { public int upsertSttcFromInferenceResults(Long analUid) {
String sql = String sql =
""" """
@@ -306,7 +311,7 @@ public class InferenceResultRepositoryImpl implements InferenceResultRepositoryC
,updated_uid = EXCLUDED.updated_uid ,updated_uid = EXCLUDED.updated_uid
"""; """;
em.createNativeQuery(sql).setParameter("analUid", analUid).executeUpdate(); return em.createNativeQuery(sql).setParameter("analUid", analUid).executeUpdate();
} }
// =============================== // ===============================
@@ -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

@@ -1,10 +1,17 @@
package com.kamco.cd.kamcoback.postgres.repository.Inference; package com.kamco.cd.kamcoback.postgres.repository.Inference;
import com.kamco.cd.kamcoback.inference.dto.InferenceResultsTestingDto;
import com.kamco.cd.kamcoback.postgres.entity.InferenceResultsTestingEntity; import com.kamco.cd.kamcoback.postgres.entity.InferenceResultsTestingEntity;
import java.util.List; import java.util.List;
public interface InferenceResultsTestingRepositoryCustom { public interface InferenceResultsTestingRepositoryCustom {
/**
* 추론 결과 조회
*
* @param batchIds batch id
* @return 추론 결과 목록
*/
List<InferenceResultsTestingEntity> getInferenceResultList(List<Long> batchIds); List<InferenceResultsTestingEntity> getInferenceResultList(List<Long> batchIds);
/** /**
@@ -14,4 +21,12 @@ public interface InferenceResultsTestingRepositoryCustom {
* @return batchIds 조회 count 수 * @return batchIds 조회 count 수
*/ */
Long getInferenceResultCnt(List<Long> batchIds); Long getInferenceResultCnt(List<Long> batchIds);
/**
* testing 테이블 결과로 기본정보 조회
*
* @param batchIds batch id
* @return batch id, model ver, year 정보
*/
List<InferenceResultsTestingDto.Basic> getInferenceResultGroupList(List<Long> batchIds);
} }

View File

@@ -2,7 +2,9 @@ package com.kamco.cd.kamcoback.postgres.repository.Inference;
import static com.kamco.cd.kamcoback.postgres.entity.QInferenceResultsTestingEntity.inferenceResultsTestingEntity; import static com.kamco.cd.kamcoback.postgres.entity.QInferenceResultsTestingEntity.inferenceResultsTestingEntity;
import com.kamco.cd.kamcoback.inference.dto.InferenceResultsTestingDto;
import com.kamco.cd.kamcoback.postgres.entity.InferenceResultsTestingEntity; import com.kamco.cd.kamcoback.postgres.entity.InferenceResultsTestingEntity;
import com.querydsl.core.types.Projections;
import com.querydsl.jpa.impl.JPAQueryFactory; import com.querydsl.jpa.impl.JPAQueryFactory;
import java.util.List; import java.util.List;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
@@ -21,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();
} }
@@ -42,9 +45,38 @@ 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;
} }
@Override
public List<InferenceResultsTestingDto.Basic> getInferenceResultGroupList(List<Long> batchIds) {
return queryFactory
.select(
Projections.constructor(
InferenceResultsTestingDto.Basic.class,
inferenceResultsTestingEntity.batchId,
inferenceResultsTestingEntity.modelVersion.max(),
inferenceResultsTestingEntity.beforeYear.max(),
inferenceResultsTestingEntity.afterYear.max()))
.from(inferenceResultsTestingEntity)
.where(
inferenceResultsTestingEntity.batchId.in(batchIds),
inferenceResultsTestingEntity.afterC.isNotNull(),
inferenceResultsTestingEntity.afterP.isNotNull(),
inferenceResultsTestingEntity.beforeC.isNotNull(),
inferenceResultsTestingEntity.beforeP.isNotNull(),
inferenceResultsTestingEntity.probability.isNotNull())
.groupBy(
inferenceResultsTestingEntity.batchId,
inferenceResultsTestingEntity.modelVersion,
inferenceResultsTestingEntity.beforeYear,
inferenceResultsTestingEntity.afterYear)
.fetch();
}
} }

View File

@@ -6,12 +6,42 @@ import java.util.UUID;
public interface MapSheetLearn5kRepositoryCustom { public interface MapSheetLearn5kRepositoryCustom {
/**
* 추론 실행 실패 정보 저장
*
* @param uuid 추론 uuid
* @param jobDto AI API에서 조회한 Job 정보
* @param type 모델 타입
*/
void saveFail5k(UUID uuid, JobStatusDto jobDto, String type); void saveFail5k(UUID uuid, JobStatusDto jobDto, String type);
/**
* 추론 실행중인 Job id 저장
*
* @param uuid 추론 uuid
* @param jobDto AI API에서 조회한 Job 정보
* @param type 모델 타입
*/
void saveJobId(UUID uuid, JobStatusDto jobDto, String type); void saveJobId(UUID uuid, JobStatusDto jobDto, String type);
/**
* 실패 처리되어야 할 목록 중에 아직 실패로 표시되지 않은 ID 조회
*
* @param uuid 추론 uuid
* @param failIds AI API 연결하여 조회한 실패 job id
* @param type 모델 타입
* @return 실패로 표시되지 않은 ID
*/
List<Long> findFail5kList(UUID uuid, List<Long> failIds, String type); List<Long> findFail5kList(UUID uuid, List<Long> failIds, String type);
/**
* 완료된 것으로 들어온 목록 중 실제로 존재하는 5k jobId 조회
*
* @param uuid 추론 uuid
* @param completedIds AI API 연결하여 조회한 실패 job id
* @param type 모델 타입
* @return 성공한 job id
*/
List<Long> findCompleted5kList(UUID uuid, List<Long> completedIds, String type); List<Long> findCompleted5kList(UUID uuid, List<Long> completedIds, String type);
/** /**

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)
@@ -315,7 +316,8 @@ public class MapSheetLearnRepositoryImpl implements MapSheetLearnRepositoryCusto
Expressions.stringTemplate("substring({0} from 1 for 8)", mapSheetLearnEntity.uid), Expressions.stringTemplate("substring({0} from 1 for 8)", mapSheetLearnEntity.uid),
mapSheetLearnEntity.applyYn, mapSheetLearnEntity.applyYn,
mapSheetLearnEntity.applyDttm, mapSheetLearnEntity.applyDttm,
mapSheetAnalInferenceEntity.uuid)) mapSheetAnalInferenceEntity.uuid,
mapSheetLearnEntity.status))
.from(mapSheetLearnEntity) .from(mapSheetLearnEntity)
.leftJoin(m1) .leftJoin(m1)
.on(mapSheetLearnEntity.m1ModelUuid.eq(m1.uuid)) .on(mapSheetLearnEntity.m1ModelUuid.eq(m1.uuid))
@@ -444,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 =
@@ -457,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,
@@ -471,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)
@@ -486,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
@@ -518,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);
} }
// 부분 도엽 실행인지 확인 // 부분 도엽 실행인지 확인
@@ -528,10 +560,11 @@ 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))
@@ -539,8 +572,9 @@ public class MapSheetLearnRepositoryImpl implements MapSheetLearnRepositoryCusto
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 =
@@ -555,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

@@ -3,6 +3,8 @@ package com.kamco.cd.kamcoback.postgres.repository.changedetection;
import com.kamco.cd.kamcoback.changedetection.dto.ChangeDetectionDto; import com.kamco.cd.kamcoback.changedetection.dto.ChangeDetectionDto;
import com.kamco.cd.kamcoback.changedetection.dto.ChangeDetectionDto.MapScaleType; import com.kamco.cd.kamcoback.changedetection.dto.ChangeDetectionDto.MapScaleType;
import com.kamco.cd.kamcoback.changedetection.dto.ChangeDetectionDto.MapSheetList; import com.kamco.cd.kamcoback.changedetection.dto.ChangeDetectionDto.MapSheetList;
import com.kamco.cd.kamcoback.changedetection.dto.ChangeDetectionDto.PointFeatureList;
import com.kamco.cd.kamcoback.changedetection.dto.ChangeDetectionDto.PolygonFeatureList;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.UUID; import java.util.UUID;
@@ -31,10 +33,10 @@ public interface ChangeDetectionRepositoryCustom {
List<MapSheetList> getChangeDetectionMapSheet50kList(UUID uuid); List<MapSheetList> getChangeDetectionMapSheet50kList(UUID uuid);
ChangeDetectionDto.PolygonFeatureList getPolygonListByCd( ChangeDetectionDto.PolygonFeatureList getPolygonListByCd(
String chnDtctId, String cdObjectId, List<String> cdObjectIds); String chnDtctId, String cdObjectId, List<String> cdObjectIds, String pnu);
ChangeDetectionDto.PointFeatureList getPointListByCd( ChangeDetectionDto.PointFeatureList getPointListByCd(
String chnDtctId, String cdObjectId, List<String> cdObjectIds); String chnDtctId, String cdObjectId, List<String> cdObjectIds, String pnu);
ChangeDetectionDto.PolygonFeatureList getSelectedChangeDetectionPolygonListByPnu( ChangeDetectionDto.PolygonFeatureList getSelectedChangeDetectionPolygonListByPnu(
String chnDtctId, String pnu); String chnDtctId, String pnu);
@@ -43,4 +45,8 @@ public interface ChangeDetectionRepositoryCustom {
String chnDtctId, String pnu); String chnDtctId, String pnu);
Optional<UUID> getLearnUuid(String chnDtctId); Optional<UUID> getLearnUuid(String chnDtctId);
PolygonFeatureList getChangeDetectionPnuPolygonList(UUID uuid, String pnu);
PointFeatureList getChangeDetectionPnuPointList(UUID uuid, String pnu);
} }

View File

@@ -18,7 +18,9 @@ import com.kamco.cd.kamcoback.changedetection.dto.ChangeDetectionDto.DetectSearc
import com.kamco.cd.kamcoback.changedetection.dto.ChangeDetectionDto.MapScaleType; import com.kamco.cd.kamcoback.changedetection.dto.ChangeDetectionDto.MapScaleType;
import com.kamco.cd.kamcoback.changedetection.dto.ChangeDetectionDto.MapSheetList; import com.kamco.cd.kamcoback.changedetection.dto.ChangeDetectionDto.MapSheetList;
import com.kamco.cd.kamcoback.changedetection.dto.ChangeDetectionDto.PointFeatureList; import com.kamco.cd.kamcoback.changedetection.dto.ChangeDetectionDto.PointFeatureList;
import com.kamco.cd.kamcoback.changedetection.dto.ChangeDetectionDto.PointQueryData;
import com.kamco.cd.kamcoback.changedetection.dto.ChangeDetectionDto.PolygonFeatureList; import com.kamco.cd.kamcoback.changedetection.dto.ChangeDetectionDto.PolygonFeatureList;
import com.kamco.cd.kamcoback.changedetection.dto.ChangeDetectionDto.PolygonQueryData;
import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.Status; import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.Status;
import com.kamco.cd.kamcoback.postgres.entity.MapSheetAnalDataInferenceGeomEntity; import com.kamco.cd.kamcoback.postgres.entity.MapSheetAnalDataInferenceGeomEntity;
import com.kamco.cd.kamcoback.postgres.entity.QMapSheetAnalDataInferenceEntity; import com.kamco.cd.kamcoback.postgres.entity.QMapSheetAnalDataInferenceEntity;
@@ -31,6 +33,7 @@ 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.StringExpression; import com.querydsl.core.types.dsl.StringExpression;
import com.querydsl.jpa.JPAExpressions; import com.querydsl.jpa.JPAExpressions;
import com.querydsl.jpa.impl.JPAQuery;
import com.querydsl.jpa.impl.JPAQueryFactory; import com.querydsl.jpa.impl.JPAQueryFactory;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
@@ -208,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();
} }
@@ -391,16 +395,23 @@ public class ChangeDetectionRepositoryImpl extends QuerydslRepositorySupport
@Override @Override
public PolygonFeatureList getPolygonListByCd( public PolygonFeatureList getPolygonListByCd(
String chnDtctId, String cdObjectId, List<String> cdObjectIds) { String chnDtctId, String cdObjectId, List<String> cdObjectIds, String pnu) {
BooleanBuilder builder = new BooleanBuilder(); BooleanBuilder builder = new BooleanBuilder();
builder.and(mapSheetLearnEntity.uid.eq(chnDtctId)); builder.and(mapSheetLearnEntity.uid.eq(chnDtctId));
// pnu가 들어온 경우
boolean hasPnu = pnu != null && !pnu.isBlank();
// pnu 가 없을 때
if (!hasPnu) {
builder.and( builder.and(
mapSheetAnalDataInferenceGeomEntity mapSheetAnalDataInferenceGeomEntity
.resultUid .resultUid
.eq(chnDtctId) .eq(cdObjectId)
.or(mapSheetAnalDataInferenceGeomEntity.resultUid.in(cdObjectIds))); .or(mapSheetAnalDataInferenceGeomEntity.resultUid.in(cdObjectIds)));
}
List<ChangeDetectionDto.PolygonQueryData> list = JPAQuery<PolygonQueryData> query =
queryFactory queryFactory
.select( .select(
Projections.constructor( Projections.constructor(
@@ -425,10 +436,19 @@ public class ChangeDetectionRepositoryImpl extends QuerydslRepositorySupport
.innerJoin(mapSheetAnalInferenceEntity) .innerJoin(mapSheetAnalInferenceEntity)
.on(mapSheetAnalInferenceEntity.id.eq(mapSheetAnalDataInferenceEntity.analUid)) .on(mapSheetAnalInferenceEntity.id.eq(mapSheetAnalDataInferenceEntity.analUid))
.innerJoin(mapSheetLearnEntity) .innerJoin(mapSheetLearnEntity)
.on(mapSheetLearnEntity.id.eq(mapSheetAnalInferenceEntity.learnId)) .on(mapSheetLearnEntity.id.eq(mapSheetAnalInferenceEntity.learnId));
.where(builder)
.orderBy(mapSheetAnalDataInferenceGeomEntity.mapSheetNum.asc()) // pnu 조건이면 tb_pnu join 추가
.fetch(); if (hasPnu) {
query
.innerJoin(pnuEntity)
.on(pnuEntity.geo.geoUid.eq(mapSheetAnalDataInferenceGeomEntity.geoUid));
builder.and(pnuEntity.pnu.eq(pnu));
}
List<ChangeDetectionDto.PolygonQueryData> list =
query.where(builder).orderBy(mapSheetAnalDataInferenceGeomEntity.mapSheetNum.asc()).fetch();
ObjectMapper mapper = new ObjectMapper(); ObjectMapper mapper = new ObjectMapper();
List<ChangeDetectionDto.PolygonFeature> result = List<ChangeDetectionDto.PolygonFeature> result =
@@ -470,16 +490,23 @@ public class ChangeDetectionRepositoryImpl extends QuerydslRepositorySupport
@Override @Override
public PointFeatureList getPointListByCd( public PointFeatureList getPointListByCd(
String chnDtctId, String cdObjectId, List<String> cdObjectIds) { String chnDtctId, String cdObjectId, List<String> cdObjectIds, String pnu) {
BooleanBuilder builder = new BooleanBuilder(); BooleanBuilder builder = new BooleanBuilder();
builder.and(mapSheetLearnEntity.uid.eq(chnDtctId)); builder.and(mapSheetLearnEntity.uid.eq(chnDtctId));
// pnu가 들어온 경우
boolean hasPnu = pnu != null && !pnu.isBlank();
// pnu 가 없을 때
if (!hasPnu) {
builder.and( builder.and(
mapSheetAnalDataInferenceGeomEntity mapSheetAnalDataInferenceGeomEntity
.resultUid .resultUid
.eq(chnDtctId) .eq(cdObjectId)
.or(mapSheetAnalDataInferenceGeomEntity.resultUid.in(cdObjectIds))); .or(mapSheetAnalDataInferenceGeomEntity.resultUid.in(cdObjectIds)));
}
List<ChangeDetectionDto.PointQueryData> list = JPAQuery<PointQueryData> query =
queryFactory queryFactory
.select( .select(
Projections.constructor( Projections.constructor(
@@ -498,9 +525,19 @@ public class ChangeDetectionRepositoryImpl extends QuerydslRepositorySupport
.innerJoin(mapSheetAnalInferenceEntity) .innerJoin(mapSheetAnalInferenceEntity)
.on(mapSheetAnalInferenceEntity.id.eq(mapSheetAnalDataInferenceEntity.analUid)) .on(mapSheetAnalInferenceEntity.id.eq(mapSheetAnalDataInferenceEntity.analUid))
.innerJoin(mapSheetLearnEntity) .innerJoin(mapSheetLearnEntity)
.on(mapSheetLearnEntity.id.eq(mapSheetAnalInferenceEntity.learnId)) .on(mapSheetLearnEntity.id.eq(mapSheetAnalInferenceEntity.learnId));
.where(builder)
.fetch(); // pnu 조건이면 tb_pnu join 추가
if (hasPnu) {
query
.innerJoin(pnuEntity)
.on(pnuEntity.geo.geoUid.eq(mapSheetAnalDataInferenceGeomEntity.geoUid));
builder.and(pnuEntity.pnu.eq(pnu));
}
List<ChangeDetectionDto.PointQueryData> list =
query.where(builder).orderBy(mapSheetAnalDataInferenceGeomEntity.mapSheetNum.asc()).fetch();
ObjectMapper mapper = new ObjectMapper(); ObjectMapper mapper = new ObjectMapper();
List<ChangeDetectionDto.PointFeature> result = List<ChangeDetectionDto.PointFeature> result =
@@ -659,4 +696,120 @@ public class ChangeDetectionRepositoryImpl extends QuerydslRepositorySupport
.where(mapSheetLearnEntity.uid.eq(chnDtctId)) .where(mapSheetLearnEntity.uid.eq(chnDtctId))
.fetchOne()); .fetchOne());
} }
@Override
public PolygonFeatureList getChangeDetectionPnuPolygonList(UUID uuid, String pnu) {
List<ChangeDetectionDto.PolygonQueryData> list =
queryFactory
.select(
Projections.constructor(
ChangeDetectionDto.PolygonQueryData.class,
Expressions.stringTemplate("{0}", "Feature"),
Expressions.stringTemplate(
"ST_AsGeoJSON({0})", mapSheetAnalDataInferenceGeomEntity.geom),
mapSheetAnalDataInferenceGeomEntity.geoUid,
mapSheetAnalDataInferenceGeomEntity.area,
mapSheetAnalDataInferenceGeomEntity.compareYyyy,
mapSheetAnalDataInferenceGeomEntity.classBeforeProb,
mapSheetAnalDataInferenceGeomEntity.classBeforeCd.toUpperCase(),
mapSheetAnalDataInferenceGeomEntity.targetYyyy,
mapSheetAnalDataInferenceGeomEntity.classAfterProb,
mapSheetAnalDataInferenceGeomEntity.classAfterCd.toUpperCase(),
mapSheetAnalDataInferenceGeomEntity.cdProb,
mapSheetAnalDataInferenceGeomEntity.uuid,
mapSheetAnalDataInferenceGeomEntity.resultUid))
.from(mapSheetAnalDataInferenceGeomEntity)
.innerJoin(mapSheetAnalDataInferenceEntity)
.on(mapSheetAnalDataInferenceGeomEntity.dataUid.eq(mapSheetAnalDataInferenceEntity.id))
.innerJoin(mapSheetAnalInferenceEntity)
.on(mapSheetAnalInferenceEntity.id.eq(mapSheetAnalDataInferenceEntity.analUid))
.innerJoin(pnuEntity)
.on(mapSheetAnalDataInferenceGeomEntity.geoUid.eq(pnuEntity.geo.geoUid))
.where(mapSheetAnalInferenceEntity.uuid.eq(uuid), pnuEntity.pnu.eq(pnu))
.orderBy(mapSheetAnalDataInferenceGeomEntity.mapSheetNum.asc())
.fetch();
ObjectMapper mapper = new ObjectMapper();
List<ChangeDetectionDto.PolygonFeature> result =
list.stream()
.map(
data -> {
String geoJson = data.getGeometry();
JsonNode jsonNode;
try {
jsonNode = mapper.readTree(geoJson);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
ChangeDetectionDto.PolygonProperties properties =
new ChangeDetectionDto.PolygonProperties(
data.getGeoUid(),
data.getArea(),
data.getBeforeYear(),
data.getBeforeConfidence(),
data.getBeforeClass(),
data.getAfterYear(),
data.getAfterConfidence(),
data.getAfterClass(),
data.getCdProb(),
data.getUuid(),
data.getResultUid());
return new ChangeDetectionDto.PolygonFeature(
data.getType(), jsonNode, properties);
})
.collect(Collectors.toList());
ChangeDetectionDto.PolygonFeatureList polygonList = new ChangeDetectionDto.PolygonFeatureList();
polygonList.setType("FeatureCollection");
polygonList.setFeatures(result);
return polygonList;
}
@Override
public PointFeatureList getChangeDetectionPnuPointList(UUID uuid, String pnu) {
List<ChangeDetectionDto.PointQueryData> list =
queryFactory
.select(
Projections.constructor(
ChangeDetectionDto.PointQueryData.class,
Expressions.stringTemplate("{0}", "Feature"),
Expressions.stringTemplate(
"ST_AsGeoJSON({0})",
mapSheetAnalDataInferenceGeomEntity.geomCenter), // point
Projections.constructor(
ChangeDetectionDto.PointProperties.class,
mapSheetAnalDataInferenceGeomEntity.geoUid,
mapSheetAnalDataInferenceGeomEntity.classAfterCd.toUpperCase())))
.from(mapSheetAnalDataInferenceGeomEntity)
.innerJoin(mapSheetAnalDataInferenceEntity)
.on(mapSheetAnalDataInferenceGeomEntity.dataUid.eq(mapSheetAnalDataInferenceEntity.id))
.innerJoin(mapSheetAnalInferenceEntity)
.on(mapSheetAnalInferenceEntity.id.eq(mapSheetAnalDataInferenceEntity.analUid))
.innerJoin(pnuEntity)
.on(mapSheetAnalDataInferenceGeomEntity.geoUid.eq(pnuEntity.geo.geoUid))
.where(mapSheetAnalInferenceEntity.uuid.eq(uuid), pnuEntity.pnu.eq(pnu))
.fetch();
ObjectMapper mapper = new ObjectMapper();
List<ChangeDetectionDto.PointFeature> result =
list.stream()
.map(
data -> {
String geoJson = data.getGeometry();
JsonNode jsonNode;
try {
jsonNode = mapper.readTree(geoJson);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
return new ChangeDetectionDto.PointFeature(
data.getType(), jsonNode, data.getProperties());
})
.collect(Collectors.toList());
return new ChangeDetectionDto.PointFeatureList("FeatureCollection", result);
}
} }

View File

@@ -0,0 +1,7 @@
package com.kamco.cd.kamcoback.postgres.repository.gukyuin;
import com.kamco.cd.kamcoback.postgres.entity.MapSheetLearnEntity;
import org.springframework.data.jpa.repository.JpaRepository;
public interface GukYuinPnuCntUpdateJobRepository
extends JpaRepository<MapSheetLearnEntity, Long>, GukYuinPnuCntUpdateJobRepositoryCustom {}

View File

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

View File

@@ -0,0 +1,48 @@
package com.kamco.cd.kamcoback.postgres.repository.gukyuin;
import static com.kamco.cd.kamcoback.postgres.entity.QMapSheetLearnEntity.mapSheetLearnEntity;
import com.querydsl.jpa.impl.JPAQueryFactory;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import java.time.ZonedDateTime;
import lombok.RequiredArgsConstructor;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
@Repository
@RequiredArgsConstructor
public class GukYuinPnuCntUpdateJobRepositoryImpl
implements GukYuinPnuCntUpdateJobRepositoryCustom {
private final JPAQueryFactory queryFactory;
private final JdbcTemplate jdbcTemplate;
@PersistenceContext private EntityManager em;
@Override
public void updateGukYuinContListPnuUpdateCnt() {
String sql =
"""
update tb_map_sheet_anal_data_inference_geom p
set pnu = c_count.actual_count
from (
select geo_uid, count(*) actual_count
from tb_pnu
group by geo_uid
) c_count
where p.geo_uid = c_count.geo_uid and p.pnu != c_count.actual_count;
""";
jdbcTemplate.update(sql);
}
@Override
public void updateGukYuinApplyStatus(String uid, String status) {
queryFactory
.update(mapSheetLearnEntity)
.set(mapSheetLearnEntity.applyStatus, status)
.set(mapSheetLearnEntity.applyStatusDttm, ZonedDateTime.now())
.where(mapSheetLearnEntity.uid.eq(uid))
.execute();
}
}

View File

@@ -45,4 +45,8 @@ public interface GukYuinRepositoryCustom {
void updateMapSheetLearnGukyuinEndStatus(Long learnId); void updateMapSheetLearnGukyuinEndStatus(Long learnId);
void updateMapSheetInferenceLabelEndStatus(Long learnId); void updateMapSheetInferenceLabelEndStatus(Long learnId);
List<String> findStbltObjectIds(String uid, String mapSheetNum);
Integer updateStbltRandomData(String uid, int updateCnt);
} }

View File

@@ -1,11 +1,13 @@
package com.kamco.cd.kamcoback.postgres.repository.gukyuin; package com.kamco.cd.kamcoback.postgres.repository.gukyuin;
import static com.kamco.cd.kamcoback.postgres.entity.QLabelingAssignmentEntity.labelingAssignmentEntity; import static com.kamco.cd.kamcoback.postgres.entity.QLabelingAssignmentEntity.labelingAssignmentEntity;
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.QMapSheetLearnEntity.mapSheetLearnEntity; import static com.kamco.cd.kamcoback.postgres.entity.QMapSheetLearnEntity.mapSheetLearnEntity;
import static com.kamco.cd.kamcoback.postgres.entity.QPnuEntity.pnuEntity; import static com.kamco.cd.kamcoback.postgres.entity.QPnuEntity.pnuEntity;
import com.kamco.cd.kamcoback.common.enums.ImageryFitStatus;
import com.kamco.cd.kamcoback.gukyuin.dto.ChngDetectMastDto.Basic; import com.kamco.cd.kamcoback.gukyuin.dto.ChngDetectMastDto.Basic;
import com.kamco.cd.kamcoback.gukyuin.dto.ChngDetectMastDto.LabelSendDto; import com.kamco.cd.kamcoback.gukyuin.dto.ChngDetectMastDto.LabelSendDto;
import com.kamco.cd.kamcoback.gukyuin.dto.ChngDetectMastDto.LearnKeyDto; import com.kamco.cd.kamcoback.gukyuin.dto.ChngDetectMastDto.LearnKeyDto;
@@ -318,6 +320,61 @@ public class GukYuinRepositoryImpl implements GukYuinRepositoryCustom {
.execute(); .execute();
} }
@Override
public List<String> findStbltObjectIds(String uid, String mapSheetNum) {
return queryFactory
.select(mapSheetAnalDataInferenceGeomEntity.resultUid)
.from(mapSheetLearnEntity)
.innerJoin(mapSheetAnalInferenceEntity)
.on(mapSheetLearnEntity.id.eq(mapSheetAnalInferenceEntity.learnId))
.innerJoin(mapSheetAnalDataInferenceEntity)
.on(mapSheetAnalInferenceEntity.id.eq(mapSheetAnalDataInferenceEntity.analUid))
.innerJoin(mapSheetAnalDataInferenceGeomEntity)
.on(mapSheetAnalDataInferenceEntity.id.eq(mapSheetAnalDataInferenceGeomEntity.dataUid))
.where(
mapSheetLearnEntity.uid.eq(uid),
mapSheetAnalDataInferenceGeomEntity.mapSheetNum.eq(Long.valueOf(mapSheetNum)))
.fetch();
}
/**
* mapSheetAnalDataInferenceGeomEntity 데이터 에 실태조사 값 들어온 것으로 간주하고 update 랜덤으로 하기
*
* @param uid
* @param updateCnt
* @return
*/
@Override
public Integer updateStbltRandomData(String uid, int updateCnt) {
List<Long> geoUids =
queryFactory
.select(mapSheetAnalDataInferenceGeomEntity.geoUid)
.from(mapSheetLearnEntity)
.innerJoin(mapSheetAnalInferenceEntity)
.on(mapSheetLearnEntity.id.eq(mapSheetAnalInferenceEntity.learnId))
.innerJoin(mapSheetAnalDataInferenceEntity)
.on(mapSheetAnalInferenceEntity.id.eq(mapSheetAnalDataInferenceEntity.analUid))
.innerJoin(mapSheetAnalDataInferenceGeomEntity)
.on(
mapSheetAnalDataInferenceEntity.id.eq(mapSheetAnalDataInferenceGeomEntity.dataUid),
mapSheetAnalDataInferenceGeomEntity.fitState.isNull())
.where(mapSheetLearnEntity.uid.eq(uid))
.orderBy(mapSheetAnalDataInferenceGeomEntity.geoUid.asc())
.limit(updateCnt)
.fetch();
for (Long geoUid : geoUids) {
queryFactory
.update(mapSheetAnalDataInferenceGeomEntity)
.set(mapSheetAnalDataInferenceGeomEntity.pnu, 1L)
.set(mapSheetAnalDataInferenceGeomEntity.fitState, ImageryFitStatus.UNFIT.getId())
.set(mapSheetAnalDataInferenceGeomEntity.fitStateDttm, ZonedDateTime.now())
.where(mapSheetAnalDataInferenceGeomEntity.geoUid.eq(geoUid))
.execute();
}
return updateCnt;
}
@Override @Override
@Transactional @Transactional
public void updateGukYuinApplyStateComplete(Long id, GukYuinStatus status) { public void updateGukYuinApplyStateComplete(Long id, GukYuinStatus status) {

View File

@@ -99,8 +99,8 @@ public class GukYuinStbltJobRepositoryImpl implements GukYuinStbltJobRepositoryC
.set( .set(
mapSheetAnalDataInferenceGeomEntity.fitState, mapSheetAnalDataInferenceGeomEntity.fitState,
stbResult.getStbltYn().equals("Y") stbResult.getStbltYn().equals("Y")
? ImageryFitStatus.UNFIT.getId() ? ImageryFitStatus.FIT.getId()
: ImageryFitStatus.FIT.getId()) // 적합여부가 Y 이면 부적합인 것, N 이면 적합한 것이라고 함 : ImageryFitStatus.UNFIT.getId()) // 적합여부가 N 이면 부적합인 것, Y 이면 적합한 것이라고 함
.set(mapSheetAnalDataInferenceGeomEntity.fitStateDttm, ZonedDateTime.now()) .set(mapSheetAnalDataInferenceGeomEntity.fitStateDttm, ZonedDateTime.now())
.set(mapSheetAnalDataInferenceGeomEntity.fitStateCmmnt, stbResult.getIncyCmnt()) .set(mapSheetAnalDataInferenceGeomEntity.fitStateCmmnt, stbResult.getIncyCmnt())
.where(mapSheetAnalDataInferenceGeomEntity.resultUid.eq(resultUid)) .where(mapSheetAnalDataInferenceGeomEntity.resultUid.eq(resultUid))

View File

@@ -388,12 +388,8 @@ public class LabelAllocateRepositoryImpl implements LabelAllocateRepositoryCusto
mapSheetAnalDataInferenceGeomEntity.compareYyyy.eq(analEntity.getCompareYyyy()), mapSheetAnalDataInferenceGeomEntity.compareYyyy.eq(analEntity.getCompareYyyy()),
mapSheetAnalDataInferenceGeomEntity.targetYyyy.eq(analEntity.getTargetYyyy()), mapSheetAnalDataInferenceGeomEntity.targetYyyy.eq(analEntity.getTargetYyyy()),
mapSheetAnalDataInferenceGeomEntity.stage.eq(analEntity.getStage()), mapSheetAnalDataInferenceGeomEntity.stage.eq(analEntity.getStage()),
// mapSheetAnalDataInferenceGeomEntity.pnu.isNotNull()
mapSheetAnalDataInferenceGeomEntity.pnu.gt(0L), mapSheetAnalDataInferenceGeomEntity.pnu.gt(0L),
mapSheetAnalDataInferenceGeomEntity.fitState.eq( mapSheetAnalDataInferenceGeomEntity.fitState.eq(ImageryFitStatus.UNFIT.getId()))
ImageryFitStatus.UNFIT.getId()) // TODO:
// 추후 라벨링 대상 조건 수정하기
)
.fetchOne(); .fetchOne();
} }
@@ -493,6 +489,23 @@ 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 =
queryFactory
.select(labelingAssignmentEntity.inferenceGeomUid.count())
.from(labelingAssignmentEntity)
.innerJoin(mapSheetAnalInferenceEntity)
.on(
labelingAssignmentEntity.analUid.eq(mapSheetAnalInferenceEntity.id),
mapSheetAnalInferenceEntity.id.eq(analUid))
.where(
labelingAssignmentEntity.inspectState.eq(InspectState.COMPLETE.getId()),
labelingAssignmentEntity.inspectStatDttm.lt(end))
.fetchOne();
return WorkProgressInfo.builder() return WorkProgressInfo.builder()
// 라벨링 (pass_yn = false인 부적합 데이터 기준) // 라벨링 (pass_yn = false인 부적합 데이터 기준)
.labelingProgressRate(labelingRate) .labelingProgressRate(labelingRate)
@@ -516,6 +529,7 @@ public class LabelAllocateRepositoryImpl implements LabelAllocateRepositoryCusto
.completedCount(labelCompleted) .completedCount(labelCompleted)
.remainingLabelCount(labelingRemaining) .remainingLabelCount(labelingRemaining)
.remainingInspectCount(inspectionRemaining) .remainingInspectCount(inspectionRemaining)
.downloadPolygonCnt(downloadPolygonCnt)
.build(); .build();
} }
@@ -659,6 +673,23 @@ 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 =
queryFactory
.select(labelingAssignmentEntity.inferenceGeomUid.count())
.from(labelingAssignmentEntity)
.innerJoin(mapSheetAnalInferenceEntity)
.on(
labelingAssignmentEntity.analUid.eq(mapSheetAnalInferenceEntity.id),
mapSheetAnalInferenceEntity.uuid.eq(targetUuid))
.where(
labelingAssignmentEntity.inspectState.eq(InspectState.COMPLETE.getId()),
labelingAssignmentEntity.inspectStatDttm.lt(end))
.fetchOne();
return WorkProgressInfo.builder() return WorkProgressInfo.builder()
.labelingProgressRate(labelingRate) .labelingProgressRate(labelingRate)
.labelingStatus(labelingStatus) .labelingStatus(labelingStatus)
@@ -679,6 +710,7 @@ public class LabelAllocateRepositoryImpl implements LabelAllocateRepositoryCusto
.completedCount(labelCompleted) .completedCount(labelCompleted)
.remainingLabelCount(labelingRemaining) .remainingLabelCount(labelingRemaining)
.remainingInspectCount(inspectionRemaining) .remainingInspectCount(inspectionRemaining)
.downloadPolygonCnt(downloadPolygonCnt)
.build(); .build();
} }

View File

@@ -4,8 +4,10 @@ import static com.kamco.cd.kamcoback.postgres.entity.QMapSheetLearnEntity.mapShe
import com.kamco.cd.kamcoback.common.enums.ImageryFitStatus; import com.kamco.cd.kamcoback.common.enums.ImageryFitStatus;
import com.kamco.cd.kamcoback.label.dto.LabelAllocateDto.InspectState; 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.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;
@@ -17,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;
@@ -85,7 +86,7 @@ public class LabelWorkRepositoryImpl implements LabelWorkRepositoryCustom {
} }
/** /**
* 라벨링 작업관리 목록 조회 (복잡한 집계 쿼리로 인해 DTO 직접 반환) * 라벨링 작업관리 목록 조회 :: 전체 geom 집계 -> inference 먼저 쿼리 하고 count 정보 따로 집계하도록 수정
* *
* @param searchReq 검색 조건 * @param searchReq 검색 조건
* @return 라벨링 작업관리 목록 페이지 * @return 라벨링 작업관리 목록 페이지
@@ -94,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("-");
@@ -105,125 +105,52 @@ 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( /** 순서 ING 진행중 ASSIGNED 작업할당 PENDING 작업대기 FINISH 종료 */
mapSheetAnalInferenceEntity.id.eq(mapSheetAnalDataInferenceEntity.analUid)); NumberExpression<Integer> stateOrder =
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() new CaseBuilder()
.when( .when(mapSheetAnalInferenceEntity.analState.eq(LabelMngState.ING.getId()))
mapSheetAnalDataInferenceGeomEntity .then(0)
.pnu .when(mapSheetAnalInferenceEntity.analState.eq(LabelMngState.ASSIGNED.getId()))
.gt(0) .then(1)
.and( .when(mapSheetAnalInferenceEntity.analState.eq(LabelMngState.PENDING.getId()))
mapSheetAnalDataInferenceGeomEntity.fitState.eq( .then(2)
ImageryFitStatus.UNFIT.getId()))) .when(mapSheetAnalInferenceEntity.analState.eq(LabelMngState.FINISH.getId()))
.then(1L) .then(3)
.otherwise(0L) .otherwise(99);
.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));
// 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)
@@ -233,24 +160,13 @@ 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(),
mapSheetAnalInferenceEntity.targetYyyy.desc(), mapSheetAnalInferenceEntity.targetYyyy.desc(),
mapSheetAnalInferenceEntity.compareYyyy.desc(), mapSheetAnalInferenceEntity.compareYyyy.desc(),
mapSheetAnalInferenceEntity.stage.desc()) mapSheetAnalInferenceEntity.stage.desc())
@@ -258,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

@@ -15,16 +15,39 @@ import org.springframework.data.domain.Page;
public interface MapSheetMngRepositoryCustom { public interface MapSheetMngRepositoryCustom {
/**
* 영상데이터관리 > 목록 조회
*
* @return
*/
List<MapSheetMngDto.MngDto> findMapSheetMngList(); List<MapSheetMngDto.MngDto> findMapSheetMngList();
/**
* 영상데이터관리 > 데이터 등록 > 연도 선택 목록
*
* @return
*/
List<Integer> findMapSheetMngYyyyList(); List<Integer> findMapSheetMngYyyyList();
/**
* 영상데이터관리 > 상세 조회
*
* @param mngYyyy
* @return
*/
MapSheetMngDto.MngDto findMapSheetMng(int mngYyyy); MapSheetMngDto.MngDto findMapSheetMng(int mngYyyy);
void MapSheetMngComplete(int mngYyyy); void MapSheetMngComplete(int mngYyyy);
Optional<MapSheetMngHstEntity> findMapSheetMngHstInfo(Long hstUid); Optional<MapSheetMngHstEntity> findMapSheetMngHstInfo(Long hstUid);
/**
* 5k 도엽 기준으로 tb_map_sheet_hst 테이블에 먼저 insert 하기
*
* @param mngYyyy
* @param mngPath
* @return
*/
int insertMapSheetOrgDataToMapSheetMngHst(int mngYyyy, String mngPath); int insertMapSheetOrgDataToMapSheetMngHst(int mngYyyy, String mngPath);
List<MapSheetMngDto.MngFilesDto> findHstUidToMapSheetFileList(Long hstUid); List<MapSheetMngDto.MngFilesDto> findHstUidToMapSheetFileList(Long hstUid);
@@ -33,6 +56,11 @@ public interface MapSheetMngRepositoryCustom {
MapSheetMngDto.MngFilesDto findYyyyToMapSheetFilePathRefer(int mngYyyy); MapSheetMngDto.MngFilesDto findYyyyToMapSheetFilePathRefer(int mngYyyy);
/**
* 같은 년도로 저장된 데이터가 있다면 삭제
*
* @param mngYyyy
*/
void deleteByMngYyyyMngAll(int mngYyyy); void deleteByMngYyyyMngAll(int mngYyyy);
void deleteByMngYyyyMng(int mngYyyy); void deleteByMngYyyyMng(int mngYyyy);
@@ -53,13 +81,37 @@ public interface MapSheetMngRepositoryCustom {
void deleteByNotInFileUidMngFile(Long hstUid, List<Long> fileUids); void deleteByNotInFileUidMngFile(Long hstUid, List<Long> fileUids);
/**
* tb_year 에 해당 년도 완료로 업데이트
*
* @param yyyy
* @param status
*/
void updateYearState(int yyyy, String status); void updateYearState(int yyyy, String status);
/**
* 영상데이터관리 > 상세 > 오류 처리 내역
*
* @param searchReq 오류 검색 조건
* @return
*/
Page<MapSheetMngDto.ErrorDataDto> findMapSheetErrorList( Page<MapSheetMngDto.ErrorDataDto> findMapSheetErrorList(
MapSheetMngDto.@Valid ErrorSearchReq searchReq); MapSheetMngDto.@Valid ErrorSearchReq searchReq);
/**
* hst 테이블에 선택한 hstUid row 정보 조회
*
* @param hstUid
* @return
*/
MapSheetMngDto.ErrorDataDto findMapSheetError(Long hstUid); MapSheetMngDto.ErrorDataDto findMapSheetError(Long hstUid);
/**
* 파일 목록으로 업로드 경로 확인
*
* @param hstUid
* @return
*/
List<MapSheetMngDto.MngFilesDto> findByHstUidMapSheetFileList(Long hstUid); List<MapSheetMngDto.MngFilesDto> findByHstUidMapSheetFileList(Long hstUid);
MapSheetMngDto.MngFilesDto findByFileUidMapSheetFile(Long fileUid); MapSheetMngDto.MngFilesDto findByFileUidMapSheetFile(Long fileUid);
@@ -81,15 +133,21 @@ public interface MapSheetMngRepositoryCustom {
void updateMapSheetMngHstUploadId(Long hstUid, UUID uuid, String uploadId); void updateMapSheetMngHstUploadId(Long hstUid, UUID uuid, String uploadId);
/**
* 년도별 Tile 정보 등록
*
* @param addReq
*/
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;
@@ -59,16 +58,17 @@ public class MapSheetMngRepositoryImpl extends QuerydslRepositorySupport
this.queryFactory = queryFactory; this.queryFactory = queryFactory;
} }
/**
* 영상데이터관리 > 목록 조회
*
* @return
*/
@Override @Override
public List<MapSheetMngDto.MngDto> findMapSheetMngList() { public List<MapSheetMngDto.MngDto> findMapSheetMngList() {
// Pageable pageable = searchReq.toPageable(); // Pageable pageable = searchReq.toPageable();
BooleanBuilder whereBuilder = new BooleanBuilder(); BooleanBuilder whereBuilder = new BooleanBuilder();
// if (searchReq.getMngYyyy() != null) {
// whereBuilder.and(mapSheetMngEntity.mngYyyy.eq(searchReq.getMngYyyy()));
// }
NumberExpression<Long> totalCount = mapSheetMngHstEntity.count().as("syncTotCnt"); NumberExpression<Long> totalCount = mapSheetMngHstEntity.count().as("syncTotCnt");
NumberExpression<Long> doneCount = NumberExpression<Long> doneCount =
@@ -227,6 +227,11 @@ public class MapSheetMngRepositoryImpl extends QuerydslRepositorySupport
return foundContent; return foundContent;
} }
/**
* 영상데이터관리 > 데이터 등록 > 연도 선택 목록
*
* @return List<Integer>
*/
public List<Integer> findMapSheetMngYyyyList() { public List<Integer> findMapSheetMngYyyyList() {
List<Integer> foundContent = List<Integer> foundContent =
@@ -245,6 +250,12 @@ public class MapSheetMngRepositoryImpl extends QuerydslRepositorySupport
return foundContent; return foundContent;
} }
/**
* tb_year 에 해당 년도 완료로 업데이트
*
* @param yyyy
* @param status
*/
public void updateYearState(int yyyy, String status) { public void updateYearState(int yyyy, String status) {
long execCount = long execCount =
queryFactory queryFactory
@@ -254,6 +265,12 @@ public class MapSheetMngRepositoryImpl extends QuerydslRepositorySupport
.execute(); .execute();
} }
/**
* 영상데이터관리 > 상세 조회
*
* @param mngYyyy
* @return
*/
public MapSheetMngDto.MngDto findMapSheetMng(int mngYyyy) { public MapSheetMngDto.MngDto findMapSheetMng(int mngYyyy) {
BooleanBuilder whereBuilder = new BooleanBuilder(); BooleanBuilder whereBuilder = new BooleanBuilder();
@@ -399,6 +416,12 @@ public class MapSheetMngRepositoryImpl extends QuerydslRepositorySupport
return foundContent; return foundContent;
} }
/**
* 영상데이터관리 > 상세 > 오류 처리 내역
*
* @param searchReq 오류 검색 조건
* @return
*/
@Override @Override
public Page<MapSheetMngDto.ErrorDataDto> findMapSheetErrorList( public Page<MapSheetMngDto.ErrorDataDto> findMapSheetErrorList(
MapSheetMngDto.@Valid ErrorSearchReq searchReq) { MapSheetMngDto.@Valid ErrorSearchReq searchReq) {
@@ -407,6 +430,10 @@ public class MapSheetMngRepositoryImpl extends QuerydslRepositorySupport
BooleanBuilder whereBuilder = new BooleanBuilder(); BooleanBuilder whereBuilder = new BooleanBuilder();
whereBuilder.and(mapSheetMngHstEntity.mngYyyy.eq(searchReq.getMngYyyy())); whereBuilder.and(mapSheetMngHstEntity.mngYyyy.eq(searchReq.getMngYyyy()));
// syncState : 동기화 상태
// hst 테이블의 syncState 가 DONE, NOTYET 이 아닌 것 -> 오류
// (오류인 상태 SyncStateType : DUPLICATE, NOFILE, NOTPAIR, TYPEERROR)
whereBuilder.and( whereBuilder.and(
mapSheetMngHstEntity.syncState.ne("DONE").and(mapSheetMngHstEntity.syncState.ne("NOTYET"))); mapSheetMngHstEntity.syncState.ne("DONE").and(mapSheetMngHstEntity.syncState.ne("NOTYET")));
@@ -414,10 +441,12 @@ public class MapSheetMngRepositoryImpl extends QuerydslRepositorySupport
whereBuilder.and(mapSheetMngHstEntity.syncState.eq(searchReq.getSyncState())); whereBuilder.and(mapSheetMngHstEntity.syncState.eq(searchReq.getSyncState()));
} }
// syncCheckState : 동기화 이후, 에러 처리 상태 - 처리 DONE / 미처리 NOTYET
if (searchReq.getSyncCheckState() != null && !searchReq.getSyncCheckState().isEmpty()) { if (searchReq.getSyncCheckState() != null && !searchReq.getSyncCheckState().isEmpty()) {
whereBuilder.and(mapSheetMngHstEntity.syncCheckState.eq(searchReq.getSyncCheckState())); whereBuilder.and(mapSheetMngHstEntity.syncCheckState.eq(searchReq.getSyncCheckState()));
} }
// 도엽 검색 조건 있을 때 whereBuilder에 추가
if (searchReq.getSearchValue() != null && !searchReq.getSearchValue().isEmpty()) { if (searchReq.getSearchValue() != null && !searchReq.getSearchValue().isEmpty()) {
whereBuilder.and( whereBuilder.and(
mapSheetMngHstEntity mapSheetMngHstEntity
@@ -475,7 +504,8 @@ public class MapSheetMngRepositoryImpl extends QuerydslRepositorySupport
mapSheetMngHstEntity mapSheetMngHstEntity
.mapSheetNum .mapSheetNum
.eq(mapInkx5kEntity.mapidcdNo) .eq(mapInkx5kEntity.mapidcdNo)
.and(mapInkx5kEntity.useInference.eq(CommonUseStatus.USE))) .and(
mapInkx5kEntity.useInference.eq(CommonUseStatus.USE))) // 도엽 사용인 것만 오류 내역 표기
.where(whereBuilder) .where(whereBuilder)
.orderBy(mapSheetMngHstEntity.createdDate.desc()) .orderBy(mapSheetMngHstEntity.createdDate.desc())
.offset(pageable.getOffset()) .offset(pageable.getOffset())
@@ -491,13 +521,20 @@ public class MapSheetMngRepositoryImpl extends QuerydslRepositorySupport
mapSheetMngHstEntity mapSheetMngHstEntity
.mapSheetNum .mapSheetNum
.eq(mapInkx5kEntity.mapidcdNo) .eq(mapInkx5kEntity.mapidcdNo)
.and(mapInkx5kEntity.useInference.eq(CommonUseStatus.USE))) .and(
mapInkx5kEntity.useInference.eq(CommonUseStatus.USE))) // 도엽 사용인 것만 오류 내역 표기
.where(whereBuilder) .where(whereBuilder)
.fetchOne(); .fetchOne();
return new PageImpl<>(foundContent, pageable, countQuery); return new PageImpl<>(foundContent, pageable, countQuery);
} }
/**
* hst 테이블에 선택한 hstUid row 정보 조회
*
* @param hstUid
* @return
*/
@Override @Override
public MapSheetMngDto.ErrorDataDto findMapSheetError(Long hstUid) { public MapSheetMngDto.ErrorDataDto findMapSheetError(Long hstUid) {
@@ -680,6 +717,11 @@ public class MapSheetMngRepositoryImpl extends QuerydslRepositorySupport
.execute(); .execute();
} }
/**
* 같은 년도로 저장된 데이터가 있다면 삭제
*
* @param mngYyyy
*/
@Override @Override
public void deleteByMngYyyyMngAll(int mngYyyy) { public void deleteByMngYyyyMngAll(int mngYyyy) {
@@ -700,6 +742,19 @@ public class MapSheetMngRepositoryImpl extends QuerydslRepositorySupport
.delete(mapSheetMngEntity) .delete(mapSheetMngEntity)
.where(mapSheetMngEntity.mngYyyy.eq(mngYyyy)) .where(mapSheetMngEntity.mngYyyy.eq(mngYyyy))
.execute(); .execute();
long deletedTileCount =
queryFactory
.delete(mapSheetMngTileEntity)
.where(mapSheetMngTileEntity.mngYyyy.eq(mngYyyy))
.execute();
long updateNotYetCount =
queryFactory
.update(yearEntity)
.set(yearEntity.status, "NOTYET")
.where(yearEntity.yyyy.eq(mngYyyy))
.execute();
} }
@Override @Override
@@ -830,6 +885,13 @@ public class MapSheetMngRepositoryImpl extends QuerydslRepositorySupport
.execute(); .execute();
} }
/**
* 5k 도엽 기준으로 tb_map_sheet_hst 테이블에 먼저 insert 하기
*
* @param mngYyyy
* @param mngPath
* @return
*/
@Override @Override
public int insertMapSheetOrgDataToMapSheetMngHst(int mngYyyy, String mngPath) { public int insertMapSheetOrgDataToMapSheetMngHst(int mngYyyy, String mngPath) {
@@ -927,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()) {
@@ -966,6 +1029,11 @@ public class MapSheetMngRepositoryImpl extends QuerydslRepositorySupport
.execute(); .execute();
} }
/**
* 년도별 Tile 정보 등록
*
* @param addReq
*/
@Override @Override
public void insertMapSheetMngTile(AddReq addReq) { public void insertMapSheetMngTile(AddReq addReq) {
long execute = long execute =
@@ -1022,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
@@ -1035,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(
@@ -1084,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();
} }
@@ -1116,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인 행만 남김
@@ -1133,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

@@ -39,4 +39,12 @@ public interface ModelMngRepositoryCustom {
Optional<ModelMngEntity> findByModelId(Long id); Optional<ModelMngEntity> findByModelId(Long id);
Optional<ModelMngEntity> findByModelId(UUID id); Optional<ModelMngEntity> findByModelId(UUID id);
/**
* 버전명으로 모델 조회
*
* @param ver 모델버전
* @return 모델정보
*/
Optional<ModelMngEntity> findByModelVer(String ver);
} }

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 =
@@ -246,4 +246,14 @@ public class ModelMngRepositoryImpl extends QuerydslRepositorySupport
return Optional.ofNullable( return Optional.ofNullable(
queryFactory.selectFrom(modelMngEntity).where(modelMngEntity.uuid.eq(uuid)).fetchOne()); queryFactory.selectFrom(modelMngEntity).where(modelMngEntity.uuid.eq(uuid)).fetchOne());
} }
@Override
public Optional<ModelMngEntity> findByModelVer(String ver) {
return Optional.ofNullable(
queryFactory
.selectFrom(modelMngEntity)
.where(modelMngEntity.modelVer.eq(ver))
.limit(1)
.fetchOne());
}
} }

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;
} }

Some files were not shown because too many files have changed in this diff Show More