Compare commits
177 Commits
feat/dean/
...
develop
| Author | SHA1 | Date | |
|---|---|---|---|
| 0425a6486d | |||
| 241c7222d1 | |||
| 71e4ab14bd | |||
| 52da4dafc3 | |||
| 24d615174d | |||
| 12b0f0867d | |||
| 283d906da6 | |||
| 360b451c38 | |||
| 80e281cb99 | |||
| b07bc38ee8 | |||
| e4c1c76b2b | |||
| 01b64eeca7 | |||
| 516f949a37 | |||
| b6ed3b57ef | |||
| 9eebf23774 | |||
| 4f742edf8b | |||
| 0aa415cf3a | |||
| 884b635585 | |||
| 6861f6b8b6 | |||
| a97af0d4dd | |||
| 9297d19e24 | |||
| 65c38b3083 | |||
| 24dca652f0 | |||
| 193cd449a8 | |||
|
|
0efde4e5bb | ||
|
|
548d82da12 | ||
| 536ff8fc65 | |||
| 1dc1ce741e | |||
| d21ed61666 | |||
| c9a1007c21 | |||
| 413afb0b7c | |||
| e69eccc82b | |||
| 828a4c5dca | |||
| 5d417d85ff | |||
| 614d6da695 | |||
| 8d45e91982 | |||
| e1febf5863 | |||
| 97fb659f15 | |||
| ebb48c3f57 | |||
| a35b4b8f59 | |||
| 0a53e186dc | |||
| f3c7c5e8e8 | |||
| c505e9b740 | |||
| 8c2f2eff1b | |||
| ade8bfa76a | |||
| 3752b83292 | |||
| 9f31f661fc | |||
| b477928261 | |||
| f4f75f353c | |||
| f977e4be7c | |||
| 573da5b53a | |||
| bd72852556 | |||
| e4b904606f | |||
| 0d14dafecc | |||
| 37f534abff | |||
| 3521a5fd3d | |||
| cbae052338 | |||
| b2c9c36d4c | |||
| 114088469e | |||
| 7d6dca8b24 | |||
| 2e7ad26528 | |||
| 0353e172ed | |||
| 1d5b1343a9 | |||
| 65f9026922 | |||
| 9b79f31d7b | |||
| de45bf47c5 | |||
| a413de4b93 | |||
| 815675f112 | |||
| b9f7e36175 | |||
| 855aca6e5a | |||
| 206dba6ff9 | |||
| 5db9127f0c | |||
| 132bad8c33 | |||
| 6dde4cd891 | |||
| ac248c2f30 | |||
| 15d082af0e | |||
| 3be536424a | |||
| a3b2fd0c73 | |||
| 9b504396bc | |||
| 2cc490012e | |||
| f99144eccc | |||
| 66b78022a9 | |||
| 199e302068 | |||
| 5a3370d5fb | |||
| 5d7cb18fb8 | |||
| 57baf4d911 | |||
| 2fee2143ba | |||
| f4a410ba49 | |||
| deba2b84f0 | |||
| 85d8efc493 | |||
| 5a82a9507a | |||
| b9a268d43a | |||
| 4d1ea53d8f | |||
| 278d5d20e8 | |||
| 4d9fa2d6a5 | |||
| e28e5ba371 | |||
| 22cc996399 | |||
| e32f5e33d9 | |||
| f95b82bf7f | |||
| 060a0db126 | |||
| 0692456b5b | |||
| 8fb9f89c8f | |||
| 1cfe6e923f | |||
| af2721949c | |||
| e9b9075c72 | |||
| 2b28241110 | |||
| 8e002f5673 | |||
| 972cf808a9 | |||
| 086eb20e8d | |||
| c3c194c067 | |||
| 0acaeaac09 | |||
| 53ce735bca | |||
| a341be7ed6 | |||
| 9f6dc2b3c6 | |||
| cc46315e3a | |||
| ca631d5d58 | |||
| 5d0590bd3c | |||
| 3aed3cf1ec | |||
| 396e76c362 | |||
| 9f379c6dc3 | |||
| d069981c8f | |||
| ece70f1d68 | |||
| b7470d11d4 | |||
| a7108c44f4 | |||
| 5039dd0f51 | |||
| 9cb3a100aa | |||
| dc42baf91a | |||
| 9d36208845 | |||
| ae4c1c61e8 | |||
| 369f303f6c | |||
| 96d4bb4af3 | |||
| 7b55204ae1 | |||
| f54655c191 | |||
| f4cbb48aa2 | |||
| fbad8d1cd3 | |||
| 6e4682dad6 | |||
| 4629715443 | |||
| 5ba7f9ccfc | |||
| 264dae3ba9 | |||
| fab5b02211 | |||
| 38f70b933f | |||
| a4b1db462b | |||
| 2bf7c42a3f | |||
| a1be4e9faf | |||
| 8904de0e3d | |||
| a21df9d018 | |||
| 85cad2dd28 | |||
| 7b8bf8726b | |||
| c841d460aa | |||
| 3a8ac3a24f | |||
| 046f4f06d3 | |||
| 5c9f33d210 | |||
| ea7e98d28e | |||
| 3e78f744a4 | |||
| cea1f01ed9 | |||
| d7f2d22b93 | |||
| eccdfb17e6 | |||
| d2fa86a89f | |||
| 32d56cf8fe | |||
| c3b7daebb7 | |||
| 2188d426d4 | |||
| 5c2ee0974b | |||
| 7980fe1d42 | |||
| c10141e915 | |||
| 97565c5369 | |||
| ba562261c3 | |||
| a084c80715 | |||
| a44e93c234 | |||
| 7b15e5bb8c | |||
| 001ad73de7 | |||
| cefacb291b | |||
| 744cbb55a9 | |||
| 4863091406 | |||
| 70c28e0b54 | |||
| 9197819340 | |||
| f2500c33e6 | |||
| 18dc831b05 |
6
.gitignore
vendored
6
.gitignore
vendored
@@ -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
47
inference-table-index.sh
Normal 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 "모든 인덱스 시도 완료"
|
||||||
@@ -19,9 +19,9 @@ 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"
|
||||||
"/api/auth/signin", "/api/auth/refresh", "/api/auth/logout"
|
|
||||||
};
|
};
|
||||||
private final JwtTokenProvider jwtTokenProvider;
|
private final JwtTokenProvider jwtTokenProvider;
|
||||||
private final UserDetailsService userDetailsService;
|
private final UserDetailsService userDetailsService;
|
||||||
@@ -31,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());
|
||||||
@@ -58,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 ")) {
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
package com.kamco.cd.kamcoback.changedetection.dto;
|
package com.kamco.cd.kamcoback.changedetection.dto;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
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 io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
@@ -143,6 +146,23 @@ public class ChangeDetectionDto {
|
|||||||
private String mapSheetNum;
|
private String mapSheetNum;
|
||||||
private String mapSheetName;
|
private String mapSheetName;
|
||||||
private String alias;
|
private String alias;
|
||||||
|
@JsonIgnore private String bboxStr;
|
||||||
|
private JsonNode bbox;
|
||||||
|
|
||||||
|
public MapSheetList(String mapSheetNum, String mapSheetName, String alias, String bboxStr) {
|
||||||
|
this.mapSheetNum = mapSheetNum;
|
||||||
|
this.mapSheetName = mapSheetName;
|
||||||
|
this.alias = alias;
|
||||||
|
|
||||||
|
if (bboxStr != null) {
|
||||||
|
ObjectMapper mapper = new ObjectMapper();
|
||||||
|
try {
|
||||||
|
this.bbox = mapper.readTree(bboxStr);
|
||||||
|
} catch (JsonProcessingException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Schema(name = "PolygonFeatureList", description = "Geometry 리턴 객체")
|
@Schema(name = "PolygonFeatureList", description = "Geometry 리턴 객체")
|
||||||
@@ -262,6 +282,7 @@ public class ChangeDetectionDto {
|
|||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
public static class ChangeDetectionMapDto {
|
public static class ChangeDetectionMapDto {
|
||||||
|
|
||||||
private Integer compareYyyy;
|
private Integer compareYyyy;
|
||||||
private Integer targetYyyy;
|
private Integer targetYyyy;
|
||||||
private String cdObjectId;
|
private String cdObjectId;
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -5,7 +5,9 @@ import com.kamco.cd.kamcoback.common.download.dto.DownloadAuditEvent;
|
|||||||
import com.kamco.cd.kamcoback.menu.dto.MenuDto;
|
import com.kamco.cd.kamcoback.menu.dto.MenuDto;
|
||||||
import com.kamco.cd.kamcoback.menu.service.MenuService;
|
import com.kamco.cd.kamcoback.menu.service.MenuService;
|
||||||
import com.kamco.cd.kamcoback.postgres.entity.AuditLogEntity;
|
import com.kamco.cd.kamcoback.postgres.entity.AuditLogEntity;
|
||||||
|
import com.kamco.cd.kamcoback.postgres.entity.MemberEntity;
|
||||||
import com.kamco.cd.kamcoback.postgres.repository.log.AuditLogRepository;
|
import com.kamco.cd.kamcoback.postgres.repository.log.AuditLogRepository;
|
||||||
|
import com.kamco.cd.kamcoback.postgres.repository.members.MembersRepository;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -24,6 +26,7 @@ import org.springframework.transaction.annotation.Transactional;
|
|||||||
public class DownloadAuditEventListener {
|
public class DownloadAuditEventListener {
|
||||||
|
|
||||||
private final AuditLogRepository auditLogRepository;
|
private final AuditLogRepository auditLogRepository;
|
||||||
|
private final MembersRepository membersRepository;
|
||||||
private final MenuService menuService;
|
private final MenuService menuService;
|
||||||
private final ObjectMapper objectMapper;
|
private final ObjectMapper objectMapper;
|
||||||
|
|
||||||
@@ -42,9 +45,23 @@ public class DownloadAuditEventListener {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Long userId = ev.userId();
|
||||||
|
|
||||||
|
if (userId == null) {
|
||||||
|
// a 링크로 들어온 download는 사번으로 파라미터가 전달 되므로 사번으로 user id 조회 하기
|
||||||
|
MemberEntity memberEntity =
|
||||||
|
membersRepository.findByEmployeeNo(ev.employeeNo()).orElse(null);
|
||||||
|
|
||||||
|
if (memberEntity == null) {
|
||||||
|
return; // 매핑 실패 시 로그 저장 안 함
|
||||||
|
}
|
||||||
|
|
||||||
|
userId = memberEntity.getId();
|
||||||
|
}
|
||||||
|
|
||||||
AuditLogEntity logEntity =
|
AuditLogEntity logEntity =
|
||||||
AuditLogEntity.forFileDownload(
|
AuditLogEntity.forFileDownload(
|
||||||
ev.userId(), ev.requestUri(), menuUid, ev.ip(), ev.status(), ev.downloadUuid());
|
userId, ev.requestUri(), menuUid, ev.ip(), ev.status(), ev.downloadUuid());
|
||||||
|
|
||||||
auditLogRepository.save(logEntity);
|
auditLogRepository.save(logEntity);
|
||||||
|
|
||||||
|
|||||||
@@ -14,23 +14,56 @@ import org.springframework.http.MediaType;
|
|||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Range(부분 다운로드) 지원 파일 다운로드 응답 생성기.
|
||||||
|
*
|
||||||
|
* <p>브라우저/다운로드 매니저가 Range 헤더를 보내면 206 Partial Content로 일부 구간만 내려주고, Range 헤더가 없으면 200 OK로 전체 파일을
|
||||||
|
* 내려준다.
|
||||||
|
*
|
||||||
|
* <p>대용량 ZIP(또는 바이너리) 파일 다운로드 시: - 메모리에 파일 전체를 올리지 않고(Resource/FileSystemResource 스트리밍) -
|
||||||
|
* 이어받기(Resume) 및 병렬 다운로드(일부 클라이언트) 지원 - 잘못된 Range에 대해 416 Range Not Satisfiable 처리
|
||||||
|
*/
|
||||||
@Component
|
@Component
|
||||||
public class RangeDownloadResponder {
|
public class RangeDownloadResponder {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ZIP(또는 바이너리) 파일 다운로드 응답을 생성한다.
|
||||||
|
*
|
||||||
|
* @param filePath 실제 서버 파일 경로
|
||||||
|
* @param downloadFileName 사용자에게 노출될 다운로드 파일명
|
||||||
|
* @param request Range 헤더 확인용 HttpServletRequest
|
||||||
|
* @return Range 유무에 따라 200(전체) 또는 206(부분) ResponseEntity 반환
|
||||||
|
* @throws IOException 파일 접근/조회 실패 시
|
||||||
|
*/
|
||||||
public ResponseEntity<?> buildZipResponse(
|
public ResponseEntity<?> buildZipResponse(
|
||||||
Path filePath, String downloadFileName, HttpServletRequest request) throws IOException {
|
Path filePath, String downloadFileName, HttpServletRequest request) throws IOException {
|
||||||
|
|
||||||
|
// 1) 파일 존재/정상 파일 여부 확인
|
||||||
|
// - 일반 파일(regular file)이 아니면 404 반환 (디렉토리/없는 파일/특수 파일 등 방지)
|
||||||
if (!Files.isRegularFile(filePath)) {
|
if (!Files.isRegularFile(filePath)) {
|
||||||
return ResponseEntity.notFound().build();
|
return ResponseEntity.notFound().build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 2) 파일 전체 크기 및 Spring Resource 래핑
|
||||||
|
// - Files.size: 전체 파일 크기(Content-Range 계산/검증에 필요)
|
||||||
|
// - FileSystemResource: 스프링이 스트리밍 형태로 파일을 응답 바디로 내려줄 수 있게 함
|
||||||
long totalSize = Files.size(filePath);
|
long totalSize = Files.size(filePath);
|
||||||
Resource resource = new FileSystemResource(filePath);
|
Resource resource = new FileSystemResource(filePath);
|
||||||
|
|
||||||
|
// 3) 다운로드 강제(Content-Disposition)
|
||||||
|
// - attachment; filename="xxx.zip" 형태로 브라우저가 저장 대화상자/다운로드로 처리
|
||||||
String disposition = "attachment; filename=\"" + downloadFileName + "\"";
|
String disposition = "attachment; filename=\"" + downloadFileName + "\"";
|
||||||
|
|
||||||
|
// 4) Range 헤더 조회
|
||||||
|
// - Range: bytes=0-1023 (일부 구간 요청)
|
||||||
|
// - Range가 없으면 전체 다운로드로 처리
|
||||||
String rangeHeader = request.getHeader(HttpHeaders.RANGE);
|
String rangeHeader = request.getHeader(HttpHeaders.RANGE);
|
||||||
|
|
||||||
// 🔥 공통 헤더 (여기 고정)
|
// 5) 공통 헤더(전체/부분 다운로드 공통으로 넣을 것)
|
||||||
|
// - Content-Type: 바이너리(필요시 application/zip 으로 바꿔도 됨)
|
||||||
|
// - Content-Disposition: 다운로드 강제
|
||||||
|
// - Accept-Ranges: bytes -> 서버가 Range(이어받기/부분요청) 지원함을 알림
|
||||||
|
// - X-Accel-Buffering: no -> Nginx 사용 시 버퍼링 비활성화(스트리밍/대용량에 유리)
|
||||||
ResponseEntity.BodyBuilder base =
|
ResponseEntity.BodyBuilder base =
|
||||||
ResponseEntity.ok()
|
ResponseEntity.ok()
|
||||||
.contentType(MediaType.APPLICATION_OCTET_STREAM)
|
.contentType(MediaType.APPLICATION_OCTET_STREAM)
|
||||||
@@ -38,10 +71,15 @@ public class RangeDownloadResponder {
|
|||||||
.header(HttpHeaders.ACCEPT_RANGES, "bytes")
|
.header(HttpHeaders.ACCEPT_RANGES, "bytes")
|
||||||
.header("X-Accel-Buffering", "no");
|
.header("X-Accel-Buffering", "no");
|
||||||
|
|
||||||
|
// 6) Range 헤더가 없으면 전체 파일 반환 (200 OK)
|
||||||
if (rangeHeader == null || rangeHeader.isBlank()) {
|
if (rangeHeader == null || rangeHeader.isBlank()) {
|
||||||
|
// Content-Length를 전체 크기로 설정하고 Resource를 그대로 바디에 담아 스트리밍
|
||||||
return base.contentLength(totalSize).body(resource);
|
return base.contentLength(totalSize).body(resource);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 7) Range 헤더 파싱
|
||||||
|
// - 잘못된 Range 헤더 형식이면 parseRanges에서 IllegalArgumentException 발생 가능
|
||||||
|
// - RFC에 따라 416 응답 + Content-Range: bytes */{total} 형태로 알려줌
|
||||||
List<HttpRange> ranges;
|
List<HttpRange> ranges;
|
||||||
try {
|
try {
|
||||||
ranges = HttpRange.parseRanges(rangeHeader);
|
ranges = HttpRange.parseRanges(rangeHeader);
|
||||||
@@ -52,11 +90,18 @@ public class RangeDownloadResponder {
|
|||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 8) 다중 Range 요청이 와도(예: bytes=0-99,200-299) 여기서는 첫 번째 Range만 처리
|
||||||
|
// - 대부분 브라우저는 단일 Range를 사용
|
||||||
|
// - 멀티파트/byteranges 처리를 하려면 별도 구현 필요
|
||||||
HttpRange range = ranges.get(0);
|
HttpRange range = ranges.get(0);
|
||||||
|
|
||||||
|
// 9) 실제 시작/끝 범위 계산
|
||||||
|
// - bytes=500- : end를 파일 끝으로 해석
|
||||||
|
// - bytes=-500 : 마지막 500바이트로 해석
|
||||||
long start = range.getRangeStart(totalSize);
|
long start = range.getRangeStart(totalSize);
|
||||||
long end = range.getRangeEnd(totalSize);
|
long end = range.getRangeEnd(totalSize);
|
||||||
|
|
||||||
|
// 10) 시작점이 파일 크기 이상이면 만족 불가 -> 416
|
||||||
if (start >= totalSize) {
|
if (start >= totalSize) {
|
||||||
return ResponseEntity.status(416)
|
return ResponseEntity.status(416)
|
||||||
.header(HttpHeaders.CONTENT_RANGE, "bytes */" + totalSize)
|
.header(HttpHeaders.CONTENT_RANGE, "bytes */" + totalSize)
|
||||||
@@ -64,9 +109,18 @@ public class RangeDownloadResponder {
|
|||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 11) 요청 구간 길이 계산
|
||||||
|
// - end/start는 inclusive이므로 +1 필요
|
||||||
long regionLength = end - start + 1;
|
long regionLength = end - start + 1;
|
||||||
|
|
||||||
|
// 12) ResourceRegion 생성
|
||||||
|
// - resource의 start부터 regionLength 만큼만 응답으로 내려줄 수 있게 함
|
||||||
|
// - 파일 전체를 메모리에 올리지 않고 필요한 부분만 스트리밍
|
||||||
ResourceRegion region = new ResourceRegion(resource, start, regionLength);
|
ResourceRegion region = new ResourceRegion(resource, start, regionLength);
|
||||||
|
|
||||||
|
// 13) 206 Partial Content로 응답 구성
|
||||||
|
// - Content-Range: bytes start-end/total
|
||||||
|
// - Content-Length: regionLength(부분 크기)
|
||||||
return ResponseEntity.status(206)
|
return ResponseEntity.status(206)
|
||||||
.contentType(MediaType.APPLICATION_OCTET_STREAM)
|
.contentType(MediaType.APPLICATION_OCTET_STREAM)
|
||||||
.header(HttpHeaders.CONTENT_DISPOSITION, disposition)
|
.header(HttpHeaders.CONTENT_DISPOSITION, disposition)
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import java.util.UUID;
|
|||||||
|
|
||||||
public record DownloadAuditEvent(
|
public record DownloadAuditEvent(
|
||||||
Long userId,
|
Long userId,
|
||||||
|
String employeeNo,
|
||||||
String requestUri,
|
String requestUri,
|
||||||
String normalizedUri,
|
String normalizedUri,
|
||||||
String ip,
|
String ip,
|
||||||
|
|||||||
@@ -11,7 +11,8 @@ import lombok.Getter;
|
|||||||
public enum CrsType implements EnumType {
|
public enum CrsType implements EnumType {
|
||||||
EPSG_3857("Web Mercator, 웹지도 미터(EPSG:900913 동일)"),
|
EPSG_3857("Web Mercator, 웹지도 미터(EPSG:900913 동일)"),
|
||||||
EPSG_4326("WGS84 위경도, GeoJSON/OSM 기본"),
|
EPSG_4326("WGS84 위경도, GeoJSON/OSM 기본"),
|
||||||
EPSG_5186("Korea 2000 중부 TM, 한국 SHP");
|
EPSG_5186("5186::Korea 2000 중부 TM, 한국 SHP"),
|
||||||
|
EPSG_5179("5179::Korea 2000 중부 TM, 한국 SHP");
|
||||||
|
|
||||||
private final String desc;
|
private final String desc;
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,9 @@ public enum LayerType implements EnumType {
|
|||||||
TILE("배경지도"),
|
TILE("배경지도"),
|
||||||
GEOJSON("객체데이터"),
|
GEOJSON("객체데이터"),
|
||||||
WMTS("타일레이어"),
|
WMTS("타일레이어"),
|
||||||
WMS("지적도");
|
WMS("지적도"),
|
||||||
|
KAMCO_WMS("국유인WMS"),
|
||||||
|
KAMCO_WMTS("국유인WMTS");
|
||||||
|
|
||||||
private final String desc;
|
private final String desc;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,111 @@
|
|||||||
|
package com.kamco.cd.kamcoback.common.inference.service;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
|
import com.fasterxml.jackson.core.type.TypeReference;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.kamco.cd.kamcoback.common.exception.CustomApiException;
|
||||||
|
import com.kamco.cd.kamcoback.config.resttemplate.ExternalHttpClient;
|
||||||
|
import com.kamco.cd.kamcoback.config.resttemplate.ExternalHttpClient.ExternalCallResult;
|
||||||
|
import com.kamco.cd.kamcoback.inference.dto.InferenceSendDto;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.log4j.Log4j2;
|
||||||
|
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.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@Log4j2
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class InferenceCommonService {
|
||||||
|
|
||||||
|
@Value("${spring.profiles.active}")
|
||||||
|
private String profile;
|
||||||
|
|
||||||
|
@Value("${inference.url}")
|
||||||
|
private String inferenceUrl;
|
||||||
|
|
||||||
|
private final ObjectMapper objectMapper;
|
||||||
|
private final ExternalHttpClient externalHttpClient;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 추론 AI API 호출 batch id를 리턴
|
||||||
|
*
|
||||||
|
* @param dto
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public Long ensureAccepted(InferenceSendDto dto) {
|
||||||
|
|
||||||
|
if (dto == null) {
|
||||||
|
log.warn("not InferenceSendDto dto");
|
||||||
|
throw new CustomApiException("BAD_REQUEST", HttpStatus.BAD_REQUEST);
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("");
|
||||||
|
log.info("========================================================");
|
||||||
|
log.info("[SEND INFERENCE] Inference request dto= {}", dto);
|
||||||
|
log.info("========================================================");
|
||||||
|
log.info("");
|
||||||
|
|
||||||
|
// 1) 요청 로그
|
||||||
|
try {
|
||||||
|
log.debug("Inference request dto={}", objectMapper.writeValueAsString(dto));
|
||||||
|
} catch (JsonProcessingException e) {
|
||||||
|
log.warn("Failed to serialize inference dto", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2) local 환경 임시 처리
|
||||||
|
// if ("local".equals(profile)) {
|
||||||
|
// if (dto.getPred_requests_areas() == null) {
|
||||||
|
// throw new IllegalStateException("pred_requests_areas is null");
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// dto.getPred_requests_areas().setInput1_scene_path("/kamco-nfs/requests/2023_local.geojson");
|
||||||
|
//
|
||||||
|
// dto.getPred_requests_areas().setInput2_scene_path("/kamco-nfs/requests/2024_local.geojson");
|
||||||
|
// }
|
||||||
|
|
||||||
|
// 3) HTTP 호출
|
||||||
|
HttpHeaders headers = new HttpHeaders();
|
||||||
|
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||||
|
headers.setAccept(List.of(MediaType.APPLICATION_JSON));
|
||||||
|
|
||||||
|
// 4) 추론 실행 API 호출
|
||||||
|
ExternalCallResult<String> result =
|
||||||
|
externalHttpClient.callLong(inferenceUrl, HttpMethod.POST, dto, headers, String.class);
|
||||||
|
|
||||||
|
if (result.statusCode() < 200 || result.statusCode() >= 300) {
|
||||||
|
log.error("Inference API failed. status={}, body={}", result.statusCode(), result.body());
|
||||||
|
throw new CustomApiException("BAD_GATEWAY", HttpStatus.BAD_GATEWAY);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5) 응답 파싱
|
||||||
|
try {
|
||||||
|
List<Map<String, Object>> list =
|
||||||
|
objectMapper.readValue(result.body(), new TypeReference<>() {});
|
||||||
|
|
||||||
|
if (list.isEmpty()) {
|
||||||
|
throw new CustomApiException(
|
||||||
|
"NOT_FOUND", HttpStatus.NOT_FOUND, "Inference response is empty");
|
||||||
|
}
|
||||||
|
|
||||||
|
Object batchIdObj = list.get(0).get("batch_id");
|
||||||
|
if (batchIdObj == null) {
|
||||||
|
throw new CustomApiException(
|
||||||
|
"NOT_FOUND", HttpStatus.NOT_FOUND, "batch_id not found in response");
|
||||||
|
}
|
||||||
|
|
||||||
|
return Long.valueOf(batchIdObj.toString());
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Failed to parse inference response. body={}", result.body(), e);
|
||||||
|
throw new CustomApiException("INVALID_INFERENCE_RESPONSE", HttpStatus.BAD_GATEWAY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,274 @@
|
|||||||
|
package com.kamco.cd.kamcoback.common.inference.utils;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
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 org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.web.server.ResponseStatusException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GeoJSON 파일의 "features[].properties.scene_id" 값들이 "요청한 도엽번호 목록(requestedMapSheetNums)"과 정확히 일치하는지
|
||||||
|
* 검증하는 유틸.
|
||||||
|
*
|
||||||
|
* <p>핵심 목적: - 요청한 도엽번호를 기반으로 GeoJSON을 생성했는데, 실제 결과 파일에 누락/추가/중복/빈값(scene_id 없음) 등이 발생했는지 빠르게 잡아내기.
|
||||||
|
*
|
||||||
|
* <p>검증 실패 시: - 404: 파일 자체가 없음 - 400: 파일이 비어있거나(0 byte), features 구조가 이상하거나, 요청 목록이 비어있음 - 500: 파일
|
||||||
|
* IO/파싱 자체가 실패(읽기 실패 등) - 422: 정합성(요청 vs 결과)이 맞지 않음 (누락/추가/중복/빈 scene_id 존재)
|
||||||
|
*/
|
||||||
|
public class GeoJsonValidator {
|
||||||
|
|
||||||
|
/** GeoJSON 파싱용 ObjectMapper (정적 1개로 재사용) */
|
||||||
|
private static final ObjectMapper om = new ObjectMapper();
|
||||||
|
|
||||||
|
/** 로그 출력용 */
|
||||||
|
private static final Logger log = LogManager.getLogger(GeoJsonValidator.class);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param geojsonPath GeoJSON 파일 경로(문자열)
|
||||||
|
* @param requestedMapSheetNums "요청한 도엽번호" 리스트 (중복/공백/NULL 포함 가능)
|
||||||
|
* <p>동작 개요: 1) 파일 존재/크기 검증 2) 요청 도엽번호 목록 정리(Trim + 공백 제거 + 중복 제거) 3) GeoJSON 파싱 후 features 배열
|
||||||
|
* 확보 4) features에서 scene_id 추출하여 유니크 set 구성 5) requested vs found 비교: - missing: requested -
|
||||||
|
* found - extra : found - requested - duplicates: GeoJSON 내부에서 scene_id 중복 등장 - nullIdCount:
|
||||||
|
* scene_id가 null/blank 인 feature 개수 6) 이상 있으면 422로 실패 처리
|
||||||
|
*/
|
||||||
|
public static void validateWithRequested(String geojsonPath, List<String> requestedMapSheetNums) {
|
||||||
|
|
||||||
|
// 문자열 경로를 Path로 변환 (Files API 사용 목적)
|
||||||
|
Path path = Path.of(geojsonPath);
|
||||||
|
|
||||||
|
// =========================================================
|
||||||
|
// 1) 파일 기본 검증
|
||||||
|
// - 파일이 존재하는지
|
||||||
|
// - 파일 크기가 0인지(비어있으면 생성 실패/오류 가능성)
|
||||||
|
// =========================================================
|
||||||
|
try {
|
||||||
|
// 파일 존재 여부 체크 (없으면 404)
|
||||||
|
if (!Files.exists(path)) {
|
||||||
|
throw new ResponseStatusException(
|
||||||
|
HttpStatus.NOT_FOUND, "GeoJSON 파일이 존재하지 않습니다: " + geojsonPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 파일 사이즈 체크 (0 byte면 400)
|
||||||
|
if (Files.size(path) == 0) {
|
||||||
|
throw new ResponseStatusException(
|
||||||
|
HttpStatus.BAD_REQUEST, "GeoJSON 파일이 비어있습니다: " + geojsonPath);
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
// 파일 사이즈/상태 확인 중 IO 오류면 서버오류로 처리
|
||||||
|
log.error("GeoJSON 파일 상태 확인 실패: path={}", path, e);
|
||||||
|
throw new ResponseStatusException(
|
||||||
|
HttpStatus.INTERNAL_SERVER_ERROR, "GeoJSON 파일 상태 확인 실패: " + geojsonPath, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========================================================
|
||||||
|
// 2) 요청 도엽 리스트 유효성 검증
|
||||||
|
// - 요청 목록 자체가 null/empty면 검증할 기준이 없으므로 400
|
||||||
|
// =========================================================
|
||||||
|
if (requestedMapSheetNums == null || requestedMapSheetNums.isEmpty()) {
|
||||||
|
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "requestedMapSheetNums 가 비어있습니다.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========================================================
|
||||||
|
// 2-1) 요청 도엽 Set 정리 (중복/공백/NULL 제거)
|
||||||
|
// - null 제거
|
||||||
|
// - trim 적용
|
||||||
|
// - 빈 문자열 제거
|
||||||
|
// - LinkedHashSet 사용: "중복 제거 + 원래 입력 순서 유지"
|
||||||
|
// =========================================================
|
||||||
|
Set<String> requested =
|
||||||
|
requestedMapSheetNums.stream()
|
||||||
|
.filter(Objects::nonNull) // null 제거
|
||||||
|
.map(String::trim) // 앞뒤 공백 제거
|
||||||
|
.filter(s -> !s.isEmpty()) // "" 제거
|
||||||
|
.collect(Collectors.toCollection(LinkedHashSet::new)); // 중복 제거 + 순서 유지
|
||||||
|
|
||||||
|
// 정리 결과가 비어있으면(전부 null/공백)이므로 400
|
||||||
|
if (requested.isEmpty()) {
|
||||||
|
throw new ResponseStatusException(
|
||||||
|
HttpStatus.BAD_REQUEST, "requestedMapSheetNums 가 공백/NULL만 포함합니다.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========================================================
|
||||||
|
// 3) GeoJSON 파싱
|
||||||
|
// 기대 구조:
|
||||||
|
// {
|
||||||
|
// "type": "FeatureCollection",
|
||||||
|
// "features": [ ... ]
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// - features가 없거나 배열이 아니면 "유효하지 않은 GeoJSON" (400)
|
||||||
|
// - 파일 읽기/파싱 IO 문제는 500
|
||||||
|
// - JSON 자체가 깨진 경우는 400
|
||||||
|
// =========================================================
|
||||||
|
final JsonNode features;
|
||||||
|
try {
|
||||||
|
// JSON 파일을 트리 형태로 파싱
|
||||||
|
JsonNode root = om.readTree(path.toFile());
|
||||||
|
|
||||||
|
// GeoJSON FeatureCollection의 핵심은 features 배열
|
||||||
|
features = root.get("features");
|
||||||
|
|
||||||
|
// features가 없거나 배열이 아니면 GeoJSON 구조가 이상한 것
|
||||||
|
if (features == null || !features.isArray()) {
|
||||||
|
throw new ResponseStatusException(
|
||||||
|
HttpStatus.BAD_REQUEST, "유효하지 않은 GeoJSON: features가 없거나 배열이 아닙니다.");
|
||||||
|
}
|
||||||
|
} catch (ResponseStatusException e) {
|
||||||
|
// 위에서 직접 던진 에러는 그대로 전달
|
||||||
|
throw e;
|
||||||
|
} catch (IOException e) {
|
||||||
|
// 읽기/파싱 과정에서 IO 문제가 터지면 서버오류
|
||||||
|
log.error("GeoJSON 파일 읽기/파싱 실패: path={}", path, e);
|
||||||
|
throw new ResponseStatusException(
|
||||||
|
HttpStatus.INTERNAL_SERVER_ERROR, "GeoJSON 파일 읽기/파싱 실패: " + geojsonPath, e);
|
||||||
|
} catch (Exception e) {
|
||||||
|
// JSON 문법 오류/예상치 못한 파싱 오류는 클라이언트 입력/파일 자체 문제로 400 처리
|
||||||
|
log.error("GeoJSON 파싱 오류(비정상 JSON): path={}", path, e);
|
||||||
|
throw new ResponseStatusException(
|
||||||
|
HttpStatus.BAD_REQUEST, "GeoJSON 파싱 오류(비정상 JSON): " + geojsonPath, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========================================================
|
||||||
|
// 4) 검증 로직
|
||||||
|
// - featureCount: 전체 feature 수 (중복 포함)
|
||||||
|
// - foundUnique: GeoJSON에 등장한 유니크 scene_id 집합
|
||||||
|
// - duplicates: GeoJSON 내부에서 scene_id가 중복된 목록(샘플 출력용)
|
||||||
|
// - nullIdCount: scene_id가 없거나 빈 값인 feature 개수
|
||||||
|
// =========================================================
|
||||||
|
int featureCount = features.size();
|
||||||
|
|
||||||
|
// 유니크 scene_id를 담는 Set (중복 판단을 위해 add 결과를 사용)
|
||||||
|
Set<String> foundUnique = new HashSet<>();
|
||||||
|
|
||||||
|
// 중복된 scene_id 목록 (샘플 로그 출력용이라 순서 유지 가능한 LinkedHashSet 사용)
|
||||||
|
Set<String> duplicates = new LinkedHashSet<>();
|
||||||
|
|
||||||
|
// scene_id가 null 또는 blank인 feature의 개수 (데이터 이상)
|
||||||
|
int nullIdCount = 0;
|
||||||
|
|
||||||
|
// ---------------------------------------------------------
|
||||||
|
// features를 돌면서 feature.properties.scene_id를 추출한다.
|
||||||
|
//
|
||||||
|
// 기대 구조(일반적):
|
||||||
|
// features[i] = {
|
||||||
|
// "type": "Feature",
|
||||||
|
// "properties": {
|
||||||
|
// "scene_id": "도엽번호"
|
||||||
|
// },
|
||||||
|
// "geometry": {...}
|
||||||
|
// }
|
||||||
|
// ---------------------------------------------------------
|
||||||
|
for (JsonNode feature : features) {
|
||||||
|
JsonNode props = feature.get("properties");
|
||||||
|
|
||||||
|
// properties가 있고 scene_id가 null이 아니면 텍스트로 읽음
|
||||||
|
// 없으면 null 처리
|
||||||
|
String sceneId =
|
||||||
|
(props != null && props.hasNonNull("scene_id"))
|
||||||
|
? props.get("scene_id").asText().trim()
|
||||||
|
: null;
|
||||||
|
|
||||||
|
// scene_id가 없거나 빈값이면 "정상적으로 도엽번호가 들어오지 않은 feature"로 카운트
|
||||||
|
if (sceneId == null || sceneId.isBlank()) {
|
||||||
|
nullIdCount++; // 도엽번호가 없으면 증가
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// foundUnique.add(sceneId)가 false면 "이미 같은 값이 있었다"는 뜻 => 중복
|
||||||
|
if (!foundUnique.add(sceneId)) {
|
||||||
|
duplicates.add(sceneId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========================================================
|
||||||
|
// 4-1) requested vs found 비교(set 차집합)
|
||||||
|
//
|
||||||
|
// missing = requested - found
|
||||||
|
// : 요청은 했는데 결과 GeoJSON에 없는 도엽번호
|
||||||
|
//
|
||||||
|
// extra = found - requested
|
||||||
|
// : 요청하지 않았는데 결과 GeoJSON에 들어간 도엽번호
|
||||||
|
// =========================================================
|
||||||
|
|
||||||
|
// missing: requested를 복사한 뒤(foundUnique에 있는 값들을 제거) => 남은 것이 누락분
|
||||||
|
Set<String> missing = new LinkedHashSet<>(requested);
|
||||||
|
missing.removeAll(foundUnique);
|
||||||
|
|
||||||
|
// extra: foundUnique를 복사한 뒤(requested에 있는 값들을 제거) => 남은 것이 추가분
|
||||||
|
Set<String> extra = new LinkedHashSet<>(foundUnique);
|
||||||
|
extra.removeAll(requested);
|
||||||
|
|
||||||
|
// =========================================================
|
||||||
|
// 5) 로그 출력
|
||||||
|
// - 운영에서 문제 생겼을 때 "요청 vs 생성 결과"를 한 눈에 보게
|
||||||
|
// - sample 로그는 너무 길어질 수 있으므로 limit 걸어줌
|
||||||
|
// =========================================================
|
||||||
|
log.info(
|
||||||
|
"""
|
||||||
|
===== GeoJSON Validation =====
|
||||||
|
file: {}
|
||||||
|
features(total): {}
|
||||||
|
requested(unique): {}
|
||||||
|
found(unique scene_id): {}
|
||||||
|
scene_id null/blank: {}
|
||||||
|
duplicates(scene_id): {}
|
||||||
|
missing(requested - found): {}
|
||||||
|
extra(found - requested): {}
|
||||||
|
==============================
|
||||||
|
""",
|
||||||
|
geojsonPath,
|
||||||
|
featureCount, // 중복 포함한 전체 feature 수
|
||||||
|
requested.size(), // 요청 도엽 유니크 수
|
||||||
|
foundUnique.size(), // GeoJSON에서 발견된 scene_id 유니크 수
|
||||||
|
nullIdCount, // scene_id가 비어있는 feature 수
|
||||||
|
duplicates.size(), // 중복 scene_id 종류 수
|
||||||
|
missing.size(), // 요청했지만 빠진 도엽 수
|
||||||
|
extra.size()); // 요청하지 않았는데 들어온 도엽 수
|
||||||
|
|
||||||
|
// 중복/누락/추가 항목은 전체를 다 찍으면 로그 폭발하므로 샘플만
|
||||||
|
// if (!duplicates.isEmpty())
|
||||||
|
// log.warn("duplicates sample: {}", duplicates.stream().limit(20).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());
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========================================================
|
||||||
|
// 6) 실패 조건 판정
|
||||||
|
//
|
||||||
|
// 아래 중 하나라도 있으면 "요청 대비 결과 정합성이 깨졌다"로 보고 실패 처리(422):
|
||||||
|
// - missing 존재: 요청했는데 결과에 없음
|
||||||
|
// - extra 존재 : 요청 안했는데 결과에 있음
|
||||||
|
// - duplicates 존재: 동일 도엽이 중복 생성됨
|
||||||
|
// - nullIdCount > 0: scene_id가 비어있는 feature가 있음(데이터 이상)
|
||||||
|
//
|
||||||
|
// 422(Unprocessable Entity):
|
||||||
|
// - 요청 문법은 맞지만(파일은 있고 JSON도 읽힘),
|
||||||
|
// 내용(정합성)이 요구사항을 만족하지 못하는 경우에 적합.
|
||||||
|
// =========================================================
|
||||||
|
if (!missing.isEmpty() || !extra.isEmpty() || !duplicates.isEmpty() || nullIdCount > 0) {
|
||||||
|
throw new ResponseStatusException(
|
||||||
|
HttpStatus.UNPROCESSABLE_ENTITY,
|
||||||
|
String.format(
|
||||||
|
"GeoJSON validation failed: missing=%d, extra=%d, duplicates=%d, nullId=%d",
|
||||||
|
missing.size(), extra.size(), duplicates.size(), nullIdCount));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 모든 조건을 통과하면 정상
|
||||||
|
log.info("GeoJSON validation OK");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import org.springframework.context.ApplicationEventPublisher;
|
|||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import org.springframework.web.servlet.HandlerInterceptor;
|
import org.springframework.web.servlet.HandlerInterceptor;
|
||||||
|
|
||||||
|
/** 파일 다운로드 log 저장 */
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Component
|
@Component
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
@@ -30,9 +31,17 @@ public class FileDownloadInteceptor implements HandlerInterceptor {
|
|||||||
if (request.getDispatcherType() != DispatcherType.REQUEST) return;
|
if (request.getDispatcherType() != DispatcherType.REQUEST) return;
|
||||||
|
|
||||||
Long userId;
|
Long userId;
|
||||||
|
String employeeNo = "";
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
// a 링크 다운로드일경우 userId가 없으므로 전달받은 사번을 넣는다
|
||||||
userId = userUtil.getId();
|
userId = userUtil.getId();
|
||||||
if (userId == null) return; // userId null 불가면 스킵
|
if (userId == null) {
|
||||||
|
employeeNo = request.getParameter("employeeNo");
|
||||||
|
if (employeeNo == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.warn("Download audit userId resolve failed. uri={}, err={}", uri, e.toString());
|
log.warn("Download audit userId resolve failed. uri={}, err={}", uri, e.toString());
|
||||||
return;
|
return;
|
||||||
@@ -48,8 +57,9 @@ public class FileDownloadInteceptor implements HandlerInterceptor {
|
|||||||
return; // downloadUuid null 불가 -> 스킵
|
return; // downloadUuid null 불가 -> 스킵
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// log저장 DownloadAuditEventListener 클래스 호출
|
||||||
publisher.publishEvent(
|
publisher.publishEvent(
|
||||||
new DownloadAuditEvent(userId, uri, normalizedUri, ip, status, downloadUuid));
|
new DownloadAuditEvent(userId, employeeNo, uri, normalizedUri, ip, status, downloadUuid));
|
||||||
}
|
}
|
||||||
|
|
||||||
private UUID extractUuidFromUri(String uri) {
|
private UUID extractUuidFromUri(String uri) {
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
package com.kamco.cd.kamcoback.config;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@Component
|
||||||
|
@ConfigurationProperties(prefix = "file")
|
||||||
|
public class FileProperties {
|
||||||
|
|
||||||
|
private String root;
|
||||||
|
private String nfs;
|
||||||
|
private String syncRootDir;
|
||||||
|
private String syncTmpDir;
|
||||||
|
private String syncFileExtention;
|
||||||
|
private String datasetDir;
|
||||||
|
private String datasetTmpDir;
|
||||||
|
private String modelDir;
|
||||||
|
private String modelTmpDir;
|
||||||
|
private String modelFileExtention;
|
||||||
|
private String ptPath;
|
||||||
|
private String datasetResponse;
|
||||||
|
private TrainingData trainingData;
|
||||||
|
private String outputDir;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
public static class TrainingData {
|
||||||
|
|
||||||
|
private String geojsonDir;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package com.kamco.cd.kamcoback.config;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@Component
|
||||||
|
@ConfigurationProperties(prefix = "inference")
|
||||||
|
public class InferenceProperties {
|
||||||
|
|
||||||
|
private String nfs;
|
||||||
|
private String url;
|
||||||
|
private String batchUrl;
|
||||||
|
private String geojsonDir;
|
||||||
|
private String jarPath;
|
||||||
|
// 0312
|
||||||
|
private String jarPathV2;
|
||||||
|
private String inferenceServerName;
|
||||||
|
}
|
||||||
@@ -71,13 +71,17 @@ 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()
|
||||||
|
|
||||||
// 다운로드는 인증 필요
|
// 다운로드는 인증 필요
|
||||||
.requestMatchers(HttpMethod.GET, DownloadPaths.PATTERNS)
|
.requestMatchers(HttpMethod.GET, DownloadPaths.PATTERNS)
|
||||||
.authenticated()
|
.permitAll()
|
||||||
|
|
||||||
// 메뉴 등록 ADMIN만 가능
|
// 메뉴 등록 ADMIN만 가능
|
||||||
.requestMatchers(HttpMethod.POST, "/api/menu/auth")
|
.requestMatchers(HttpMethod.POST, "/api/menu/auth")
|
||||||
@@ -102,6 +106,7 @@ public class SecurityConfig {
|
|||||||
"/api/upload/file-chunk-upload",
|
"/api/upload/file-chunk-upload",
|
||||||
"/api/upload/chunk-upload-complete",
|
"/api/upload/chunk-upload-complete",
|
||||||
"/api/change-detection/**",
|
"/api/change-detection/**",
|
||||||
|
"/api/members/*/password",
|
||||||
"/api/layer/map/**",
|
"/api/layer/map/**",
|
||||||
"/api/layer/tile-url",
|
"/api/layer/tile-url",
|
||||||
"/api/layer/tile-url-year",
|
"/api/layer/tile-url-year",
|
||||||
@@ -111,7 +116,6 @@ public class SecurityConfig {
|
|||||||
.requestMatchers(
|
.requestMatchers(
|
||||||
"/api/user/**",
|
"/api/user/**",
|
||||||
"/api/my/menus",
|
"/api/my/menus",
|
||||||
"/api/members/*/password",
|
|
||||||
"/api/training-data/label/**",
|
"/api/training-data/label/**",
|
||||||
"/api/training-data/review/**")
|
"/api/training-data/review/**")
|
||||||
.authenticated()
|
.authenticated()
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ public class StartupLogger {
|
|||||||
|
|
||||||
private final Environment environment;
|
private final Environment environment;
|
||||||
private final DataSource dataSource;
|
private final DataSource dataSource;
|
||||||
|
private final FileProperties fileProperties;
|
||||||
|
private final InferenceProperties inferenceProperties;
|
||||||
|
|
||||||
@EventListener(ApplicationReadyEvent.class)
|
@EventListener(ApplicationReadyEvent.class)
|
||||||
public void logStartupInfo() {
|
public void logStartupInfo() {
|
||||||
@@ -79,6 +81,26 @@ public class StartupLogger {
|
|||||||
│ DDL Auto : %s
|
│ DDL Auto : %s
|
||||||
│ JDBC Batch Size : %s
|
│ JDBC Batch Size : %s
|
||||||
│ Fetch Batch Size : %s
|
│ Fetch Batch Size : %s
|
||||||
|
╠════════════════════════════════════════════════════════════════════════════════╣
|
||||||
|
║ FILE CONFIGURATION ║
|
||||||
|
╠────────────────────────────────────────────────────────────────────────────────╣
|
||||||
|
│ Root Directory : %s
|
||||||
|
│ NFS Mount Path : %s
|
||||||
|
│ Sync Root Dir : %s
|
||||||
|
│ Sync Tmp Dir : %s
|
||||||
|
│ Dataset Dir : %s
|
||||||
|
│ Model Dir : %s
|
||||||
|
│ PT Path : %s
|
||||||
|
│ Output Dir : %s
|
||||||
|
╠════════════════════════════════════════════════════════════════════════════════╣
|
||||||
|
║ INFERENCE CONFIGURATION ║
|
||||||
|
╠────────────────────────────────────────────────────────────────────────────────╣
|
||||||
|
│ NFS Mount Path : %s
|
||||||
|
│ Inference URL : %s
|
||||||
|
│ Batch URL : %s
|
||||||
|
│ GeoJSON Dir : %s
|
||||||
|
│ JAR Path : %s
|
||||||
|
│ Server Names : %s
|
||||||
╚════════════════════════════════════════════════════════════════════════════════╝
|
╚════════════════════════════════════════════════════════════════════════════════╝
|
||||||
""",
|
""",
|
||||||
profileInfo,
|
profileInfo,
|
||||||
@@ -89,7 +111,25 @@ public class StartupLogger {
|
|||||||
showSql,
|
showSql,
|
||||||
ddlAuto,
|
ddlAuto,
|
||||||
batchSize,
|
batchSize,
|
||||||
batchFetchSize);
|
batchFetchSize,
|
||||||
|
fileProperties.getRoot() != null ? fileProperties.getRoot() : "N/A",
|
||||||
|
fileProperties.getNfs() != null ? fileProperties.getNfs() : "N/A",
|
||||||
|
fileProperties.getSyncRootDir() != null ? fileProperties.getSyncRootDir() : "N/A",
|
||||||
|
fileProperties.getSyncTmpDir() != null ? fileProperties.getSyncTmpDir() : "N/A",
|
||||||
|
fileProperties.getDatasetDir() != null ? fileProperties.getDatasetDir() : "N/A",
|
||||||
|
fileProperties.getModelDir() != null ? fileProperties.getModelDir() : "N/A",
|
||||||
|
fileProperties.getPtPath() != null ? fileProperties.getPtPath() : "N/A",
|
||||||
|
fileProperties.getOutputDir() != null ? fileProperties.getOutputDir() : "N/A",
|
||||||
|
inferenceProperties.getNfs() != null ? inferenceProperties.getNfs() : "N/A",
|
||||||
|
inferenceProperties.getUrl() != null ? inferenceProperties.getUrl() : "N/A",
|
||||||
|
inferenceProperties.getBatchUrl() != null ? inferenceProperties.getBatchUrl() : "N/A",
|
||||||
|
inferenceProperties.getGeojsonDir() != null
|
||||||
|
? inferenceProperties.getGeojsonDir()
|
||||||
|
: "N/A",
|
||||||
|
inferenceProperties.getJarPath() != null ? inferenceProperties.getJarPath() : "N/A",
|
||||||
|
inferenceProperties.getInferenceServerName() != null
|
||||||
|
? inferenceProperties.getInferenceServerName()
|
||||||
|
: "N/A");
|
||||||
|
|
||||||
log.info(startupMessage);
|
log.info(startupMessage);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -173,8 +173,9 @@ public class ApiResponseDto<T> {
|
|||||||
+ "To reset your password again, please submit a new request through \"Forgot"
|
+ "To reset your password again, please submit a new request through \"Forgot"
|
||||||
+ " Password.\""),
|
+ " Password.\""),
|
||||||
PAYLOAD_TOO_LARGE("업로드 용량 제한을 초과했습니다."),
|
PAYLOAD_TOO_LARGE("업로드 용량 제한을 초과했습니다."),
|
||||||
NOT_FOUND_TARGET_YEAR("기준년도 도엽을 찾을 수 없습니다."),
|
NOT_FOUND_TARGET_YEAR("기준연도 도엽을 찾을 수 없습니다."),
|
||||||
NOT_FOUND_COMPARE_YEAR("비교년도 도엽을 찾을 수 없습니다."),
|
NOT_FOUND_COMPARE_YEAR("비교연도 도엽을 찾을 수 없습니다."),
|
||||||
|
NOT_FOUND_MAP_SHEET_NUM("추론 가능한 도엽이 없습니다."),
|
||||||
FAIL_SAVE_MAP_SHEET("도엽 저장 중 오류가 발생했습니다."),
|
FAIL_SAVE_MAP_SHEET("도엽 저장 중 오류가 발생했습니다."),
|
||||||
FAIL_CREATE_MAP_SHEET_FILE("도엽 설정파일 생성 중 오류가 발생했습니다."),
|
FAIL_CREATE_MAP_SHEET_FILE("도엽 설정파일 생성 중 오류가 발생했습니다."),
|
||||||
;
|
;
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ package com.kamco.cd.kamcoback.config.resttemplate;
|
|||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import lombok.extern.log4j.Log4j2;
|
import lombok.extern.log4j.Log4j2;
|
||||||
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
import org.springframework.http.HttpEntity;
|
import org.springframework.http.HttpEntity;
|
||||||
import org.springframework.http.HttpHeaders;
|
import org.springframework.http.HttpHeaders;
|
||||||
import org.springframework.http.HttpMethod;
|
import org.springframework.http.HttpMethod;
|
||||||
@@ -14,54 +14,86 @@ import org.springframework.stereotype.Component;
|
|||||||
import org.springframework.web.client.HttpStatusCodeException;
|
import org.springframework.web.client.HttpStatusCodeException;
|
||||||
import org.springframework.web.client.RestTemplate;
|
import org.springframework.web.client.RestTemplate;
|
||||||
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
@Component
|
@Component
|
||||||
@Log4j2
|
@Log4j2
|
||||||
public class ExternalHttpClient {
|
public class ExternalHttpClient {
|
||||||
|
|
||||||
private final RestTemplate restTemplate;
|
private final RestTemplate restTemplate; // short (@Primary)
|
||||||
|
private final RestTemplate restTemplateLong; // long
|
||||||
private final ObjectMapper objectMapper;
|
private final ObjectMapper objectMapper;
|
||||||
|
|
||||||
|
public ExternalHttpClient(
|
||||||
|
RestTemplate restTemplate,
|
||||||
|
@Qualifier("restTemplateLong") RestTemplate restTemplateLong,
|
||||||
|
ObjectMapper objectMapper) {
|
||||||
|
this.restTemplate = restTemplate;
|
||||||
|
this.restTemplateLong = restTemplateLong;
|
||||||
|
this.objectMapper = objectMapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 기본(짧은 timeout) 호출 */
|
||||||
public <T> ExternalCallResult<T> call(
|
public <T> ExternalCallResult<T> call(
|
||||||
String url, HttpMethod method, Object body, HttpHeaders headers, Class<T> responseType) {
|
String url, HttpMethod method, Object body, HttpHeaders headers, Class<T> responseType) {
|
||||||
|
|
||||||
// responseType 기반으로 Accept 동적 세팅
|
return doCall(restTemplate, url, method, body, headers, responseType);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 추론/대용량 전용 (긴 timeout) */
|
||||||
|
public <T> ExternalCallResult<T> callLong(
|
||||||
|
String url, HttpMethod method, Object body, HttpHeaders headers, Class<T> responseType) {
|
||||||
|
|
||||||
|
return doCall(restTemplateLong, url, method, body, headers, responseType);
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T> ExternalCallResult<T> doCall(
|
||||||
|
RestTemplate rt,
|
||||||
|
String url,
|
||||||
|
HttpMethod method,
|
||||||
|
Object body,
|
||||||
|
HttpHeaders headers,
|
||||||
|
Class<T> responseType) {
|
||||||
|
|
||||||
HttpHeaders resolvedHeaders = resolveHeaders(headers, responseType);
|
HttpHeaders resolvedHeaders = resolveHeaders(headers, responseType);
|
||||||
logRequestBody(body);
|
logRequestBody(body);
|
||||||
|
|
||||||
HttpEntity<Object> entity = new HttpEntity<>(body, resolvedHeaders);
|
HttpEntity<Object> entity = new HttpEntity<>(body, resolvedHeaders);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// String: raw bytes -> UTF-8 string
|
|
||||||
|
// String 응답은 raw byte로 받아 UTF-8 변환
|
||||||
if (responseType == String.class) {
|
if (responseType == String.class) {
|
||||||
ResponseEntity<byte[]> res = restTemplate.exchange(url, method, entity, byte[].class);
|
ResponseEntity<byte[]> res = rt.exchange(url, method, entity, byte[].class);
|
||||||
String raw =
|
String raw =
|
||||||
(res.getBody() == null) ? null : new String(res.getBody(), StandardCharsets.UTF_8);
|
(res.getBody() == null) ? null : new String(res.getBody(), StandardCharsets.UTF_8);
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
T casted = (T) raw;
|
T casted = (T) raw;
|
||||||
|
|
||||||
return new ExternalCallResult<>(res.getStatusCodeValue(), true, casted, null);
|
return new ExternalCallResult<>(res.getStatusCodeValue(), true, casted, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
// byte[]: raw bytes로 받고, JSON이면 에러로 처리
|
// byte[] 응답 처리
|
||||||
if (responseType == byte[].class) {
|
if (responseType == byte[].class) {
|
||||||
ResponseEntity<byte[]> res = restTemplate.exchange(url, method, entity, byte[].class);
|
ResponseEntity<byte[]> res = rt.exchange(url, method, entity, byte[].class);
|
||||||
|
|
||||||
MediaType ct = res.getHeaders().getContentType();
|
MediaType ct = res.getHeaders().getContentType();
|
||||||
byte[] bytes = res.getBody();
|
byte[] bytes = res.getBody();
|
||||||
|
|
||||||
|
// JSON이면 에러로 간주
|
||||||
if (isJsonLike(ct)) {
|
if (isJsonLike(ct)) {
|
||||||
String err = (bytes == null) ? null : new String(bytes, StandardCharsets.UTF_8);
|
String err = (bytes == null) ? null : new String(bytes, StandardCharsets.UTF_8);
|
||||||
|
|
||||||
return new ExternalCallResult<>(res.getStatusCodeValue(), false, null, err);
|
return new ExternalCallResult<>(res.getStatusCodeValue(), false, null, err);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
T casted = (T) bytes;
|
T casted = (T) bytes;
|
||||||
|
|
||||||
return new ExternalCallResult<>(res.getStatusCodeValue(), true, casted, null);
|
return new ExternalCallResult<>(res.getStatusCodeValue(), true, casted, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
// DTO 등: 일반 역직렬화
|
// DTO 응답
|
||||||
ResponseEntity<T> res = restTemplate.exchange(url, method, entity, responseType);
|
ResponseEntity<T> res = rt.exchange(url, method, entity, responseType);
|
||||||
return new ExternalCallResult<>(res.getStatusCodeValue(), true, res.getBody(), null);
|
return new ExternalCallResult<>(res.getStatusCodeValue(), true, res.getBody(), null);
|
||||||
|
|
||||||
} catch (HttpStatusCodeException e) {
|
} catch (HttpStatusCodeException e) {
|
||||||
@@ -70,29 +102,28 @@ public class ExternalHttpClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 기존 resolveJsonHeaders를 "동적"으로 교체
|
/** Accept / Content-Type 자동 처리 */
|
||||||
private HttpHeaders resolveHeaders(HttpHeaders headers, Class<?> responseType) {
|
private HttpHeaders resolveHeaders(HttpHeaders headers, Class<?> responseType) {
|
||||||
// 원본 headers를 그대로 쓰면 외부에서 재사용할 때 사이드이펙트 날 수 있어서 복사 권장
|
|
||||||
HttpHeaders h = (headers == null) ? new HttpHeaders() : new HttpHeaders(headers);
|
HttpHeaders h = (headers == null) ? new HttpHeaders() : new HttpHeaders(headers);
|
||||||
|
|
||||||
// 요청 바디 기본은 JSON이라고 가정 (필요하면 호출부에서 덮어쓰기)
|
// 기본 Content-Type
|
||||||
if (h.getContentType() == null) {
|
if (h.getContentType() == null) {
|
||||||
h.setContentType(MediaType.APPLICATION_JSON);
|
h.setContentType(MediaType.APPLICATION_JSON);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 호출부에서 Accept를 명시했으면 존중
|
// Accept 이미 있으면 존중
|
||||||
if (h.getAccept() != null && !h.getAccept().isEmpty()) {
|
if (h.getAccept() != null && !h.getAccept().isEmpty()) {
|
||||||
return h;
|
return h;
|
||||||
}
|
}
|
||||||
|
|
||||||
// responseType 기반 Accept 자동 지정
|
// 응답 타입 기준 Accept 자동 지정
|
||||||
if (responseType == byte[].class) {
|
if (responseType == byte[].class) {
|
||||||
h.setAccept(
|
h.setAccept(
|
||||||
List.of(
|
List.of(
|
||||||
MediaType.APPLICATION_OCTET_STREAM,
|
MediaType.APPLICATION_OCTET_STREAM,
|
||||||
MediaType.valueOf("application/zip"),
|
MediaType.valueOf("application/zip"),
|
||||||
MediaType.APPLICATION_JSON // 실패(JSON 에러 바디) 대비
|
MediaType.APPLICATION_JSON));
|
||||||
));
|
|
||||||
} else {
|
} else {
|
||||||
h.setAccept(List.of(MediaType.APPLICATION_JSON));
|
h.setAccept(List.of(MediaType.APPLICATION_JSON));
|
||||||
}
|
}
|
||||||
@@ -100,12 +131,15 @@ public class ExternalHttpClient {
|
|||||||
return h;
|
return h;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** JSON 응답 여부 체크 */
|
||||||
private boolean isJsonLike(MediaType ct) {
|
private boolean isJsonLike(MediaType ct) {
|
||||||
if (ct == null) return false;
|
if (ct == null) return false;
|
||||||
|
|
||||||
return ct.includes(MediaType.APPLICATION_JSON)
|
return ct.includes(MediaType.APPLICATION_JSON)
|
||||||
|| "application/problem+json".equalsIgnoreCase(ct.toString());
|
|| "application/problem+json".equalsIgnoreCase(ct.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 요청 바디 로그 */
|
||||||
private void logRequestBody(Object body) {
|
private void logRequestBody(Object body) {
|
||||||
try {
|
try {
|
||||||
if (body != null) {
|
if (body != null) {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import lombok.extern.log4j.Log4j2;
|
|||||||
import org.springframework.boot.web.client.RestTemplateBuilder;
|
import org.springframework.boot.web.client.RestTemplateBuilder;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.context.annotation.Primary;
|
||||||
import org.springframework.http.client.BufferingClientHttpRequestFactory;
|
import org.springframework.http.client.BufferingClientHttpRequestFactory;
|
||||||
import org.springframework.http.client.SimpleClientHttpRequestFactory;
|
import org.springframework.http.client.SimpleClientHttpRequestFactory;
|
||||||
import org.springframework.web.client.RestTemplate;
|
import org.springframework.web.client.RestTemplate;
|
||||||
@@ -13,10 +14,20 @@ import org.springframework.web.client.RestTemplate;
|
|||||||
public class RestTemplateConfig {
|
public class RestTemplateConfig {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
|
@Primary
|
||||||
public RestTemplate restTemplate(RestTemplateBuilder builder) {
|
public RestTemplate restTemplate(RestTemplateBuilder builder) {
|
||||||
|
return build(builder, 20000, 60000);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean("restTemplateLong")
|
||||||
|
public RestTemplate restTemplateLong(RestTemplateBuilder builder) {
|
||||||
|
return build(builder, 20000, 60000);
|
||||||
|
}
|
||||||
|
|
||||||
|
private RestTemplate build(RestTemplateBuilder builder, int connectTimeoutMs, int readTimeoutMs) {
|
||||||
SimpleClientHttpRequestFactory baseFactory = new SimpleClientHttpRequestFactory();
|
SimpleClientHttpRequestFactory baseFactory = new SimpleClientHttpRequestFactory();
|
||||||
baseFactory.setConnectTimeout(2000);
|
baseFactory.setConnectTimeout(connectTimeoutMs);
|
||||||
baseFactory.setReadTimeout(3000);
|
baseFactory.setReadTimeout(readTimeoutMs);
|
||||||
|
|
||||||
RestTemplate rt =
|
RestTemplate rt =
|
||||||
builder
|
builder
|
||||||
@@ -24,10 +35,8 @@ public class RestTemplateConfig {
|
|||||||
.additionalInterceptors(new RetryInterceptor())
|
.additionalInterceptors(new RetryInterceptor())
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
// byte[] 응답은 무조건 raw로 읽게 강제 (Jackson이 끼어들 여지 제거)
|
|
||||||
rt.getMessageConverters()
|
rt.getMessageConverters()
|
||||||
.add(0, new org.springframework.http.converter.ByteArrayHttpMessageConverter());
|
.add(0, new org.springframework.http.converter.ByteArrayHttpMessageConverter());
|
||||||
|
|
||||||
return rt;
|
return rt;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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, ""));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
@@ -36,8 +36,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 +46,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
|
||||||
|
|||||||
@@ -69,8 +69,11 @@ public class GukYuinApiService {
|
|||||||
@Value("${file.nfs}")
|
@Value("${file.nfs}")
|
||||||
private String nfs;
|
private String nfs;
|
||||||
|
|
||||||
// @Value("${file.dataset-dir}")
|
@Value("${file.output-dir}") // 국유인 반영 파일 경로
|
||||||
// private String datasetDir;
|
private String outputDir;
|
||||||
|
|
||||||
|
@Value("${file.dataset-dir}")
|
||||||
|
private String datasetDir;
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public ChngDetectMastDto.RegistResDto regist(
|
public ChngDetectMastDto.RegistResDto regist(
|
||||||
@@ -234,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -246,9 +252,9 @@ public class GukYuinApiService {
|
|||||||
return GukYuinLinkFailCode.NOT_FOUND;
|
return GukYuinLinkFailCode.NOT_FOUND;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (f.isPartScope()) {
|
// if (f.isPartScope()) {
|
||||||
return GukYuinLinkFailCode.SCOPE_PART_NOT_ALLOWED;
|
// return GukYuinLinkFailCode.SCOPE_PART_NOT_ALLOWED;
|
||||||
}
|
// }
|
||||||
|
|
||||||
if (f.hasRunningInference()) {
|
if (f.hasRunningInference()) {
|
||||||
return GukYuinLinkFailCode.HAS_RUNNING_INFERENCE;
|
return GukYuinLinkFailCode.HAS_RUNNING_INFERENCE;
|
||||||
@@ -278,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,
|
||||||
@@ -286,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(
|
||||||
@@ -345,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,
|
||||||
@@ -352,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(),
|
||||||
@@ -405,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(),
|
||||||
@@ -456,10 +468,13 @@ public class GukYuinApiService {
|
|||||||
return new ResponseObj(ApiResponseCode.DUPLICATE_DATA, "이미 국유인 연동을 한 회차입니다.");
|
return new ResponseObj(ApiResponseCode.DUPLICATE_DATA, "이미 국유인 연동을 한 회차입니다.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// String kamconfsDatasetExportPathfsDatasetExportPath = "/kamco-nfs/dataset/export/";
|
// 추론 shp 파일 생성되는 위치
|
||||||
String kamconfsDatasetExportPathfsDatasetExportPath =
|
log.info(
|
||||||
String.format("%s%s", nfs, "/dataset/export/");
|
"═════════════════════════════════[s] datasetDir : 추론 shp파일 생성되는 위치 ══════════════════════════════════");
|
||||||
if (!Files.isDirectory(Path.of(kamconfsDatasetExportPathfsDatasetExportPath + info.getUid()))) {
|
log.info("datasetDir path : " + datasetDir + info.getUid());
|
||||||
|
log.info(
|
||||||
|
"═════════════════════════════════[e] datasetDir : 추론 shp파일 생성되는 위치 ════════════════════════════");
|
||||||
|
if (!Files.isDirectory(Path.of(datasetDir + info.getUid()))) {
|
||||||
return new ResponseObj(
|
return new ResponseObj(
|
||||||
ApiResponseCode.NOT_FOUND_DATA, "파일 경로에 회차 실행 파일이 생성되지 않았습니다. 확인 부탁드립니다.");
|
ApiResponseCode.NOT_FOUND_DATA, "파일 경로에 회차 실행 파일이 생성되지 않았습니다. 확인 부탁드립니다.");
|
||||||
}
|
}
|
||||||
@@ -469,6 +484,13 @@ public class GukYuinApiService {
|
|||||||
gukyuinCoreService.findMapSheetLearnYearStage(info.getCompareYyyy(), info.getTargetYyyy());
|
gukyuinCoreService.findMapSheetLearnYearStage(info.getCompareYyyy(), info.getTargetYyyy());
|
||||||
|
|
||||||
// reqDto 셋팅
|
// reqDto 셋팅
|
||||||
|
// 마운트된 추론 shp 파일 생성되는 위치
|
||||||
|
log.info(
|
||||||
|
"═════════════════════════════════[s] outputDir : 마운트된 추론 shp 파일 생성되는 위치 ══════════════════════════════════");
|
||||||
|
log.info("outputDir path : " + outputDir + info.getUid());
|
||||||
|
log.info(
|
||||||
|
"═════════════════════════════════[e] outputDir : 마운트된 추론 shp 파일 생성되는 위치 ════════════════════════════");
|
||||||
|
String kamconfsDatasetExportPathfsDatasetExportPath = outputDir;
|
||||||
ChnDetectMastReqDto reqDto = new ChnDetectMastReqDto();
|
ChnDetectMastReqDto reqDto = new ChnDetectMastReqDto();
|
||||||
reqDto.setCprsYr(String.valueOf(info.getCompareYyyy()));
|
reqDto.setCprsYr(String.valueOf(info.getCompareYyyy()));
|
||||||
reqDto.setCrtrYr(String.valueOf(info.getTargetYyyy()));
|
reqDto.setCrtrYr(String.valueOf(info.getTargetYyyy()));
|
||||||
@@ -549,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(),
|
||||||
@@ -586,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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
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 org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
@Tag(name = "추론결과 데이터 생성", description = "추론결과 데이터 생성 API")
|
||||||
|
@RestController
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@RequestMapping("/api/inference/manual")
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
@@ -18,7 +19,6 @@ import com.kamco.cd.kamcoback.model.dto.ModelMngDto;
|
|||||||
import com.kamco.cd.kamcoback.model.service.ModelMngService;
|
import com.kamco.cd.kamcoback.model.service.ModelMngService;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.Parameter;
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
import io.swagger.v3.oas.annotations.enums.ParameterIn;
|
|
||||||
import io.swagger.v3.oas.annotations.media.ArraySchema;
|
import io.swagger.v3.oas.annotations.media.ArraySchema;
|
||||||
import io.swagger.v3.oas.annotations.media.Content;
|
import io.swagger.v3.oas.annotations.media.Content;
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
@@ -28,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;
|
||||||
@@ -36,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;
|
||||||
@@ -54,11 +56,13 @@ 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;
|
||||||
|
|
||||||
@Operation(summary = "추론관리 목록", description = "어드민 홈 > 추론관리 > 추론관리 > 추론관리 목록")
|
/** 추론관리 목록 화면에서 호출 */
|
||||||
|
@Operation(summary = "추론관리 목록", description = "추론관리 > 추론관리 목록 ")
|
||||||
@ApiResponses(
|
@ApiResponses(
|
||||||
value = {
|
value = {
|
||||||
@ApiResponse(
|
@ApiResponse(
|
||||||
@@ -90,7 +94,8 @@ public class InferenceResultApiController {
|
|||||||
return ApiResponseDto.ok(analResList);
|
return ApiResponseDto.ok(analResList);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operation(summary = "추론 진행 여부 확인", description = "어드민 홈 > 추론관리 > 추론관리 > 추론관리 목록")
|
/** 추론관리 목록 화면에서 호출 */
|
||||||
|
@Operation(summary = "추론 진행 여부 확인", description = "추론관리 > 추론관리 목록")
|
||||||
@ApiResponses(
|
@ApiResponses(
|
||||||
value = {
|
value = {
|
||||||
@ApiResponse(
|
@ApiResponse(
|
||||||
@@ -112,7 +117,8 @@ public class InferenceResultApiController {
|
|||||||
return ApiResponseDto.ok(inferenceResultService.getProcessing());
|
return ApiResponseDto.ok(inferenceResultService.getProcessing());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operation(summary = "년도 목록 조회", description = "어드민 홈 > 추론관리 > 추론목록 > 변화탐지 실행 정보 입력 > 년도 목록 조회")
|
/** 추론관리 목록 화면에서 호출 */
|
||||||
|
@Operation(summary = "년도 목록 조회", description = "추론관리 > 추론목록 > 변화탐지 실행 정보 입력 > 년도 목록 조회")
|
||||||
@ApiResponses(
|
@ApiResponses(
|
||||||
value = {
|
value = {
|
||||||
@ApiResponse(
|
@ApiResponse(
|
||||||
@@ -130,7 +136,8 @@ public class InferenceResultApiController {
|
|||||||
return ApiResponseDto.ok(mapSheetMngService.findMapSheetMngDoneYyyyList());
|
return ApiResponseDto.ok(mapSheetMngService.findMapSheetMngDoneYyyyList());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operation(summary = "변화탐지 실행 정보 입력", description = "어드민 홈 > 추론관리 > 추론목록 > 변화탐지 실행 정보 입력")
|
/** 변화탐지 실행 정보 입력화면에서 호출 */
|
||||||
|
@Operation(summary = "변화탐지 실행 정보 입력, 추론실행", description = "추론관리 > 추론목록 > 변화탐지 실행 정보 입력")
|
||||||
@ApiResponses(
|
@ApiResponses(
|
||||||
value = {
|
value = {
|
||||||
@ApiResponse(
|
@ApiResponse(
|
||||||
@@ -155,7 +162,8 @@ public class InferenceResultApiController {
|
|||||||
return ApiResponseDto.ok(uuid);
|
return ApiResponseDto.ok(uuid);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operation(summary = "추론 종료", description = "추론 종료")
|
/** 추론진행 현황 화면에서 호출 */
|
||||||
|
@Operation(summary = "추론 종료", description = "추론관리 > 추론목록 > 추론진행 현황")
|
||||||
@ApiResponses(
|
@ApiResponses(
|
||||||
value = {
|
value = {
|
||||||
@ApiResponse(
|
@ApiResponse(
|
||||||
@@ -170,11 +178,13 @@ 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operation(summary = "분석 모델 선택 조회", description = "변화탐지 실행 정보 입력 모델선택 팝업 ")
|
/** 변화탐지 실행 정보 입력화면에서 호출 */
|
||||||
|
@Operation(summary = "분석 모델 선택 조회", description = "추론관리 > 추론목록 > 변화탐지 실행 정보 입력 > 모델선택 팝업 ")
|
||||||
@ApiResponses(
|
@ApiResponses(
|
||||||
value = {
|
value = {
|
||||||
@ApiResponse(
|
@ApiResponse(
|
||||||
@@ -205,7 +215,8 @@ public class InferenceResultApiController {
|
|||||||
return ApiResponseDto.ok(result);
|
return ApiResponseDto.ok(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operation(summary = "추론관리 추론진행 서버 현황", description = "추론관리 추론진행 서버 현황")
|
/** 추론진행 현황 화면에서 호출 */
|
||||||
|
@Operation(summary = "추론관리 추론진행 서버 현황", description = "추론관리 > 추론목록 > 추론진행 현황")
|
||||||
@ApiResponses(
|
@ApiResponses(
|
||||||
value = {
|
value = {
|
||||||
@ApiResponse(
|
@ApiResponse(
|
||||||
@@ -224,7 +235,8 @@ public class InferenceResultApiController {
|
|||||||
return ApiResponseDto.ok(inferenceResultService.getInferenceServerStatusList());
|
return ApiResponseDto.ok(inferenceResultService.getInferenceServerStatusList());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operation(summary = "추론관리 진행현황 상세", description = "어드민 홈 > 추론관리 > 추론관리 > 진행현황 상세")
|
/** 추론진행 현황 화면에서 호출 */
|
||||||
|
@Operation(summary = "추론관리 진행현황 상세", description = "추론관리 > 추론진행 현황")
|
||||||
@ApiResponses(
|
@ApiResponses(
|
||||||
value = {
|
value = {
|
||||||
@ApiResponse(
|
@ApiResponse(
|
||||||
@@ -248,7 +260,8 @@ public class InferenceResultApiController {
|
|||||||
return ApiResponseDto.ok(inferenceResultService.getInferenceStatus(uuid));
|
return ApiResponseDto.ok(inferenceResultService.getInferenceStatus(uuid));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operation(summary = "추론결과 기본정보", description = "추론결과 기본정보")
|
/** 추론결과 화면에서 호출 */
|
||||||
|
@Operation(summary = "추론결과 기본정보", description = "추론관리 > 추론결과")
|
||||||
@ApiResponses(
|
@ApiResponses(
|
||||||
value = {
|
value = {
|
||||||
@ApiResponse(
|
@ApiResponse(
|
||||||
@@ -269,7 +282,8 @@ public class InferenceResultApiController {
|
|||||||
return ApiResponseDto.ok(inferenceResultService.getInferenceResultInfo(uuid));
|
return ApiResponseDto.ok(inferenceResultService.getInferenceResultInfo(uuid));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operation(summary = "추론결과 분류별 탐지 건수", description = "추론결과 분류별 탐지 건수")
|
/** 추론결과 화면에서 호출 */
|
||||||
|
@Operation(summary = "추론결과 분류별 탐지 건수", description = "추론관리 > 추론결과")
|
||||||
@ApiResponses(
|
@ApiResponses(
|
||||||
value = {
|
value = {
|
||||||
@ApiResponse(
|
@ApiResponse(
|
||||||
@@ -290,6 +304,7 @@ public class InferenceResultApiController {
|
|||||||
return ApiResponseDto.ok(inferenceResultService.getInferenceClassCountList(uuid));
|
return ApiResponseDto.ok(inferenceResultService.getInferenceClassCountList(uuid));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 추론결과 화면에서 호출 */
|
||||||
@Operation(summary = "추론관리 분석결과 상세 목록", description = "추론관리 분석결과 상세 목록 geojson 데이터 조회")
|
@Operation(summary = "추론관리 분석결과 상세 목록", description = "추론관리 분석결과 상세 목록 geojson 데이터 조회")
|
||||||
@ApiResponses(
|
@ApiResponses(
|
||||||
value = {
|
value = {
|
||||||
@@ -329,26 +344,14 @@ public class InferenceResultApiController {
|
|||||||
return ApiResponseDto.ok(geomList);
|
return ApiResponseDto.ok(geomList);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operation(
|
/** 추론결과 화면에서 호출 */
|
||||||
summary = "shp 파일 다운로드",
|
/** 다운로드는 a 링크로 받는걸로 변경되어 사번을 파라미터로 받아서 로그에 저장하는걸로 변경함 */
|
||||||
description = "추론관리 분석결과 shp 파일 다운로드",
|
@Operation(summary = "shp 파일 다운로드", description = "추론관리 분석결과 shp 파일 다운로드")
|
||||||
parameters = {
|
|
||||||
@Parameter(
|
|
||||||
name = "kamco-download-uuid",
|
|
||||||
in = ParameterIn.HEADER,
|
|
||||||
required = true,
|
|
||||||
description = "다운로드 요청 UUID",
|
|
||||||
schema =
|
|
||||||
@Schema(
|
|
||||||
type = "string",
|
|
||||||
format = "uuid",
|
|
||||||
example = "69c4e56c-e0bf-4742-9225-bba9aae39052"))
|
|
||||||
})
|
|
||||||
@ApiResponses(
|
@ApiResponses(
|
||||||
value = {
|
value = {
|
||||||
@ApiResponse(
|
@ApiResponse(
|
||||||
responseCode = "200",
|
responseCode = "200",
|
||||||
description = "shp zip파일 다운로드",
|
description = "shp 파일 다운로드",
|
||||||
content =
|
content =
|
||||||
@Content(
|
@Content(
|
||||||
mediaType = "application/octet-stream",
|
mediaType = "application/octet-stream",
|
||||||
@@ -357,13 +360,16 @@ public class InferenceResultApiController {
|
|||||||
@ApiResponse(responseCode = "500", description = "서버 오류", content = @Content)
|
@ApiResponse(responseCode = "500", description = "서버 오류", content = @Content)
|
||||||
})
|
})
|
||||||
@GetMapping("/download/{uuid}")
|
@GetMapping("/download/{uuid}")
|
||||||
public ResponseEntity<?> download(@PathVariable UUID uuid, HttpServletRequest request)
|
public ResponseEntity<?> download(
|
||||||
|
@PathVariable UUID uuid,
|
||||||
|
@Parameter(description = "사번", example = "123456") @RequestParam String employeeNo,
|
||||||
|
HttpServletRequest request)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
|
|
||||||
String path;
|
String path;
|
||||||
String uid;
|
String uid;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
// 추론결과 shp zip 파일 확인하여 다운로드 경로 생성
|
||||||
Map<String, Object> map = inferenceResultService.shpDownloadPath(uuid);
|
Map<String, Object> map = inferenceResultService.shpDownloadPath(uuid);
|
||||||
path = String.valueOf(map.get("path"));
|
path = String.valueOf(map.get("path"));
|
||||||
uid = String.valueOf(map.get("uid"));
|
uid = String.valueOf(map.get("uid"));
|
||||||
@@ -372,11 +378,14 @@ public class InferenceResultApiController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Path zipPath = Path.of(path);
|
Path zipPath = Path.of(path);
|
||||||
|
if (!Files.isRegularFile(zipPath)) {
|
||||||
|
return ResponseEntity.status(HttpStatus.NOT_FOUND).body("다운로드 받을 파일이 없습니다.");
|
||||||
|
}
|
||||||
|
|
||||||
// Range + 200/206/416 공통 처리 (추가 헤더 포함)
|
|
||||||
return rangeDownloadResponder.buildZipResponse(zipPath, uid + ".zip", request);
|
return rangeDownloadResponder.buildZipResponse(zipPath, uid + ".zip", request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 추론결과 화면에서 호출 */
|
||||||
@Operation(summary = "shp 파일 다운로드 이력 조회", description = "추론관리 분석결과 shp 파일 다운로드 이력 조회")
|
@Operation(summary = "shp 파일 다운로드 이력 조회", description = "추론관리 분석결과 shp 파일 다운로드 이력 조회")
|
||||||
@GetMapping(value = "/download-audit/{uuid}")
|
@GetMapping(value = "/download-audit/{uuid}")
|
||||||
@ApiResponses(
|
@ApiResponses(
|
||||||
@@ -419,7 +428,8 @@ public class InferenceResultApiController {
|
|||||||
return ApiResponseDto.ok(inferenceResultService.getDownloadAudit(searchReq, downloadReq));
|
return ApiResponseDto.ok(inferenceResultService.getDownloadAudit(searchReq, downloadReq));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operation(summary = "추론 실행중인 도엽 목록", description = "추론관리 실행중인 도엽명 5k 목록")
|
/** 추론진행 현황 화면에서 호출, 분석도엽 부분 옵션일때 분석중인 도엽 확인용 */
|
||||||
|
@Operation(summary = "추론관리 분석중인 도엽명 5k 목록", description = "추론관리 분석중인 도엽명 50k 목록")
|
||||||
@ApiResponses({
|
@ApiResponses({
|
||||||
@ApiResponse(
|
@ApiResponse(
|
||||||
responseCode = "200",
|
responseCode = "200",
|
||||||
|
|||||||
@@ -1,90 +0,0 @@
|
|||||||
package com.kamco.cd.kamcoback.inference;
|
|
||||||
|
|
||||||
import com.kamco.cd.kamcoback.common.exception.CustomApiException;
|
|
||||||
import com.kamco.cd.kamcoback.common.geometry.GeoJsonFileWriter.Scene;
|
|
||||||
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.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.UUID;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import lombok.extern.log4j.Log4j2;
|
|
||||||
import org.springframework.http.HttpStatus;
|
|
||||||
import org.springframework.web.bind.annotation.PathVariable;
|
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RequestBody;
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Operation(summary = "추론실행에 필요한 geojson 파일 생성", description = "추론실행에 필요한 geojson 파일 생성")
|
|
||||||
@PostMapping("/geojson/{yyyy}/{mapSheetScope}/{detectOption}")
|
|
||||||
public ApiResponseDto<Scene> createGeojson(
|
|
||||||
@Schema(description = "년도") @PathVariable String yyyy,
|
|
||||||
@Schema(description = "전체(ALL),부분(PART)", example = "PART") @PathVariable
|
|
||||||
String mapSheetScope,
|
|
||||||
@Schema(description = "추론제외(EXCL),이전 년도 도엽 사용(PREV)", example = "EXCL") @PathVariable
|
|
||||||
String detectOption,
|
|
||||||
@Schema(description = "5k도엽번호", example = MAP_ID) @RequestBody Map<String, Object> body) {
|
|
||||||
|
|
||||||
Object raw = body.get("mapIds");
|
|
||||||
|
|
||||||
if (raw == null) {
|
|
||||||
throw new CustomApiException("BAD_REQUEST", HttpStatus.BAD_REQUEST);
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
List<String> mapIds = (List<String>) raw;
|
|
||||||
|
|
||||||
Scene scene =
|
|
||||||
inferenceResultShpService.createGeojson(yyyy, mapSheetScope, detectOption, mapIds);
|
|
||||||
|
|
||||||
return ApiResponseDto.createOK(scene);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
package com.kamco.cd.kamcoback.inference;
|
|
||||||
|
|
||||||
import com.kamco.cd.kamcoback.config.api.ApiResponseDto;
|
|
||||||
import com.kamco.cd.kamcoback.inference.service.InferenceRunService;
|
|
||||||
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.GetMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RequestParam;
|
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
|
||||||
|
|
||||||
@Tag(name = "추론 실행", description = "추론 실행")
|
|
||||||
@Log4j2
|
|
||||||
@RequestMapping("/api/inference/run")
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
@RestController
|
|
||||||
public class InferenceRunController {
|
|
||||||
|
|
||||||
private final InferenceRunService inferenceRunService;
|
|
||||||
|
|
||||||
@Operation(summary = "추론 진행 여부 확인", description = "어드민 홈 > 추론관리 > 추론관리 > 추론관리 목록")
|
|
||||||
@ApiResponses(
|
|
||||||
value = {
|
|
||||||
@ApiResponse(
|
|
||||||
responseCode = "200",
|
|
||||||
description = "검색 성공",
|
|
||||||
content =
|
|
||||||
@Content(
|
|
||||||
mediaType = "application/json",
|
|
||||||
schema =
|
|
||||||
@Schema(
|
|
||||||
description = "진행 여부 (UUID 있으면 진행중)",
|
|
||||||
type = "UUID",
|
|
||||||
example = "44709877-2e27-4fc5-bacb-8e0328c69b64"))),
|
|
||||||
@ApiResponse(responseCode = "400", description = "잘못된 검색 조건", content = @Content),
|
|
||||||
@ApiResponse(responseCode = "500", description = "서버 오류", content = @Content)
|
|
||||||
})
|
|
||||||
@GetMapping
|
|
||||||
public ApiResponseDto<Void> getProcessing(
|
|
||||||
@Parameter(description = "비교년도", example = "2021") @RequestParam(required = false)
|
|
||||||
Integer compareYear,
|
|
||||||
@Parameter(description = "기준년도", example = "2022") @RequestParam(required = false)
|
|
||||||
Integer targetYear,
|
|
||||||
@Parameter(description = "모델 uuid") @RequestParam(required = false) UUID modelUuid) {
|
|
||||||
|
|
||||||
inferenceRunService.run(compareYear, targetYear, modelUuid);
|
|
||||||
return ApiResponseDto.ok(null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -458,6 +458,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 +475,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 +491,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 +541,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
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ 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;
|
||||||
|
|
||||||
@@ -86,6 +87,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;
|
||||||
|
|
||||||
@@ -240,21 +242,22 @@ 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 변화탐지 테스트")
|
||||||
@NotBlank
|
@NotBlank
|
||||||
private String title;
|
private String title;
|
||||||
|
|
||||||
@Schema(description = "G1", example = "b40e0f68-c1d8-49fc-93f9-a36270093861")
|
@Schema(description = "G1", example = "643adead-f3d2-4f10-9037-862bee919399")
|
||||||
@NotNull
|
@NotNull
|
||||||
private UUID model1Uuid;
|
private UUID model1Uuid;
|
||||||
|
|
||||||
@Schema(description = "G2", example = "ec92b7d2-b5a3-4915-9bdf-35fb3ca8ad27")
|
@Schema(description = "G2", example = "dd86b4ef-28e3-4e3d-9ee4-f60d9cb54e13")
|
||||||
@NotNull
|
@NotNull
|
||||||
private UUID model2Uuid;
|
private UUID model2Uuid;
|
||||||
|
|
||||||
@Schema(description = "G3", example = "37f45782-8ccf-4cf6-911c-a055a1510d39")
|
@Schema(description = "G3", example = "58c1153e-dec6-4424-82a1-189083a9d9dc")
|
||||||
@NotNull
|
@NotNull
|
||||||
private UUID model3Uuid;
|
private UUID model3Uuid;
|
||||||
|
|
||||||
@@ -272,11 +275,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
|
||||||
@@ -676,4 +678,14 @@ public class InferenceResultDto {
|
|||||||
private Long m2ModelBatchId;
|
private Long m2ModelBatchId;
|
||||||
private Long m3ModelBatchId;
|
private Long m3ModelBatchId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public static class MapSheetFallbackYearDto {
|
||||||
|
|
||||||
|
private String mapSheetNum;
|
||||||
|
private Integer mngYyyy;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
package com.kamco.cd.kamcoback.inference.service;
|
|
||||||
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
|
||||||
|
|
||||||
@RestController
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
@RequestMapping("/api/inference/manual")
|
|
||||||
public class InferenceManualApiController {}
|
|
||||||
@@ -1,22 +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) {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
package com.kamco.cd.kamcoback.inference.service;
|
package com.kamco.cd.kamcoback.inference.service;
|
||||||
|
|
||||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
|
||||||
import com.fasterxml.jackson.core.type.TypeReference;
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
||||||
import com.kamco.cd.kamcoback.common.exception.CustomApiException;
|
import com.kamco.cd.kamcoback.common.exception.CustomApiException;
|
||||||
|
import com.kamco.cd.kamcoback.common.geometry.GeoJsonFileWriter;
|
||||||
import com.kamco.cd.kamcoback.common.geometry.GeoJsonFileWriter.ImageFeature;
|
import com.kamco.cd.kamcoback.common.geometry.GeoJsonFileWriter.ImageFeature;
|
||||||
import com.kamco.cd.kamcoback.common.geometry.GeoJsonFileWriter.Scene;
|
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.utils.GeoJsonValidator;
|
||||||
import com.kamco.cd.kamcoback.common.utils.UserUtil;
|
import com.kamco.cd.kamcoback.common.utils.UserUtil;
|
||||||
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;
|
||||||
@@ -21,7 +21,7 @@ import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.DetectOption;
|
|||||||
import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.InferenceLearnDto;
|
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.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.MapSheetNumDto;
|
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.ResultList;
|
||||||
import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.SaveInferenceAiDto;
|
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.InferenceResultDto.Status;
|
||||||
@@ -39,6 +39,8 @@ import com.kamco.cd.kamcoback.postgres.core.InferenceResultCoreService;
|
|||||||
import com.kamco.cd.kamcoback.postgres.core.MapSheetMngCoreService;
|
import com.kamco.cd.kamcoback.postgres.core.MapSheetMngCoreService;
|
||||||
import com.kamco.cd.kamcoback.postgres.core.ModelMngCoreService;
|
import com.kamco.cd.kamcoback.postgres.core.ModelMngCoreService;
|
||||||
import jakarta.validation.constraints.NotNull;
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import java.io.IOException;
|
||||||
|
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;
|
||||||
@@ -54,7 +56,7 @@ import java.util.Set;
|
|||||||
import java.util.UUID;
|
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;
|
||||||
@@ -64,8 +66,9 @@ import org.springframework.http.MediaType;
|
|||||||
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
|
@Slf4j
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
@Transactional(readOnly = true)
|
@Transactional(readOnly = true)
|
||||||
public class InferenceResultService {
|
public class InferenceResultService {
|
||||||
@@ -74,14 +77,11 @@ public class InferenceResultService {
|
|||||||
private final MapSheetMngCoreService mapSheetMngCoreService;
|
private final MapSheetMngCoreService mapSheetMngCoreService;
|
||||||
private final ModelMngCoreService modelMngCoreService;
|
private final ModelMngCoreService modelMngCoreService;
|
||||||
private final AuditLogCoreService auditLogCoreService;
|
private final AuditLogCoreService auditLogCoreService;
|
||||||
|
private final InferenceCommonService inferenceCommonService;
|
||||||
|
|
||||||
private final ExternalHttpClient externalHttpClient;
|
private final ExternalHttpClient externalHttpClient;
|
||||||
private final ObjectMapper objectMapper;
|
|
||||||
private final UserUtil userUtil;
|
private final UserUtil userUtil;
|
||||||
|
|
||||||
@Value("${inference.url}")
|
|
||||||
private String inferenceUrl;
|
|
||||||
|
|
||||||
@Value("${inference.batch-url}")
|
@Value("${inference.batch-url}")
|
||||||
private String batchUrl;
|
private String batchUrl;
|
||||||
|
|
||||||
@@ -92,7 +92,10 @@ public class InferenceResultService {
|
|||||||
private String datasetDir;
|
private String datasetDir;
|
||||||
|
|
||||||
@Value("${spring.profiles.active}")
|
@Value("${spring.profiles.active}")
|
||||||
private String profile;
|
private String activeEnv;
|
||||||
|
|
||||||
|
@Value("${inference.geojson-dir}")
|
||||||
|
private String inferenceDir;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 추론관리 목록
|
* 추론관리 목록
|
||||||
@@ -105,7 +108,7 @@ public class InferenceResultService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 추론 진행중인지 확인
|
* 추론 진행중인지 확인, 변화탐지 설정 등록 버튼 활성화 여부에 필요함
|
||||||
*
|
*
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
@@ -117,110 +120,326 @@ public class InferenceResultService {
|
|||||||
return dto.getUuid();
|
return dto.getUuid();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 추론 실행 - 추론제외, 이전연도 도엽 사용 분기
|
||||||
|
*
|
||||||
|
* @param req
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
@Transactional
|
@Transactional
|
||||||
public UUID run(InferenceResultDto.RegReq req) {
|
public UUID run(InferenceResultDto.RegReq req) {
|
||||||
List<MngListDto> targetDtoList = mapSheetMngCoreService.getHstMapSheetList(req);
|
log.info("inference start request = {}", req);
|
||||||
List<String> compareList = mapSheetMngCoreService.getMapSheetMngHst(req.getCompareYyyy());
|
DetectOption detectOption = req.getDetectOption();
|
||||||
List<String> targetList =
|
if (detectOption == DetectOption.EXCL) {
|
||||||
mapSheetMngCoreService.getHstMapSheetList(req).stream()
|
// 추론 제외 일때 EXCL
|
||||||
|
return runExcl(req);
|
||||||
|
}
|
||||||
|
// 이전연도 도엽 사용 일때 PREV
|
||||||
|
return runPrev(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 변화탐지 [옵션 추론제외 실행]
|
||||||
|
*
|
||||||
|
* @param req
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public UUID runExcl(InferenceResultDto.RegReq req) {
|
||||||
|
// 기준연도 실행가능 도엽 조회
|
||||||
|
List<MngListDto> targetMngList =
|
||||||
|
mapSheetMngCoreService.getMapSheetMngHst(req.getTargetYyyy(), req.getMapSheetNum());
|
||||||
|
|
||||||
|
if (targetMngList == null || targetMngList.isEmpty()) {
|
||||||
|
throw new CustomApiException("NOT_FOUND_MAP_SHEET_NUM", HttpStatus.NOT_FOUND);
|
||||||
|
}
|
||||||
|
log.info("targetMngList size = {}", targetMngList.size());
|
||||||
|
|
||||||
|
// 비교연도 실행가능 도엽 조회
|
||||||
|
List<MngListDto> compareMngList =
|
||||||
|
mapSheetMngCoreService.getMapSheetMngHst(req.getCompareYyyy(), req.getMapSheetNum());
|
||||||
|
|
||||||
|
if (compareMngList == null || compareMngList.isEmpty()) {
|
||||||
|
throw new CustomApiException("NOT_FOUND_COMPARE_YEAR", HttpStatus.NOT_FOUND);
|
||||||
|
}
|
||||||
|
log.info("compareMngList size = {}", compareMngList.size());
|
||||||
|
|
||||||
|
// compare 도엽번호 Set 구성
|
||||||
|
Set<String> compareSet =
|
||||||
|
compareMngList.stream()
|
||||||
|
.map(MngListDto::getMapSheetNum)
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
|
||||||
|
// 기준년도 비교년도 동일한 도엽번호만 담기
|
||||||
|
List<MngListDto> intersectionList =
|
||||||
|
targetMngList.stream()
|
||||||
|
.filter(dto -> dto.getMapSheetNum() != null)
|
||||||
|
.filter(dto -> compareSet.contains(dto.getMapSheetNum()))
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
log.info("target size = {}", targetMngList.size());
|
||||||
|
log.info("compare size = {}", compareMngList.size());
|
||||||
|
log.info("intersection size = {}", intersectionList.size());
|
||||||
|
|
||||||
|
// 비교 연도 도엽번호를 꺼내와서 최종 추론 대상 도엽번호를 담기
|
||||||
|
List<String> mapSheetNums =
|
||||||
|
intersectionList.stream()
|
||||||
.map(MngListDto::getMapSheetNum)
|
.map(MngListDto::getMapSheetNum)
|
||||||
.filter(Objects::nonNull)
|
.filter(Objects::nonNull)
|
||||||
.distinct()
|
.distinct()
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
log.info(
|
int targetTotal = targetMngList.size();
|
||||||
"hst list count compareList = {}, targetList = {}", compareList.size(), targetList.size());
|
int compareTotal = compareMngList.size();
|
||||||
|
int intersection = intersectionList.size();
|
||||||
|
|
||||||
Set<String> compareSet = new HashSet<>(compareList);
|
// ===== MapSheet Year Comparison =====
|
||||||
Set<String> targetSet = new HashSet<>(targetList);
|
// target Total : 기준연도 실행가능 전체 도엽 수
|
||||||
|
// compare Total : 비교연도 실행가능 전체 도엽 수
|
||||||
long intersectionCount =
|
// Intersection : 양 연도에 모두 존재하는 도엽 수 (최종 추론 대상)
|
||||||
targetSet.stream()
|
// target Only (Excluded) : 기준연도에만 존재하고 비교연도에는 없는 도엽 수
|
||||||
.distinct()
|
// compare Only : 비교연도에만 존재하고 기준연도에는 없는 도엽 수
|
||||||
.filter(compareSet::contains)
|
// ====================================
|
||||||
.count(); // compare와 target에 공통으로 존재하는 도협 수
|
|
||||||
|
|
||||||
long excludedTargetCount =
|
|
||||||
targetSet.stream()
|
|
||||||
.distinct()
|
|
||||||
.filter(s -> !compareSet.contains(s))
|
|
||||||
.count(); // target 에만 존재하는 도협 수 (compare 에는 없음)
|
|
||||||
|
|
||||||
long onlyCompareCount =
|
|
||||||
compareSet.stream()
|
|
||||||
.distinct()
|
|
||||||
.filter(s -> !targetSet.contains(s))
|
|
||||||
.count(); // compare 에만 존재하는 도협 수 (target 에는 없음)
|
|
||||||
|
|
||||||
log.info(
|
log.info(
|
||||||
"""
|
"""
|
||||||
===== MapSheet Year Comparison =====
|
===== MapSheet Year Comparison =====
|
||||||
target Total: {}
|
target Total: {}
|
||||||
compare Total: {}
|
compare Total: {}
|
||||||
Intersection: {}
|
Intersection: {}
|
||||||
target Only (Excluded from compare): {}
|
target Only (Excluded): {}
|
||||||
compare Only: {}
|
compare Only: {}
|
||||||
====================================
|
====================================
|
||||||
""",
|
""",
|
||||||
targetSet.size(),
|
targetTotal,
|
||||||
compareSet.size(),
|
compareTotal,
|
||||||
intersectionCount,
|
intersection,
|
||||||
excludedTargetCount,
|
targetTotal - intersection,
|
||||||
onlyCompareCount);
|
compareTotal - intersection);
|
||||||
|
|
||||||
List<String> filteredTargetList =
|
if (mapSheetNums.isEmpty()) {
|
||||||
targetSet.stream() // target 기준으로
|
// 추론 가능한 도엽이 없습니다.
|
||||||
.filter(compareSet::contains) // compare에 있는 도협만 남김
|
throw new CustomApiException("NOT_FOUND_MAP_SHEET_NUM", HttpStatus.NOT_FOUND);
|
||||||
.toList();
|
|
||||||
|
|
||||||
Scene modelComparePath =
|
|
||||||
getSceneInference(req.getCompareYyyy().toString(), filteredTargetList, "", "");
|
|
||||||
|
|
||||||
Scene modelTargetPath =
|
|
||||||
getSceneInference(req.getTargetYyyy().toString(), filteredTargetList, "", "");
|
|
||||||
|
|
||||||
// 작은 쪽 기준으로 탐지건수/파일생성 리스트 결정
|
|
||||||
List<ImageFeature> imageFeatureList;
|
|
||||||
if (modelComparePath.getFeatures().size() <= modelTargetPath.getFeatures().size()) {
|
|
||||||
imageFeatureList = modelComparePath.getFeatures();
|
|
||||||
} else {
|
|
||||||
imageFeatureList = modelTargetPath.getFeatures();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// imageFeatureList 기준 sceneId Set
|
// compare geojson 파일 생성
|
||||||
Set<String> sceneIdSet =
|
Scene compareScene =
|
||||||
imageFeatureList.stream()
|
getSceneInference(
|
||||||
.map(ImageFeature::getSceneId)
|
req.getCompareYyyy().toString(), // 기준년도
|
||||||
|
mapSheetNums, // 최종 추론 대상
|
||||||
|
req.getMapSheetScope(), // ALL / 부분
|
||||||
|
req.getDetectOption()); // EXCL / PREV
|
||||||
|
|
||||||
|
// target geojson 파일 생성
|
||||||
|
Scene targetScene =
|
||||||
|
getSceneInference(
|
||||||
|
req.getTargetYyyy().toString(), // 대상년도
|
||||||
|
mapSheetNums, // 최종 추론 대상
|
||||||
|
req.getMapSheetScope(),
|
||||||
|
req.getDetectOption());
|
||||||
|
|
||||||
|
log.info("비교년도 geojson 파일 validation ===== {}", compareScene.getFilePath());
|
||||||
|
GeoJsonValidator.validateWithRequested(compareScene.getFilePath(), mapSheetNums);
|
||||||
|
log.info("기준년도 geojson 파일 validation ===== {}", targetScene.getFilePath());
|
||||||
|
GeoJsonValidator.validateWithRequested(targetScene.getFilePath(), mapSheetNums);
|
||||||
|
|
||||||
|
// 추론 실행
|
||||||
|
return executeInference(
|
||||||
|
req,
|
||||||
|
intersectionList, // 전체 target 목록
|
||||||
|
mapSheetNums, // 최종 추론 대상
|
||||||
|
compareScene, // compare geojson
|
||||||
|
targetScene // target geojson
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 변화탐지 옵션 이전 년도 도엽 사용 실행
|
||||||
|
*
|
||||||
|
* @param req
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Transactional
|
||||||
|
public UUID runPrev(InferenceResultDto.RegReq req) {
|
||||||
|
Integer targetYyyy = req.getTargetYyyy();
|
||||||
|
Integer compareYyyy = req.getCompareYyyy();
|
||||||
|
String mapSheetScope = req.getMapSheetScope();
|
||||||
|
|
||||||
|
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()) {
|
||||||
|
throw new CustomApiException("NOT_FOUND_TARGET_YEAR", HttpStatus.NOT_FOUND);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 비교연도 실행가능 도엽 조회
|
||||||
|
List<MngListDto> compareMngList =
|
||||||
|
mapSheetMngCoreService.getMapSheetMngHst(compareYyyy, req.getMapSheetNum());
|
||||||
|
|
||||||
|
log.info("[runPrev] compareMngList size = {}", compareMngList.size());
|
||||||
|
if (compareMngList == null || compareMngList.isEmpty()) {
|
||||||
|
throw new CustomApiException("NOT_FOUND_COMPARE_YEAR", HttpStatus.NOT_FOUND);
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("[runPrev] Difference in count = {}", targetMngList.size() - compareMngList.size());
|
||||||
|
|
||||||
|
// 로그용 원본 카운트 (이전도엽 추가 전)
|
||||||
|
int targetTotal = targetMngList.size();
|
||||||
|
int compareTotalBeforeFallback = compareMngList.size();
|
||||||
|
|
||||||
|
// 기준연도 기준 비교연도 구해서 이전년도로 compare 보완 하기위해서 도엽번호만 정리
|
||||||
|
Set<String> compareSet0 =
|
||||||
|
compareMngList.stream()
|
||||||
|
.map(MngListDto::getMapSheetNum)
|
||||||
.filter(Objects::nonNull)
|
.filter(Objects::nonNull)
|
||||||
.collect(Collectors.toSet());
|
.collect(Collectors.toSet());
|
||||||
|
|
||||||
// targetList(List<MngListDto>) 리턴용으로 필터링
|
// 기준연도 기준 비교연도에 도협번호가 없으면 이전연도 조회해서 compare 보완, 없는거 담기
|
||||||
|
List<String> targetOnlyMapSheetNums =
|
||||||
|
targetMngList.stream()
|
||||||
|
.map(MngListDto::getMapSheetNum)
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.filter(num -> !compareSet0.contains(num))
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
log.info("[runPrev] targetOnlyMapSheetNums in count = {}", targetOnlyMapSheetNums.size());
|
||||||
|
|
||||||
|
// 이전연도 초회 추가
|
||||||
|
compareMngList.addAll(
|
||||||
|
mapSheetMngCoreService.findFallbackCompareYearByMapSheets(
|
||||||
|
compareYyyy, targetOnlyMapSheetNums));
|
||||||
|
|
||||||
|
log.info("[runPrev] fallback compare size= {}", compareMngList.size());
|
||||||
|
|
||||||
|
// 이전연도 추가 후 compare 총 개수
|
||||||
|
int compareTotalAfterFallback = compareMngList.size();
|
||||||
|
|
||||||
|
// 이전연도 추가한 기준연도 값 도협번호만 담기
|
||||||
|
Set<String> compareSet1 =
|
||||||
|
compareMngList.stream()
|
||||||
|
.map(MngListDto::getMapSheetNum)
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
|
||||||
|
// 기준연도 기준으로 비교연도에 있는것만 담기 (도협번호) 결국 비교년도와 개수가 같아짐
|
||||||
|
List<String> mapSheetNums =
|
||||||
|
targetMngList.stream()
|
||||||
|
.map(MngListDto::getMapSheetNum)
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.filter(compareSet1::contains)
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
int intersection = mapSheetNums.size();
|
||||||
|
|
||||||
|
Set<String> intersectionSet = new HashSet<>(mapSheetNums);
|
||||||
|
|
||||||
|
// 비교연도 같은거 담기(dto list)
|
||||||
|
compareMngList =
|
||||||
|
compareMngList.stream()
|
||||||
|
.filter(c -> c.getMapSheetNum() != null)
|
||||||
|
.filter(c -> intersectionSet.contains(c.getMapSheetNum()))
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
// 기준연도 같은거 담기(dto list)
|
||||||
|
List<MngListDto> filteredTargetMngList =
|
||||||
|
targetMngList.stream()
|
||||||
|
.filter(t -> t.getMapSheetNum() != null)
|
||||||
|
.filter(t -> intersectionSet.contains(t.getMapSheetNum()))
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
// 로그
|
||||||
|
int targetOnlyExcluded = targetTotal - intersection;
|
||||||
|
int compareOnly = compareTotalAfterFallback - intersection;
|
||||||
|
|
||||||
|
log.info(
|
||||||
|
"""
|
||||||
|
===== MapSheet Year Comparison =====
|
||||||
|
target Total: {}
|
||||||
|
compare Total(before fallback): {}
|
||||||
|
compare Total(after fallback): {}
|
||||||
|
Intersection: {}
|
||||||
|
target Only (Excluded): {}
|
||||||
|
compare Only: {}
|
||||||
|
====================================
|
||||||
|
""",
|
||||||
|
targetTotal,
|
||||||
|
compareTotalBeforeFallback,
|
||||||
|
compareTotalAfterFallback,
|
||||||
|
intersection,
|
||||||
|
targetOnlyExcluded,
|
||||||
|
compareOnly);
|
||||||
|
|
||||||
|
if (mapSheetNums.isEmpty()) {
|
||||||
|
throw new CustomApiException("NOT_FOUND_MAP_SHEET_NUM", HttpStatus.NOT_FOUND);
|
||||||
|
}
|
||||||
|
|
||||||
|
// compare 기준 geojson 생성
|
||||||
|
Scene compareScene =
|
||||||
|
getSceneInference(
|
||||||
|
compareMngList, compareYyyy.toString(), mapSheetScope, req.getDetectOption());
|
||||||
|
|
||||||
|
// target 기준 geojson 생성
|
||||||
|
Scene targetScene =
|
||||||
|
getSceneInference(
|
||||||
|
targetYyyy.toString(), mapSheetNums, mapSheetScope, req.getDetectOption());
|
||||||
|
|
||||||
|
log.info("비교년도 geojson 파일 validation ===== {}", compareScene.getFilePath());
|
||||||
|
GeoJsonValidator.validateWithRequested(compareScene.getFilePath(), mapSheetNums);
|
||||||
|
|
||||||
|
log.info("기준년도 geojson 파일 validation ===== {}", targetScene.getFilePath());
|
||||||
|
GeoJsonValidator.validateWithRequested(targetScene.getFilePath(), mapSheetNums);
|
||||||
|
|
||||||
|
// 추론 실행
|
||||||
|
return executeInference(req, filteredTargetMngList, mapSheetNums, compareScene, targetScene);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* learn 테이블 저장 및 AI 추론 API 호출
|
||||||
|
*
|
||||||
|
* @param req
|
||||||
|
* @param targetDtoList
|
||||||
|
* @param filteredTargetList
|
||||||
|
* @param modelComparePath
|
||||||
|
* @param modelTargetPath
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private UUID executeInference(
|
||||||
|
InferenceResultDto.RegReq req,
|
||||||
|
List<MngListDto> targetDtoList,
|
||||||
|
List<String> filteredTargetList,
|
||||||
|
Scene modelComparePath,
|
||||||
|
Scene modelTargetPath) {
|
||||||
|
Set<String> filteredSet = new HashSet<>(filteredTargetList);
|
||||||
|
|
||||||
List<MngListDto> newTargetList =
|
List<MngListDto> newTargetList =
|
||||||
targetDtoList.stream()
|
targetDtoList.stream()
|
||||||
.filter(m -> m.getMapSheetNum() != null)
|
.filter(m -> m.getMapSheetNum() != null)
|
||||||
.filter(m -> sceneIdSet.contains(m.getMapSheetNum()))
|
.filter(m -> filteredSet.contains(m.getMapSheetNum()))
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
// 목록 및 추론 대상 도엽정보 저장
|
// 추론 실행 목록 테이블 저장, 도엽목록별 상태 체크 테이블 저장
|
||||||
UUID uuid = inferenceResultCoreService.saveInferenceInfo(req, newTargetList);
|
UUID uuid = inferenceResultCoreService.saveInferenceInfo(req, newTargetList);
|
||||||
|
|
||||||
// ai 서버에 전달할 파라미터 생성
|
// 추론 AI 전달 파라미터 생성
|
||||||
pred_requests_areas predRequestsAreas = new pred_requests_areas();
|
pred_requests_areas predRequestsAreas = new pred_requests_areas();
|
||||||
predRequestsAreas.setInput1_year(req.getCompareYyyy());
|
predRequestsAreas.setInput1_year(req.getCompareYyyy());
|
||||||
predRequestsAreas.setInput2_year(req.getTargetYyyy());
|
predRequestsAreas.setInput2_year(req.getTargetYyyy());
|
||||||
predRequestsAreas.setInput1_scene_path(modelComparePath.getFilePath());
|
predRequestsAreas.setInput1_scene_path(modelComparePath.getFilePath());
|
||||||
predRequestsAreas.setInput2_scene_path(modelTargetPath.getFilePath());
|
predRequestsAreas.setInput2_scene_path(modelTargetPath.getFilePath());
|
||||||
|
|
||||||
|
// 모델정보 조회 dto 생성 후 반환
|
||||||
InferenceSendDto m1 = this.getModelInfo(req.getModel1Uuid());
|
InferenceSendDto m1 = this.getModelInfo(req.getModel1Uuid());
|
||||||
m1.setPred_requests_areas(predRequestsAreas);
|
m1.setPred_requests_areas(predRequestsAreas);
|
||||||
|
|
||||||
log.info("[INFERENCE] Start m1 = {}", m1);
|
log.info("[INFERENCE] Start m1 = {}", m1);
|
||||||
m1.setPred_requests_areas(predRequestsAreas);
|
|
||||||
|
|
||||||
// ai 추론 실행 api 호출
|
// AI 호출
|
||||||
Long batchId = ensureAccepted(m1);
|
Long batchId = inferenceCommonService.ensureAccepted(m1);
|
||||||
|
|
||||||
// ai 추론 실행후 응답값 update
|
|
||||||
SaveInferenceAiDto saveInferenceAiDto = new SaveInferenceAiDto();
|
SaveInferenceAiDto saveInferenceAiDto = new SaveInferenceAiDto();
|
||||||
saveInferenceAiDto.setUuid(uuid);
|
saveInferenceAiDto.setUuid(uuid);
|
||||||
saveInferenceAiDto.setBatchId(batchId);
|
saveInferenceAiDto.setBatchId(batchId);
|
||||||
@@ -230,13 +449,15 @@ public class InferenceResultService {
|
|||||||
saveInferenceAiDto.setModelComparePath(modelComparePath.getFilePath());
|
saveInferenceAiDto.setModelComparePath(modelComparePath.getFilePath());
|
||||||
saveInferenceAiDto.setModelTargetPath(modelTargetPath.getFilePath());
|
saveInferenceAiDto.setModelTargetPath(modelTargetPath.getFilePath());
|
||||||
saveInferenceAiDto.setModelStartDttm(ZonedDateTime.now());
|
saveInferenceAiDto.setModelStartDttm(ZonedDateTime.now());
|
||||||
|
|
||||||
|
// AI 호출 하고 리턴 받은 정보 추론 실행 목록 테이블에 업데이트
|
||||||
inferenceResultCoreService.update(saveInferenceAiDto);
|
inferenceResultCoreService.update(saveInferenceAiDto);
|
||||||
|
|
||||||
return uuid;
|
return uuid;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 변화탐지 실행 정보 생성
|
* 변화탐지 실행 정보 생성 TODO 미사용, 새로운 추론실행 로직 테스트후 삭제 해야합니다.
|
||||||
*
|
*
|
||||||
* @param req
|
* @param req
|
||||||
*/
|
*/
|
||||||
@@ -244,7 +465,7 @@ public class InferenceResultService {
|
|||||||
public UUID saveInferenceInfo(InferenceResultDto.RegReq req) {
|
public UUID saveInferenceInfo(InferenceResultDto.RegReq req) {
|
||||||
|
|
||||||
// 변화탐지 실행 가능 기준 년도 조회
|
// 변화탐지 실행 가능 기준 년도 조회
|
||||||
List<MngListDto> targetList = mapSheetMngCoreService.getHstMapSheetList(req);
|
List<MngListDto> targetList = null; // mapSheetMngCoreService.getHstMapSheetList(req);
|
||||||
|
|
||||||
if (targetList.isEmpty()) {
|
if (targetList.isEmpty()) {
|
||||||
throw new CustomApiException("NOT_FOUND_TARGET_YEAR", HttpStatus.NOT_FOUND);
|
throw new CustomApiException("NOT_FOUND_TARGET_YEAR", HttpStatus.NOT_FOUND);
|
||||||
@@ -265,31 +486,31 @@ public class InferenceResultService {
|
|||||||
|
|
||||||
List<TotalListDto> totalNumList = new ArrayList<>();
|
List<TotalListDto> totalNumList = new ArrayList<>();
|
||||||
|
|
||||||
// if (DetectOption.EXCL.getId().equals(req.getDetectOption())) {
|
if (DetectOption.EXCL.getId().equals(req.getDetectOption())) {
|
||||||
// // "추론제외" 일때 전년도 이전 값이 있어도 전년도 도엽이 없으면 비교 안함
|
// "추론제외" 일때 전년도 이전 값이 있어도 전년도 도엽이 없으면 비교 안함
|
||||||
// for (MngListCompareDto dto : compareList) {
|
for (MngListCompareDto dto : compareList) {
|
||||||
// if (Objects.equals(dto.getBeforeYear(), req.getCompareYyyy())) {
|
if (Objects.equals(dto.getBeforeYear(), req.getCompareYyyy())) {
|
||||||
// TotalListDto totalDto = new TotalListDto();
|
TotalListDto totalDto = new TotalListDto();
|
||||||
// totalDto.setBeforeYear(dto.getBeforeYear() == null ? 0 : dto.getBeforeYear());
|
totalDto.setBeforeYear(dto.getBeforeYear() == null ? 0 : dto.getBeforeYear());
|
||||||
// totalDto.setMapSheetNum(dto.getMapSheetNum());
|
totalDto.setMapSheetNum(dto.getMapSheetNum());
|
||||||
// totalNumList.add(totalDto);
|
totalNumList.add(totalDto);
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
// } else if (DetectOption.PREV.getId().equals(req.getDetectOption())) {
|
} else if (DetectOption.PREV.getId().equals(req.getDetectOption())) {
|
||||||
// // "이전 년도 도엽 사용" 이면 전년도 이전 도엽도 사용
|
// "이전 년도 도엽 사용" 이면 전년도 이전 도엽도 사용
|
||||||
// for (MngListCompareDto dto : compareList) {
|
for (MngListCompareDto dto : compareList) {
|
||||||
// if (dto.getBeforeYear() != 0) {
|
if (dto.getBeforeYear() != 0) {
|
||||||
// TotalListDto totalDto = new TotalListDto();
|
TotalListDto totalDto = new TotalListDto();
|
||||||
// totalDto.setBeforeYear(dto.getBeforeYear() == null ? 0 : dto.getBeforeYear());
|
totalDto.setBeforeYear(dto.getBeforeYear() == null ? 0 : dto.getBeforeYear());
|
||||||
// totalDto.setMapSheetNum(dto.getMapSheetNum());
|
totalDto.setMapSheetNum(dto.getMapSheetNum());
|
||||||
// totalNumList.add(totalDto);
|
totalNumList.add(totalDto);
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
//
|
|
||||||
// if (totalNumList.isEmpty()) {
|
if (totalNumList.isEmpty()) {
|
||||||
// throw new CustomApiException("NOT_FOUND_COMPARE_YEAR", HttpStatus.NOT_FOUND);
|
throw new CustomApiException("NOT_FOUND_COMPARE_YEAR", HttpStatus.NOT_FOUND);
|
||||||
// }
|
}
|
||||||
|
|
||||||
// 사용할 영상파일 년도 기록 및 추론에 포함되는지 설정
|
// 사용할 영상파일 년도 기록 및 추론에 포함되는지 설정
|
||||||
for (MngListDto target : targetList) {
|
for (MngListDto target : targetList) {
|
||||||
@@ -362,7 +583,7 @@ public class InferenceResultService {
|
|||||||
m1.setPred_requests_areas(predRequestsAreas);
|
m1.setPred_requests_areas(predRequestsAreas);
|
||||||
|
|
||||||
// ai 추론 실행 api 호출
|
// ai 추론 실행 api 호출
|
||||||
Long batchId = ensureAccepted(m1);
|
Long batchId = inferenceCommonService.ensureAccepted(m1);
|
||||||
|
|
||||||
// ai 추론 실행후 응답값 update
|
// ai 추론 실행후 응답값 update
|
||||||
SaveInferenceAiDto saveInferenceAiDto = new SaveInferenceAiDto();
|
SaveInferenceAiDto saveInferenceAiDto = new SaveInferenceAiDto();
|
||||||
@@ -379,142 +600,6 @@ public class InferenceResultService {
|
|||||||
return uuid;
|
return uuid;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 비교년도 탐지 제이터 옵션 별로 조회하여 req에 적용
|
|
||||||
private List<MapSheetNumDto> createdMngDto(
|
|
||||||
InferenceResultDto.RegReq req, List<MngListDto> targetList) {
|
|
||||||
List<String> mapTargetIds = new ArrayList<>();
|
|
||||||
|
|
||||||
targetList.forEach(
|
|
||||||
hstMapSheet -> {
|
|
||||||
// 비교년도는 target 년도 기준으로 가져옴 파라미터 만들기
|
|
||||||
mapTargetIds.add(hstMapSheet.getMapSheetNum());
|
|
||||||
});
|
|
||||||
|
|
||||||
// 비교년도 조회
|
|
||||||
List<String> mapCompareIds = new ArrayList<>();
|
|
||||||
List<MngListCompareDto> compareList =
|
|
||||||
mapSheetMngCoreService.getByHstMapSheetCompareList(req.getCompareYyyy(), mapTargetIds);
|
|
||||||
|
|
||||||
for (MngListCompareDto dto : compareList) {
|
|
||||||
// 추론 제외일때 이전년도 파일이 없으면 제외
|
|
||||||
if (req.getDetectOption().equals(DetectOption.EXCL.getId())) {
|
|
||||||
int targetYear = req.getTargetYyyy() - 1;
|
|
||||||
if (dto.getBeforeYear() != targetYear) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 비교년도는 target 년도 기준으로 가져옴
|
|
||||||
mapCompareIds.add(dto.getMapSheetNum());
|
|
||||||
}
|
|
||||||
|
|
||||||
Set<String> compareSet =
|
|
||||||
mapCompareIds.stream()
|
|
||||||
.filter(Objects::nonNull)
|
|
||||||
.map(String::trim) // 공백/개행 방지
|
|
||||||
.collect(Collectors.toSet());
|
|
||||||
|
|
||||||
// target 기준 compare 비교하여 서로 있는것만 저장
|
|
||||||
List<String> commonIds =
|
|
||||||
mapTargetIds.stream()
|
|
||||||
.filter(Objects::nonNull)
|
|
||||||
.map(String::trim)
|
|
||||||
.filter(compareSet::contains)
|
|
||||||
.toList();
|
|
||||||
|
|
||||||
Set<String> commonIdSet =
|
|
||||||
commonIds.stream().filter(Objects::nonNull).map(String::trim).collect(Collectors.toSet());
|
|
||||||
|
|
||||||
// 저장하기위해 파라미터 다시 구성
|
|
||||||
List<MapSheetNumDto> mapSheetNum =
|
|
||||||
targetList.stream()
|
|
||||||
.filter(dto -> dto.getMapSheetNum() != null)
|
|
||||||
.filter(dto -> commonIdSet.contains(dto.getMapSheetNum().trim()))
|
|
||||||
.map(
|
|
||||||
dto -> {
|
|
||||||
MapSheetNumDto mapSheetNumDto = new MapSheetNumDto();
|
|
||||||
mapSheetNumDto.setMapSheetNum(dto.getMapSheetNum());
|
|
||||||
mapSheetNumDto.setMapSheetName(dto.getMapSheetName());
|
|
||||||
return mapSheetNumDto;
|
|
||||||
})
|
|
||||||
.toList();
|
|
||||||
|
|
||||||
return mapSheetNum;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 추론 AI API 호출
|
|
||||||
*
|
|
||||||
* @param dto
|
|
||||||
*/
|
|
||||||
// 같은함수가 왜 두개지
|
|
||||||
private Long ensureAccepted(InferenceSendDto dto) {
|
|
||||||
|
|
||||||
if (dto == null) {
|
|
||||||
log.warn("not InferenceSendDto dto");
|
|
||||||
throw new CustomApiException("BAD_REQUEST", HttpStatus.BAD_REQUEST);
|
|
||||||
}
|
|
||||||
|
|
||||||
// [중복]운영환경일때 경로수정 dean 260226
|
|
||||||
if (profile != null && profile.equals("prod")) {
|
|
||||||
log.info("========================================================");
|
|
||||||
log.info("[CHANGE INFERENCE] profile = {} Inforence req", profile);
|
|
||||||
log.info("========================================================");
|
|
||||||
log.info("");
|
|
||||||
}
|
|
||||||
|
|
||||||
// 1) 요청 로그
|
|
||||||
try {
|
|
||||||
log.debug("Inference request dto={}", objectMapper.writeValueAsString(dto));
|
|
||||||
} catch (JsonProcessingException e) {
|
|
||||||
log.warn("Failed to serialize inference dto", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2) local 환경 임시 처리
|
|
||||||
// if ("local".equals(profile)) {
|
|
||||||
// if (dto.getPred_requests_areas() == null) {
|
|
||||||
// throw new IllegalStateException("pred_requests_areas is null");
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// dto.getPred_requests_areas().setInput1_scene_path("/kamco-nfs/requests/2023_local.geojson");
|
|
||||||
//
|
|
||||||
// dto.getPred_requests_areas().setInput2_scene_path("/kamco-nfs/requests/2024_local.geojson");
|
|
||||||
// }
|
|
||||||
|
|
||||||
// 3) HTTP 호출
|
|
||||||
HttpHeaders headers = new HttpHeaders();
|
|
||||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
|
||||||
headers.setAccept(List.of(MediaType.APPLICATION_JSON));
|
|
||||||
|
|
||||||
ExternalCallResult<String> result =
|
|
||||||
externalHttpClient.call(inferenceUrl, HttpMethod.POST, dto, headers, String.class);
|
|
||||||
|
|
||||||
if (result.statusCode() < 200 || result.statusCode() >= 300) {
|
|
||||||
log.error("Inference API failed. status={}, body={}", result.statusCode(), result.body());
|
|
||||||
throw new CustomApiException("BAD_GATEWAY", HttpStatus.BAD_GATEWAY);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 4) 응답 파싱
|
|
||||||
try {
|
|
||||||
List<Map<String, Object>> list =
|
|
||||||
objectMapper.readValue(result.body(), new TypeReference<>() {});
|
|
||||||
|
|
||||||
if (list.isEmpty()) {
|
|
||||||
throw new IllegalStateException("Inference response is empty");
|
|
||||||
}
|
|
||||||
|
|
||||||
Object batchIdObj = list.get(0).get("batch_id");
|
|
||||||
if (batchIdObj == null) {
|
|
||||||
throw new IllegalStateException("batch_id not found in response");
|
|
||||||
}
|
|
||||||
|
|
||||||
return Long.valueOf(batchIdObj.toString());
|
|
||||||
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.error("Failed to parse inference response. body={}", result.body(), e);
|
|
||||||
throw new CustomApiException("INVALID_INFERENCE_RESPONSE", HttpStatus.BAD_GATEWAY);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 모델정보 조회 dto 생성 후 반환
|
* 모델정보 조회 dto 생성 후 반환
|
||||||
*
|
*
|
||||||
@@ -523,6 +608,7 @@ public class InferenceResultService {
|
|||||||
*/
|
*/
|
||||||
private InferenceSendDto getModelInfo(UUID uuid) {
|
private InferenceSendDto getModelInfo(UUID uuid) {
|
||||||
|
|
||||||
|
// 모델정보 조회
|
||||||
Basic modelInfo = modelMngCoreService.findByModelUuid(uuid);
|
Basic modelInfo = modelMngCoreService.findByModelUuid(uuid);
|
||||||
|
|
||||||
String cdModelPath = "";
|
String cdModelPath = "";
|
||||||
@@ -575,9 +661,37 @@ 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) {
|
||||||
return mapSheetMngCoreService.getSceneInference(
|
|
||||||
yyyy, mapSheetNums, mapSheetScope, detectOption);
|
// geojson 생성시 필요한 영상파일 정보 조회
|
||||||
|
List<ImageFeature> features =
|
||||||
|
mapSheetMngCoreService.loadSceneInferenceBySheets(yyyy, mapSheetNums);
|
||||||
|
|
||||||
|
if (features == null || features.isEmpty()) {
|
||||||
|
log.warn(
|
||||||
|
"NOT_FOUND_MAP_SHEET_NUM : yyyy={}, scenesSize={}",
|
||||||
|
yyyy,
|
||||||
|
mapSheetNums == null ? 0 : mapSheetNums.size());
|
||||||
|
throw new CustomApiException("NOT_FOUND_MAP_SHEET_NUM", HttpStatus.NOT_FOUND);
|
||||||
|
}
|
||||||
|
return writeSceneGeoJson(yyyy, mapSheetScope, detectOption, features);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 년도 별로 조회하여 geojson 파일 생성
|
||||||
|
*
|
||||||
|
* @param yearDtos
|
||||||
|
* @param yyyy
|
||||||
|
* @param mapSheetScope
|
||||||
|
* @param detectOption
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private Scene getSceneInference(
|
||||||
|
List<MngListDto> yearDtos, String yyyy, String mapSheetScope, DetectOption detectOption) {
|
||||||
|
|
||||||
|
List<ImageFeature> features =
|
||||||
|
mapSheetMngCoreService.loadSceneInferenceByFallbackYears(yearDtos);
|
||||||
|
return writeSceneGeoJson(yyyy, mapSheetScope, detectOption, features);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -656,11 +770,17 @@ public class InferenceResultService {
|
|||||||
return inferenceResultCoreService.listGetScenes5k(id);
|
return inferenceResultCoreService.listGetScenes5k(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 추론 서버 현황 cpu, gpu 확인
|
||||||
|
*
|
||||||
|
* @return 서버 정보
|
||||||
|
*/
|
||||||
public List<InferenceServerStatusDto> getInferenceServerStatusList() {
|
public List<InferenceServerStatusDto> getInferenceServerStatusList() {
|
||||||
|
|
||||||
String[] serverNames = inferenceServerName.split(",");
|
String[] serverNames = inferenceServerName.split(",");
|
||||||
int serveCnt = serverNames.length;
|
int serveCnt = serverNames.length;
|
||||||
|
|
||||||
|
// 서버정보 조회
|
||||||
List<InferenceServerStatusDto> dtoList =
|
List<InferenceServerStatusDto> dtoList =
|
||||||
inferenceResultCoreService.getInferenceServerStatusList();
|
inferenceResultCoreService.getInferenceServerStatusList();
|
||||||
int size = dtoList.size();
|
int size = dtoList.size();
|
||||||
@@ -670,6 +790,7 @@ public class InferenceResultService {
|
|||||||
System.out.println("size =" + size);
|
System.out.println("size =" + size);
|
||||||
|
|
||||||
if (size == 0) {
|
if (size == 0) {
|
||||||
|
// 서버 정보가 없을때
|
||||||
for (int k = 0; k < serveCnt; k++) {
|
for (int k = 0; k < serveCnt; k++) {
|
||||||
InferenceServerStatusDto dto = new InferenceServerStatusDto();
|
InferenceServerStatusDto dto = new InferenceServerStatusDto();
|
||||||
dto.setServerName(serverNames[k]);
|
dto.setServerName(serverNames[k]);
|
||||||
@@ -737,17 +858,35 @@ public class InferenceResultService {
|
|||||||
return inferenceResultCoreService.getInferenceResultInfo(uuid);
|
return inferenceResultCoreService.getInferenceResultInfo(uuid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 분류별 탐지건수 조회
|
||||||
|
*
|
||||||
|
* @param uuid 추론 uuid
|
||||||
|
* @return 분류별 탐지건수 정보
|
||||||
|
*/
|
||||||
public List<Dashboard> getInferenceClassCountList(UUID uuid) {
|
public List<Dashboard> getInferenceClassCountList(UUID uuid) {
|
||||||
return inferenceResultCoreService.getInferenceClassCountList(uuid);
|
return inferenceResultCoreService.getInferenceClassCountList(uuid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 추론결과 geom 목록 조회
|
||||||
|
*
|
||||||
|
* @param uuid 추론 uuid
|
||||||
|
* @param searchGeoReq 추론 결과 상세화면 geom 조회 조건
|
||||||
|
* @return geom 목록 정보
|
||||||
|
*/
|
||||||
public Page<Geom> getInferenceGeomList(UUID uuid, SearchGeoReq searchGeoReq) {
|
public Page<Geom> getInferenceGeomList(UUID uuid, SearchGeoReq searchGeoReq) {
|
||||||
return inferenceResultCoreService.getInferenceGeomList(uuid, searchGeoReq);
|
return inferenceResultCoreService.getInferenceGeomList(uuid, searchGeoReq);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 추론 종료 */
|
/**
|
||||||
|
* 추론 종료
|
||||||
|
*
|
||||||
|
* @return 호출한 uuid
|
||||||
|
*/
|
||||||
@Transactional
|
@Transactional
|
||||||
public UUID deleteInferenceEnd() {
|
public UUID deleteInferenceEnd() {
|
||||||
|
// 추론 진행중인지 확인
|
||||||
SaveInferenceAiDto dto = inferenceResultCoreService.getProcessing();
|
SaveInferenceAiDto dto = inferenceResultCoreService.getProcessing();
|
||||||
if (dto == null) {
|
if (dto == null) {
|
||||||
throw new CustomApiException("NOT_FOUND", HttpStatus.NOT_FOUND);
|
throw new CustomApiException("NOT_FOUND", HttpStatus.NOT_FOUND);
|
||||||
@@ -759,13 +898,15 @@ public class InferenceResultService {
|
|||||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||||
headers.setAccept(List.of(MediaType.APPLICATION_JSON));
|
headers.setAccept(List.of(MediaType.APPLICATION_JSON));
|
||||||
|
|
||||||
|
// 종료 api 호출
|
||||||
ExternalCallResult<String> result =
|
ExternalCallResult<String> result =
|
||||||
externalHttpClient.call(url, HttpMethod.DELETE, dto, headers, String.class);
|
externalHttpClient.callLong(url, HttpMethod.DELETE, dto, headers, String.class);
|
||||||
|
|
||||||
if (!result.success()) {
|
if (!result.success()) {
|
||||||
throw new CustomApiException("BAD_GATEWAY", HttpStatus.BAD_GATEWAY);
|
throw new CustomApiException("BAD_GATEWAY", HttpStatus.BAD_GATEWAY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 추론 정보 테이블 update
|
||||||
SaveInferenceAiDto request = new SaveInferenceAiDto();
|
SaveInferenceAiDto request = new SaveInferenceAiDto();
|
||||||
request.setStatus(Status.FORCED_END.getId());
|
request.setStatus(Status.FORCED_END.getId());
|
||||||
request.setUuid(dto.getUuid());
|
request.setUuid(dto.getUuid());
|
||||||
@@ -780,14 +921,17 @@ public class InferenceResultService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 추론결과 shp zip 파일 다운로드 경로 생성
|
* 추론결과 shp zip 파일 확인하여 다운로드 경로 생성
|
||||||
*
|
*
|
||||||
* @param uuid
|
* @param uuid 추론 uuid
|
||||||
* @return
|
* @return 32자 추론 uid, shp 파일 경로
|
||||||
*/
|
*/
|
||||||
public Map<String, Object> shpDownloadPath(UUID uuid) {
|
public Map<String, Object> shpDownloadPath(UUID uuid) {
|
||||||
|
// 추론정보 조회
|
||||||
InferenceLearnDto dto = inferenceResultCoreService.getInferenceUid(uuid);
|
InferenceLearnDto dto = inferenceResultCoreService.getInferenceUid(uuid);
|
||||||
String uid = dto.getUid();
|
String uid = dto.getUid();
|
||||||
|
|
||||||
|
// 파일 경로 생성
|
||||||
Path path = Path.of(datasetDir).resolve(uid).resolve("merge").resolve(uid + ".zip");
|
Path path = Path.of(datasetDir).resolve(uid).resolve("merge").resolve(uid + ".zip");
|
||||||
|
|
||||||
Map<String, Object> downloadMap = new HashMap<>();
|
Map<String, Object> downloadMap = new HashMap<>();
|
||||||
@@ -809,7 +953,7 @@ public class InferenceResultService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 실행중인 추론 도엽명 목록
|
* 분석중인 추론 도엽명 목록
|
||||||
*
|
*
|
||||||
* @param uuid uuid
|
* @param uuid uuid
|
||||||
* @return
|
* @return
|
||||||
@@ -817,4 +961,76 @@ public class InferenceResultService {
|
|||||||
public List<String> getInferenceRunMapId(UUID uuid) {
|
public List<String> getInferenceRunMapId(UUID uuid) {
|
||||||
return inferenceResultCoreService.getInferenceRunMapId(uuid);
|
return inferenceResultCoreService.getInferenceRunMapId(uuid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 파일 경로/이름 , 파일 생성 , 도엽번호 반환
|
||||||
|
*
|
||||||
|
* @param yyyy
|
||||||
|
* @param mapSheetScope
|
||||||
|
* @param detectOption
|
||||||
|
* @param sceneInference
|
||||||
|
* @return Scene
|
||||||
|
*/
|
||||||
|
private Scene writeSceneGeoJson(
|
||||||
|
String yyyy,
|
||||||
|
String mapSheetScope,
|
||||||
|
DetectOption detectOption,
|
||||||
|
List<ImageFeature> sceneInference) {
|
||||||
|
|
||||||
|
boolean isAll = MapSheetScope.ALL.getId().equals(mapSheetScope);
|
||||||
|
String optionSuffix = buildOptionSuffix(detectOption);
|
||||||
|
|
||||||
|
String targetDir =
|
||||||
|
"local".equals(activeEnv) ? System.getProperty("user.home") + "/geojson" : inferenceDir;
|
||||||
|
|
||||||
|
// 파일명 생성
|
||||||
|
String filename =
|
||||||
|
isAll
|
||||||
|
? String.format("%s_%s_ALL%s.geojson", yyyy, activeEnv, optionSuffix)
|
||||||
|
: String.format("%s_%s%s.geojson", yyyy, activeEnv, optionSuffix);
|
||||||
|
|
||||||
|
Path outputPath = Paths.get(targetDir, filename);
|
||||||
|
|
||||||
|
try {
|
||||||
|
log.info("create Directories outputPath: {}", outputPath);
|
||||||
|
log.info(
|
||||||
|
"activeEnv={}, inferenceDir={}, targetDir={}, filename={}",
|
||||||
|
activeEnv,
|
||||||
|
inferenceDir,
|
||||||
|
targetDir,
|
||||||
|
filename);
|
||||||
|
log.info("outputPath={}, parent={}", outputPath.toAbsolutePath(), outputPath.getParent());
|
||||||
|
|
||||||
|
Files.createDirectories(outputPath.getParent());
|
||||||
|
|
||||||
|
new GeoJsonFileWriter()
|
||||||
|
.exportToFile(sceneInference, "scene_inference_" + yyyy, 5186, outputPath.toString());
|
||||||
|
|
||||||
|
Scene scene = new Scene();
|
||||||
|
scene.setFeatures(sceneInference);
|
||||||
|
scene.setFilePath(outputPath.toString());
|
||||||
|
return scene;
|
||||||
|
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.error(
|
||||||
|
"FAIL_CREATE_MAP_SHEET_FILE: yyyy={}, isAll={}, path={}", yyyy, isAll, outputPath, e);
|
||||||
|
throw new CustomApiException("INTERNAL_SERVER_ERROR", HttpStatus.INTERNAL_SERVER_ERROR, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* geojson 파일명 Suffix
|
||||||
|
*
|
||||||
|
* @param detectOption
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private String buildOptionSuffix(DetectOption detectOption) {
|
||||||
|
if (DetectOption.EXCL == detectOption) {
|
||||||
|
return "_EXCL";
|
||||||
|
}
|
||||||
|
if (DetectOption.PREV == detectOption) {
|
||||||
|
return "_PREV";
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,11 @@
|
|||||||
package com.kamco.cd.kamcoback.inference.service;
|
package com.kamco.cd.kamcoback.inference.service;
|
||||||
|
|
||||||
import com.kamco.cd.kamcoback.common.geometry.GeoJsonFileWriter.Scene;
|
|
||||||
import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.InferenceLearnDto;
|
import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.InferenceLearnDto;
|
||||||
import com.kamco.cd.kamcoback.inference.dto.InferenceResultShpDto;
|
import com.kamco.cd.kamcoback.inference.dto.InferenceResultShpDto;
|
||||||
import com.kamco.cd.kamcoback.postgres.core.InferenceResultCoreService;
|
import com.kamco.cd.kamcoback.postgres.core.InferenceResultCoreService;
|
||||||
import com.kamco.cd.kamcoback.postgres.core.InferenceResultShpCoreService;
|
import com.kamco.cd.kamcoback.postgres.core.InferenceResultShpCoreService;
|
||||||
import com.kamco.cd.kamcoback.postgres.core.MapSheetMngCoreService;
|
import com.kamco.cd.kamcoback.postgres.core.MapSheetMngCoreService;
|
||||||
import com.kamco.cd.kamcoback.scheduler.service.ShpPipelineService;
|
import com.kamco.cd.kamcoback.scheduler.service.ShpPipelineService;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
@@ -39,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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -57,30 +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());
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 추론 실행전 geojson 파일 생성
|
|
||||||
*
|
|
||||||
* @param yyyy
|
|
||||||
* @param mapSheetScope
|
|
||||||
* @param detectOption
|
|
||||||
* @param mapIds
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public Scene createGeojson(
|
|
||||||
String yyyy, String mapSheetScope, String detectOption, List<String> mapIds) {
|
|
||||||
Scene getSceneInference =
|
|
||||||
mapSheetMngCoreService.getSceneInference(yyyy, mapIds, mapSheetScope, detectOption);
|
|
||||||
log.info("getSceneInference: {}", getSceneInference);
|
|
||||||
return getSceneInference;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,224 +0,0 @@
|
|||||||
package com.kamco.cd.kamcoback.inference.service;
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
|
||||||
import com.fasterxml.jackson.core.type.TypeReference;
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
||||||
import com.kamco.cd.kamcoback.common.exception.CustomApiException;
|
|
||||||
import com.kamco.cd.kamcoback.common.geometry.GeoJsonFileWriter.Scene;
|
|
||||||
import com.kamco.cd.kamcoback.config.resttemplate.ExternalHttpClient;
|
|
||||||
import com.kamco.cd.kamcoback.config.resttemplate.ExternalHttpClient.ExternalCallResult;
|
|
||||||
import com.kamco.cd.kamcoback.inference.dto.InferenceSendDto;
|
|
||||||
import com.kamco.cd.kamcoback.inference.dto.InferenceSendDto.pred_requests_areas;
|
|
||||||
import com.kamco.cd.kamcoback.model.dto.ModelMngDto.Basic;
|
|
||||||
import com.kamco.cd.kamcoback.model.dto.ModelMngDto.ModelType;
|
|
||||||
import com.kamco.cd.kamcoback.postgres.core.MapSheetMngCoreService;
|
|
||||||
import com.kamco.cd.kamcoback.postgres.core.ModelMngCoreService;
|
|
||||||
import java.nio.file.Paths;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.UUID;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import lombok.extern.log4j.Log4j2;
|
|
||||||
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.stereotype.Service;
|
|
||||||
|
|
||||||
@Service
|
|
||||||
@Log4j2
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
public class InferenceRunService {
|
|
||||||
private final ExternalHttpClient externalHttpClient;
|
|
||||||
private final MapSheetMngCoreService mapSheetMngCoreService;
|
|
||||||
private final ModelMngCoreService modelMngCoreService;
|
|
||||||
private final ObjectMapper objectMapper;
|
|
||||||
|
|
||||||
@Value("${spring.profiles.active}")
|
|
||||||
private String profile;
|
|
||||||
|
|
||||||
@Value("${inference.url}")
|
|
||||||
private String inferenceUrl;
|
|
||||||
|
|
||||||
// TODO 이거 쓰는건가?
|
|
||||||
public void run(Integer compareYear, Integer targetYear, UUID modelUuid) {
|
|
||||||
|
|
||||||
List<String> compareList = mapSheetMngCoreService.getMapSheetMngHst(compareYear);
|
|
||||||
List<String> targetList = mapSheetMngCoreService.getMapSheetMngHst(targetYear);
|
|
||||||
|
|
||||||
log.info(
|
|
||||||
"hst list count compareList = {}, targetList = {}", compareList.size(), targetList.size());
|
|
||||||
|
|
||||||
Set<String> compareSet = new HashSet<>(compareList);
|
|
||||||
Set<String> targetSet = new HashSet<>(targetList);
|
|
||||||
|
|
||||||
long intersectionCount =
|
|
||||||
targetSet.stream()
|
|
||||||
.distinct()
|
|
||||||
.filter(compareSet::contains)
|
|
||||||
.count(); // compare와 target에 공통으로 존재하는 도협 수
|
|
||||||
|
|
||||||
long excludedTargetCount =
|
|
||||||
targetSet.stream()
|
|
||||||
.distinct()
|
|
||||||
.filter(s -> !compareSet.contains(s))
|
|
||||||
.count(); // target 에만 존재하는 도협 수 (compare 에는 없음)
|
|
||||||
|
|
||||||
long onlyCompareCount =
|
|
||||||
compareSet.stream()
|
|
||||||
.distinct()
|
|
||||||
.filter(s -> !targetSet.contains(s))
|
|
||||||
.count(); // compare 에만 존재하는 도협 수 (target 에는 없음)
|
|
||||||
|
|
||||||
log.info(
|
|
||||||
"""
|
|
||||||
===== MapSheet Year Comparison =====
|
|
||||||
target Total: {}
|
|
||||||
compare Total: {}
|
|
||||||
Intersection: {}
|
|
||||||
target Only (Excluded from compare): {}
|
|
||||||
compare Only: {}
|
|
||||||
====================================
|
|
||||||
""",
|
|
||||||
targetSet.size(),
|
|
||||||
compareSet.size(),
|
|
||||||
intersectionCount,
|
|
||||||
excludedTargetCount,
|
|
||||||
onlyCompareCount);
|
|
||||||
|
|
||||||
List<String> filteredTargetList =
|
|
||||||
targetSet.stream() // target 기준으로
|
|
||||||
.filter(compareSet::contains) // compare에 있는 도협만 남김
|
|
||||||
.toList();
|
|
||||||
|
|
||||||
Scene modelComparePath = getSceneInference(compareYear.toString(), filteredTargetList, "", "");
|
|
||||||
|
|
||||||
Scene modelTargetPath = getSceneInference(targetYear.toString(), filteredTargetList, "", "");
|
|
||||||
|
|
||||||
// ai 서버에 전달할 파라미터 생성
|
|
||||||
pred_requests_areas predRequestsAreas = new pred_requests_areas();
|
|
||||||
predRequestsAreas.setInput1_year(compareYear);
|
|
||||||
predRequestsAreas.setInput2_year(targetYear);
|
|
||||||
predRequestsAreas.setInput1_scene_path(modelComparePath.getFilePath());
|
|
||||||
predRequestsAreas.setInput2_scene_path(modelTargetPath.getFilePath());
|
|
||||||
|
|
||||||
InferenceSendDto m1 = this.getModelInfo(modelUuid);
|
|
||||||
m1.setPred_requests_areas(predRequestsAreas);
|
|
||||||
|
|
||||||
// ai 추론 실행 api 호출
|
|
||||||
Long batchId = ensureAccepted(m1);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Scene getSceneInference(
|
|
||||||
String yyyy, List<String> mapSheetNums, String mapSheetScope, String detectOption) {
|
|
||||||
return mapSheetMngCoreService.getSceneInference(
|
|
||||||
yyyy, mapSheetNums, mapSheetScope, detectOption);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 추론 AI API 호출
|
|
||||||
*
|
|
||||||
* @param dto
|
|
||||||
*/
|
|
||||||
private Long ensureAccepted(InferenceSendDto dto) {
|
|
||||||
|
|
||||||
if (dto == null) {
|
|
||||||
log.warn("not InferenceSendDto dto");
|
|
||||||
throw new CustomApiException("BAD_REQUEST", HttpStatus.BAD_REQUEST);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 1) 요청 로그
|
|
||||||
try {
|
|
||||||
log.info("Inference request dto={}", objectMapper.writeValueAsString(dto));
|
|
||||||
} catch (JsonProcessingException e) {
|
|
||||||
log.warn("Failed to serialize inference dto", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3) HTTP 호출
|
|
||||||
HttpHeaders headers = new HttpHeaders();
|
|
||||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
|
||||||
headers.setAccept(List.of(MediaType.APPLICATION_JSON));
|
|
||||||
|
|
||||||
ExternalCallResult<String> result =
|
|
||||||
externalHttpClient.call(inferenceUrl, HttpMethod.POST, dto, headers, String.class);
|
|
||||||
|
|
||||||
if (result.statusCode() < 200 || result.statusCode() >= 300) {
|
|
||||||
log.error("Inference API failed. status={}, body={}", result.statusCode(), result.body());
|
|
||||||
throw new CustomApiException("BAD_GATEWAY", HttpStatus.BAD_GATEWAY);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 4) 응답 파싱
|
|
||||||
try {
|
|
||||||
List<Map<String, Object>> list =
|
|
||||||
objectMapper.readValue(result.body(), new TypeReference<>() {});
|
|
||||||
|
|
||||||
if (list.isEmpty()) {
|
|
||||||
throw new IllegalStateException("Inference response is empty");
|
|
||||||
}
|
|
||||||
|
|
||||||
Object batchIdObj = list.get(0).get("batch_id");
|
|
||||||
if (batchIdObj == null) {
|
|
||||||
throw new IllegalStateException("batch_id not found in response");
|
|
||||||
}
|
|
||||||
|
|
||||||
return Long.valueOf(batchIdObj.toString());
|
|
||||||
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.error("Failed to parse inference response. body={}", result.body(), e);
|
|
||||||
throw new CustomApiException("INVALID_INFERENCE_RESPONSE", HttpStatus.BAD_GATEWAY);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 모델정보 조회 dto 생성 후 반환
|
|
||||||
*
|
|
||||||
* @param uuid
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
private InferenceSendDto getModelInfo(UUID uuid) {
|
|
||||||
|
|
||||||
Basic modelInfo = modelMngCoreService.findByModelUuid(uuid);
|
|
||||||
|
|
||||||
String cdModelPath = "";
|
|
||||||
String cdModelConfigPath = "";
|
|
||||||
String cdClsModelPath = "";
|
|
||||||
|
|
||||||
if (modelInfo.getCdModelPath() != null && modelInfo.getCdModelFileName() != null) {
|
|
||||||
cdModelPath =
|
|
||||||
Paths.get(modelInfo.getCdModelPath(), modelInfo.getCdModelFileName()).toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (modelInfo.getCdModelConfig() != null && modelInfo.getCdModelConfigFileName() != null) {
|
|
||||||
cdModelConfigPath =
|
|
||||||
Paths.get(modelInfo.getCdModelConfig(), modelInfo.getCdModelConfigFileName()).toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (modelInfo.getClsModelPath() != null && modelInfo.getClsModelFileName() != null) {
|
|
||||||
cdClsModelPath =
|
|
||||||
Paths.get(modelInfo.getClsModelPath(), modelInfo.getClsModelFileName()).toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
String modelType = "";
|
|
||||||
|
|
||||||
if (modelInfo.getModelType().equals(ModelType.G1.getId())) {
|
|
||||||
modelType = ModelType.G1.getId();
|
|
||||||
} else if (modelInfo.getModelType().equals(ModelType.G2.getId())) {
|
|
||||||
modelType = ModelType.G2.getId();
|
|
||||||
} else {
|
|
||||||
modelType = ModelType.G3.getId();
|
|
||||||
}
|
|
||||||
|
|
||||||
InferenceSendDto sendDto = new InferenceSendDto();
|
|
||||||
sendDto.setModel_version(modelInfo.getModelVer());
|
|
||||||
sendDto.setCd_model_path(cdModelPath);
|
|
||||||
sendDto.setCd_model_config(cdModelConfigPath);
|
|
||||||
sendDto.setCls_model_path(cdClsModelPath);
|
|
||||||
sendDto.setCls_model_version(modelInfo.getModelVer());
|
|
||||||
sendDto.setCd_model_type(modelType);
|
|
||||||
sendDto.setPriority(5.0);
|
|
||||||
return sendDto;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,10 @@
|
|||||||
package com.kamco.cd.kamcoback.mapsheet;
|
package com.kamco.cd.kamcoback.mapsheet;
|
||||||
|
|
||||||
import com.kamco.cd.kamcoback.code.dto.CommonCodeDto;
|
import com.kamco.cd.kamcoback.code.dto.CommonCodeDto;
|
||||||
import com.kamco.cd.kamcoback.code.service.CommonCodeService;
|
|
||||||
import com.kamco.cd.kamcoback.config.api.ApiResponseDto;
|
import com.kamco.cd.kamcoback.config.api.ApiResponseDto;
|
||||||
import com.kamco.cd.kamcoback.mapsheet.dto.FileDto.FilesDto;
|
|
||||||
import com.kamco.cd.kamcoback.mapsheet.dto.FileDto.FoldersDto;
|
import com.kamco.cd.kamcoback.mapsheet.dto.FileDto.FoldersDto;
|
||||||
import com.kamco.cd.kamcoback.mapsheet.dto.FileDto.SrchFilesDto;
|
|
||||||
import com.kamco.cd.kamcoback.mapsheet.dto.FileDto.SrchFoldersDto;
|
import com.kamco.cd.kamcoback.mapsheet.dto.FileDto.SrchFoldersDto;
|
||||||
import com.kamco.cd.kamcoback.mapsheet.dto.MapSheetMngDto;
|
import com.kamco.cd.kamcoback.mapsheet.dto.MapSheetMngDto;
|
||||||
import com.kamco.cd.kamcoback.mapsheet.dto.MapSheetMngDto.MngYyyyDto;
|
|
||||||
import com.kamco.cd.kamcoback.mapsheet.service.MapSheetMngService;
|
import com.kamco.cd.kamcoback.mapsheet.service.MapSheetMngService;
|
||||||
import com.kamco.cd.kamcoback.model.dto.ModelMngDto.ModelUploadResDto;
|
import com.kamco.cd.kamcoback.model.dto.ModelMngDto.ModelUploadResDto;
|
||||||
import com.kamco.cd.kamcoback.upload.dto.UploadDto;
|
import com.kamco.cd.kamcoback.upload.dto.UploadDto;
|
||||||
@@ -42,7 +38,6 @@ import org.springframework.web.multipart.MultipartFile;
|
|||||||
@RequestMapping({"/api/imagery/dataset"})
|
@RequestMapping({"/api/imagery/dataset"})
|
||||||
public class MapSheetMngApiController {
|
public class MapSheetMngApiController {
|
||||||
|
|
||||||
private final CommonCodeService commonCodeService;
|
|
||||||
private final MapSheetMngService mapSheetMngService;
|
private final MapSheetMngService mapSheetMngService;
|
||||||
|
|
||||||
@Value("${file.sync-root-dir}")
|
@Value("${file.sync-root-dir}")
|
||||||
@@ -51,7 +46,7 @@ public class MapSheetMngApiController {
|
|||||||
@Value("${file.sync-tmp-dir}")
|
@Value("${file.sync-tmp-dir}")
|
||||||
private String syncRootTmpDir;
|
private String syncRootTmpDir;
|
||||||
|
|
||||||
@Operation(summary = "영상 데이터 관리 목록 조회", description = "영상 데이터 관리 목록 조회")
|
@Operation(summary = "영상데이터관리 > 목록 조회", description = "영상데이터관리 > 목록 조회")
|
||||||
@ApiResponses(
|
@ApiResponses(
|
||||||
value = {
|
value = {
|
||||||
@ApiResponse(
|
@ApiResponse(
|
||||||
@@ -70,7 +65,7 @@ public class MapSheetMngApiController {
|
|||||||
return ApiResponseDto.ok(mapSheetMngService.findMapSheetMngList());
|
return ApiResponseDto.ok(mapSheetMngService.findMapSheetMngList());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operation(summary = "영상 데이터 관리 상세", description = "영상 데이터 관리 상세")
|
@Operation(summary = "영상데이터관리 > 상세 조회", description = "영상데이터관리 > 상세 조회")
|
||||||
@ApiResponses(
|
@ApiResponses(
|
||||||
value = {
|
value = {
|
||||||
@ApiResponse(
|
@ApiResponse(
|
||||||
@@ -89,7 +84,7 @@ public class MapSheetMngApiController {
|
|||||||
return ApiResponseDto.ok(mapSheetMngService.findMapSheetMng(mngYyyy));
|
return ApiResponseDto.ok(mapSheetMngService.findMapSheetMng(mngYyyy));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operation(summary = "영상관리 > 데이터 등록", description = "영상관리 > 데이터 등록")
|
@Operation(summary = "영상데이터관리 > 데이터 등록", description = "영상데이터관리 > 데이터 등록")
|
||||||
@ApiResponses(
|
@ApiResponses(
|
||||||
value = {
|
value = {
|
||||||
@ApiResponse(
|
@ApiResponse(
|
||||||
@@ -109,26 +104,7 @@ public class MapSheetMngApiController {
|
|||||||
return ApiResponseDto.ok(mapSheetMngService.mngDataSave(AddReq));
|
return ApiResponseDto.ok(mapSheetMngService.mngDataSave(AddReq));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operation(summary = "영상관리 > 작업완료", description = "영상관리 > 작업완료")
|
@Operation(summary = "영상데이터관리 > 데이터 등록 > 연도 선택 목록", description = "영상데이터관리 > 데이터 등록 > 연도 선택 목록")
|
||||||
@ApiResponses(
|
|
||||||
value = {
|
|
||||||
@ApiResponse(
|
|
||||||
responseCode = "201",
|
|
||||||
description = "작업완료 처리 성공",
|
|
||||||
content =
|
|
||||||
@Content(
|
|
||||||
mediaType = "application/json",
|
|
||||||
schema = @Schema(implementation = Long.class))),
|
|
||||||
@ApiResponse(responseCode = "400", description = "잘못된 요청 데이터", content = @Content),
|
|
||||||
@ApiResponse(responseCode = "404", description = "코드를 찾을 수 없음", content = @Content),
|
|
||||||
@ApiResponse(responseCode = "500", description = "서버 오류", content = @Content)
|
|
||||||
})
|
|
||||||
@PutMapping("/mng-complete")
|
|
||||||
public ApiResponseDto<MapSheetMngDto.DmlReturn> mngComplete(@RequestParam @Valid int mngYyyy) {
|
|
||||||
return ApiResponseDto.ok(mapSheetMngService.mngComplete(mngYyyy));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Operation(summary = "영상 데이터 관리 년도 목록", description = "영상 데이터 관리 년도 목록")
|
|
||||||
@ApiResponses(
|
@ApiResponses(
|
||||||
value = {
|
value = {
|
||||||
@ApiResponse(
|
@ApiResponse(
|
||||||
@@ -147,7 +123,7 @@ public class MapSheetMngApiController {
|
|||||||
return ApiResponseDto.ok(mapSheetMngService.findMapSheetMngYyyyList());
|
return ApiResponseDto.ok(mapSheetMngService.findMapSheetMngYyyyList());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operation(summary = "영상 데이터 관리 오류 목록", description = "영상 데이터 관리 오류 목록")
|
@Operation(summary = "영상데이터관리 > 상세 > 오류 처리 내역", description = "영상데이터관리 > 상세 > 오류 처리 내역")
|
||||||
@ApiResponses(
|
@ApiResponses(
|
||||||
value = {
|
value = {
|
||||||
@ApiResponse(
|
@ApiResponse(
|
||||||
@@ -166,42 +142,9 @@ public class MapSheetMngApiController {
|
|||||||
return ApiResponseDto.ok(mapSheetMngService.findMapSheetErrorList(searchReq));
|
return ApiResponseDto.ok(mapSheetMngService.findMapSheetErrorList(searchReq));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
@Operation(
|
||||||
@Operation(summary = "오류데이터 팝업 > 업로드 처리", description = "오류데이터 팝업 > 업로드 처리")
|
summary = "영상데이터관리 > 상세 > 오류 처리 내역 > 업로드 (페어 파일 저장)",
|
||||||
@ApiResponses(
|
description = "영상데이터관리 > 상세 > 오류 처리 내역 > 업로드 (페어 파일 저장)")
|
||||||
value = {
|
|
||||||
@ApiResponse(
|
|
||||||
responseCode = "201",
|
|
||||||
description = "업로드 처리 성공",
|
|
||||||
content =
|
|
||||||
@Content(
|
|
||||||
mediaType = "application/json",
|
|
||||||
schema = @Schema(implementation = Long.class))),
|
|
||||||
@ApiResponse(responseCode = "400", description = "잘못된 요청 데이터", content = @Content),
|
|
||||||
@ApiResponse(responseCode = "404", description = "코드를 찾을 수 없음", content = @Content),
|
|
||||||
@ApiResponse(responseCode = "500", description = "서버 오류", content = @Content)
|
|
||||||
})
|
|
||||||
@PutMapping("/upload-process")
|
|
||||||
public ApiResponseDto<MapSheetMngDto.DmlReturn> uploadProcess(
|
|
||||||
@RequestBody @Valid List<Long> hstUidList) {
|
|
||||||
return ApiResponseDto.ok(mapSheetMngService.uploadProcess(hstUidList));
|
|
||||||
}
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
@Operation(summary = "오류데이터 팝업 > 추론 제외", description = "오류데이터 팝업 > 추론 제외")
|
|
||||||
@PutMapping("/except-inference")
|
|
||||||
public ApiResponseDto<MapSheetMngDto.DmlReturn> updateExceptUseInference(
|
|
||||||
@RequestBody @Valid List<Long> hstUidList) {
|
|
||||||
return ApiResponseDto.ok(mapSheetMngService.updateExceptUseInference(hstUidList));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
@Operation(summary = "페어 파일 업로드", description = "TFW/TIF 두 파일을 쌍으로 업로드 및 검증")
|
|
||||||
@PostMapping(value = "/upload-pair", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
|
@PostMapping(value = "/upload-pair", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
|
||||||
public ApiResponseDto<MapSheetMngDto.DmlReturn> uploadPair(
|
public ApiResponseDto<MapSheetMngDto.DmlReturn> uploadPair(
|
||||||
@RequestPart("tfw") MultipartFile tfwFile,
|
@RequestPart("tfw") MultipartFile tfwFile,
|
||||||
@@ -213,7 +156,9 @@ public class MapSheetMngApiController {
|
|||||||
mapSheetMngService.uploadPair(tfwFile, tifFile, hstUid, tifFileSize));
|
mapSheetMngService.uploadPair(tfwFile, tifFile, hstUid, tifFileSize));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operation(summary = "영상관리 > 파일조회", description = "영상관리 > 파일조회")
|
@Operation(
|
||||||
|
summary = "영상데이터관리 > 상세 > 오류 처리 내역 > 중복제거 > 팝업 내 해당 파일조회",
|
||||||
|
description = "영상데이터관리 > 상세 > 오류 처리 내역 > 중복제거 > 팝업 내 해당 파일조회")
|
||||||
@ApiResponses(
|
@ApiResponses(
|
||||||
value = {
|
value = {
|
||||||
@ApiResponse(
|
@ApiResponse(
|
||||||
@@ -234,13 +179,13 @@ public class MapSheetMngApiController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Operation(
|
@Operation(
|
||||||
summary = "영상관리 > 파일사용설정 및 중복제거",
|
summary = "영상데이터관리 > 상세 > 오류 처리 내역 > 중복제거 업데이트",
|
||||||
description = "영상관리 >파일사용설정 및 중복제거(중복파일제거 및 선택파일사용설정)")
|
description = "영상데이터관리 > 상세 > 오류 처리 내역 > 중복제거 업데이트")
|
||||||
@ApiResponses(
|
@ApiResponses(
|
||||||
value = {
|
value = {
|
||||||
@ApiResponse(
|
@ApiResponse(
|
||||||
responseCode = "201",
|
responseCode = "201",
|
||||||
description = "파일사용설정 처리 성공",
|
description = "중복제거 업데이트 처리 성공",
|
||||||
content =
|
content =
|
||||||
@Content(
|
@Content(
|
||||||
mediaType = "application/json",
|
mediaType = "application/json",
|
||||||
@@ -255,7 +200,7 @@ public class MapSheetMngApiController {
|
|||||||
return ApiResponseDto.ok(mapSheetMngService.setUseByFileUidMngFile(fileUids));
|
return ApiResponseDto.ok(mapSheetMngService.setUseByFileUidMngFile(fileUids));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operation(summary = "폴더 조회", description = "폴더 조회 (ROOT:/app/original-images 이하로 경로입력)")
|
@Operation(summary = "영상데이터관리 > 데이터 등록 > NAS 폴더 선택", description = "영상데이터관리 > 데이터 등록 > NAS 폴더 선택")
|
||||||
@ApiResponses(
|
@ApiResponses(
|
||||||
value = {
|
value = {
|
||||||
@ApiResponse(
|
@ApiResponse(
|
||||||
@@ -274,45 +219,9 @@ public class MapSheetMngApiController {
|
|||||||
return ApiResponseDto.createOK(mapSheetMngService.getFolderAll(srchDto));
|
return ApiResponseDto.createOK(mapSheetMngService.getFolderAll(srchDto));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operation(summary = "지정폴더내 파일목록 조회", description = "지정폴더내 파일목록 조회")
|
@Operation(
|
||||||
@ApiResponses(
|
summary = "영상데이터관리 > 상세 > 오류 처리 내역 > 업로드 (영상 tif 대용량 파일 분할 전송)",
|
||||||
value = {
|
description = "영상데이터관리 > 상세 > 오류 처리 내역 > 업로드 (영상 tif 대용량 파일 분할 전송)")
|
||||||
@ApiResponse(
|
|
||||||
responseCode = "200",
|
|
||||||
description = "조회 성공",
|
|
||||||
content =
|
|
||||||
@Content(
|
|
||||||
mediaType = "application/json",
|
|
||||||
schema = @Schema(implementation = CommonCodeDto.Basic.class))),
|
|
||||||
@ApiResponse(responseCode = "404", description = "코드를 찾을 수 없음", content = @Content),
|
|
||||||
@ApiResponse(responseCode = "500", description = "서버 오류", content = @Content)
|
|
||||||
})
|
|
||||||
@PostMapping("/file-list")
|
|
||||||
public ApiResponseDto<FilesDto> getFiles(@RequestBody SrchFilesDto srchDto) {
|
|
||||||
|
|
||||||
return ApiResponseDto.createOK(mapSheetMngService.getFilesAll(srchDto));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Operation(summary = "영상 데이터 관리 완료 년도 목록 조회", description = "영상 데이터 관리 완료 년도 목록 조회")
|
|
||||||
@ApiResponses(
|
|
||||||
value = {
|
|
||||||
@ApiResponse(
|
|
||||||
responseCode = "200",
|
|
||||||
description = "조회 성공",
|
|
||||||
content =
|
|
||||||
@Content(
|
|
||||||
mediaType = "application/json",
|
|
||||||
schema = @Schema(implementation = CommonCodeDto.Basic.class))),
|
|
||||||
@ApiResponse(responseCode = "404", description = "코드를 찾을 수 없음", content = @Content),
|
|
||||||
@ApiResponse(responseCode = "500", description = "서버 오류", content = @Content)
|
|
||||||
})
|
|
||||||
@PostMapping("/mng-done-yyyy-list")
|
|
||||||
public ApiResponseDto<List<MngYyyyDto>> findMapSheetMngDoneYyyyList() {
|
|
||||||
|
|
||||||
return ApiResponseDto.ok(mapSheetMngService.findMapSheetMngDoneYyyyList());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Operation(summary = "영상 tif 대용량 파일 분할 전송", description = "영상 tif 파일 대용량 파일을 청크 단위로 전송합니다.")
|
|
||||||
@ApiResponses(
|
@ApiResponses(
|
||||||
value = {
|
value = {
|
||||||
@ApiResponse(responseCode = "200", description = "청크 업로드 성공", content = @Content),
|
@ApiResponse(responseCode = "200", description = "청크 업로드 성공", content = @Content),
|
||||||
|
|||||||
@@ -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);
|
||||||
@@ -339,7 +359,12 @@ public class MapSheetMngService {
|
|||||||
|
|
||||||
public FoldersDto getFolderAll(SrchFoldersDto srchDto) {
|
public FoldersDto getFolderAll(SrchFoldersDto srchDto) {
|
||||||
|
|
||||||
String dirPath = syncRootDir + srchDto.getDirPath();
|
// "경로중복"
|
||||||
|
String dirPath =
|
||||||
|
(srchDto.getDirPath() == null || srchDto.getDirPath().isEmpty())
|
||||||
|
? syncRootDir
|
||||||
|
: srchDto.getDirPath();
|
||||||
|
// String dirPath = syncRootDir + srchDto.getDirPath();
|
||||||
|
|
||||||
log.info("[FIND_FOLDER] DIR : {}", dirPath);
|
log.info("[FIND_FOLDER] DIR : {}", dirPath);
|
||||||
List<FIleChecker.Folder> folderList =
|
List<FIleChecker.Folder> folderList =
|
||||||
@@ -381,6 +406,11 @@ public class MapSheetMngService {
|
|||||||
mapSheetMngCoreService.getSceneInference(yyyy);
|
mapSheetMngCoreService.getSceneInference(yyyy);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 연도 목록 조회
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
public List<MngYyyyDto> findMapSheetMngDoneYyyyList() {
|
public List<MngYyyyDto> findMapSheetMngDoneYyyyList() {
|
||||||
|
|
||||||
List<MngDto> mngList = mapSheetMngCoreService.findMapSheetMngList();
|
List<MngDto> mngList = mapSheetMngCoreService.findMapSheetMngList();
|
||||||
|
|||||||
@@ -169,6 +169,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과 일치하는지 확인
|
||||||
|
|||||||
@@ -67,6 +67,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 +90,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 +113,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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -41,6 +41,16 @@ public class ModelMngService {
|
|||||||
@Value("${file.pt-FileName}")
|
@Value("${file.pt-FileName}")
|
||||||
private String ptFileName;
|
private String ptFileName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 모델조회
|
||||||
|
*
|
||||||
|
* @param searchReq 페이징
|
||||||
|
* @param startDate 시작날짜
|
||||||
|
* @param endDate 종료날짜
|
||||||
|
* @param modelType 모델 타입 G1, G2, G3
|
||||||
|
* @param searchVal 모델 ver
|
||||||
|
* @return 모델 목록
|
||||||
|
*/
|
||||||
public Page<ModelMngDto.ModelList> findModelMgmtList(
|
public Page<ModelMngDto.ModelList> findModelMgmtList(
|
||||||
ModelMngDto.searchReq searchReq,
|
ModelMngDto.searchReq searchReq,
|
||||||
LocalDate startDate,
|
LocalDate startDate,
|
||||||
|
|||||||
@@ -46,6 +46,13 @@ public class AuditLogCoreService
|
|||||||
return auditLogRepository.findLogByAccount(searchRange, searchValue);
|
return auditLogRepository.findLogByAccount(searchRange, searchValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 다운로드 이력 조회
|
||||||
|
*
|
||||||
|
* @param searchReq 페이징 파라미터
|
||||||
|
* @param downloadReq 다운로드 이력 팝업 검색 조건
|
||||||
|
* @return 다운로드 이력 정보 목록
|
||||||
|
*/
|
||||||
public Page<AuditLogDto.DownloadRes> findLogByAccount(
|
public Page<AuditLogDto.DownloadRes> findLogByAccount(
|
||||||
AuditLogDto.searchReq searchReq, DownloadReq downloadReq) {
|
AuditLogDto.searchReq searchReq, DownloadReq downloadReq) {
|
||||||
return auditLogRepository.findDownloadLog(searchReq, downloadReq);
|
return auditLogRepository.findDownloadLog(searchReq, downloadReq);
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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 {
|
||||||
|
|
||||||
@@ -79,12 +80,13 @@ public class InferenceResultCoreService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 변화탐지 실행 정보 생성
|
* 변화탐지 실행 정보 생성 - 추론 실행 목록 테이블 저장, 도엽목록별 상태 체크 테이블 저장
|
||||||
*
|
*
|
||||||
* @param req
|
* @param req 추론 실행 목록 uuid
|
||||||
*/
|
*/
|
||||||
public UUID saveInferenceInfo(InferenceResultDto.RegReq req, List<MngListDto> targetList) {
|
public UUID saveInferenceInfo(InferenceResultDto.RegReq req, List<MngListDto> targetList) {
|
||||||
|
|
||||||
|
// 대표 도엽명 외 N 건 실행 문구 만들기 위해 Null, 중복 제거
|
||||||
List<MngListDto> distinctList =
|
List<MngListDto> distinctList =
|
||||||
targetList.stream()
|
targetList.stream()
|
||||||
.filter(dto -> dto.getMapSheetName() != null && !dto.getMapSheetName().isBlank())
|
.filter(dto -> dto.getMapSheetName() != null && !dto.getMapSheetName().isBlank())
|
||||||
@@ -118,23 +120,19 @@ 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);
|
||||||
mapSheetLearnEntity.setTotalJobs((long) targetList.size());
|
mapSheetLearnEntity.setTotalJobs((long) targetList.size());
|
||||||
|
|
||||||
// 회차는 국유인 반영할때 update로 변경됨
|
|
||||||
// mapSheetLearnEntity.setStage(
|
|
||||||
// mapSheetLearnRepository.getLearnStage(req.getCompareYyyy(), req.getTargetYyyy()));
|
|
||||||
|
|
||||||
// learn 테이블 저장
|
// learn 테이블 저장
|
||||||
MapSheetLearnEntity savedLearn = mapSheetLearnRepository.save(mapSheetLearnEntity);
|
MapSheetLearnEntity savedLearn = mapSheetLearnRepository.save(mapSheetLearnEntity);
|
||||||
|
|
||||||
final int CHUNK = 1000;
|
final int CHUNK = 1000;
|
||||||
List<MapSheetLearn5kEntity> buffer = new ArrayList<>(CHUNK);
|
List<MapSheetLearn5kEntity> buffer = new ArrayList<>(CHUNK);
|
||||||
|
|
||||||
// learn 도엽별 저장
|
// learn 도엽별 저장, 도엽수가 많으므로 1000개 씩 저장함
|
||||||
for (MngListDto mngDto : targetList) {
|
for (MngListDto mngDto : targetList) {
|
||||||
MapSheetLearn5kEntity entity = new MapSheetLearn5kEntity();
|
MapSheetLearn5kEntity entity = new MapSheetLearn5kEntity();
|
||||||
entity.setLearn(savedLearn);
|
entity.setLearn(savedLearn);
|
||||||
@@ -145,12 +143,15 @@ public class InferenceResultCoreService {
|
|||||||
|
|
||||||
buffer.add(entity);
|
buffer.add(entity);
|
||||||
if (buffer.size() == CHUNK) {
|
if (buffer.size() == CHUNK) {
|
||||||
|
// 도엽별 저장 learn 5k 테이블
|
||||||
flushChunk(buffer);
|
flushChunk(buffer);
|
||||||
buffer.clear();
|
buffer.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// chunk 남은거 처리
|
||||||
if (!buffer.isEmpty()) {
|
if (!buffer.isEmpty()) {
|
||||||
|
// 도엽별 저장 learn 5k 테이블
|
||||||
flushChunk(buffer);
|
flushChunk(buffer);
|
||||||
buffer.clear();
|
buffer.clear();
|
||||||
}
|
}
|
||||||
@@ -159,9 +160,9 @@ public class InferenceResultCoreService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 도엽별 저장
|
* 도엽별 저장 learn 5k 테이블
|
||||||
*
|
*
|
||||||
* @param buffer
|
* @param buffer 저장 정보
|
||||||
*/
|
*/
|
||||||
private void flushChunk(List<MapSheetLearn5kEntity> buffer) {
|
private void flushChunk(List<MapSheetLearn5kEntity> buffer) {
|
||||||
|
|
||||||
@@ -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);
|
||||||
}
|
}
|
||||||
@@ -283,7 +284,7 @@ public class InferenceResultCoreService {
|
|||||||
.distinct() // 중복 방지 (선택)
|
.distinct() // 중복 방지 (선택)
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
// testing 추론결과 테이블 조회하여 탐지 개수 업데이트
|
// testing 테이블 추론결과 테이블 조회하여 탐지 개수 업데이트
|
||||||
Long testing = getInferenceResultCnt(batchIds);
|
Long testing = getInferenceResultCnt(batchIds);
|
||||||
|
|
||||||
// 공통 영역 업데이트
|
// 공통 영역 업데이트
|
||||||
@@ -369,6 +370,12 @@ public class InferenceResultCoreService {
|
|||||||
return mapSheetLearnRepository.getInferenceServerStatusList();
|
return mapSheetLearnRepository.getInferenceServerStatusList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 진행중 배치 조회
|
||||||
|
*
|
||||||
|
* @param status
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
public InferenceBatchSheet getInferenceResultByStatus(String status) {
|
public InferenceBatchSheet getInferenceResultByStatus(String status) {
|
||||||
MapSheetLearnEntity entity =
|
MapSheetLearnEntity entity =
|
||||||
mapSheetLearnRepository.getInferenceResultByStatus(status).orElse(null);
|
mapSheetLearnRepository.getInferenceResultByStatus(status).orElse(null);
|
||||||
@@ -403,6 +410,12 @@ public class InferenceResultCoreService {
|
|||||||
return mapSheetLearnRepository.getInferenceAiResultById(id, modelUuid);
|
return mapSheetLearnRepository.getInferenceAiResultById(id, modelUuid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 추론 진행 현황 상세
|
||||||
|
*
|
||||||
|
* @param uuid
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
public InferenceStatusDetailDto getInferenceStatus(UUID uuid) {
|
public InferenceStatusDetailDto getInferenceStatus(UUID uuid) {
|
||||||
return mapSheetLearnRepository.getInferenceStatus(uuid);
|
return mapSheetLearnRepository.getInferenceStatus(uuid);
|
||||||
}
|
}
|
||||||
@@ -410,7 +423,7 @@ public class InferenceResultCoreService {
|
|||||||
/**
|
/**
|
||||||
* 추론 진행중인지 확인
|
* 추론 진행중인지 확인
|
||||||
*
|
*
|
||||||
* @return
|
* @return 추론 실행중인 추론 uuid, batch id
|
||||||
*/
|
*/
|
||||||
public SaveInferenceAiDto getProcessing() {
|
public SaveInferenceAiDto getProcessing() {
|
||||||
MapSheetLearnEntity entity = mapSheetLearnRepository.getProcessing();
|
MapSheetLearnEntity entity = mapSheetLearnRepository.getProcessing();
|
||||||
@@ -441,19 +454,45 @@ 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 조회
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 분류별 탐지건수 조회
|
||||||
|
*
|
||||||
|
* @param uuid 추론 uuid
|
||||||
|
* @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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param uuid 추론 uuid
|
||||||
|
* @param searchGeoReq 추론 결과 상세화면 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -463,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);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -510,15 +553,21 @@ public class InferenceResultCoreService {
|
|||||||
return list.stream().map(InferenceResultsTestingEntity::toDto).toList();
|
return list.stream().map(InferenceResultsTestingEntity::toDto).toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 테스팅 테이블 조회하여 탐지건수 조회
|
||||||
|
*
|
||||||
|
* @param batchIds batchIds
|
||||||
|
* @return batchIds 조회 count 수
|
||||||
|
*/
|
||||||
public Long getInferenceResultCnt(List<Long> batchIds) {
|
public Long getInferenceResultCnt(List<Long> batchIds) {
|
||||||
return inferenceResultsTestingRepository.getInferenceResultCnt(batchIds);
|
return inferenceResultsTestingRepository.getInferenceResultCnt(batchIds);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* uid 조회
|
* 추론 정보 조회 하여 batch id, 32자 uid 리턴
|
||||||
*
|
*
|
||||||
* @param uuid
|
* @param uuid 추론 uuid
|
||||||
* @return
|
* @return 추론정보
|
||||||
*/
|
*/
|
||||||
public InferenceLearnDto getInferenceUid(UUID uuid) {
|
public InferenceLearnDto getInferenceUid(UUID uuid) {
|
||||||
MapSheetLearnEntity entity = inferenceResultRepository.getInferenceUid(uuid).orElse(null);
|
MapSheetLearnEntity entity = inferenceResultRepository.getInferenceUid(uuid).orElse(null);
|
||||||
@@ -535,7 +584,7 @@ public class InferenceResultCoreService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 실행중인 추론 도엽명 목록
|
* 분석중인 추론 도엽명 목록
|
||||||
*
|
*
|
||||||
* @param uuid 추론 실행중인 uuid
|
* @param uuid 추론 실행중인 uuid
|
||||||
* @return
|
* @return
|
||||||
@@ -544,11 +593,37 @@ 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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,8 @@
|
|||||||
package com.kamco.cd.kamcoback.postgres.core;
|
package com.kamco.cd.kamcoback.postgres.core;
|
||||||
|
|
||||||
import com.kamco.cd.kamcoback.common.enums.MngStateType;
|
import com.kamco.cd.kamcoback.common.enums.MngStateType;
|
||||||
import com.kamco.cd.kamcoback.common.exception.CustomApiException;
|
|
||||||
import com.kamco.cd.kamcoback.common.geometry.GeoJsonFileWriter;
|
import com.kamco.cd.kamcoback.common.geometry.GeoJsonFileWriter;
|
||||||
import com.kamco.cd.kamcoback.common.geometry.GeoJsonFileWriter.ImageFeature;
|
import com.kamco.cd.kamcoback.common.geometry.GeoJsonFileWriter.ImageFeature;
|
||||||
import com.kamco.cd.kamcoback.common.geometry.GeoJsonFileWriter.Scene;
|
|
||||||
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.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.MngListCompareDto;
|
import com.kamco.cd.kamcoback.mapsheet.dto.MapSheetMngDto.MngListCompareDto;
|
||||||
import com.kamco.cd.kamcoback.mapsheet.dto.MapSheetMngDto.MngListDto;
|
import com.kamco.cd.kamcoback.mapsheet.dto.MapSheetMngDto.MngListDto;
|
||||||
@@ -21,21 +16,21 @@ import jakarta.persistence.EntityNotFoundException;
|
|||||||
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.Files;
|
||||||
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.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
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.HttpStatus;
|
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
@@ -53,13 +48,20 @@ public class MapSheetMngCoreService {
|
|||||||
@Value("${file.sync-root-dir}")
|
@Value("${file.sync-root-dir}")
|
||||||
private String syncRootDir;
|
private String syncRootDir;
|
||||||
|
|
||||||
@Value("${inference.geojson-dir}")
|
/**
|
||||||
private String inferenceDir;
|
* 영상데이터관리 > 목록 조회
|
||||||
|
*
|
||||||
|
* @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();
|
||||||
}
|
}
|
||||||
@@ -76,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);
|
||||||
}
|
}
|
||||||
@@ -103,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);
|
||||||
}
|
}
|
||||||
@@ -154,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 정보 등록
|
||||||
@@ -233,94 +266,70 @@ public class MapSheetMngCoreService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 추론 실행에 필요한 geojson 파일 생성
|
* geojson 생성시 필요한 영상파일 정보 조회
|
||||||
*
|
*
|
||||||
* @param yyyy 영상관리 파일별 년도
|
* @param yyyy
|
||||||
* @param scenes 5k 도엽 번호 리스트
|
* @param mapSheetNums
|
||||||
* @param mapSheetScope EXCL : 추론제외, PREV 이전 년도 도엽 사용
|
* @return ImageFeature
|
||||||
* @return
|
|
||||||
*/
|
*/
|
||||||
public Scene getSceneInference(
|
public List<ImageFeature> loadSceneInferenceBySheets(String yyyy, List<String> mapSheetNums) {
|
||||||
String yyyy, List<String> scenes, String mapSheetScope, String detectOption) {
|
|
||||||
|
|
||||||
Map<String, Object> result = new HashMap<>();
|
if (mapSheetNums == null || mapSheetNums.isEmpty()) {
|
||||||
boolean isAll = MapSheetScope.ALL.getId().equals(mapSheetScope);
|
return List.of();
|
||||||
|
|
||||||
String optionSuffix = "";
|
|
||||||
if (DetectOption.EXCL.getId().equals(detectOption)) {
|
|
||||||
optionSuffix = "_EXCL";
|
|
||||||
} else if (DetectOption.PREV.getId().equals(detectOption)) {
|
|
||||||
optionSuffix = "_PREV";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1) 경로/파일명 결정
|
// CHUNK_SIZE 단위로 나누어 여러 번 조회한다.
|
||||||
String targetDir =
|
final int CHUNK_SIZE = 1000;
|
||||||
"local".equals(activeEnv) ? System.getProperty("user.home") + "/geojson" : inferenceDir;
|
List<ImageFeature> features = new ArrayList<>();
|
||||||
|
|
||||||
String filename =
|
// i부터 CHUNK_SIZE만큼 잘라서 조회
|
||||||
isAll
|
// 마지막 구간은 남은 개수만큼만 처리하기 위해 Math.min 사용
|
||||||
? String.format("%s_%s_ALL%s.geojson", yyyy, activeEnv, optionSuffix)
|
for (int i = 0; i < mapSheetNums.size(); i += CHUNK_SIZE) {
|
||||||
: String.format("%s_%s%s.geojson", yyyy, activeEnv, optionSuffix);
|
List<String> chunk = mapSheetNums.subList(i, Math.min(i + CHUNK_SIZE, mapSheetNums.size()));
|
||||||
|
features.addAll(mapSheetMngRepository.getSceneInference(yyyy, chunk));
|
||||||
Path outputPath = Paths.get(targetDir, filename);
|
|
||||||
|
|
||||||
// 2) ALL일 때만 재사용
|
|
||||||
// if (isAll && Files.exists(outputPath)) {
|
|
||||||
// return outputPath.toString();
|
|
||||||
// }
|
|
||||||
|
|
||||||
// 3) 데이터 조회
|
|
||||||
List<ImageFeature> sceneInference = mapSheetMngRepository.getSceneInference(yyyy, scenes);
|
|
||||||
|
|
||||||
if (sceneInference == null || sceneInference.isEmpty()) {
|
|
||||||
log.warn(
|
|
||||||
"NOT_FOUND_TARGET_YEAR: yyyy={}, isAll={}, scenesSize={}",
|
|
||||||
yyyy,
|
|
||||||
isAll,
|
|
||||||
scenes == null ? 0 : scenes.size());
|
|
||||||
throw new CustomApiException("NOT_FOUND_TARGET_YEAR", HttpStatus.NOT_FOUND);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 4) 파일 생성
|
|
||||||
try {
|
|
||||||
log.info("create Directories outputPath: {}", outputPath);
|
|
||||||
log.info(
|
|
||||||
"activeEnv={}, inferenceDir={}, targetDir={}, filename={}",
|
|
||||||
activeEnv,
|
|
||||||
inferenceDir,
|
|
||||||
targetDir,
|
|
||||||
filename);
|
|
||||||
log.info("outputPath={}, parent={}", outputPath.toAbsolutePath(), outputPath.getParent());
|
|
||||||
Files.createDirectories(outputPath.getParent());
|
|
||||||
|
|
||||||
new GeoJsonFileWriter()
|
|
||||||
.exportToFile(sceneInference, "scene_inference_" + yyyy, 5186, outputPath.toString());
|
|
||||||
log.info("GeoJsonFileWriter: {}", "scene_inference_" + yyyy);
|
|
||||||
Scene scene = new Scene();
|
|
||||||
scene.setFeatures(sceneInference);
|
|
||||||
scene.setFilePath(outputPath.toString());
|
|
||||||
|
|
||||||
return scene;
|
|
||||||
|
|
||||||
} catch (IOException e) {
|
|
||||||
log.error(
|
|
||||||
"FAIL_CREATE_MAP_SHEET_FILE: yyyy={}, isAll={}, path={}", yyyy, isAll, outputPath, e);
|
|
||||||
throw new CustomApiException("INTERNAL_SERVER_ERROR", HttpStatus.INTERNAL_SERVER_ERROR, e);
|
|
||||||
}
|
}
|
||||||
|
return features;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 변화탐지 실행 가능 기준 년도 조회
|
* 년도별로 나눠 조회
|
||||||
*
|
*
|
||||||
* @param req
|
* @param yearDtos
|
||||||
* @return
|
* @return ImageFeature
|
||||||
*/
|
*/
|
||||||
public List<MngListDto> getHstMapSheetList(InferenceResultDto.RegReq req) {
|
public List<ImageFeature> loadSceneInferenceByFallbackYears(List<MngListDto> yearDtos) {
|
||||||
return mapSheetMngRepository.findByHstMapSheetTargetList(req);
|
if (yearDtos == null || yearDtos.isEmpty()) {
|
||||||
}
|
return List.of();
|
||||||
|
}
|
||||||
|
|
||||||
public List<MngListDto> getHstMapSheetList(int mngYyyy, List<String> mapIds) {
|
// 년도 별로 루프를 돌리기위해 년도별 정리
|
||||||
return mapSheetMngRepository.findByHstMapSheetTargetList(mngYyyy, mapIds);
|
Map<Integer, List<MngListDto>> groupedByYear =
|
||||||
|
yearDtos.stream()
|
||||||
|
.filter(d -> d.getMngYyyy() != 0 && d.getMapSheetNum() != null)
|
||||||
|
.collect(Collectors.groupingBy(MngListDto::getMngYyyy));
|
||||||
|
|
||||||
|
List<ImageFeature> sceneInference = new ArrayList<>();
|
||||||
|
|
||||||
|
for (Map.Entry<Integer, List<MngListDto>> entry : groupedByYear.entrySet()) {
|
||||||
|
Integer year = entry.getKey();
|
||||||
|
|
||||||
|
// 년도별 mapSheetNum 만들기
|
||||||
|
List<String> sheetNums =
|
||||||
|
entry.getValue().stream()
|
||||||
|
.map(MngListDto::getMapSheetNum)
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.distinct()
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
// tif파일 정보 조회
|
||||||
|
List<ImageFeature> temp = loadSceneInferenceBySheets(year.toString(), sheetNums);
|
||||||
|
|
||||||
|
if (temp != null && !temp.isEmpty()) {
|
||||||
|
sceneInference.addAll(temp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return sceneInference;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateMapSheetMngHstUploadId(Long hstUid, UUID uuid, String uploadId) {
|
public void updateMapSheetMngHstUploadId(Long hstUid, UUID uuid, String uploadId) {
|
||||||
@@ -336,15 +345,38 @@ 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<String> getMapSheetMngHst(Integer year) {
|
public List<MngListDto> getMapSheetMngHst(Integer year, List<String> mapSheetNums50k) {
|
||||||
List<MapSheetMngHstEntity> entity = mapSheetMngRepository.getMapSheetMngHst(year);
|
return mapSheetMngRepository.getMapSheetMngHst(year, mapSheetNums50k);
|
||||||
return entity.stream().map(MapSheetMngHstEntity::getMapSheetNum).toList();
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 이전 년도 도엽 조회 조건이 많을 수 있으므로 chunk 줘서 끊어서 조회
|
||||||
|
*
|
||||||
|
* @param year
|
||||||
|
* @param mapIds
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public List<MngListDto> findFallbackCompareYearByMapSheets(Integer year, List<String> mapIds) {
|
||||||
|
if (mapIds == null || mapIds.isEmpty()) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
int chunkSize = 1000;
|
||||||
|
List<MngListDto> result = new ArrayList<>();
|
||||||
|
|
||||||
|
for (int i = 0; i < mapIds.size(); i += chunkSize) {
|
||||||
|
List<String> chunk = mapIds.subList(i, Math.min(i + chunkSize, mapIds.size()));
|
||||||
|
|
||||||
|
result.addAll(mapSheetMngRepository.findFallbackCompareYearByMapSheets(year, chunk));
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,16 @@ public class ModelMngCoreService {
|
|||||||
|
|
||||||
private final ModelMngRepository modelMngRepository;
|
private final ModelMngRepository modelMngRepository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 모델조회
|
||||||
|
*
|
||||||
|
* @param searchReq 페이징
|
||||||
|
* @param startDate 시작날짜
|
||||||
|
* @param endDate 종료날짜
|
||||||
|
* @param modelType 모델 타입 G1, G2, G3
|
||||||
|
* @param searchVal 모델 ver
|
||||||
|
* @return 모델 목록
|
||||||
|
*/
|
||||||
public Page<ModelMngDto.ModelList> findModelMgmtList(
|
public Page<ModelMngDto.ModelList> findModelMgmtList(
|
||||||
ModelMngDto.searchReq searchReq,
|
ModelMngDto.searchReq searchReq,
|
||||||
LocalDate startDate,
|
LocalDate startDate,
|
||||||
@@ -99,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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -202,6 +202,33 @@ 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;
|
||||||
|
|
||||||
public InferenceResultDto.ResultList toDto() {
|
public InferenceResultDto.ResultList toDto() {
|
||||||
return new InferenceResultDto.ResultList(
|
return new InferenceResultDto.ResultList(
|
||||||
this.uuid,
|
this.uuid,
|
||||||
|
|||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,20 +1,65 @@
|
|||||||
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);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 추론 정보 조회
|
||||||
|
*
|
||||||
|
* @param uuid 추론 uuid
|
||||||
|
* @return 추론 정보
|
||||||
|
*/
|
||||||
Optional<MapSheetLearnEntity> getInferenceUid(UUID uuid);
|
Optional<MapSheetLearnEntity> getInferenceUid(UUID uuid);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* learn id 로 analInference 값 조회
|
||||||
|
*
|
||||||
|
* @param id 추론 id
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
Optional<MapSheetAnalInferenceEntity> getAnalInferenceDataByLearnId(Long id);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,14 @@ 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());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,32 @@
|
|||||||
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);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 테스팅 테이블 조회하여 탐지건수 조회
|
||||||
|
*
|
||||||
|
* @param batchIds batchIds
|
||||||
|
* @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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,13 +6,49 @@ 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);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 추론 실행중일때 분석중인 도엽명 목록 조회
|
||||||
|
*
|
||||||
|
* @param uuid 추론 uuid
|
||||||
|
* @return 도엽명+50K 도엽번호
|
||||||
|
*/
|
||||||
List<String> getInferenceRunMapId(UUID uuid);
|
List<String> getInferenceRunMapId(UUID uuid);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,29 +18,99 @@ import org.springframework.data.domain.Page;
|
|||||||
|
|
||||||
public interface MapSheetLearnRepositoryCustom {
|
public interface MapSheetLearnRepositoryCustom {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 추론 관리 목록 조회
|
||||||
|
*
|
||||||
|
* @param req 추론관리 목록 화면 조회 조건
|
||||||
|
* @return 추론 관리 목록
|
||||||
|
*/
|
||||||
Page<MapSheetLearnEntity> getInferenceMgnResultList(InferenceResultDto.SearchListReq req);
|
Page<MapSheetLearnEntity> getInferenceMgnResultList(InferenceResultDto.SearchListReq req);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* uuid 조건으로 추론 실행 정보 조회
|
||||||
|
*
|
||||||
|
* @param uuid uuid
|
||||||
|
* @return 추론 실행 정보
|
||||||
|
*/
|
||||||
Optional<MapSheetLearnEntity> getInferenceResultByUuid(UUID uuid);
|
Optional<MapSheetLearnEntity> getInferenceResultByUuid(UUID uuid);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 추론 실행중 서버정보 조회 cpu, gpu
|
||||||
|
*
|
||||||
|
* @return cpu, gpu 정보
|
||||||
|
*/
|
||||||
List<InferenceServerStatusDto> getInferenceServerStatusList();
|
List<InferenceServerStatusDto> getInferenceServerStatusList();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 추론 실행 목록 진행 상태별 조회
|
||||||
|
*
|
||||||
|
* @param status 추론 진행 상태
|
||||||
|
* @return 추론 실행 정보
|
||||||
|
*/
|
||||||
Optional<MapSheetLearnEntity> getInferenceResultByStatus(String status);
|
Optional<MapSheetLearnEntity> getInferenceResultByStatus(String status);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 등록된 추론 실행목록 및 등록된 모델 정보 조회
|
||||||
|
*
|
||||||
|
* @param id 추론 실행 테이블 id
|
||||||
|
* @param modelUuid 모델 uuid
|
||||||
|
* @return 모델 정보
|
||||||
|
*/
|
||||||
InferenceProgressDto getInferenceAiResultById(Long id, UUID modelUuid);
|
InferenceProgressDto getInferenceAiResultById(Long id, UUID modelUuid);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 진행중인 추론 정보 상세 조회
|
||||||
|
*
|
||||||
|
* @param uuid 추론진행 uuid
|
||||||
|
* @return 진행중인 추론정보 상세 정보
|
||||||
|
*/
|
||||||
InferenceStatusDetailDto getInferenceStatus(UUID uuid);
|
InferenceStatusDetailDto getInferenceStatus(UUID uuid);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 진행중인 추론이 있는지 조회
|
||||||
|
*
|
||||||
|
* @return 진행중인 추론 정보
|
||||||
|
*/
|
||||||
MapSheetLearnEntity getProcessing();
|
MapSheetLearnEntity getProcessing();
|
||||||
|
|
||||||
Integer getLearnStage(Integer compareYear, Integer targetYear);
|
/**
|
||||||
|
* 추론 결과 정보 조회
|
||||||
|
*
|
||||||
|
* @param uuid 추론 uuid
|
||||||
|
* @return 추론 결과 및 사용 모델 정보
|
||||||
|
*/
|
||||||
AnalResultInfo getInferenceResultInfo(UUID uuid);
|
AnalResultInfo getInferenceResultInfo(UUID uuid);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 추론 결과 bbox, point 조회
|
||||||
|
*
|
||||||
|
* @param uuid 추론 uuid
|
||||||
|
* @return bbox, pont 정보
|
||||||
|
*/
|
||||||
BboxPointDto getBboxPoint(UUID uuid);
|
BboxPointDto getBboxPoint(UUID uuid);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 분류별 탐지건수 조회
|
||||||
|
*
|
||||||
|
* @param uuid 추론 uuid
|
||||||
|
* @return 분류별 탐지건수 정보
|
||||||
|
*/
|
||||||
List<Dashboard> getInferenceClassCountList(UUID uuid);
|
List<Dashboard> getInferenceClassCountList(UUID uuid);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 추론 결과 상세 geom 목록 조회
|
||||||
|
*
|
||||||
|
* @param uuid 추론 uuid
|
||||||
|
* @param searchGeoReq 추론 결과 상세화면 geom 조회 조건
|
||||||
|
* @return geom 목록 정보
|
||||||
|
*/
|
||||||
Page<Geom> getInferenceGeomList(UUID uuid, SearchGeoReq searchGeoReq);
|
Page<Geom> getInferenceGeomList(UUID uuid, SearchGeoReq searchGeoReq);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 국유in연동 가능여부 확인 조회
|
||||||
|
*
|
||||||
|
* @param uuid 추론 uuid
|
||||||
|
* @return 추론 존재여부, 부분도엽 여부, 추론 진행중 여부, 국유인 작업 진행중 여부
|
||||||
|
*/
|
||||||
GukYuinLinkFacts findLinkFacts(UUID uuid);
|
GukYuinLinkFacts findLinkFacts(UUID uuid);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -291,24 +291,6 @@ public class MapSheetLearnRepositoryImpl implements MapSheetLearnRepositoryCusto
|
|||||||
.fetchOne();
|
.fetchOne();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Integer getLearnStage(Integer compareYear, Integer targetYear) {
|
|
||||||
Integer stage =
|
|
||||||
queryFactory
|
|
||||||
.select(mapSheetLearnEntity.stage)
|
|
||||||
.from(mapSheetLearnEntity)
|
|
||||||
.where(
|
|
||||||
mapSheetLearnEntity
|
|
||||||
.compareYyyy
|
|
||||||
.eq(compareYear)
|
|
||||||
.and(mapSheetLearnEntity.targetYyyy.eq(targetYear)))
|
|
||||||
.orderBy(mapSheetLearnEntity.id.desc())
|
|
||||||
.limit(1)
|
|
||||||
.fetchOne();
|
|
||||||
|
|
||||||
return stage == null ? 1 : stage + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AnalResultInfo getInferenceResultInfo(UUID uuid) {
|
public AnalResultInfo getInferenceResultInfo(UUID uuid) {
|
||||||
QModelMngEntity m1 = new QModelMngEntity("m1");
|
QModelMngEntity m1 = new QModelMngEntity("m1");
|
||||||
@@ -333,7 +315,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))
|
||||||
@@ -528,6 +511,7 @@ public class MapSheetLearnRepositoryImpl implements MapSheetLearnRepositoryCusto
|
|||||||
@Override
|
@Override
|
||||||
public GukYuinLinkFacts findLinkFacts(UUID uuid) {
|
public GukYuinLinkFacts findLinkFacts(UUID uuid) {
|
||||||
|
|
||||||
|
// 해당 추론 있는지 확인
|
||||||
MapSheetLearnEntity learn =
|
MapSheetLearnEntity learn =
|
||||||
queryFactory
|
queryFactory
|
||||||
.selectFrom(QMapSheetLearnEntity.mapSheetLearnEntity)
|
.selectFrom(QMapSheetLearnEntity.mapSheetLearnEntity)
|
||||||
@@ -535,28 +519,33 @@ 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 부분 도엽 실행인지 확인
|
||||||
boolean isPartScope = MapSheetScope.PART.getId().equals(learn.getMapSheetScope());
|
boolean isPartScope = MapSheetScope.PART.getId().equals(learn.getMapSheetScope());
|
||||||
|
|
||||||
QMapSheetAnalInferenceEntity inf = QMapSheetAnalInferenceEntity.mapSheetAnalInferenceEntity;
|
QMapSheetAnalInferenceEntity inf = QMapSheetAnalInferenceEntity.mapSheetAnalInferenceEntity;
|
||||||
QMapSheetLearnEntity learn2 = new QMapSheetLearnEntity("learn2");
|
QMapSheetLearnEntity learn2 = new QMapSheetLearnEntity("learn2");
|
||||||
QMapSheetLearnEntity learnQ = QMapSheetLearnEntity.mapSheetLearnEntity;
|
QMapSheetLearnEntity learnQ = QMapSheetLearnEntity.mapSheetLearnEntity;
|
||||||
|
|
||||||
boolean hasRunningInference =
|
// 현재 국유인 연동하려는 추론의 비교년도,기준년도와 같은 회차 중, 할당되거나 진행중인 학습데이터 uuid 조회
|
||||||
|
// ex. 2022-2023년도 9회차 학습데이터 제작 진행중 -> 10회차 연동하려고 할 시, 먼저 9회차를 종료해야 함
|
||||||
|
UUID runningInferenceUuid =
|
||||||
queryFactory
|
queryFactory
|
||||||
.selectOne()
|
.select(inf.uuid)
|
||||||
.from(inf)
|
.from(inf)
|
||||||
.join(learn2)
|
.join(learn2)
|
||||||
.on(inf.learnId.eq(learn2.id))
|
.on(inf.learnId.eq(learn2.id))
|
||||||
.where(
|
.where(
|
||||||
learn2.compareYyyy.eq(learn.getCompareYyyy()),
|
learn2.compareYyyy.eq(learn.getCompareYyyy()),
|
||||||
learn2.targetYyyy.eq(learn.getTargetYyyy()),
|
learn2.targetYyyy.eq(learn.getTargetYyyy()),
|
||||||
inf.analState.in("ASSIGNED", "ING"))
|
inf.analState.in("ASSIGNED", "ING"))
|
||||||
.fetchFirst()
|
.fetchFirst();
|
||||||
!= null;
|
|
||||||
|
|
||||||
|
boolean hasRunningInference = runningInferenceUuid != null;
|
||||||
|
|
||||||
|
// 국유인 작업 진행중 있는지 확인
|
||||||
boolean hasOtherUnfinishedGukYuin =
|
boolean hasOtherUnfinishedGukYuin =
|
||||||
queryFactory
|
queryFactory
|
||||||
.selectOne()
|
.selectOne()
|
||||||
@@ -569,6 +558,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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
@@ -341,7 +344,10 @@ public class ChangeDetectionRepositoryImpl extends QuerydslRepositorySupport
|
|||||||
mapInkx5kEntity.mapidcdNo,
|
mapInkx5kEntity.mapidcdNo,
|
||||||
mapInkx5kEntity.mapidNm,
|
mapInkx5kEntity.mapidNm,
|
||||||
Expressions.stringTemplate(
|
Expressions.stringTemplate(
|
||||||
"concat({0}, ' ', {1})", mapInkx5kEntity.mapidNm, mapInkx5kEntity.mapidcdNo)))
|
"concat({0}, ' ', {1})", mapInkx5kEntity.mapidNm, mapInkx5kEntity.mapidcdNo),
|
||||||
|
Expressions.stringTemplate(
|
||||||
|
"ST_AsGeoJSON(ST_Transform({0}, 5186))", mapInkx5kEntity.geom)
|
||||||
|
.as("bbox")))
|
||||||
.from(mapSheetAnalInferenceEntity)
|
.from(mapSheetAnalInferenceEntity)
|
||||||
.innerJoin(mapSheetAnalDataInferenceEntity)
|
.innerJoin(mapSheetAnalDataInferenceEntity)
|
||||||
.on(mapSheetAnalInferenceEntity.id.eq(mapSheetAnalDataInferenceEntity.analUid))
|
.on(mapSheetAnalInferenceEntity.id.eq(mapSheetAnalDataInferenceEntity.analUid))
|
||||||
@@ -361,7 +367,10 @@ public class ChangeDetectionRepositoryImpl extends QuerydslRepositorySupport
|
|||||||
mapInkx50kEntity.mapidcdNo,
|
mapInkx50kEntity.mapidcdNo,
|
||||||
mapInkx50kEntity.mapidNm,
|
mapInkx50kEntity.mapidNm,
|
||||||
Expressions.stringTemplate(
|
Expressions.stringTemplate(
|
||||||
"concat({0}, ' ', {1})", mapInkx50kEntity.mapidNm, mapInkx50kEntity.mapidcdNo)))
|
"concat({0}, ' ', {1})", mapInkx50kEntity.mapidNm, mapInkx50kEntity.mapidcdNo),
|
||||||
|
Expressions.stringTemplate(
|
||||||
|
"ST_AsGeoJSON(ST_Transform({0}, 5186))", mapInkx50kEntity.geom)
|
||||||
|
.as("bbox")))
|
||||||
.from(mapSheetAnalInferenceEntity)
|
.from(mapSheetAnalInferenceEntity)
|
||||||
.innerJoin(mapSheetAnalDataInferenceEntity)
|
.innerJoin(mapSheetAnalDataInferenceEntity)
|
||||||
.on(mapSheetAnalInferenceEntity.id.eq(mapSheetAnalDataInferenceEntity.analUid))
|
.on(mapSheetAnalInferenceEntity.id.eq(mapSheetAnalDataInferenceEntity.analUid))
|
||||||
@@ -369,7 +378,7 @@ public class ChangeDetectionRepositoryImpl extends QuerydslRepositorySupport
|
|||||||
.on(mapSheetAnalDataInferenceEntity.mapSheetNum.stringValue().eq(mapInkx5kEntity.mapidcdNo))
|
.on(mapSheetAnalDataInferenceEntity.mapSheetNum.stringValue().eq(mapInkx5kEntity.mapidcdNo))
|
||||||
.innerJoin(mapInkx5kEntity.mapInkx50k, mapInkx50kEntity)
|
.innerJoin(mapInkx5kEntity.mapInkx50k, mapInkx50kEntity)
|
||||||
.where(mapSheetAnalInferenceEntity.uuid.eq(uuid))
|
.where(mapSheetAnalInferenceEntity.uuid.eq(uuid))
|
||||||
.groupBy(mapInkx50kEntity.mapidcdNo, mapInkx50kEntity.mapidNm)
|
.groupBy(mapInkx50kEntity.mapidcdNo, mapInkx50kEntity.mapidNm, mapInkx50kEntity.geom)
|
||||||
.orderBy(mapInkx50kEntity.mapidcdNo.asc())
|
.orderBy(mapInkx50kEntity.mapidcdNo.asc())
|
||||||
.fetch();
|
.fetch();
|
||||||
}
|
}
|
||||||
@@ -385,16 +394,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));
|
||||||
builder.and(
|
|
||||||
mapSheetAnalDataInferenceGeomEntity
|
|
||||||
.resultUid
|
|
||||||
.eq(chnDtctId)
|
|
||||||
.or(mapSheetAnalDataInferenceGeomEntity.resultUid.in(cdObjectIds)));
|
|
||||||
|
|
||||||
List<ChangeDetectionDto.PolygonQueryData> list =
|
// pnu가 들어온 경우
|
||||||
|
boolean hasPnu = pnu != null && !pnu.isBlank();
|
||||||
|
|
||||||
|
// pnu 가 없을 때
|
||||||
|
if (!hasPnu) {
|
||||||
|
builder.and(
|
||||||
|
mapSheetAnalDataInferenceGeomEntity
|
||||||
|
.resultUid
|
||||||
|
.eq(cdObjectId)
|
||||||
|
.or(mapSheetAnalDataInferenceGeomEntity.resultUid.in(cdObjectIds)));
|
||||||
|
}
|
||||||
|
|
||||||
|
JPAQuery<PolygonQueryData> query =
|
||||||
queryFactory
|
queryFactory
|
||||||
.select(
|
.select(
|
||||||
Projections.constructor(
|
Projections.constructor(
|
||||||
@@ -419,10 +435,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 =
|
||||||
@@ -464,16 +489,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));
|
||||||
builder.and(
|
|
||||||
mapSheetAnalDataInferenceGeomEntity
|
|
||||||
.resultUid
|
|
||||||
.eq(chnDtctId)
|
|
||||||
.or(mapSheetAnalDataInferenceGeomEntity.resultUid.in(cdObjectIds)));
|
|
||||||
|
|
||||||
List<ChangeDetectionDto.PointQueryData> list =
|
// pnu가 들어온 경우
|
||||||
|
boolean hasPnu = pnu != null && !pnu.isBlank();
|
||||||
|
|
||||||
|
// pnu 가 없을 때
|
||||||
|
if (!hasPnu) {
|
||||||
|
builder.and(
|
||||||
|
mapSheetAnalDataInferenceGeomEntity
|
||||||
|
.resultUid
|
||||||
|
.eq(cdObjectId)
|
||||||
|
.or(mapSheetAnalDataInferenceGeomEntity.resultUid.in(cdObjectIds)));
|
||||||
|
}
|
||||||
|
|
||||||
|
JPAQuery<PointQueryData> query =
|
||||||
queryFactory
|
queryFactory
|
||||||
.select(
|
.select(
|
||||||
Projections.constructor(
|
Projections.constructor(
|
||||||
@@ -492,9 +524,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 =
|
||||||
@@ -653,4 +695,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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 {}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package com.kamco.cd.kamcoback.postgres.repository.gukyuin;
|
||||||
|
|
||||||
|
public interface GukYuinPnuCntUpdateJobRepositoryCustom {
|
||||||
|
|
||||||
|
void updateGukYuinContListPnuUpdateCnt();
|
||||||
|
|
||||||
|
void updateGukYuinApplyStatus(String uid, String status);
|
||||||
|
}
|
||||||
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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))
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ 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.LabelWorkMng;
|
import com.kamco.cd.kamcoback.label.dto.LabelWorkDto.LabelWorkMng;
|
||||||
@@ -166,6 +167,19 @@ public class LabelWorkRepositoryImpl implements LabelWorkRepositoryCustom {
|
|||||||
.from(labelingAssignmentEntity)
|
.from(labelingAssignmentEntity)
|
||||||
.where(labelingAssignmentEntity.analUid.eq(mapSheetAnalInferenceEntity.id));
|
.where(labelingAssignmentEntity.analUid.eq(mapSheetAnalInferenceEntity.id));
|
||||||
|
|
||||||
|
/** 순서 ING 진행중 ASSIGNED 작업할당 PENDING 작업대기 FINISH 종료 */
|
||||||
|
NumberExpression<Integer> stateOrder =
|
||||||
|
new CaseBuilder()
|
||||||
|
.when(mapSheetAnalInferenceEntity.analState.eq(LabelMngState.ING.getId()))
|
||||||
|
.then(0)
|
||||||
|
.when(mapSheetAnalInferenceEntity.analState.eq(LabelMngState.ASSIGNED.getId()))
|
||||||
|
.then(1)
|
||||||
|
.when(mapSheetAnalInferenceEntity.analState.eq(LabelMngState.PENDING.getId()))
|
||||||
|
.then(2)
|
||||||
|
.when(mapSheetAnalInferenceEntity.analState.eq(LabelMngState.FINISH.getId()))
|
||||||
|
.then(3)
|
||||||
|
.otherwise(99);
|
||||||
|
|
||||||
List<LabelWorkMng> foundContent =
|
List<LabelWorkMng> foundContent =
|
||||||
queryFactory
|
queryFactory
|
||||||
.select(
|
.select(
|
||||||
@@ -251,6 +265,7 @@ public class LabelWorkRepositoryImpl implements LabelWorkRepositoryCustom {
|
|||||||
mapSheetLearnEntity.uid,
|
mapSheetLearnEntity.uid,
|
||||||
mapSheetLearnEntity.uuid)
|
mapSheetLearnEntity.uuid)
|
||||||
.orderBy(
|
.orderBy(
|
||||||
|
stateOrder.asc(),
|
||||||
mapSheetAnalInferenceEntity.targetYyyy.desc(),
|
mapSheetAnalInferenceEntity.targetYyyy.desc(),
|
||||||
mapSheetAnalInferenceEntity.compareYyyy.desc(),
|
mapSheetAnalInferenceEntity.compareYyyy.desc(),
|
||||||
mapSheetAnalInferenceEntity.stage.desc())
|
mapSheetAnalInferenceEntity.stage.desc())
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package com.kamco.cd.kamcoback.postgres.repository.mapsheet;
|
package com.kamco.cd.kamcoback.postgres.repository.mapsheet;
|
||||||
|
|
||||||
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;
|
|
||||||
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;
|
||||||
@@ -16,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);
|
||||||
@@ -34,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);
|
||||||
@@ -54,19 +81,39 @@ 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);
|
||||||
|
|
||||||
List<MngListDto> findByHstMapSheetTargetList(InferenceResultDto.RegReq req);
|
|
||||||
|
|
||||||
List<MngListDto> findByHstMapSheetTargetList(int mngYyyy, List<String> mapIds);
|
|
||||||
|
|
||||||
MapSheetMngDto.MngFilesDto findByFileUidMapSheetFile(Long fileUid);
|
MapSheetMngDto.MngFilesDto findByFileUidMapSheetFile(Long fileUid);
|
||||||
|
|
||||||
void updateHstFileSizes(Long hstUid, long tifSizeBytes, long tfwSizeBytes, long totalSizeBytes);
|
void updateHstFileSizes(Long hstUid, long tifSizeBytes, long tfwSizeBytes, long totalSizeBytes);
|
||||||
@@ -75,11 +122,39 @@ public interface MapSheetMngRepositoryCustom {
|
|||||||
|
|
||||||
Page<YearEntity> getYears(YearSearchReq req);
|
Page<YearEntity> getYears(YearSearchReq req);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 도엽 영상파일 정보 조회
|
||||||
|
*
|
||||||
|
* @param yyyy 연도
|
||||||
|
* @param mapSheetNums 도엽목록
|
||||||
|
* @return 도엽 영상파일 정보
|
||||||
|
*/
|
||||||
List<ImageFeature> getSceneInference(String yyyy, List<String> mapSheetNums);
|
List<ImageFeature> getSceneInference(String yyyy, List<String> mapSheetNums);
|
||||||
|
|
||||||
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);
|
||||||
|
|
||||||
List<MapSheetMngHstEntity> getMapSheetMngHst(Integer year);
|
/**
|
||||||
|
* 연도별 도엽 목록 조회
|
||||||
|
*
|
||||||
|
* @param year 관리연도
|
||||||
|
* @param mapSheetNums50k 50k 도엽번호 리스트 (null 또는 empty인 경우 전체 조회)
|
||||||
|
* @return 도엽 목록
|
||||||
|
*/
|
||||||
|
List<MngListDto> getMapSheetMngHst(Integer year, List<String> mapSheetNums50k);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 비교연도 사용 가능한 이전도엽을 조회한다.
|
||||||
|
*
|
||||||
|
* @param year 연도
|
||||||
|
* @param mapIds 도엽목록
|
||||||
|
* @return 사용 가능한 이전도엽목록
|
||||||
|
*/
|
||||||
|
List<MngListDto> findFallbackCompareYearByMapSheets(Integer year, List<String> mapIds);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,13 +11,15 @@ 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;
|
|
||||||
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.QMapSheetMngFileEntity;
|
||||||
|
import com.kamco.cd.kamcoback.postgres.entity.QMapSheetMngHstEntity;
|
||||||
import com.kamco.cd.kamcoback.postgres.entity.QYearEntity;
|
import com.kamco.cd.kamcoback.postgres.entity.QYearEntity;
|
||||||
import com.kamco.cd.kamcoback.postgres.entity.YearEntity;
|
import com.kamco.cd.kamcoback.postgres.entity.YearEntity;
|
||||||
import com.querydsl.core.BooleanBuilder;
|
import com.querydsl.core.BooleanBuilder;
|
||||||
@@ -25,7 +28,7 @@ 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.impl.JPAQueryFactory;
|
import com.querydsl.jpa.impl.JPAQueryFactory;
|
||||||
import jakarta.persistence.EntityManager;
|
import jakarta.persistence.EntityManager;
|
||||||
import jakarta.persistence.PersistenceContext;
|
import jakarta.persistence.PersistenceContext;
|
||||||
@@ -47,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;
|
||||||
|
|
||||||
@@ -56,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 =
|
||||||
@@ -224,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 =
|
||||||
@@ -242,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
|
||||||
@@ -251,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();
|
||||||
@@ -396,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) {
|
||||||
@@ -404,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")));
|
||||||
|
|
||||||
@@ -411,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
|
||||||
@@ -472,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())
|
||||||
@@ -488,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) {
|
||||||
|
|
||||||
@@ -570,87 +610,6 @@ public class MapSheetMngRepositoryImpl extends QuerydslRepositorySupport
|
|||||||
return foundContent;
|
return foundContent;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 기준년도 추론 실행 가능 도엽 조회
|
|
||||||
*
|
|
||||||
* @param req
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public List<MngListDto> findByHstMapSheetTargetList(InferenceResultDto.RegReq req) {
|
|
||||||
BooleanBuilder whereBuilder = new BooleanBuilder();
|
|
||||||
|
|
||||||
whereBuilder.and(mapSheetMngHstEntity.dataState.eq("DONE"));
|
|
||||||
whereBuilder.and(
|
|
||||||
mapSheetMngHstEntity
|
|
||||||
.syncState
|
|
||||||
.eq("DONE")
|
|
||||||
.or(mapSheetMngHstEntity.syncCheckState.eq("DONE")));
|
|
||||||
whereBuilder.and(mapSheetMngHstEntity.useInference.eq("USE"));
|
|
||||||
|
|
||||||
whereBuilder.and(mapSheetMngHstEntity.mngYyyy.eq(req.getTargetYyyy()));
|
|
||||||
|
|
||||||
BooleanBuilder likeBuilder = new BooleanBuilder();
|
|
||||||
|
|
||||||
if (MapSheetScope.PART.getId().equals(req.getMapSheetScope())) {
|
|
||||||
List<String> list = req.getMapSheetNum();
|
|
||||||
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
|
|
||||||
.select(
|
|
||||||
Projections.constructor(
|
|
||||||
MngListDto.class,
|
|
||||||
mapSheetMngHstEntity.mngYyyy,
|
|
||||||
mapSheetMngHstEntity.mapSheetNum,
|
|
||||||
mapSheetMngHstEntity.mapSheetName,
|
|
||||||
nullExpression(Integer.class),
|
|
||||||
nullExpression(Boolean.class)))
|
|
||||||
.from(mapSheetMngHstEntity)
|
|
||||||
.innerJoin(mapInkx5kEntity)
|
|
||||||
.on(
|
|
||||||
mapInkx5kEntity
|
|
||||||
.mapidcdNo
|
|
||||||
.eq(mapSheetMngHstEntity.mapSheetNum)
|
|
||||||
.and(mapInkx5kEntity.useInference.eq(CommonUseStatus.USE)))
|
|
||||||
.where(whereBuilder)
|
|
||||||
.fetch();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<MngListDto> findByHstMapSheetTargetList(int mngYyyy, List<String> mapIds) {
|
|
||||||
BooleanBuilder whereBuilder = new BooleanBuilder();
|
|
||||||
whereBuilder.and(mapSheetMngHstEntity.mngYyyy.eq(mngYyyy));
|
|
||||||
whereBuilder.and(mapSheetMngHstEntity.mapSheetNum.in(mapIds));
|
|
||||||
whereBuilder.and(mapSheetMngHstEntity.dataState.eq("DONE"));
|
|
||||||
whereBuilder.and(mapSheetMngHstEntity.syncState.eq("DONE")); // TODO 싱크체크 or조건 추가
|
|
||||||
whereBuilder.and(mapSheetMngHstEntity.useInference.eq("USE"));
|
|
||||||
|
|
||||||
return queryFactory
|
|
||||||
.select(
|
|
||||||
Projections.constructor(
|
|
||||||
MngListDto.class,
|
|
||||||
mapSheetMngHstEntity.mngYyyy,
|
|
||||||
mapSheetMngHstEntity.mapSheetNum,
|
|
||||||
mapSheetMngHstEntity.mapSheetName))
|
|
||||||
.from(mapSheetMngHstEntity)
|
|
||||||
.where(whereBuilder)
|
|
||||||
.fetch();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<MapSheetMngDto.MngFilesDto> findHstUidToMapSheetFileList(Long hstUid) {
|
public List<MapSheetMngDto.MngFilesDto> findHstUidToMapSheetFileList(Long hstUid) {
|
||||||
BooleanBuilder whereBuilder = new BooleanBuilder();
|
BooleanBuilder whereBuilder = new BooleanBuilder();
|
||||||
@@ -758,6 +717,11 @@ public class MapSheetMngRepositoryImpl extends QuerydslRepositorySupport
|
|||||||
.execute();
|
.execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 같은 년도로 저장된 데이터가 있다면 삭제
|
||||||
|
*
|
||||||
|
* @param mngYyyy
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void deleteByMngYyyyMngAll(int mngYyyy) {
|
public void deleteByMngYyyyMngAll(int mngYyyy) {
|
||||||
|
|
||||||
@@ -778,6 +742,12 @@ 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();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -908,6 +878,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) {
|
||||||
|
|
||||||
@@ -1005,7 +982,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()) {
|
||||||
@@ -1044,6 +1022,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 =
|
||||||
@@ -1100,23 +1083,133 @@ public class MapSheetMngRepositoryImpl extends QuerydslRepositorySupport
|
|||||||
mapSheetMngHstEntity.mapSheetNum));
|
mapSheetMngHstEntity.mapSheetNum));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 영상데이터관리 > 연도별 도엽 목록 조회
|
||||||
|
*
|
||||||
|
* @param year 관리연도
|
||||||
|
* @param mapSheetNums50k 50k 도엽번호 리스트 (null 또는 empty인 경우 전체 조회)
|
||||||
|
* @return 도엽 목록
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public List<MapSheetMngHstEntity> getMapSheetMngHst(Integer year) {
|
public List<MngListDto> getMapSheetMngHst(Integer year, List<String> mapSheetNums50k) {
|
||||||
|
/*
|
||||||
|
검색조건:
|
||||||
|
- ✅ 데이터 처리 완료(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();
|
||||||
|
whereBuilder.and(mapSheetMngHstEntity.mngYyyy.eq(year));
|
||||||
|
whereBuilder.and(mapSheetMngHstEntity.dataState.eq("DONE"));
|
||||||
|
whereBuilder.and(
|
||||||
|
mapSheetMngHstEntity
|
||||||
|
.syncState
|
||||||
|
.eq("DONE")
|
||||||
|
.or(mapSheetMngHstEntity.syncCheckState.eq("DONE")));
|
||||||
|
whereBuilder.and(mapSheetMngHstEntity.useInference.eq("USE"));
|
||||||
|
|
||||||
|
// TIF 파일 존재 여부 확인
|
||||||
|
whereBuilder.and(
|
||||||
|
JPAExpressions.selectOne()
|
||||||
|
.from(mapSheetMngFileEntity)
|
||||||
|
.where(
|
||||||
|
mapSheetMngFileEntity
|
||||||
|
.hstUid
|
||||||
|
.eq(mapSheetMngHstEntity.hstUid)
|
||||||
|
.and(mapSheetMngHstEntity.mngYyyy.eq(year))
|
||||||
|
.and(mapSheetMngFileEntity.fileExt.eq("tif"))
|
||||||
|
.and(mapSheetMngFileEntity.fileState.eq("DONE"))
|
||||||
|
.and(mapSheetMngFileEntity.fileDel.eq(false)))
|
||||||
|
.exists());
|
||||||
|
|
||||||
return queryFactory
|
return queryFactory
|
||||||
.select(mapSheetMngHstEntity)
|
.select(
|
||||||
|
Projections.constructor(
|
||||||
|
MngListDto.class,
|
||||||
|
mapSheetMngHstEntity.mngYyyy,
|
||||||
|
mapSheetMngHstEntity.mapSheetNum,
|
||||||
|
mapSheetMngHstEntity.mapSheetName,
|
||||||
|
nullExpression(Integer.class),
|
||||||
|
nullExpression(Boolean.class)))
|
||||||
.from(mapSheetMngHstEntity)
|
.from(mapSheetMngHstEntity)
|
||||||
.innerJoin(mapSheetMngFileEntity)
|
.innerJoin(mapInkx5kEntity)
|
||||||
.on(mapSheetMngFileEntity.hstUid.eq(mapSheetMngHstEntity.hstUid))
|
.on(
|
||||||
.where(
|
mapInkx5kEntity
|
||||||
mapSheetMngHstEntity
|
.mapidcdNo
|
||||||
.mngYyyy
|
.eq(mapSheetMngHstEntity.mapSheetNum)
|
||||||
.eq(year)
|
.and(mapInkx5kEntity.useInference.eq(CommonUseStatus.USE)))
|
||||||
.and(
|
.innerJoin(mapInkx5kEntity.mapInkx50k, mapInkx50kEntity)
|
||||||
mapSheetMngHstEntity
|
.where(whereBuilder, inScenes50(mapInkx50kEntity, mapSheetNums50k))
|
||||||
.syncState
|
|
||||||
.eq("DONE")
|
|
||||||
.or(mapSheetMngHstEntity.syncCheckState.eq("DONE")))
|
|
||||||
.and(mapSheetMngFileEntity.fileExt.eq("tif")))
|
|
||||||
.fetch();
|
.fetch();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<MngListDto> findFallbackCompareYearByMapSheets(Integer year, List<String> mapIds) {
|
||||||
|
QMapSheetMngHstEntity h = QMapSheetMngHstEntity.mapSheetMngHstEntity;
|
||||||
|
QMapSheetMngHstEntity h2 = new QMapSheetMngHstEntity("h2");
|
||||||
|
QMapSheetMngFileEntity f = QMapSheetMngFileEntity.mapSheetMngFileEntity;
|
||||||
|
QMapSheetMngFileEntity f2 = new QMapSheetMngFileEntity("f2");
|
||||||
|
QMapInkx5kEntity inkk = QMapInkx5kEntity.mapInkx5kEntity;
|
||||||
|
|
||||||
|
return queryFactory
|
||||||
|
.select(
|
||||||
|
Projections.constructor(
|
||||||
|
MngListDto.class,
|
||||||
|
h.mngYyyy,
|
||||||
|
h.mapSheetNum,
|
||||||
|
h.mapSheetName,
|
||||||
|
nullExpression(Integer.class),
|
||||||
|
nullExpression(Boolean.class)))
|
||||||
|
.from(h)
|
||||||
|
.innerJoin(inkk)
|
||||||
|
.on(inkk.mapidcdNo.eq(h.mapSheetNum).and(inkk.useInference.eq(CommonUseStatus.USE)))
|
||||||
|
.where(
|
||||||
|
h.mapSheetNum.in(mapIds),
|
||||||
|
h.mngYyyy.lt(year),
|
||||||
|
h.dataState.eq("DONE"),
|
||||||
|
h.useInference.eq("USE"),
|
||||||
|
h.syncState.eq("DONE").or(h.syncCheckState.eq("DONE")),
|
||||||
|
JPAExpressions.selectOne()
|
||||||
|
.from(f)
|
||||||
|
.where(
|
||||||
|
f.hstUid.eq(h.hstUid),
|
||||||
|
f.mngYyyy.eq(year),
|
||||||
|
f.fileExt.eq("tif"),
|
||||||
|
f.fileState.eq("DONE"),
|
||||||
|
f.fileDel.eq(false))
|
||||||
|
.exists(),
|
||||||
|
|
||||||
|
// mapSheetNum별 최대 mngYyyy인 행만 남김
|
||||||
|
h.mngYyyy.eq(
|
||||||
|
JPAExpressions.select(h2.mngYyyy.max())
|
||||||
|
.from(h2)
|
||||||
|
.where(
|
||||||
|
h2.mapSheetNum.eq(h.mapSheetNum),
|
||||||
|
h2.mngYyyy.lt(year),
|
||||||
|
h2.dataState.eq("DONE"),
|
||||||
|
h2.useInference.eq("USE"),
|
||||||
|
h2.syncState.eq("DONE").or(h2.syncCheckState.eq("DONE")),
|
||||||
|
JPAExpressions.selectOne()
|
||||||
|
.from(f2)
|
||||||
|
.where(
|
||||||
|
f2.hstUid.eq(h2.hstUid),
|
||||||
|
f2.mngYyyy.eq(year),
|
||||||
|
f2.fileExt.eq("tif"),
|
||||||
|
f2.fileState.eq("DONE"),
|
||||||
|
f2.fileDel.eq(false))
|
||||||
|
.exists())))
|
||||||
|
.fetch();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 도엽번호(1:50k) IN 쿼리조건
|
||||||
|
private BooleanExpression inScenes50(QMapInkx50kEntity mapInkx50k, List<String> sceneIds) {
|
||||||
|
if (sceneIds == null || sceneIds.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return mapInkx50k.mapidcdNo.in(sceneIds);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,12 @@ public interface MembersRepositoryCustom {
|
|||||||
|
|
||||||
boolean existsByEmployeeNo(String employeeNo);
|
boolean existsByEmployeeNo(String employeeNo);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 사번으로 사용자 조회
|
||||||
|
*
|
||||||
|
* @param employeeNo 사번
|
||||||
|
* @return 사용자 정보 조회
|
||||||
|
*/
|
||||||
Optional<MemberEntity> findByEmployeeNo(String employeeNo);
|
Optional<MemberEntity> findByEmployeeNo(String employeeNo);
|
||||||
|
|
||||||
Optional<MemberEntity> findByUserId(String userId);
|
Optional<MemberEntity> findByUserId(String userId);
|
||||||
|
|||||||
@@ -9,6 +9,16 @@ import org.springframework.data.domain.Page;
|
|||||||
|
|
||||||
public interface ModelMngRepositoryCustom {
|
public interface ModelMngRepositoryCustom {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 모델조회
|
||||||
|
*
|
||||||
|
* @param searchReq 페이징
|
||||||
|
* @param startDate 시작날짜
|
||||||
|
* @param endDate 종료날짜
|
||||||
|
* @param modelType 모델 타입 G1, G2, G3
|
||||||
|
* @param searchVal 모델 ver
|
||||||
|
* @return 모델 목록
|
||||||
|
*/
|
||||||
Page<ModelMngDto.ModelList> findModelMgmtList(
|
Page<ModelMngDto.ModelList> findModelMgmtList(
|
||||||
ModelMngDto.searchReq searchReq,
|
ModelMngDto.searchReq searchReq,
|
||||||
LocalDate startDate,
|
LocalDate startDate,
|
||||||
@@ -29,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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,6 +59,8 @@ public class MapInkxMngApiController {
|
|||||||
mapInkxMngService.findMapInkxMngList(searchReq, useInference, searchVal));
|
mapInkxMngService.findMapInkxMngList(searchReq, useInference, searchVal));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 미사용 : 기획에서 도엽정보 등록 로직 제거됨
|
||||||
|
@Hidden
|
||||||
@Operation(summary = "저장", description = "도엽정보를 저장 합니다.")
|
@Operation(summary = "저장", description = "도엽정보를 저장 합니다.")
|
||||||
@ApiResponses(
|
@ApiResponses(
|
||||||
value = {
|
value = {
|
||||||
|
|||||||
@@ -2,6 +2,9 @@ package com.kamco.cd.kamcoback.scheduler;
|
|||||||
|
|
||||||
import com.kamco.cd.kamcoback.code.dto.CommonCodeDto;
|
import com.kamco.cd.kamcoback.code.dto.CommonCodeDto;
|
||||||
import com.kamco.cd.kamcoback.config.api.ApiResponseDto;
|
import com.kamco.cd.kamcoback.config.api.ApiResponseDto;
|
||||||
|
import com.kamco.cd.kamcoback.gukyuin.service.GukYuinApiService;
|
||||||
|
import com.kamco.cd.kamcoback.inference.dto.InferenceResultShpDto;
|
||||||
|
import com.kamco.cd.kamcoback.inference.service.InferenceResultShpService;
|
||||||
import com.kamco.cd.kamcoback.scheduler.service.GukYuinApiLabelJobService;
|
import com.kamco.cd.kamcoback.scheduler.service.GukYuinApiLabelJobService;
|
||||||
import com.kamco.cd.kamcoback.scheduler.service.GukYuinApiPnuJobService;
|
import com.kamco.cd.kamcoback.scheduler.service.GukYuinApiPnuJobService;
|
||||||
import com.kamco.cd.kamcoback.scheduler.service.GukYuinApiStatusJobService;
|
import com.kamco.cd.kamcoback.scheduler.service.GukYuinApiStatusJobService;
|
||||||
@@ -10,20 +13,26 @@ import com.kamco.cd.kamcoback.scheduler.service.MemberInactiveJobService;
|
|||||||
import com.kamco.cd.kamcoback.scheduler.service.TrainingDataLabelJobService;
|
import com.kamco.cd.kamcoback.scheduler.service.TrainingDataLabelJobService;
|
||||||
import com.kamco.cd.kamcoback.scheduler.service.TrainingDataReviewJobService;
|
import com.kamco.cd.kamcoback.scheduler.service.TrainingDataReviewJobService;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
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.Content;
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
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 java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
import org.springframework.web.bind.annotation.PutMapping;
|
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.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestParam;
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
@Tag(name = "스케줄링 수동 호출 테스트", description = "스케줄링 수동 호출 테스트 API")
|
@Tag(name = "스케줄링 및 jar 수동 호출 테스트", description = "스케줄링 및 jar 수동 호출 테스트 API")
|
||||||
@RestController
|
@RestController
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
@RequestMapping("/api/schedule")
|
@RequestMapping("/api/schedule")
|
||||||
@@ -37,6 +46,8 @@ public class SchedulerApiController {
|
|||||||
private final TrainingDataReviewJobService trainingDataReviewJobService;
|
private final TrainingDataReviewJobService trainingDataReviewJobService;
|
||||||
private final MemberInactiveJobService memberInactiveJobService;
|
private final MemberInactiveJobService memberInactiveJobService;
|
||||||
private final MapSheetMngFileJobController mapSheetMngFileJobController;
|
private final MapSheetMngFileJobController mapSheetMngFileJobController;
|
||||||
|
private final InferenceResultShpService inferenceResultShpService;
|
||||||
|
private final GukYuinApiService gukYuinApiService;
|
||||||
|
|
||||||
@Operation(summary = "국유인 탐지객체 조회 PNU 업데이트 스케줄링", description = "국유인 탐지객체 조회 PNU 업데이트 스케줄링")
|
@Operation(summary = "국유인 탐지객체 조회 PNU 업데이트 스케줄링", description = "국유인 탐지객체 조회 PNU 업데이트 스케줄링")
|
||||||
@GetMapping("/gukyuin/pnu")
|
@GetMapping("/gukyuin/pnu")
|
||||||
@@ -48,7 +59,7 @@ public class SchedulerApiController {
|
|||||||
@Operation(summary = "국유인 등록 상태 체크 스케줄링", description = "국유인 등록 상태 체크 스케줄링")
|
@Operation(summary = "국유인 등록 상태 체크 스케줄링", description = "국유인 등록 상태 체크 스케줄링")
|
||||||
@GetMapping("/gukyuin/status")
|
@GetMapping("/gukyuin/status")
|
||||||
public ApiResponseDto<Void> findGukYuinMastCompleteYn() {
|
public ApiResponseDto<Void> findGukYuinMastCompleteYn() {
|
||||||
gukYuinApiStatusJobService.findGukYuinMastCompleteYn();
|
gukYuinApiStatusJobService.findGukYuinPnuCntUpdate();
|
||||||
return ApiResponseDto.ok(null);
|
return ApiResponseDto.ok(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,7 +71,21 @@ public class SchedulerApiController {
|
|||||||
return ApiResponseDto.ok(null);
|
return ApiResponseDto.ok(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operation(summary = "국유인 실태조사 적합여부 업데이트 스케줄링", description = "국유인 실태조사 적합여부 업데이트 스케줄링")
|
@Operation(summary = "국유인 실태조사 적합여부 전송가능한 객체 ids", description = "국유인 실태조사 적합여부 전송가능한 객체 ids")
|
||||||
|
@GetMapping("/gukyuin/stblt-object-ids/{uid}/{mapSheetNum}")
|
||||||
|
public ApiResponseDto<List<String>> findStbltObjectIds(
|
||||||
|
@PathVariable String uid, @PathVariable String mapSheetNum) {
|
||||||
|
return ApiResponseDto.ok(gukYuinApiService.findStbltObjectIds(uid, mapSheetNum));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "국유인 라벨 완료 전송 스케줄링", description = "국유인 라벨 완료 전송 스케줄링")
|
||||||
|
@PostMapping("/gukyuin/stblt-update")
|
||||||
|
public ApiResponseDto<Void> stbltBulkUpdate(@RequestBody List<String> objectIds) {
|
||||||
|
gukYuinApiService.stbltBulkUpdate(objectIds);
|
||||||
|
return ApiResponseDto.ok(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "국유인 실태조사 적합여부 조회 스케줄링", description = "국유인 실태조사 적합여부 조회 스케줄링")
|
||||||
@GetMapping("/gukyuin/stblt")
|
@GetMapping("/gukyuin/stblt")
|
||||||
public ApiResponseDto<Void> findGukYuinEligibleForSurvey(
|
public ApiResponseDto<Void> findGukYuinEligibleForSurvey(
|
||||||
@RequestParam(required = false) LocalDate baseDate) {
|
@RequestParam(required = false) LocalDate baseDate) {
|
||||||
@@ -117,4 +142,44 @@ public class SchedulerApiController {
|
|||||||
|
|
||||||
return ApiResponseDto.createOK("OK");
|
return ApiResponseDto.createOK("OK");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@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/inference/{uuid}")
|
||||||
|
public ApiResponseDto<InferenceResultShpDto.InferenceCntDto> saveInferenceData(
|
||||||
|
@Parameter(example = "feb2ec0b-a0f7-49ca-95e4-98b2231bdaae", description = "추론 uuid")
|
||||||
|
@PathVariable
|
||||||
|
UUID uuid) {
|
||||||
|
return ApiResponseDto.createOK(inferenceResultShpService.saveInferenceResultData(uuid));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "추론결과 shp 생성", description = "추론결과 shp 생성")
|
||||||
|
@PostMapping("/shp/inference/{uuid}")
|
||||||
|
public ApiResponseDto<Void> createShp(
|
||||||
|
@Parameter(example = "feb2ec0b-a0f7-49ca-95e4-98b2231bdaae", description = "추론 uuid")
|
||||||
|
@PathVariable
|
||||||
|
UUID uuid) {
|
||||||
|
// shp 파일 수동생성
|
||||||
|
inferenceResultShpService.createShp(uuid);
|
||||||
|
return ApiResponseDto.createOK(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "국유인 실태조사 적합여부 랜덤 업데이트", description = "국유인 실태조사 적합여부 랜덤 업데이트")
|
||||||
|
@PutMapping("/gukyuin/random-stblt-update/{uid}/{updateCnt}")
|
||||||
|
public ApiResponseDto<Integer> updateStbltRandomData(
|
||||||
|
@PathVariable String uid, @PathVariable int updateCnt) {
|
||||||
|
return ApiResponseDto.ok(gukYuinApiService.updateStbltRandomData(uid, updateCnt));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,17 @@ public class AsyncConfig {
|
|||||||
return ex;
|
return ex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean(name = "makeShapeFile")
|
||||||
|
public Executor makeShapeFileExecutor() {
|
||||||
|
ThreadPoolTaskExecutor ex = new ThreadPoolTaskExecutor();
|
||||||
|
ex.setCorePoolSize(2);
|
||||||
|
ex.setMaxPoolSize(4);
|
||||||
|
ex.setQueueCapacity(50);
|
||||||
|
ex.setThreadNamePrefix("makeShapeFile-");
|
||||||
|
ex.initialize();
|
||||||
|
return ex;
|
||||||
|
}
|
||||||
|
|
||||||
@Bean(name = "auditLogExecutor")
|
@Bean(name = "auditLogExecutor")
|
||||||
public Executor auditLogExecutor() {
|
public Executor auditLogExecutor() {
|
||||||
ThreadPoolTaskExecutor exec = new ThreadPoolTaskExecutor();
|
ThreadPoolTaskExecutor exec = new ThreadPoolTaskExecutor();
|
||||||
@@ -31,4 +42,16 @@ public class AsyncConfig {
|
|||||||
exec.initialize();
|
exec.initialize();
|
||||||
return exec;
|
return exec;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 0313
|
||||||
|
@Bean(name = "inferenceEndExecutor")
|
||||||
|
public Executor inferenceEndExecutor() {
|
||||||
|
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
|
||||||
|
executor.setCorePoolSize(5);
|
||||||
|
executor.setMaxPoolSize(10);
|
||||||
|
executor.setQueueCapacity(100);
|
||||||
|
executor.setThreadNamePrefix("inference-async-");
|
||||||
|
executor.initialize();
|
||||||
|
return executor;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,19 +7,76 @@ import org.springframework.stereotype.Component;
|
|||||||
@Component
|
@Component
|
||||||
public class ShpKeyLock {
|
public class ShpKeyLock {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* key별 Lock 객체를 저장하는 맵
|
||||||
|
*
|
||||||
|
* <p>- key: 예) shp 파일 경로, uuid, 도엽번호 등 - value: 해당 key 전용 ReentrantLock
|
||||||
|
*
|
||||||
|
* <p>ConcurrentHashMap을 사용하여 멀티스레드 환경에서도 안전하게 접근 가능
|
||||||
|
*/
|
||||||
private final ConcurrentHashMap<String, ReentrantLock> locks = new ConcurrentHashMap<>();
|
private final ConcurrentHashMap<String, ReentrantLock> locks = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 특정 key에 대한 Lock 시도
|
||||||
|
*
|
||||||
|
* @param key 동시성 제어 대상 식별자
|
||||||
|
* @return true → 락 획득 성공 false → 이미 다른 스레드가 사용 중
|
||||||
|
*/
|
||||||
public boolean tryLock(String key) {
|
public boolean tryLock(String key) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* computeIfAbsent 설명:
|
||||||
|
*
|
||||||
|
* <p>- 해당 key가 이미 존재하면 기존 Lock 반환 - 없으면 새 ReentrantLock 생성 후 저장
|
||||||
|
*
|
||||||
|
* <p>동시성 환경에서도 원자적으로 실행됨
|
||||||
|
*/
|
||||||
ReentrantLock lock = locks.computeIfAbsent(key, k -> new ReentrantLock());
|
ReentrantLock lock = locks.computeIfAbsent(key, k -> new ReentrantLock());
|
||||||
|
|
||||||
|
/**
|
||||||
|
* tryLock():
|
||||||
|
*
|
||||||
|
* <p>- 대기하지 않고 즉시 시도 - 이미 다른 스레드가 점유 중이면 false 반환
|
||||||
|
*
|
||||||
|
* <p>→ 다운로드 중복 방지에 적합
|
||||||
|
*/
|
||||||
return lock.tryLock();
|
return lock.tryLock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 특정 key에 대한 Lock 해제
|
||||||
|
*
|
||||||
|
* <p>반드시 tryLock 성공한 동일 스레드에서 호출해야 함
|
||||||
|
*/
|
||||||
public void unlock(String key) {
|
public void unlock(String key) {
|
||||||
|
|
||||||
|
// 해당 key에 등록된 Lock 조회
|
||||||
ReentrantLock lock = locks.get(key);
|
ReentrantLock lock = locks.get(key);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* isHeldByCurrentThread():
|
||||||
|
*
|
||||||
|
* <p>- 현재 스레드가 해당 락을 보유 중인지 확인 - 다른 스레드가 unlock 호출하는 것 방지
|
||||||
|
*/
|
||||||
if (lock != null && lock.isHeldByCurrentThread()) {
|
if (lock != null && lock.isHeldByCurrentThread()) {
|
||||||
|
|
||||||
|
// 실제 락 해제
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
// 메모리 누수 방지(락이 비어있으면 제거)
|
|
||||||
|
/**
|
||||||
|
* 메모리 누수 방지 처리
|
||||||
|
*
|
||||||
|
* <p>hasQueuedThreads(): - 현재 이 락을 기다리는 스레드가 있는지 확인
|
||||||
|
*
|
||||||
|
* <p>대기 스레드가 없다면 locks 맵에서 제거하여 불필요한 Lock 객체 정리
|
||||||
|
*/
|
||||||
if (!lock.hasQueuedThreads()) {
|
if (!lock.hasQueuedThreads()) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* remove(key, lock):
|
||||||
|
*
|
||||||
|
* <p>- 현재 맵에 등록된 값이 'lock'일 경우에만 제거 - 동시성 안전 (CAS 방식)
|
||||||
|
*/
|
||||||
locks.remove(key, lock);
|
locks.remove(key, lock);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,26 +31,33 @@ public class GukYuinApiLabelJobService {
|
|||||||
return "local".equalsIgnoreCase(profile);
|
return "local".equalsIgnoreCase(profile);
|
||||||
}
|
}
|
||||||
|
|
||||||
// @Scheduled(cron = "0 0 2 * * *")
|
|
||||||
public void runTask() {
|
public void runTask() {
|
||||||
findLabelingCompleteSend(null);
|
findLabelingCompleteSend(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 어제 라벨링 검수 완료된 것 -> 국유인에 전송 */
|
/** 어제 라벨링 검수 완료된 것 -> 국유인에 전송 */
|
||||||
public void findLabelingCompleteSend(LocalDate baseDate) {
|
public void findLabelingCompleteSend(LocalDate baseDate) {
|
||||||
// if (isLocalProfile()) {
|
|
||||||
// return;
|
log.info("[Step 1-1] 어제 검수완료된 라벨링을 검색한다.");
|
||||||
// }
|
log.info("=== baseDate : {}", baseDate);
|
||||||
|
log.info("=== baseDate 있으면 해당 일자, 없으면 어제일자로 조회");
|
||||||
|
|
||||||
List<GeomUidDto> list = gukYuinLabelJobCoreService.findYesterdayLabelingCompleteList(baseDate);
|
List<GeomUidDto> list = gukYuinLabelJobCoreService.findYesterdayLabelingCompleteList(baseDate);
|
||||||
|
log.info("[Step 1-2] 검수완료된 폴리곤 객체 수 : {}", list == null ? 0 : list.size());
|
||||||
if (list.isEmpty()) {
|
if (list.isEmpty()) {
|
||||||
|
log.info("[Step 1-3] 객체 없어서 return : 스케줄링 종료");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.info("[Step 2-1] 객체 목록으로 라벨링 전송 API 호출 시작");
|
||||||
for (GeomUidDto gto : list) {
|
for (GeomUidDto gto : list) {
|
||||||
|
log.info("[Step 2-2] 객체ID 확인 gto.getResultUid(): {}", gto.getResultUid());
|
||||||
ChngDetectContDto.ResultLabelDto dto =
|
ChngDetectContDto.ResultLabelDto dto =
|
||||||
gukYuinApiService.updateChnDtctObjtLabelingYn(gto.getResultUid(), "Y", "Y");
|
gukYuinApiService.updateChnDtctObjtLabelingYn(gto.getResultUid(), "Y", "Y");
|
||||||
|
log.info("[Step 2-3] 결과 dto.getSuccess(): {}", dto.getSuccess());
|
||||||
if (dto.getSuccess()) {
|
if (dto.getSuccess()) {
|
||||||
|
log.info("[Step 2-4] 결과가 성공일 때 inference_geom에 label_send_dttm 업데이트 하기");
|
||||||
|
log.info("==== 업데이트 하는 객체 gto.getGeoUid(): {}", gto.getGeoUid());
|
||||||
// inference_geom 에 label_send_dttm 업데이트 하기
|
// inference_geom 에 label_send_dttm 업데이트 하기
|
||||||
gukYuinLabelJobCoreService.updateAnalDataInferenceGeomSendDttm(gto.getGeoUid());
|
gukYuinLabelJobCoreService.updateAnalDataInferenceGeomSendDttm(gto.getGeoUid());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,16 +41,24 @@ public class GukYuinApiPnuJobService {
|
|||||||
// return;
|
// return;
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
log.info("[Step 1-1] 국유인 연동까지 완료된 추론 목록 가져오기");
|
||||||
|
log.info("=== apply_status -> 100% 다운 완료: GUK_COMPLETED, PNU매핑 실패: PNU_FAILED");
|
||||||
List<LearnKeyDto> list =
|
List<LearnKeyDto> list =
|
||||||
gukYuinPnuJobCoreService.findGukyuinApplyStatusUidList(
|
gukYuinPnuJobCoreService.findGukyuinApplyStatusUidList(
|
||||||
List.of(GukYuinStatus.GUK_COMPLETED.getId(), GukYuinStatus.PNU_FAILED.getId()));
|
List.of(GukYuinStatus.GUK_COMPLETED.getId(), GukYuinStatus.PNU_FAILED.getId()));
|
||||||
|
|
||||||
|
log.info("[Step 1-2] 매핑할 추론 회차 갯수 : {}", list == null ? 0 : list.size());
|
||||||
if (list.isEmpty()) {
|
if (list.isEmpty()) {
|
||||||
|
log.info("[Step 1-3] 매핑할 추론 회차 갯수 없어서 리턴하고 끝남");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.info("[Step 2-1] 추론 회차별 pnu 매핑 for문 시작 ");
|
||||||
for (LearnKeyDto dto : list) {
|
for (LearnKeyDto dto : list) {
|
||||||
try {
|
try {
|
||||||
processUid(dto.getUid(), dto.getUid());
|
log.info("[Step 2-2] 진행하는 추론 Uid: {}", dto.getUid());
|
||||||
|
processUid(dto.getUid());
|
||||||
|
|
||||||
gukYuinPnuJobCoreService.updateGukYuinApplyStateComplete(
|
gukYuinPnuJobCoreService.updateGukYuinApplyStateComplete(
|
||||||
dto.getId(), GukYuinStatus.PNU_COMPLETED);
|
dto.getId(), GukYuinStatus.PNU_COMPLETED);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@@ -61,15 +69,21 @@ public class GukYuinApiPnuJobService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void processUid(String chnDtctId, String uid) {
|
private void processUid(String uid) {
|
||||||
ResultDto result = gukYuinApiService.listChnDtctId(chnDtctId, "Y");
|
log.info("[Step 2-4] 탐지 등록목록 상세 API 호출 시작");
|
||||||
|
ResultDto result = gukYuinApiService.listChnDtctId(uid, "Y");
|
||||||
|
|
||||||
if (result == null || result.getResult() == null || result.getResult().isEmpty()) {
|
if (result == null || result.getResult() == null || result.getResult().isEmpty()) {
|
||||||
|
log.info("[Step 2-5] 결과값 없어서 return");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.info("[Step 2-5] 결과값 첫번째 값 가져오기");
|
||||||
ChngDetectMastDto.Basic basic = result.getResult().get(0);
|
ChngDetectMastDto.Basic basic = result.getResult().get(0);
|
||||||
String chnDtctCnt = basic.getChnDtctCnt();
|
String chnDtctCnt = basic.getChnDtctCnt();
|
||||||
|
log.info("[Step 2-6] 탐지 객체 전체 갯수 chnDtctCnt = {}", chnDtctCnt);
|
||||||
if (chnDtctCnt == null || chnDtctCnt.isEmpty()) {
|
if (chnDtctCnt == null || chnDtctCnt.isEmpty()) {
|
||||||
|
log.info("[Step 2-76] 탐지 객체 전체 갯수 없어서 return");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,29 +92,44 @@ public class GukYuinApiPnuJobService {
|
|||||||
int totalCount = Integer.parseInt(chnDtctCnt);
|
int totalCount = Integer.parseInt(chnDtctCnt);
|
||||||
int totalPages = (totalCount + pageSize - 1) / pageSize;
|
int totalPages = (totalCount + pageSize - 1) / pageSize;
|
||||||
|
|
||||||
|
log.info("[Step 3-1] 탐지 객체 전체 수로 페이지 계산 : {}", totalPages);
|
||||||
for (int page = 0; page < totalPages; page++) {
|
for (int page = 0; page < totalPages; page++) {
|
||||||
|
log.info("[Step 3-2] 페이지 별 호출 : {}", page);
|
||||||
processPage(uid, page, pageSize);
|
processPage(uid, page, pageSize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void processPage(String uid, int page, int pageSize) {
|
private void processPage(String uid, int page, int pageSize) {
|
||||||
|
log.info("[Step 4-1] 탐지객체 목록 API 호출 시작");
|
||||||
ResultContDto resContList = gukYuinApiService.findChnContList(uid, page, pageSize, "Y");
|
ResultContDto resContList = gukYuinApiService.findChnContList(uid, page, pageSize, "Y");
|
||||||
|
|
||||||
if (resContList.getResult() == null || resContList.getResult().isEmpty()) {
|
if (resContList.getResult() == null || resContList.getResult().isEmpty()) {
|
||||||
|
log.info("[Step 4-2] 탐지객체 목록 결과 없어서 return");
|
||||||
return; // 외부 API 이상 방어
|
return; // 외부 API 이상 방어
|
||||||
}
|
}
|
||||||
|
|
||||||
List<ContBasic> contList = resContList.getResult();
|
List<ContBasic> contList = resContList.getResult();
|
||||||
|
log.info("[Step 4-3] 탐지객체 목록 결과 contList.size : {}", contList == null ? 0 : contList.size());
|
||||||
for (ContBasic cont : contList) {
|
for (ContBasic cont : contList) {
|
||||||
String[] pnuList = cont.getPnuList();
|
String[] pnuList = cont.getPnuList();
|
||||||
long pnuCnt = pnuList == null ? 0 : pnuList.length;
|
long pnuCnt = pnuList == null ? 0 : pnuList.length;
|
||||||
|
log.info("[Step 4-4] 객체에 연결된 pnuCnt : {}", pnuCnt);
|
||||||
if (cont.getChnDtctObjtId() != null) {
|
if (cont.getChnDtctObjtId() != null) {
|
||||||
|
log.info(
|
||||||
|
"[Step 4-5] inference_geom 에 pnu 갯수 update : cont.getChnDtctObjtId = {}",
|
||||||
|
cont.getChnDtctObjtId());
|
||||||
|
log.info(" === cont.getChnDtctObjtId : {}", cont.getChnDtctObjtId());
|
||||||
|
log.info(" === pnuCnt : {}", pnuCnt);
|
||||||
gukYuinPnuJobCoreService.updateInferenceGeomDataPnuCnt(cont.getChnDtctObjtId(), pnuCnt);
|
gukYuinPnuJobCoreService.updateInferenceGeomDataPnuCnt(cont.getChnDtctObjtId(), pnuCnt);
|
||||||
|
|
||||||
if (pnuCnt > 0) {
|
if (pnuCnt > 0) {
|
||||||
|
log.info("[Step 4-6] 객체 ID로 geoUid 검색 = {}", cont.getChnDtctObjtId());
|
||||||
Long geoUid =
|
Long geoUid =
|
||||||
gukYuinPnuJobCoreService.findMapSheetAnalDataInferenceGeomUid(
|
gukYuinPnuJobCoreService.findMapSheetAnalDataInferenceGeomUid(
|
||||||
cont.getChnDtctObjtId());
|
cont.getChnDtctObjtId());
|
||||||
|
|
||||||
|
log.info("[Step 4-7] tb_pnu 에 데이터 upsert 수행");
|
||||||
|
log.info("===== geoUid = {}", geoUid);
|
||||||
gukYuinPnuJobCoreService.insertGeoUidPnuData(geoUid, pnuList, cont.getChnDtctObjtId());
|
gukYuinPnuJobCoreService.insertGeoUidPnuData(geoUid, pnuList, cont.getChnDtctObjtId());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import com.kamco.cd.kamcoback.gukyuin.dto.ChngDetectMastDto.LearnKeyDto;
|
|||||||
import com.kamco.cd.kamcoback.gukyuin.dto.GukYuinStatus;
|
import com.kamco.cd.kamcoback.gukyuin.dto.GukYuinStatus;
|
||||||
import com.kamco.cd.kamcoback.gukyuin.service.GukYuinApiService;
|
import com.kamco.cd.kamcoback.gukyuin.service.GukYuinApiService;
|
||||||
import com.kamco.cd.kamcoback.postgres.core.GukYuinJobCoreService;
|
import com.kamco.cd.kamcoback.postgres.core.GukYuinJobCoreService;
|
||||||
|
import com.kamco.cd.kamcoback.postgres.core.GukYuinPnuCntUpdateJobCoreService;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.log4j.Log4j2;
|
import lombok.extern.log4j.Log4j2;
|
||||||
@@ -18,6 +19,7 @@ public class GukYuinApiStatusJobService {
|
|||||||
|
|
||||||
private final GukYuinJobCoreService gukYuinJobCoreService;
|
private final GukYuinJobCoreService gukYuinJobCoreService;
|
||||||
private final GukYuinApiService gukYuinApiService;
|
private final GukYuinApiService gukYuinApiService;
|
||||||
|
private final GukYuinPnuCntUpdateJobCoreService gukYuinPnuCntUpdateJobCoreService;
|
||||||
|
|
||||||
@Value("${spring.profiles.active}")
|
@Value("${spring.profiles.active}")
|
||||||
private String profile;
|
private String profile;
|
||||||
@@ -31,12 +33,8 @@ public class GukYuinApiStatusJobService {
|
|||||||
return "local".equalsIgnoreCase(profile);
|
return "local".equalsIgnoreCase(profile);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 국유인 연동 후, 100% 되었는지 확인하는 스케줄링 매 10분마다 호출 */
|
/** 매일 00시에 pnu cnt 업데이트 */
|
||||||
// @Scheduled(cron = "0 0/10 * * * *")
|
public void findGukYuinPnuCntUpdate() {
|
||||||
public void findGukYuinMastCompleteYn() {
|
|
||||||
// if (isLocalProfile()) {
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
List<LearnKeyDto> list =
|
List<LearnKeyDto> list =
|
||||||
gukYuinJobCoreService.findGukyuinApplyStatusUidList(
|
gukYuinJobCoreService.findGukyuinApplyStatusUidList(
|
||||||
@@ -59,8 +57,9 @@ public class GukYuinApiStatusJobService {
|
|||||||
Integer progress =
|
Integer progress =
|
||||||
basic.getExcnPgrt() == null ? null : Integer.parseInt(basic.getExcnPgrt().trim());
|
basic.getExcnPgrt() == null ? null : Integer.parseInt(basic.getExcnPgrt().trim());
|
||||||
if (progress != null && progress == 100) {
|
if (progress != null && progress == 100) {
|
||||||
gukYuinJobCoreService.updateGukYuinApplyStateComplete(
|
gukYuinPnuCntUpdateJobCoreService.updateGukYuinContListPnuUpdateCnt();
|
||||||
dto.getId(), GukYuinStatus.GUK_COMPLETED);
|
gukYuinPnuCntUpdateJobCoreService.updateGukYuinApplyStatus(
|
||||||
|
dto.getUid(), GukYuinStatus.PNU_COMPLETED.getId());
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("[GUKYUIN] failed uid={}", dto.getChnDtctMstId(), e);
|
log.error("[GUKYUIN] failed uid={}", dto.getChnDtctMstId(), e);
|
||||||
|
|||||||
@@ -38,24 +38,26 @@ public class GukYuinApiStbltJobService {
|
|||||||
return "local".equalsIgnoreCase(profile);
|
return "local".equalsIgnoreCase(profile);
|
||||||
}
|
}
|
||||||
|
|
||||||
// @Scheduled(cron = "0 0 3 * * *")
|
|
||||||
public void runTask() {
|
public void runTask() {
|
||||||
findGukYuinEligibleForSurvey(null);
|
findGukYuinEligibleForSurvey(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 국유인 연동 후, 실태조사 적합여부 확인하여 update */
|
/** 국유인 연동 후, 실태조사 적합여부 확인하여 update */
|
||||||
public void findGukYuinEligibleForSurvey(LocalDate baseDate) {
|
public void findGukYuinEligibleForSurvey(LocalDate baseDate) {
|
||||||
// if (isLocalProfile()) {
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
log.info("[Step 1-1] 국유인 연동 PNU 완료된 추론 회차 정보 가져오기 ");
|
||||||
|
log.info(" learn 테이블의 apply_status : {}", GukYuinStatus.PNU_COMPLETED.getId());
|
||||||
List<LearnKeyDto> list =
|
List<LearnKeyDto> list =
|
||||||
gukYuinStbltJobCoreService.findGukYuinEligibleForSurveyList(
|
gukYuinStbltJobCoreService.findGukYuinEligibleForSurveyList(
|
||||||
GukYuinStatus.PNU_COMPLETED.getId());
|
GukYuinStatus.PNU_COMPLETED.getId());
|
||||||
|
|
||||||
|
log.info("[Step 1-2] 국유인 연동 PNU 완료된 추론 회차 갯수 : {}", list == null ? 0 : list.size());
|
||||||
if (list.isEmpty()) {
|
if (list.isEmpty()) {
|
||||||
|
log.info("[Step 1-3] 국유인 연동 PNU 완료된 추론 회차 갯수 없어서 return");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.info("[Step 2-1] 추론 회차 list 로 for문 실행하기 ");
|
||||||
for (LearnKeyDto dto : list) {
|
for (LearnKeyDto dto : list) {
|
||||||
try {
|
try {
|
||||||
String targetDate =
|
String targetDate =
|
||||||
@@ -63,19 +65,30 @@ public class GukYuinApiStbltJobService {
|
|||||||
.minusDays(1)
|
.minusDays(1)
|
||||||
.format(DateTimeFormatter.ofPattern("yyyyMMdd"));
|
.format(DateTimeFormatter.ofPattern("yyyyMMdd"));
|
||||||
|
|
||||||
|
log.info("[Step 2-2] 실태조사 적합여부 조회 날짜 확인 : {}", targetDate);
|
||||||
if (baseDate != null) { // 파라미터가 있으면
|
if (baseDate != null) { // 파라미터가 있으면
|
||||||
targetDate = baseDate.format(DateTimeFormatter.ofPattern("yyyyMMdd"));
|
targetDate = baseDate.format(DateTimeFormatter.ofPattern("yyyyMMdd"));
|
||||||
|
log.info("[Step 2-3] 수동호출 baseDate 가 있을 경우, 실태조사 적합여부 조회 날짜 확인 : {}", targetDate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.info("[Step 3-1] 국유인 실태조사 적합여부 API 호출 시작 ");
|
||||||
|
log.info(" === 값 확인 - uid : {}", dto.getUid());
|
||||||
|
log.info(" === 값 확인 - targetDate : {}", targetDate);
|
||||||
RlbDtctDto result = gukYuinApiService.findRlbDtctList(dto.getUid(), targetDate, "Y");
|
RlbDtctDto result = gukYuinApiService.findRlbDtctList(dto.getUid(), targetDate, "Y");
|
||||||
|
|
||||||
if (result == null || result.getResult() == null || result.getResult().isEmpty()) {
|
if (result == null || result.getResult() == null || result.getResult().isEmpty()) {
|
||||||
log.warn("[GUKYUIN] empty result chnDtctMstId={}", dto.getChnDtctMstId());
|
log.info("[GUKYUIN] empty result chnDtctId={}", dto.getUid());
|
||||||
|
log.info("=== 국유인 API 조회 결과 없어서 continue");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.info("[Step 4-1] 국유인 실태조사 적합여부 result 값으로 데이터 업데이트");
|
||||||
|
log.info(" === 데이터 갯수 : {}", result.getResult() == null ? 0 : result.getResult().size());
|
||||||
|
|
||||||
for (RlbDtctMastDto stbltDto : result.getResult()) {
|
for (RlbDtctMastDto stbltDto : result.getResult()) {
|
||||||
|
log.info("[Step 4-2] 국유인 실태조사 적합여부 결과 가져오기");
|
||||||
String resultUid = stbltDto.getChnDtctObjtId();
|
String resultUid = stbltDto.getChnDtctObjtId();
|
||||||
|
log.info(" == 테이블 tb_pnu 에 적합여부 리턴 결과를 upsert 진행, 객체 uid : {}", resultUid);
|
||||||
gukYuinStbltJobCoreService.updateGukYuinEligibleForSurvey(resultUid, stbltDto);
|
gukYuinStbltJobCoreService.updateGukYuinEligibleForSurvey(resultUid, stbltDto);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,6 +103,7 @@ public class GukYuinApiStbltJobService {
|
|||||||
e -> {
|
e -> {
|
||||||
List<RlbDtctMastDto> pnuList = e.getValue();
|
List<RlbDtctMastDto> pnuList = e.getValue();
|
||||||
|
|
||||||
|
log.info("[Step 4-3] 국유인 실태조사 적합여부 업데이트 값을 객체 uid 기준으로 DTO 생성");
|
||||||
boolean hasY = pnuList.stream().anyMatch(v -> "Y".equals(v.getStbltYn()));
|
boolean hasY = pnuList.stream().anyMatch(v -> "Y".equals(v.getStbltYn()));
|
||||||
|
|
||||||
String fitYn = hasY ? "Y" : "N";
|
String fitYn = hasY ? "Y" : "N";
|
||||||
@@ -105,7 +119,9 @@ public class GukYuinApiStbltJobService {
|
|||||||
.findFirst()
|
.findFirst()
|
||||||
.orElse(null);
|
.orElse(null);
|
||||||
|
|
||||||
|
log.info(" === selected DTO : {}", selected);
|
||||||
if (selected == null) {
|
if (selected == null) {
|
||||||
|
log.info(" === selected NULL");
|
||||||
return null; // 방어 코드
|
return null; // 방어 코드
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,10 +129,11 @@ public class GukYuinApiStbltJobService {
|
|||||||
fitYn, selected.getIncyCd(), selected.getIncyRsnCont());
|
fitYn, selected.getIncyCd(), selected.getIncyRsnCont());
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
log.info("[Step 4-4] 국유인 실태조사 적합여부, 사유, 내용을 inference_geom 테이블에 update");
|
||||||
resultMap.forEach(gukYuinStbltJobCoreService::updateGukYuinObjectStbltYn);
|
resultMap.forEach(gukYuinStbltJobCoreService::updateGukYuinObjectStbltYn);
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("[GUKYUIN] failed uid={}", dto.getChnDtctMstId(), e);
|
log.error("[GUKYUIN] failed uid={}", dto.getUid(), e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
package com.kamco.cd.kamcoback.scheduler.service;
|
package com.kamco.cd.kamcoback.scheduler.service;
|
||||||
|
|
||||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
import com.fasterxml.jackson.core.type.TypeReference;
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import com.kamco.cd.kamcoback.common.exception.CustomApiException;
|
import com.kamco.cd.kamcoback.common.inference.service.InferenceCommonService;
|
||||||
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.InferenceBatchSheet;
|
import com.kamco.cd.kamcoback.inference.dto.InferenceDetailDto.InferenceBatchSheet;
|
||||||
@@ -19,7 +18,6 @@ import java.nio.file.Paths;
|
|||||||
import java.time.ZonedDateTime;
|
import java.time.ZonedDateTime;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@@ -31,7 +29,6 @@ import lombok.extern.log4j.Log4j2;
|
|||||||
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.HttpMethod;
|
import org.springframework.http.HttpMethod;
|
||||||
import org.springframework.http.HttpStatus;
|
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.scheduling.annotation.Scheduled;
|
import org.springframework.scheduling.annotation.Scheduled;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
@@ -43,6 +40,7 @@ public class MapSheetInferenceJobService {
|
|||||||
|
|
||||||
private final InferenceResultCoreService inferenceResultCoreService;
|
private final InferenceResultCoreService inferenceResultCoreService;
|
||||||
private final ShpPipelineService shpPipelineService;
|
private final ShpPipelineService shpPipelineService;
|
||||||
|
private final InferenceCommonService inferenceCommonService;
|
||||||
|
|
||||||
private final ExternalHttpClient externalHttpClient;
|
private final ExternalHttpClient externalHttpClient;
|
||||||
private final ObjectMapper objectMapper;
|
private final ObjectMapper objectMapper;
|
||||||
@@ -216,7 +214,7 @@ public class MapSheetInferenceJobService {
|
|||||||
// 현재 모델 종료 업데이트
|
// 현재 모델 종료 업데이트
|
||||||
updateProcessingEndTimeByModel(job, sheet.getUuid(), now, currentType);
|
updateProcessingEndTimeByModel(job, sheet.getUuid(), now, currentType);
|
||||||
|
|
||||||
// M3이면 전체 종료
|
// G3이면 전체 종료
|
||||||
if (ModelType.G3.getId().equals(currentType)) {
|
if (ModelType.G3.getId().equals(currentType)) {
|
||||||
endAll(sheet, now);
|
endAll(sheet, now);
|
||||||
return;
|
return;
|
||||||
@@ -237,6 +235,13 @@ public class MapSheetInferenceJobService {
|
|||||||
* @param now
|
* @param now
|
||||||
*/
|
*/
|
||||||
private void endAll(InferenceBatchSheet sheet, ZonedDateTime now) {
|
private void endAll(InferenceBatchSheet sheet, ZonedDateTime now) {
|
||||||
|
|
||||||
|
List<Long> batchIds =
|
||||||
|
Stream.of(sheet.getM1BatchId(), sheet.getM2BatchId(), sheet.getM3BatchId())
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.distinct()
|
||||||
|
.toList();
|
||||||
|
|
||||||
SaveInferenceAiDto save = new SaveInferenceAiDto();
|
SaveInferenceAiDto save = new SaveInferenceAiDto();
|
||||||
save.setUuid(sheet.getUuid());
|
save.setUuid(sheet.getUuid());
|
||||||
save.setStatus(Status.END.getId());
|
save.setStatus(Status.END.getId());
|
||||||
@@ -248,16 +253,11 @@ public class MapSheetInferenceJobService {
|
|||||||
inferenceResultCoreService.upsertGeomData(sheet.getId());
|
inferenceResultCoreService.upsertGeomData(sheet.getId());
|
||||||
|
|
||||||
// 추론 종료일때 shp 파일 생성
|
// 추론 종료일때 shp 파일 생성
|
||||||
List<Long> batchIds =
|
|
||||||
Stream.of(sheet.getM1BatchId(), sheet.getM2BatchId(), sheet.getM3BatchId())
|
|
||||||
.filter(Objects::nonNull)
|
|
||||||
.distinct()
|
|
||||||
.toList();
|
|
||||||
|
|
||||||
String batchIdStr = batchIds.stream().map(String::valueOf).collect(Collectors.joining(","));
|
String batchIdStr = batchIds.stream().map(String::valueOf).collect(Collectors.joining(","));
|
||||||
|
|
||||||
// shp 파일 비동기 생성
|
// 0312 shp 파일 비동기 생성 (바꿔주세요)
|
||||||
shpPipelineService.runPipeline(jarPath, datasetDir, batchIdStr, sheet.getUid());
|
shpPipelineService.makeShapeFile(sheet.getUid(), batchIds);
|
||||||
|
// shpPipelineService.runPipeline(jarPath, datasetDir, batchIdStr, sheet.getUid());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -357,7 +357,7 @@ public class MapSheetInferenceJobService {
|
|||||||
m.setPriority(5d);
|
m.setPriority(5d);
|
||||||
log.info("[BEFORE INFERENCE] BEFORE SendDto={}", m);
|
log.info("[BEFORE INFERENCE] BEFORE SendDto={}", m);
|
||||||
// 추론 실행 api 호출
|
// 추론 실행 api 호출
|
||||||
Long batchId = ensureAccepted(m);
|
Long batchId = inferenceCommonService.ensureAccepted(m);
|
||||||
|
|
||||||
SaveInferenceAiDto saveInferenceAiDto = new SaveInferenceAiDto();
|
SaveInferenceAiDto saveInferenceAiDto = new SaveInferenceAiDto();
|
||||||
saveInferenceAiDto.setUuid(uuid);
|
saveInferenceAiDto.setUuid(uuid);
|
||||||
@@ -369,73 +369,6 @@ public class MapSheetInferenceJobService {
|
|||||||
inferenceResultCoreService.update(saveInferenceAiDto);
|
inferenceResultCoreService.update(saveInferenceAiDto);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* api 호출
|
|
||||||
*
|
|
||||||
* @param dto
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
// 같은함수가 왜 두개지
|
|
||||||
private Long ensureAccepted(InferenceSendDto dto) {
|
|
||||||
if (dto == null) {
|
|
||||||
log.warn("not InferenceSendDto dto");
|
|
||||||
throw new CustomApiException("BAD_REQUEST", HttpStatus.BAD_REQUEST);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 1) 요청 로그
|
|
||||||
log.info("");
|
|
||||||
log.info("========================================================");
|
|
||||||
log.info("[SEND INFERENCE] Inference request dto= {}", dto);
|
|
||||||
log.info("========================================================");
|
|
||||||
log.info("");
|
|
||||||
// 2) local 환경 임시 처리
|
|
||||||
// if ("local".equals(profile)) {
|
|
||||||
// if (dto.getPred_requests_areas() == null) {
|
|
||||||
// throw new IllegalStateException("pred_requests_areas is null");
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// dto.getPred_requests_areas().setInput1_scene_path("/kamco-nfs/requests/2023_local.geojson");
|
|
||||||
//
|
|
||||||
// dto.getPred_requests_areas().setInput2_scene_path("/kamco-nfs/requests/2024_local.geojson");
|
|
||||||
// }
|
|
||||||
|
|
||||||
// 3) HTTP 호출
|
|
||||||
HttpHeaders headers = new HttpHeaders();
|
|
||||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
|
||||||
headers.setAccept(List.of(MediaType.APPLICATION_JSON));
|
|
||||||
|
|
||||||
// TODO 어떤 URL로 어떤파리티러로 요청한 로딩해야지
|
|
||||||
ExternalCallResult<String> result =
|
|
||||||
externalHttpClient.call(inferenceUrl, HttpMethod.POST, dto, headers, String.class);
|
|
||||||
|
|
||||||
if (result.statusCode() < 200 || result.statusCode() >= 300) {
|
|
||||||
log.error("Inference API failed. status={}, body={}", result.statusCode(), result.body());
|
|
||||||
throw new CustomApiException("BAD_GATEWAY", HttpStatus.BAD_GATEWAY);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 4) 응답 파싱
|
|
||||||
try {
|
|
||||||
List<Map<String, Object>> list =
|
|
||||||
objectMapper.readValue(result.body(), new TypeReference<>() {});
|
|
||||||
|
|
||||||
if (list.isEmpty()) {
|
|
||||||
// 어떤 URL로 어떤파리티러로 요청한 정보를 봐야 재현을 할듯하지요
|
|
||||||
throw new IllegalStateException("Inference response is empty");
|
|
||||||
}
|
|
||||||
|
|
||||||
Object batchIdObj = list.get(0).get("batch_id");
|
|
||||||
if (batchIdObj == null) {
|
|
||||||
throw new IllegalStateException("batch_id not found in response");
|
|
||||||
}
|
|
||||||
|
|
||||||
return Long.valueOf(batchIdObj.toString());
|
|
||||||
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.error("Failed to parse inference response. body={}", result.body(), e);
|
|
||||||
throw new CustomApiException("INVALID_INFERENCE_RESPONSE", HttpStatus.BAD_GATEWAY);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 실행중인 profile
|
* 실행중인 profile
|
||||||
*
|
*
|
||||||
@@ -492,6 +425,7 @@ public class MapSheetInferenceJobService {
|
|||||||
.map(Long::valueOf)
|
.map(Long::valueOf)
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
|
// 추론 실행 도엽별 정보 조회
|
||||||
List<Long> jobIds = inferenceResultCoreService.findFail5kList(uuid, failedIds, type);
|
List<Long> jobIds = inferenceResultCoreService.findFail5kList(uuid, failedIds, type);
|
||||||
|
|
||||||
Set<Long> jobIdSet = new HashSet<>(jobIds);
|
Set<Long> jobIdSet = new HashSet<>(jobIds);
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
package com.kamco.cd.kamcoback.scheduler.service;
|
package com.kamco.cd.kamcoback.scheduler.service;
|
||||||
|
|
||||||
import com.kamco.cd.kamcoback.common.service.ExternalJarRunner;
|
import com.kamco.cd.kamcoback.common.service.ExternalJarRunner;
|
||||||
|
import com.kamco.cd.kamcoback.postgres.core.InferenceResultCoreService;
|
||||||
import com.kamco.cd.kamcoback.scheduler.config.ShpKeyLock;
|
import com.kamco.cd.kamcoback.scheduler.config.ShpKeyLock;
|
||||||
import java.nio.file.Paths;
|
import java.util.List;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.log4j.Log4j2;
|
import lombok.extern.log4j.Log4j2;
|
||||||
import org.springframework.scheduling.annotation.Async;
|
import org.springframework.scheduling.annotation.Async;
|
||||||
@@ -13,11 +14,44 @@ import org.springframework.stereotype.Service;
|
|||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class ShpPipelineService {
|
public class ShpPipelineService {
|
||||||
|
|
||||||
|
private final InferenceResultCoreService inferenceResultCoreService;
|
||||||
private final ExternalJarRunner externalJarRunner;
|
private final ExternalJarRunner externalJarRunner;
|
||||||
private final ShpKeyLock shpKeyLock;
|
private final ShpKeyLock shpKeyLock;
|
||||||
|
|
||||||
|
// 0312 shp 파일 비동기 생성
|
||||||
|
@Async("makeShapeFile")
|
||||||
|
public void makeShapeFile(String inferenceId, List<Long> batchIds) {
|
||||||
|
if (!shpKeyLock.tryLock(inferenceId)) {
|
||||||
|
log.info("");
|
||||||
|
log.info("============================================================");
|
||||||
|
log.info("SHP pipeline already running. inferenceId={}", inferenceId);
|
||||||
|
log.info("============================================================");
|
||||||
|
try {
|
||||||
|
log.info("SHP pipeline already start. inferenceId={}", inferenceId);
|
||||||
|
externalJarRunner.run(inferenceId, batchIds);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("SHP pipeline failed. inferenceId={}", inferenceId, e);
|
||||||
|
// TODO 실패 상태 업데이트 로직 추가
|
||||||
|
} finally {
|
||||||
|
log.info("============================================================");
|
||||||
|
log.info("SHP pipeline DONE. inferenceId={}", inferenceId);
|
||||||
|
log.info("============================================================");
|
||||||
|
log.info("");
|
||||||
|
shpKeyLock.unlock(inferenceId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* shp 파일 생성 1. merge 생성 2. 생성된 merge shp 파일로 geoserver 등록, 3.도엽별로 shp 생성
|
||||||
|
*
|
||||||
|
* @param jarPath 실행 jar 파일 경로
|
||||||
|
* @param datasetDir shp 파일이 생성될 경로
|
||||||
|
* @param batchIds 추론 batch id = 12,13,14
|
||||||
|
* @param inferenceId 추론 uid 32자
|
||||||
|
*/
|
||||||
@Async("shpExecutor")
|
@Async("shpExecutor")
|
||||||
public void runPipeline(String jarPath, String datasetDir, String batchId, String inferenceId) {
|
public void runPipeline(String jarPath, String datasetDir, String batchIds, String inferenceId) {
|
||||||
|
|
||||||
// inferenceId 기준 동시 실행 제한
|
// inferenceId 기준 동시 실행 제한
|
||||||
if (!shpKeyLock.tryLock(inferenceId)) {
|
if (!shpKeyLock.tryLock(inferenceId)) {
|
||||||
@@ -26,24 +60,33 @@ public class ShpPipelineService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
|
log.info("");
|
||||||
|
log.info("============================================================");
|
||||||
|
log.info("SHP pipeline started. inferenceId={}", inferenceId);
|
||||||
|
log.info("============================================================");
|
||||||
|
|
||||||
// uid 기준 merge shp, geojson 파일 생성
|
// uid 기준 merge shp, geojson 파일 생성
|
||||||
externalJarRunner.run(jarPath, batchId, inferenceId, "", "MERGED");
|
externalJarRunner.run(jarPath, batchIds, inferenceId, "", "MERGED");
|
||||||
|
|
||||||
// uid 기준 shp 파일 geoserver 등록
|
// uid 기준 shp 파일 geoserver 등록
|
||||||
String register =
|
// String register =
|
||||||
Paths.get(datasetDir, inferenceId, "merge", inferenceId + ".shp").toString();
|
// Paths.get(datasetDir, inferenceId, "merge", inferenceId + ".shp").toString();
|
||||||
log.info("register={}", register);
|
// log.info("register={}", register);
|
||||||
externalJarRunner.run(jarPath, register, inferenceId);
|
// externalJarRunner.run(jarPath, register, inferenceId);
|
||||||
|
//
|
||||||
// uid 기준 도엽별 shp, geojson 파일 생성
|
// // uid 기준 도엽별 shp, geojson 파일 생성
|
||||||
externalJarRunner.run(jarPath, batchId, inferenceId, "", "RESOLVE");
|
// externalJarRunner.run(jarPath, batchIds, inferenceId, "", "RESOLVE");
|
||||||
|
//
|
||||||
log.info("SHP pipeline finished. inferenceId={}", inferenceId);
|
// log.info("SHP pipeline finished. inferenceId={}", inferenceId);
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("SHP pipeline failed. inferenceId={}", inferenceId, e);
|
log.error("SHP pipeline failed. inferenceId={}", inferenceId, e);
|
||||||
// TODO 실패 상태 업데이트 로직 추가
|
// TODO 실패 상태 업데이트 로직 추가
|
||||||
} finally {
|
} finally {
|
||||||
|
log.info("============================================================");
|
||||||
|
log.info("SHP pipeline DONE. inferenceId={}", inferenceId);
|
||||||
|
log.info("============================================================");
|
||||||
shpKeyLock.unlock(inferenceId);
|
shpKeyLock.unlock(inferenceId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,7 +32,6 @@ public class TrainingDataLabelJobService {
|
|||||||
return "local".equalsIgnoreCase(profile);
|
return "local".equalsIgnoreCase(profile);
|
||||||
}
|
}
|
||||||
|
|
||||||
// @Scheduled(cron = "0 0 0 * * *")
|
|
||||||
public void runTask() {
|
public void runTask() {
|
||||||
// 프록시를 통해 호출해야 @Transactional이 적용됨
|
// 프록시를 통해 호출해야 @Transactional이 적용됨
|
||||||
applicationContext
|
applicationContext
|
||||||
@@ -43,55 +42,72 @@ public class TrainingDataLabelJobService {
|
|||||||
@Transactional
|
@Transactional
|
||||||
public void assignReviewerYesterdayLabelComplete(LocalDate baseDate) {
|
public void assignReviewerYesterdayLabelComplete(LocalDate baseDate) {
|
||||||
|
|
||||||
// if (isLocalProfile()) {
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
log.info("[Step 1-1] 라벨링 완료된 데이터 목록 조회한다.");
|
||||||
|
log.info("=== baseDate : {}", baseDate);
|
||||||
|
log.info("=== baseDate 있으면 해당 일자, 없으면 어제일자로 조회");
|
||||||
List<Tasks> tasks =
|
List<Tasks> tasks =
|
||||||
trainingDataLabelJobCoreService.findCompletedYesterdayUnassigned(baseDate);
|
trainingDataLabelJobCoreService.findCompletedYesterdayUnassigned(baseDate);
|
||||||
|
|
||||||
|
log.info("[Step 1-2] 목록 객체 건수 count : {}", tasks == null ? 0 : tasks.size());
|
||||||
if (tasks.isEmpty()) {
|
if (tasks.isEmpty()) {
|
||||||
|
log.info("[Step 1-3] 조회된 것 없어 return");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 회차별로 그룹핑
|
// 회차별로 그룹핑
|
||||||
|
log.info("[Step 2-1] 회차별로 그룹핑 시작");
|
||||||
Map<Long, List<Tasks>> taskByRound =
|
Map<Long, List<Tasks>> taskByRound =
|
||||||
tasks.stream().collect(Collectors.groupingBy(Tasks::getAnalUid));
|
tasks.stream().collect(Collectors.groupingBy(Tasks::getAnalUid));
|
||||||
|
|
||||||
// 회차별 분배
|
// 회차별 분배
|
||||||
|
log.info("[Step 3-1] 회차별로 분배 시작");
|
||||||
for (Map.Entry<Long, List<Tasks>> entry : taskByRound.entrySet()) {
|
for (Map.Entry<Long, List<Tasks>> entry : taskByRound.entrySet()) {
|
||||||
Long analUid = entry.getKey();
|
Long analUid = entry.getKey();
|
||||||
List<Tasks> analTasks = entry.getValue();
|
List<Tasks> analTasks = entry.getValue();
|
||||||
|
|
||||||
// pending 계산
|
// pending 계산
|
||||||
|
log.info("[Step 3-2] 수행하는 회차 analUid: {}", analUid);
|
||||||
|
log.info("해당 회차에 라벨링 할당받은 검수자별 완료 건수 count(), 완료한 게 적은 순으로 해야 일이 한 사람에게 몰리지 않음");
|
||||||
List<InspectorPendingDto> pendings =
|
List<InspectorPendingDto> pendings =
|
||||||
trainingDataLabelJobCoreService.findInspectorPendingByRound(analUid);
|
trainingDataLabelJobCoreService.findInspectorPendingByRound(analUid);
|
||||||
|
|
||||||
|
log.info("검수자 수: {}", pendings == null ? 0 : pendings.size());
|
||||||
if (pendings.isEmpty()) {
|
if (pendings.isEmpty()) {
|
||||||
|
log.info("[Step 3-3] 할당된 검수자가 없으면 return");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.info("[Step 4-1] 검수자 사번 List 생성");
|
||||||
List<String> reviewerIds =
|
List<String> reviewerIds =
|
||||||
pendings.stream().map(InspectorPendingDto::getInspectorUid).toList();
|
pendings.stream().map(InspectorPendingDto::getInspectorUid).toList();
|
||||||
|
|
||||||
// Lock 걸릴 수 있기 때문에 엔티티 조회하는 Repository 에서 구현
|
// Lock 걸릴 수 있기 때문에 엔티티 조회하는 Repository 에서 구현
|
||||||
|
log.info("[Step 4-2] 검수자 테이블 lock 걸리지 않게 처리");
|
||||||
trainingDataLabelJobCoreService.lockInspectors(analUid, reviewerIds);
|
trainingDataLabelJobCoreService.lockInspectors(analUid, reviewerIds);
|
||||||
|
|
||||||
// 균등 분배
|
// 균등 분배
|
||||||
|
log.info("[Step 5-1] 검수자에게 라벨 작업 균등분배 시작");
|
||||||
Map<String, List<Tasks>> assignMap = distributeByLeastPending(analTasks, reviewerIds);
|
Map<String, List<Tasks>> assignMap = distributeByLeastPending(analTasks, reviewerIds);
|
||||||
|
log.info("[Step 5-2] 검수자에게 라벨 작업 균등분배 완료");
|
||||||
|
|
||||||
// reviewer별 batch update
|
// reviewer별 batch update
|
||||||
|
log.info("[Step 5-3] 검수자별 할당 데이터를 batch update 시작");
|
||||||
assignMap.forEach(
|
assignMap.forEach(
|
||||||
(reviewerId, assignedTasks) -> {
|
(reviewerId, assignedTasks) -> {
|
||||||
if (assignedTasks.isEmpty()) {
|
if (assignedTasks.isEmpty()) {
|
||||||
|
log.info("[Step 5-4] 할당된 데이터 없으면 return");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<UUID> assignmentUids =
|
List<UUID> assignmentUids =
|
||||||
assignedTasks.stream().map(Tasks::getAssignmentUid).toList();
|
assignedTasks.stream().map(Tasks::getAssignmentUid).toList();
|
||||||
|
log.info("[Step 6-1] 할당 작업에 검수자 아이디 update");
|
||||||
|
log.info("==== 검수자 사번: {}", reviewerId);
|
||||||
|
log.info("==== 할당 갯수: {}", assignmentUids == null ? 0 : assignmentUids.size());
|
||||||
trainingDataLabelJobCoreService.assignReviewerBatch(assignmentUids, reviewerId);
|
trainingDataLabelJobCoreService.assignReviewerBatch(assignmentUids, reviewerId);
|
||||||
|
|
||||||
|
log.info("[Step 7-1] geom 테이블에 검수 상태 update");
|
||||||
List<Long> geomUids = assignedTasks.stream().map(Tasks::getInferenceUid).toList();
|
List<Long> geomUids = assignedTasks.stream().map(Tasks::getInferenceUid).toList();
|
||||||
trainingDataLabelJobCoreService.updateGeomUidTestState(geomUids);
|
trainingDataLabelJobCoreService.updateGeomUidTestState(geomUids);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ import java.nio.file.Path;
|
|||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.log4j.Log4j2;
|
import lombok.extern.log4j.Log4j2;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
@@ -39,52 +38,68 @@ public class TrainingDataReviewJobService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
// @Scheduled(cron = "0 0 2 * * *")
|
|
||||||
public void runTask() {
|
public void runTask() {
|
||||||
exportGeojsonLabelingGeom(null);
|
exportGeojsonLabelingGeom(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 미사용 -> kamco-cd-cron GIT에 kamco-make-dataset-generation jar 생성 로직에 포함되어 해당 로직은 미사용
|
||||||
|
*
|
||||||
|
* @param baseDate
|
||||||
|
*/
|
||||||
public void exportGeojsonLabelingGeom(LocalDate baseDate) {
|
public void exportGeojsonLabelingGeom(LocalDate baseDate) {
|
||||||
|
|
||||||
// if (isLocalProfile()) {
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// 1) 경로/파일명 결정
|
// 1) 경로/파일명 결정
|
||||||
String targetDir =
|
String targetDir =
|
||||||
"local".equals(profile) ? System.getProperty("user.home") + "/geojson" : trainingDataDir;
|
"local".equals(profile) ? System.getProperty("user.home") + "/geojson" : trainingDataDir;
|
||||||
|
log.info("[Step 1-1] geojson 파일 생성할 경로: {}", targetDir);
|
||||||
|
|
||||||
// 2) 진행중인 회차 중, complete_cnt 가 존재하는 회차 목록 가져오기
|
// 2) 진행중인 회차 중, complete_cnt 가 존재하는 회차 목록 가져오기
|
||||||
|
log.info("[Step 1-2] 진행중(ING)인 회차 중, 검수완료한(complete_cnt) 갯수가 존재하는 회차 목록 가져오기");
|
||||||
List<AnalCntInfo> analList = trainingDataReviewJobCoreService.findAnalCntInfoList();
|
List<AnalCntInfo> analList = trainingDataReviewJobCoreService.findAnalCntInfoList();
|
||||||
|
log.info("[Step 1-3] 회차 리스트 건수: {}", analList == null ? 0 : analList.size());
|
||||||
|
|
||||||
|
if (analList.isEmpty()) {
|
||||||
|
log.info("[Step 1-4] 회차 리스트 없어 return 하고 종료");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
for (AnalCntInfo info : analList) {
|
for (AnalCntInfo info : analList) {
|
||||||
if (Objects.equals(info.getAllCnt(), info.getFileCnt())) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
String resultUid = info.getResultUid(); // 회차의 대문자 uid (폴더명으로 사용)
|
String resultUid = info.getResultUid(); // 회차의 대문자 uid (폴더명으로 사용)
|
||||||
|
|
||||||
// 3) 회차 + 어제까지 검수 완료된 총 데이터의 도엽별 목록 가져오기
|
// 3) 회차 + 어제까지 검수 완료된 총 데이터의 도엽별 목록 가져오기
|
||||||
|
log.info("[Step 3-1] 회차 + 어제까지 검수 완료된 총 데이터의 도엽별 목록 가져오기");
|
||||||
|
log.info(" === 기준일자 baseDate : " + baseDate);
|
||||||
|
log.info(" === 검수완료일자 < 기준일자인 폴리곤의 도엽 목록을 조회");
|
||||||
|
|
||||||
List<AnalMapSheetList> analMapList =
|
List<AnalMapSheetList> analMapList =
|
||||||
trainingDataReviewJobCoreService.findCompletedAnalMapSheetList(
|
trainingDataReviewJobCoreService.findCompletedAnalMapSheetList(
|
||||||
info.getAnalUid(), baseDate);
|
info.getAnalUid(), baseDate);
|
||||||
|
|
||||||
|
log.info("=== analMapList cnt: {}", analMapList == null ? 0 : analMapList.size());
|
||||||
if (analMapList.isEmpty()) {
|
if (analMapList.isEmpty()) {
|
||||||
|
log.info("[Step 3-2] 도엽 목록 조회되지 않아 continue");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.info("[Step 4-1] 도엽별 geom 데이터 가지고 와서 geojson 만들기 시작");
|
||||||
for (AnalMapSheetList mapSheet : analMapList) {
|
for (AnalMapSheetList mapSheet : analMapList) {
|
||||||
// 4) 도엽별 geom 데이터 가지고 와서 geojson 만들기
|
// 4) 도엽별 geom 데이터 가지고 와서 geojson 만들기
|
||||||
|
log.info("[Step 4-2] 도엽별 검수완료된 폴리곤 데이터 목록 조회");
|
||||||
List<CompleteLabelData> completeList =
|
List<CompleteLabelData> completeList =
|
||||||
trainingDataReviewJobCoreService.findCompletedYesterdayLabelingList(
|
trainingDataReviewJobCoreService.findCompletedYesterdayLabelingList(
|
||||||
info.getAnalUid(), mapSheet.getMapSheetNum(), baseDate);
|
info.getAnalUid(), mapSheet.getMapSheetNum(), baseDate);
|
||||||
|
|
||||||
|
log.info("=== completeList size: {}", completeList == null ? 0 : completeList.size());
|
||||||
if (!completeList.isEmpty()) {
|
if (!completeList.isEmpty()) {
|
||||||
|
|
||||||
|
log.info("[Step 4-3] 목록에서 filter로 geoUid List 생성, 폴리곤 feature별 리스트 생성");
|
||||||
List<Long> geoUids = completeList.stream().map(CompleteLabelData::getGeoUid).toList();
|
List<Long> geoUids = completeList.stream().map(CompleteLabelData::getGeoUid).toList();
|
||||||
List<GeoJsonFeature> features = completeList.stream().map(GeoJsonFeature::from).toList();
|
List<GeoJsonFeature> features = completeList.stream().map(GeoJsonFeature::from).toList();
|
||||||
|
|
||||||
// 5) 파일서버에 uid 폴더 생성 후 업로드 하기
|
// 5) 파일서버에 uid 폴더 생성 후 업로드 하기
|
||||||
|
log.info("[Step 5-1] 파일서버에 uid 폴더 생성 후 업로드 하기 시작");
|
||||||
FeatureCollection collection = new FeatureCollection(features);
|
FeatureCollection collection = new FeatureCollection(features);
|
||||||
String filename =
|
String filename =
|
||||||
String.format(
|
String.format(
|
||||||
@@ -94,16 +109,20 @@ public class TrainingDataReviewJobService {
|
|||||||
mapSheet.getTargetYyyy(),
|
mapSheet.getTargetYyyy(),
|
||||||
mapSheet.getMapSheetNum());
|
mapSheet.getMapSheetNum());
|
||||||
|
|
||||||
|
log.info("=== filename: {}", filename);
|
||||||
|
log.info("=== 회차의 uid: {}", resultUid);
|
||||||
Path outputPath = Paths.get(targetDir + "/" + resultUid, filename);
|
Path outputPath = Paths.get(targetDir + "/" + resultUid, filename);
|
||||||
|
log.info("=== outputPath: {}", outputPath);
|
||||||
try {
|
try {
|
||||||
|
log.info("[Step 6-1] Uid로 폴더 생성");
|
||||||
Files.createDirectories(outputPath.getParent());
|
Files.createDirectories(outputPath.getParent());
|
||||||
|
|
||||||
|
log.info("[Step 6-2] geoJson 파일 생성");
|
||||||
ObjectMapper objectMapper = new ObjectMapper();
|
ObjectMapper objectMapper = new ObjectMapper();
|
||||||
objectMapper.enable(SerializationFeature.INDENT_OUTPUT);
|
objectMapper.enable(SerializationFeature.INDENT_OUTPUT);
|
||||||
objectMapper.writeValue(outputPath.toFile(), collection);
|
objectMapper.writeValue(outputPath.toFile(), collection);
|
||||||
|
|
||||||
// geoUids : file_create_yn = true 로 업데이트
|
log.info("[Step 6-3] geoJson 파일 생성 완료");
|
||||||
trainingDataReviewJobCoreService.updateLearnDataGeomFileCreateYn(geoUids);
|
|
||||||
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
log.error(e.getMessage());
|
log.error(e.getMessage());
|
||||||
|
|||||||
@@ -0,0 +1,50 @@
|
|||||||
|
package com.kamco.cd.kamcoback.test;
|
||||||
|
|
||||||
|
import com.kamco.cd.kamcoback.config.api.ApiResponseDto;
|
||||||
|
import com.kamco.cd.kamcoback.scheduler.service.ShpPipelineService;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Content;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||||
|
import io.swagger.v3.oas.annotations.responses.ApiResponses;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import java.util.List;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.web.ErrorResponse;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
@Tag(name = "test shape api", description = "test shape api")
|
||||||
|
@RestController
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@RequestMapping("/api/test")
|
||||||
|
public class TestShapeApiController {
|
||||||
|
|
||||||
|
private final ShpPipelineService shpPipelineService;
|
||||||
|
|
||||||
|
@Operation(
|
||||||
|
summary = "shapefile 생성 테스트",
|
||||||
|
description = "지정된 inference ID와 batch ID 목록으로 shapefile을 생성합니다.")
|
||||||
|
@ApiResponses({
|
||||||
|
@ApiResponse(
|
||||||
|
responseCode = "200",
|
||||||
|
description = "shapefile 생성 요청 성공",
|
||||||
|
content = @Content(schema = @Schema(implementation = String.class))),
|
||||||
|
@ApiResponse(
|
||||||
|
responseCode = "400",
|
||||||
|
description = "잘못된 요청 데이터",
|
||||||
|
content = @Content(schema = @Schema(implementation = ErrorResponse.class))),
|
||||||
|
@ApiResponse(
|
||||||
|
responseCode = "500",
|
||||||
|
description = "서버 오류",
|
||||||
|
content = @Content(schema = @Schema(implementation = ErrorResponse.class)))
|
||||||
|
})
|
||||||
|
@GetMapping("/make-shapefile")
|
||||||
|
public ApiResponseDto<String> makeShapeFile(
|
||||||
|
@RequestParam String inferenceId, @RequestParam List<Long> batchIds) {
|
||||||
|
shpPipelineService.makeShapeFile(inferenceId, batchIds);
|
||||||
|
return ApiResponseDto.ok("Shapefile 생성이 시작되었습니다. inferenceId: " + inferenceId);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -59,8 +59,8 @@ jwt:
|
|||||||
secret: "kamco_token_9b71e778-19a3-4c1d-97bf-2d687de17d5b"
|
secret: "kamco_token_9b71e778-19a3-4c1d-97bf-2d687de17d5b"
|
||||||
access-token-validity-in-ms: 86400000 # 1일
|
access-token-validity-in-ms: 86400000 # 1일
|
||||||
refresh-token-validity-in-ms: 604800000 # 7일
|
refresh-token-validity-in-ms: 604800000 # 7일
|
||||||
#access-token-validity-in-ms: 60000 # 1분
|
#access-token-validity-in-ms: 300000 # 5분
|
||||||
#refresh-token-validity-in-ms: 300000 # 5분
|
#refresh-token-validity-in-ms: 600000 # 10분
|
||||||
|
|
||||||
token:
|
token:
|
||||||
refresh-cookie-name: kamco-dev # 개발용 쿠키 이름
|
refresh-cookie-name: kamco-dev # 개발용 쿠키 이름
|
||||||
@@ -100,7 +100,9 @@ inference:
|
|||||||
url: http://192.168.2.183:8000/jobs
|
url: http://192.168.2.183:8000/jobs
|
||||||
batch-url: http://192.168.2.183:8000/batches
|
batch-url: http://192.168.2.183:8000/batches
|
||||||
jar-path: ${inference.nfs}/repo/jar/shp-exporter.jar
|
jar-path: ${inference.nfs}/repo/jar/shp-exporter.jar
|
||||||
|
jar-path-v2: ${inference.nfs}/repo/jar/shp-exporter-v2.jar
|
||||||
inference-server-name: server1,server2,server3,server4
|
inference-server-name: server1,server2,server3,server4
|
||||||
|
output-dir: ${inference.nfs}/model_output/export
|
||||||
|
|
||||||
gukyuin:
|
gukyuin:
|
||||||
#url: http://localhost:8080
|
#url: http://localhost:8080
|
||||||
|
|||||||
@@ -74,11 +74,13 @@ file:
|
|||||||
pt-FileName: v5-best.pt
|
pt-FileName: v5-best.pt
|
||||||
|
|
||||||
inference:
|
inference:
|
||||||
nfs: C:/Users/gyparkkamco-nfs
|
nfs: C:/Users/gypark/kamco-nfs
|
||||||
url: http://10.100.0.11:8000/jobs
|
url: http://10.100.0.11:8000/jobs
|
||||||
batch-url: http://10.100.0.11:8000/batches
|
batch-url: http://10.100.0.11:8000/batches
|
||||||
jar-path: jar/shp-exporter.jar
|
jar-path: jar/shp-exporter.jar
|
||||||
|
jar-path-v2: jar/shp-exporter-v2.jar
|
||||||
inference-server-name: server1,server2,server3,server4
|
inference-server-name: server1,server2,server3,server4
|
||||||
|
output-dir: ${inference.nfs}/model_output/export
|
||||||
|
|
||||||
gukyuin:
|
gukyuin:
|
||||||
#url: http://localhost:8080
|
#url: http://localhost:8080
|
||||||
@@ -90,3 +92,6 @@ layer:
|
|||||||
wms-path: geoserver/cd
|
wms-path: geoserver/cd
|
||||||
wmts-path: geoserver/cd/gwc/service
|
wmts-path: geoserver/cd/gwc/service
|
||||||
workspace: cd
|
workspace: cd
|
||||||
|
|
||||||
|
training-data:
|
||||||
|
geojson-dir: /kamco-nfs/dataset/request/
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user