125 Commits

Author SHA1 Message Date
e88ffd1260 국유인 수정, 라벨대상 건수 수정 2026-02-06 09:49:30 +09:00
533d97a573 학습데이터 라벨링 현황 건수 조건 수정, 라벨러, 검수자 목록 수정 2026-02-05 18:00:52 +09:00
3237863542 국유인 연동 경로 확인 TEST 2026-02-05 16:59:49 +09:00
41b227de3f 국유인 API 수정 추가 2026-02-05 15:09:27 +09:00
83e02c4498 국유인 API 수정 추가 2026-02-05 15:02:00 +09:00
d8804e7c9a 국유인 API 수정 추가 2026-02-05 14:58:30 +09:00
f326b5f651 국유인 API 수정 2026-02-05 14:55:16 +09:00
c1b6061e3e 라벨링 할당 수정 2026-02-05 13:55:42 +09:00
71a8f27afc 라벨링 가능 건수 조건 수정 2026-02-05 13:49:18 +09:00
299d1b09a0 국유인 API reqIp, reqEpno 추가, 스케줄러 수정 2026-02-04 17:55:39 +09:00
3461376b35 국유in 등록 로그 추가 2026-02-04 11:13:49 +09:00
17bd89fafa 국유in 등록 로그 추가 2026-02-04 11:10:16 +09:00
e5fa99daef 국유인, 라벨링 job 각각 분리 작업 2026-02-02 19:16:26 +09:00
d563f47abd spotless 적용 2026-02-02 17:17:29 +09:00
cf4f79f7ca 추론결과 국유In 등록 벨리데이션 체크 수정 2026-02-02 17:15:57 +09:00
6e9b4196b8 영상관리 count 조건 수정 2026-02-02 17:08:38 +09:00
a4f66f511e 국유인 연동 리턴 타입 수정 okObject 2026-02-02 15:59:05 +09:00
9734a5acb2 국유인 스케줄러 분리 2026-02-02 15:40:17 +09:00
9fb4a25955 국유인 연동 API 실태조사 적합여부 추가 2026-02-02 14:56:04 +09:00
3af05bbeef 인증 예시 아이디 수정 2026-02-02 14:00:30 +09:00
1c2e41ced6 사번 자리수 6자리 벨리데이션 수정 2026-02-02 13:36:53 +09:00
c52c2ab9bd 변화지도 레이어 조회 url 수정 2026-02-02 12:22:32 +09:00
eb8d798714 Merge remote-tracking branch 'origin/feat/infer_dev_260107' into feat/infer_dev_260107 2026-02-02 12:17:46 +09:00
7e95b53881 변화지도 레이어 조회 url 수정 2026-02-02 12:17:39 +09:00
74b244981b 국유인 연동 API 응답 로직 수정 2026-02-02 12:17:18 +09:00
699d39d402 타일 url 시큐리티 추가 2026-02-02 10:31:11 +09:00
e3ae889152 Merge remote-tracking branch 'origin/feat/infer_dev_260107' into feat/infer_dev_260107 2026-02-02 10:28:07 +09:00
20a835cf45 레이어관리 삭제 수정 2026-02-02 10:27:57 +09:00
c0b5dd99ef spotless 2026-02-02 10:13:58 +09:00
5015a2a437 국유인 연동 API validation 체크 주석 해제 2026-02-02 10:10:52 +09:00
d8d35c3462 crs 타입 수정하기 2026-01-30 21:42:47 +09:00
b77de057f0 영상관리 년도별 타일 crs 추가하기 2026-01-30 21:26:52 +09:00
2559b225d5 레이어관리 수정 2026-01-30 21:09:35 +09:00
9a00c38cc7 Merge remote-tracking branch 'origin/feat/infer_dev_260107' into feat/infer_dev_260107 2026-01-30 21:03:56 +09:00
e8fa7411d5 레이어관리 수정 2026-01-30 21:03:50 +09:00
71f1f03b89 영상관리 년도별 타일 crs 추가하기 2026-01-30 21:00:15 +09:00
96d7ea205c properties 커밋 2026-01-30 19:42:37 +09:00
0993ce646a wmts, wms url 추가 2026-01-30 19:40:41 +09:00
302e1ad957 geoserver url 변경 2026-01-30 19:13:02 +09:00
e11f365bf8 bbox 5186 transform 2026-01-30 18:19:47 +09:00
9dd439b920 bbox 5186 으로 변환 2026-01-30 18:09:13 +09:00
a42729a475 wmts 수정 2026-01-30 17:56:20 +09:00
bc5c5b3dd7 추론실행 퍼센트 수정 2026-01-30 17:49:16 +09:00
60ff43cd3c Merge branch 'feat/infer_dev_260107' of https://kamco.git.gs.dabeeo.com/MVPTeam/kamco-cd-api into feat/infer_dev_260107 2026-01-30 17:43:43 +09:00
ff7654baa7 RestTemplateConfig 수정 2026-01-30 17:43:27 +09:00
21f922a5f4 국유인 어제완료된 라벨전송, 전송완료된 리스트 기능 추가 2026-01-30 17:32:39 +09:00
5ff72f927c RestTemplateConfig 수정 2026-01-30 17:20:53 +09:00
e6ef4c2525 RestTemplateConfig 수정, wmts 수정 2026-01-30 17:13:08 +09:00
86e8408f27 RestTemplateConfig 수정 2026-01-30 16:51:27 +09:00
d4fb11deb2 RestTemplateConfig 수정 2026-01-30 16:44:21 +09:00
c2b87ca12a RestTemplateConfig 수정, 추론실행 수정 2026-01-30 16:18:40 +09:00
37b7877083 RestTemplateConfig 수정 2026-01-30 14:43:33 +09:00
3b636a85e6 wmts 수정 2026-01-30 14:18:57 +09:00
d95dc364aa wmts 수정 2026-01-30 14:10:22 +09:00
29556b6a05 등록 post로 수정 2026-01-30 13:33:00 +09:00
df8bac950b 국유인 API 진행중 2026-01-30 13:30:13 +09:00
f3453ab499 레이어관리 수정 2026-01-30 12:04:11 +09:00
2865b075a2 레이어관리 수정 2026-01-30 11:54:11 +09:00
10ab050a1c 년도 1개만 조회하는 타일 API 2026-01-30 11:17:13 +09:00
babf35142a 년도별 타일 API 2026-01-30 11:04:14 +09:00
ceebbdfa50 변화지도, 라벨링 맵 리스트 uuid, rawjson 추가 2026-01-30 10:51:22 +09:00
af19ca905b geoserver 등록 수정 2026-01-30 10:22:39 +09:00
3686f1d248 geoserver 등록 수정 2026-01-30 10:21:06 +09:00
565e76a4e2 Merge remote-tracking branch 'origin/feat/infer_dev_260107' into feat/infer_dev_260107 2026-01-30 09:50:38 +09:00
e379c49453 geoserver 등록 수정 2026-01-30 09:50:32 +09:00
9bec466d7c 레이어관리 - 변화지도,라벨링툴 맵 리스트 2026-01-29 20:56:14 +09:00
cea1d82bd9 ai 주소 변경 2026-01-29 20:12:00 +09:00
e4550d1e71 영상관리 등록 년도 tile 추가 2026-01-29 19:03:08 +09:00
38ad63172a ai 주소 변경 2026-01-29 17:34:15 +09:00
586c0f7e9a Merge remote-tracking branch 'origin/feat/infer_dev_260107' into feat/infer_dev_260107 2026-01-29 16:23:50 +09:00
32cb47cfc2 geojson 파일생성 api 추가 2026-01-29 16:23:44 +09:00
23a096a600 라벨링 툴 pnu 정보 list string으로 수정 2026-01-29 16:23:13 +09:00
480b016f33 geoserver url 변경 2026-01-29 14:42:21 +09:00
8563204e59 영상관리 folder-list 수정 2026-01-29 12:49:16 +09:00
e31913ff9a cls model 적용 2026-01-29 12:31:52 +09:00
f87a714892 헬스체크 시큐리티 설정 추가 2026-01-29 12:30:12 +09:00
d83f8befa9 Merge branch 'feat/infer_dev_260107' of https://kamco.git.gs.dabeeo.com/MVPTeam/kamco-cd-api into feat/infer_dev_260107 2026-01-29 12:15:45 +09:00
7f60a63a0f false 2026-01-29 12:14:27 +09:00
87374c8f6f 모델등록 pt 수정 2026-01-29 12:08:01 +09:00
0c3aaaa8f5 모델등록 pt 수정 2026-01-29 11:18:33 +09:00
43ed8b5409 국유인 API 연동 작업중 2026-01-29 10:41:08 +09:00
614f0dccf0 추론실행 수정 2026-01-29 10:34:51 +09:00
50c9efedd1 Merge branch 'develop' of https://kamco.git.gs.dabeeo.com/MVPTeam/kamco-cd-api into feat/infer_dev_260107 2026-01-29 10:29:26 +09:00
f793042927 추론실행 수정 2026-01-29 10:27:08 +09:00
0f8261b20b Merge remote-tracking branch 'origin/feat/infer_dev_260107' into feat/infer_dev_260107 2026-01-28 11:46:58 +09:00
c8c8816981 추론실행 수정 2026-01-28 11:46:51 +09:00
2cce3bd18b 에러로그 날짜 전체 나오게 수정 2026-01-28 10:21:05 +09:00
2e43ec76f2 영상관리 업로드 경로 수정 2026-01-27 21:41:45 +09:00
4e3789b5ce Merge remote-tracking branch 'origin/feat/infer_dev_260107' into feat/infer_dev_260107 2026-01-27 21:32:03 +09:00
56adcc8181 레이어 관리 api 추가 2026-01-27 21:31:56 +09:00
3445329d48 영상관리 업로드 경로 수정 2026-01-27 21:31:38 +09:00
bcf44a6f4a 레이어 관리 api 추가 2026-01-27 21:18:43 +09:00
7a932acd25 레이어 관리 api 추가 2026-01-27 21:14:12 +09:00
1b6ed5e4cf Merge remote-tracking branch 'origin/feat/infer_dev_260107' into feat/infer_dev_260107 2026-01-27 20:54:35 +09:00
52011c8f03 레이어 관리 api 추가 2026-01-27 20:54:27 +09:00
29781c9a95 Merge branch 'feat/infer_dev_260107' of https://10.100.0.10:3210/dabeeo/kamco-dabeeo-backoffice into feat/infer_dev_260107 2026-01-27 20:54:06 +09:00
74fee4b133 영상관리 업로드 경로 수정 2026-01-27 20:53:55 +09:00
6af8584526 레이어 관리 api 추가 2026-01-27 20:38:42 +09:00
22193d5200 에러로그 actionType 로직 수정 2026-01-27 20:20:54 +09:00
ef7c7b1c7e 영상관리 chunk, uploadPair 로직 수정 2026-01-27 19:52:01 +09:00
db5325a1fa 모델등록 수정 2026-01-27 17:48:46 +09:00
5e65474a39 Merge branch 'feat/infer_dev_260107' of https://10.100.0.10:3210/dabeeo/kamco-dabeeo-backoffice into feat/infer_dev_260107 2026-01-27 17:33:43 +09:00
1c477bc41f 영상관리 자동추론제외 수정 2026-01-27 17:33:27 +09:00
62305119a1 모델등록 수정 2026-01-27 17:33:03 +09:00
6ed679c8d6 모델등록 수정 2026-01-27 17:13:52 +09:00
da9da26796 Merge remote-tracking branch 'origin/feat/infer_dev_260107' into feat/infer_dev_260107 2026-01-27 16:51:16 +09:00
e4faa1ad70 모델등록 수정 2026-01-27 16:51:08 +09:00
77ac5e5b99 영상관리 목록, 상세 쿼리 수정 2026-01-27 16:49:21 +09:00
575c98f651 모델등록 수정 2026-01-27 16:48:57 +09:00
c7b316e968 Merge branch 'feat/infer_dev_260107' of https://10.100.0.10:3210/dabeeo/kamco-dabeeo-backoffice into feat/infer_dev_260107 2026-01-27 16:26:56 +09:00
ea03c5f07f uploadPair, 라벨링 현황 목록 수정 2026-01-27 16:26:41 +09:00
dc0f968926 모델등록 수정 2026-01-27 16:16:02 +09:00
310bca36a0 Merge remote-tracking branch 'origin/feat/infer_dev_260107' into feat/infer_dev_260107 2026-01-27 15:45:34 +09:00
9bdd4f5710 모델등록 수정 2026-01-27 15:45:27 +09:00
bbf0073f35 영상관리 - 자동추론제외 수정, uploadPair 수정 2026-01-27 15:44:47 +09:00
797a1ea69f Merge remote-tracking branch 'origin/feat/infer_dev_260107' into feat/infer_dev_260107 2026-01-27 13:49:31 +09:00
c8c5e8c083 모델등록 수정 2026-01-27 13:49:23 +09:00
88107ac616 영상관리 - 자동추론제외 update할 때 update_dttm 추가 2026-01-27 13:48:18 +09:00
efd6567024 추론진행 퍼센트 오류 수정 2026-01-27 13:17:19 +09:00
af25143cd5 영상관리 - 자동추론제외/해제 로직, 목록 API 수정 2026-01-27 12:29:44 +09:00
6b23a5d739 shp파일 생성 jar 경로 변경, 추론진행중 상태 추가 2026-01-27 12:21:56 +09:00
a26a8668a8 영상관리 - 상세에 도엽사용여부 join 추가 2026-01-27 11:32:23 +09:00
a5249d6e60 에러로그 레벨 CodeExpose 추가 2026-01-27 11:14:33 +09:00
833e444a08 로그관리 - 시간대 변환 수정 2026-01-27 11:11:49 +09:00
88c936ec6b 학습데이터관리 - 재할당 가능한 사용자 목록 진행률 조건 제외 2026-01-27 10:58:25 +09:00
113 changed files with 6747 additions and 747 deletions

View File

@@ -0,0 +1,27 @@
package com.kamco.cd.kamcoback.common.enums;
import com.kamco.cd.kamcoback.common.utils.enums.CodeExpose;
import com.kamco.cd.kamcoback.common.utils.enums.EnumType;
import lombok.AllArgsConstructor;
import lombok.Getter;
@CodeExpose
@Getter
@AllArgsConstructor
public enum CrsType implements EnumType {
EPSG_3857("Web Mercator, 웹지도 미터(EPSG:900913 동일)"),
EPSG_4326("WGS84 위경도, GeoJSON/OSM 기본"),
EPSG_5186("Korea 2000 중부 TM, 한국 SHP");
private final String desc;
@Override
public String getId() {
return name();
}
@Override
public String getText() {
return desc;
}
}

View File

@@ -12,7 +12,8 @@ public enum FileUploadStatus implements EnumType {
INIT("초기화"),
UPLOADING("업로드중"),
DONE("업로드완료"),
MERGED("병합완료");
MERGED("병합완료"),
MERGE_FAIL("병합 실패");
private final String desc;

View File

@@ -0,0 +1,37 @@
package com.kamco.cd.kamcoback.common.enums;
import com.kamco.cd.kamcoback.common.utils.enums.CodeExpose;
import com.kamco.cd.kamcoback.common.utils.enums.EnumType;
import java.util.Optional;
import lombok.AllArgsConstructor;
import lombok.Getter;
@CodeExpose
@Getter
@AllArgsConstructor
public enum LayerType implements EnumType {
TILE("배경지도"),
GEOJSON("객체데이터"),
WMTS("타일레이어"),
WMS("지적도");
private final String desc;
@Override
public String getId() {
return name();
}
@Override
public String getText() {
return desc;
}
public static Optional<LayerType> from(String type) {
try {
return Optional.of(LayerType.valueOf(type));
} catch (Exception e) {
return Optional.empty();
}
}
}

View File

@@ -54,8 +54,8 @@ public class ExternalJarRunner {
public void run(String jarPath, String register, String layer) {
List<String> args = new ArrayList<>();
addArg(args, "register", register);
addArg(args, "layer", layer);
addArg(args, "upload-shp", register);
// addArg(args, "layer", layer);
execJar(jarPath, args);
}

View File

@@ -30,12 +30,14 @@ import java.util.stream.Stream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FilenameUtils;
import org.geotools.coverage.grid.GridCoverage2D;
import org.geotools.gce.geotiff.GeoTiffReader;
import org.springframework.util.FileSystemUtils;
import org.springframework.web.multipart.MultipartFile;
@Slf4j
public class FIleChecker {
static SimpleDateFormat dttmFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
@@ -138,7 +140,9 @@ public class FIleChecker {
// null을 넣으면 전체 영역을 읽지 않고 메타데이터 위주로 체크하여 빠름
GridCoverage2D coverage = reader.read(null);
if (coverage == null) return false;
if (coverage == null) {
return false;
}
// 3. GIS 필수 정보(좌표계)가 있는지 확인
// if (coverage.getCoordinateReferenceSystem() == null) {
@@ -152,7 +156,9 @@ public class FIleChecker {
return false;
} finally {
// 리소스 해제 (필수)
if (reader != null) reader.dispose();
if (reader != null) {
reader.dispose();
}
}
}
@@ -296,7 +302,8 @@ public class FIleChecker {
boolean isValid =
!NameValidator.containsKorean(folderNm)
&& !NameValidator.containsWhitespaceRegex(folderNm);
&& !NameValidator.containsWhitespaceRegex(folderNm)
&& !parentFolderNm.equals("kamco-nfs");
File file = new File(fullPath);
int childCnt = getChildFolderCount(file);
@@ -586,7 +593,9 @@ public class FIleChecker {
}
public static boolean checkExtensions(String fileName, String ext) {
if (fileName == null) return false;
if (fileName == null) {
return false;
}
if (!fileName.substring(fileName.lastIndexOf('.') + 1).toLowerCase().equals(ext)) {
return false;
@@ -690,6 +699,7 @@ public class FIleChecker {
@Schema(name = "Folder", description = "폴더 정보")
@Getter
public static class Folder {
private final String folderNm;
private final String parentFolderNm;
private final String parentPath;

View File

@@ -5,11 +5,9 @@ import java.net.InetAddress;
import java.net.URLEncoder;
import java.net.UnknownHostException;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
public class NetUtils {
@@ -56,9 +54,8 @@ public class NetUtils {
public HttpHeaders jsonHeaders() {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.setAccept(List.of(MediaType.APPLICATION_JSON));
headers.set(HttpHeaders.ACCEPT, "application/json;charset=UTF-8");
headers.set(HttpHeaders.CONTENT_TYPE, "application/json;charset=UTF-8");
return headers;
}
}

View File

@@ -4,10 +4,12 @@ import com.kamco.cd.kamcoback.auth.CustomUserDetails;
import com.kamco.cd.kamcoback.common.exception.CustomApiException;
import com.kamco.cd.kamcoback.common.exception.DuplicateFileException;
import com.kamco.cd.kamcoback.common.exception.ValidationException;
import com.kamco.cd.kamcoback.common.utils.HeaderUtil;
import com.kamco.cd.kamcoback.config.api.ApiLogFunction;
import com.kamco.cd.kamcoback.config.api.ApiResponseDto;
import com.kamco.cd.kamcoback.config.api.ApiResponseDto.ApiResponseCode;
import com.kamco.cd.kamcoback.log.dto.ErrorLogDto;
import com.kamco.cd.kamcoback.log.dto.EventType;
import com.kamco.cd.kamcoback.members.exception.MemberException;
import com.kamco.cd.kamcoback.postgres.entity.ErrorLogEntity;
import com.kamco.cd.kamcoback.postgres.repository.log.ErrorLogRepository;
@@ -463,13 +465,16 @@ public class GlobalExceptionHandler {
String stackTraceStr =
Arrays.stream(stackTrace)
.map(StackTraceElement::toString)
.collect(Collectors.joining("\n"))
.substring(0, 255);
.collect(Collectors.joining("\n"));
String actionType = HeaderUtil.get(request, "kamco-action-type");
ErrorLogEntity errorLogEntity =
new ErrorLogEntity(
request.getRequestURI(),
ApiLogFunction.getEventType(request),
actionType == null
? ApiLogFunction.getEventType(request)
: EventType.fromName(actionType),
logErrorLevel,
String.valueOf(httpStatus.value()),
errorCode.getText(),

View File

@@ -45,7 +45,8 @@ public class SecurityConfig {
auth ->
auth
// .requestMatchers("/chunk_upload_test.html").authenticated()
.requestMatchers("/monitor/health", "/monitor/health/**")
.permitAll()
// 맵시트 영역 전체 허용 (우선순위 최상단)
.requestMatchers("/api/mapsheet/**")
.permitAll()
@@ -84,7 +85,10 @@ public class SecurityConfig {
"/api/model/file-chunk-upload",
"/api/upload/file-chunk-upload",
"/api/upload/chunk-upload-complete",
"/api/change-detection/**")
"/api/change-detection/**",
"/api/layer/map/**",
"/api/layer/tile-url",
"/api/layer/tile-url-year")
.permitAll()
// 로그인한 사용자만 가능 IAM
.requestMatchers(

View File

@@ -1,72 +1,120 @@
package com.kamco.cd.kamcoback.config.resttemplate;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.nio.charset.StandardCharsets;
import java.util.List;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.HttpServerErrorException;
import org.springframework.web.client.HttpStatusCodeException;
import org.springframework.web.client.RestTemplate;
@RequiredArgsConstructor
@Component
@Log4j2
@RequiredArgsConstructor
public class ExternalHttpClient {
private final RestTemplate restTemplate;
private final ObjectMapper objectMapper;
public <T> ExternalCallResult<T> call(
String url, HttpMethod method, Object body, HttpHeaders headers, Class<T> responseType) {
HttpEntity<Object> entity = new HttpEntity<>(body, headers);
// responseType 기반으로 Accept 동적 세팅
HttpHeaders resolvedHeaders = resolveHeaders(headers, responseType);
logRequestBody(body);
// 요청 로그
log.info("[HTTP-REQ] {} {}", method, url);
if (body != null) {
log.debug("[HTTP-REQ-BODY] {}", body);
}
HttpEntity<Object> entity = new HttpEntity<>(body, resolvedHeaders);
try {
// String: raw bytes -> UTF-8 string
if (responseType == String.class) {
ResponseEntity<byte[]> res = restTemplate.exchange(url, method, entity, byte[].class);
String raw =
(res.getBody() == null) ? null : new String(res.getBody(), StandardCharsets.UTF_8);
@SuppressWarnings("unchecked")
T casted = (T) raw;
return new ExternalCallResult<>(res.getStatusCodeValue(), true, casted, null);
}
// byte[]: raw bytes로 받고, JSON이면 에러로 처리
if (responseType == byte[].class) {
ResponseEntity<byte[]> res = restTemplate.exchange(url, method, entity, byte[].class);
MediaType ct = res.getHeaders().getContentType();
byte[] bytes = res.getBody();
if (isJsonLike(ct)) {
String err = (bytes == null) ? null : new String(bytes, StandardCharsets.UTF_8);
return new ExternalCallResult<>(res.getStatusCodeValue(), false, null, err);
}
@SuppressWarnings("unchecked")
T casted = (T) bytes;
return new ExternalCallResult<>(res.getStatusCodeValue(), true, casted, null);
}
// DTO 등: 일반 역직렬화
ResponseEntity<T> res = restTemplate.exchange(url, method, entity, responseType);
return new ExternalCallResult<>(res.getStatusCodeValue(), true, res.getBody(), null);
int code = res.getStatusCodeValue();
// 응답 로그
log.info("[HTTP-RES] {} {} -> {}", method, url, code);
log.debug("[HTTP-RES-BODY] {}", res.getBody());
return new ExternalCallResult<>(code, code >= 200 && code < 300, res.getBody());
} catch (HttpClientErrorException.NotFound e) {
log.info("[HTTP-RES] {} {} -> 404 (Not Found)", method, url);
log.debug("[HTTP-RES-BODY] {}", e.getResponseBodyAsString());
return new ExternalCallResult<>(404, false, null);
} catch (HttpClientErrorException e) {
// 기타 4xx
log.warn(
"[HTTP-ERR] {} {} -> {} body={}",
method,
url,
e.getStatusCode().value(),
e.getResponseBodyAsString());
throw e;
} catch (HttpServerErrorException e) {
// 5xx
log.error(
"[HTTP-ERR] {} {} -> {} body={}",
method,
url,
e.getStatusCode().value(),
e.getResponseBodyAsString());
throw e;
} catch (HttpStatusCodeException e) {
return new ExternalCallResult<>(
e.getStatusCode().value(), false, null, e.getResponseBodyAsString());
}
}
public record ExternalCallResult<T>(int statusCode, boolean success, T body) {}
// 기존 resolveJsonHeaders를 "동적"으로 교체
private HttpHeaders resolveHeaders(HttpHeaders headers, Class<?> responseType) {
// 원본 headers를 그대로 쓰면 외부에서 재사용할 때 사이드이펙트 날 수 있어서 복사 권장
HttpHeaders h = (headers == null) ? new HttpHeaders() : new HttpHeaders(headers);
// 요청 바디 기본은 JSON이라고 가정 (필요하면 호출부에서 덮어쓰기)
if (h.getContentType() == null) {
h.setContentType(MediaType.APPLICATION_JSON);
}
// 호출부에서 Accept를 명시했으면 존중
if (h.getAccept() != null && !h.getAccept().isEmpty()) {
return h;
}
// responseType 기반 Accept 자동 지정
if (responseType == byte[].class) {
h.setAccept(
List.of(
MediaType.APPLICATION_OCTET_STREAM,
MediaType.valueOf("application/zip"),
MediaType.APPLICATION_JSON // 실패(JSON 에러 바디) 대비
));
} else {
h.setAccept(List.of(MediaType.APPLICATION_JSON));
}
return h;
}
private boolean isJsonLike(MediaType ct) {
if (ct == null) return false;
return ct.includes(MediaType.APPLICATION_JSON)
|| "application/problem+json".equalsIgnoreCase(ct.toString());
}
private void logRequestBody(Object body) {
try {
if (body != null) {
log.info("[HTTP-REQ-BODY-JSON] {}", objectMapper.writeValueAsString(body));
}
} catch (Exception e) {
log.warn("[HTTP-REQ-BODY-JSON] serialize failed: {}", e.getMessage());
}
}
public record ExternalCallResult<T>(int statusCode, boolean success, T body, String errBody) {}
}

View File

@@ -4,6 +4,7 @@ import lombok.extern.log4j.Log4j2;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.BufferingClientHttpRequestFactory;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;
@@ -13,10 +14,20 @@ public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
SimpleClientHttpRequestFactory f = new SimpleClientHttpRequestFactory();
f.setConnectTimeout(2000);
f.setReadTimeout(3000);
SimpleClientHttpRequestFactory baseFactory = new SimpleClientHttpRequestFactory();
baseFactory.setConnectTimeout(2000);
baseFactory.setReadTimeout(3000);
return builder.requestFactory(() -> f).additionalInterceptors(new RetryInterceptor()).build();
RestTemplate rt =
builder
.requestFactory(() -> new BufferingClientHttpRequestFactory(baseFactory))
.additionalInterceptors(new RetryInterceptor())
.build();
// byte[] 응답은 무조건 raw로 읽게 강제 (Jackson이 끼어들 여지 제거)
rt.getMessageConverters()
.add(0, new org.springframework.http.converter.ByteArrayHttpMessageConverter());
return rt;
}
}

View File

@@ -1,12 +1,15 @@
package com.kamco.cd.kamcoback.config.resttemplate;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.TimeUnit;
import lombok.extern.log4j.Log4j2;
import org.springframework.http.HttpRequest;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;
@Log4j2
public class RetryInterceptor implements ClientHttpRequestInterceptor {
private static final int MAX_RETRY = 3;
@@ -20,21 +23,25 @@ public class RetryInterceptor implements ClientHttpRequestInterceptor {
for (int attempt = 1; attempt <= MAX_RETRY; attempt++) {
try {
// HTTP 응답을 받으면(2xx/4xx/5xx 포함) 그대로 반환
return execution.execute(request, body);
log.info("[WIRE-REQ] {} {}", request.getMethod(), request.getURI());
log.info("[WIRE-REQ-HEADERS] {}", request.getHeaders());
log.info("[WIRE-REQ-BODY] {}", new String(body, StandardCharsets.UTF_8));
ClientHttpResponse response = execution.execute(request, body);
log.info("[WIRE-RES-STATUS] {}", response.getStatusCode());
return response;
} catch (IOException e) {
// 네트워크/타임아웃 등 I/O 예외만 재시도
lastException = e;
log.error("[WIRE-IO-ERR] attempt={} msg={}", attempt, e.getMessage(), e);
}
// 마지막 시도가 아니면 대기
if (attempt < MAX_RETRY) {
sleep();
}
}
// 마지막 예외를 그대로 던져서 원인이 로그에 남게 함
throw lastException;
}

View File

@@ -1,14 +1,21 @@
package com.kamco.cd.kamcoback.gukyuin;
import com.kamco.cd.kamcoback.config.api.ApiResponseDto;
import com.kamco.cd.kamcoback.config.api.ApiResponseDto.ResponseObj;
import com.kamco.cd.kamcoback.gukyuin.dto.ChngDetectContDto;
import com.kamco.cd.kamcoback.gukyuin.dto.ChngDetectMastDto;
import com.kamco.cd.kamcoback.gukyuin.dto.ChngDetectMastDto.ChnDetectMastReqDto;
import com.kamco.cd.kamcoback.gukyuin.dto.ChngDetectMastDto.ChngDetectMastSearchDto;
import com.kamco.cd.kamcoback.gukyuin.dto.ChngDetectMastDto.ResReturn;
import com.kamco.cd.kamcoback.gukyuin.dto.ChngDetectMastDto.LabelSendDto;
import com.kamco.cd.kamcoback.gukyuin.dto.DetectMastDto.Basic;
import com.kamco.cd.kamcoback.gukyuin.dto.DetectMastDto.DetectMastReq;
import com.kamco.cd.kamcoback.gukyuin.dto.GukYuinDto.GukYuinLinkableRes;
import com.kamco.cd.kamcoback.gukyuin.service.GukYuinApiService;
import com.kamco.cd.kamcoback.scheduler.service.GukYuinApiLabelJobService;
import com.kamco.cd.kamcoback.scheduler.service.GukYuinApiPnuJobService;
import com.kamco.cd.kamcoback.scheduler.service.GukYuinApiStatusJobService;
import com.kamco.cd.kamcoback.scheduler.service.GukYuinApiStbltJobService;
import io.swagger.v3.oas.annotations.Hidden;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Content;
@@ -17,6 +24,7 @@ import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import java.time.LocalDate;
import java.util.List;
import java.util.UUID;
import lombok.RequiredArgsConstructor;
@@ -35,6 +43,10 @@ import org.springframework.web.bind.annotation.RestController;
public class GukYuinApiController {
private final GukYuinApiService gukYuinApiService;
private final GukYuinApiPnuJobService gukYuinApiPnuJobService;
private final GukYuinApiStatusJobService gukYuinApiStatusJobService;
private final GukYuinApiLabelJobService gukYuinApiLabelJobService;
private final GukYuinApiStbltJobService gukYuinApiStbltJobService;
/** 탐지결과 등록 */
@Operation(summary = "탐지결과 등록", description = "탐지결과 등록")
@@ -50,10 +62,10 @@ public class GukYuinApiController {
@ApiResponse(responseCode = "404", description = "코드를 찾을 수 없음", content = @Content),
@ApiResponse(responseCode = "500", description = "서버 오류", content = @Content)
})
@PostMapping("/mast/regist")
public ChngDetectMastDto.Basic regist(
@PostMapping("/chn/mast/regist")
public ApiResponseDto<ChngDetectMastDto.RegistResDto> regist(
@RequestBody @Valid ChngDetectMastDto.ChnDetectMastReqDto chnDetectMastReq) {
return gukYuinApiService.regist(chnDetectMastReq);
return ApiResponseDto.ok(gukYuinApiService.regist(chnDetectMastReq));
}
@Operation(summary = "탐지결과 삭제", description = "탐지결과 삭제")
@@ -69,14 +81,14 @@ public class GukYuinApiController {
@ApiResponse(responseCode = "404", description = "코드를 찾을 수 없음", content = @Content),
@ApiResponse(responseCode = "500", description = "서버 오류", content = @Content)
})
@PostMapping("/mast/remove")
public ResReturn remove(
@PostMapping("/chn/mast/remove")
public ApiResponseDto<ChngDetectMastDto.RemoveResDto> remove(
@RequestBody @Valid ChngDetectMastDto.ChnDetectMastReqDto chnDetectMastReq) {
return gukYuinApiService.remove(chnDetectMastReq);
return ApiResponseDto.ok(gukYuinApiService.remove(chnDetectMastReq));
}
@Operation(summary = "탐지결과 등록목록 조회", description = "탐지결과 등록목록 조회")
@GetMapping("/mast/list")
@Operation(summary = "탐지결과 등록목록 조회(년도,차수 조회)", description = "탐지결과 등록목록 조회")
@GetMapping("/chn/mast")
@ApiResponses(
value = {
@ApiResponse(
@@ -89,17 +101,53 @@ public class GukYuinApiController {
@ApiResponse(responseCode = "404", description = "코드를 찾을 수 없음", content = @Content),
@ApiResponse(responseCode = "500", description = "서버 오류", content = @Content)
})
public List<ChngDetectMastDto.Basic> selectChangeDetectionList(
@RequestParam(required = false) String chnDtctId,
public ApiResponseDto<ChngDetectMastDto.ResultDto> selectChangeDetectionList(
@RequestParam(required = false) String cprsYr,
@RequestParam(required = false) String crtrYr,
@RequestParam(required = false) String chnDtctSno) {
ChngDetectMastSearchDto searchDto = new ChngDetectMastSearchDto();
searchDto.setChnDtctId(chnDtctId);
searchDto.setCprsYr(cprsYr);
searchDto.setCrtrYr(crtrYr);
searchDto.setChnDtctSno(chnDtctSno);
return gukYuinApiService.list(searchDto);
return ApiResponseDto.ok(gukYuinApiService.listYearStage(searchDto));
}
@Operation(summary = "탐지결과 등록목록 조회(회차uid)", description = "탐지결과 등록목록 조회")
@GetMapping("/chn/mast/{chnDtctId}")
@ApiResponses(
value = {
@ApiResponse(
responseCode = "200",
description = "목록 성공",
content =
@Content(
mediaType = "application/json",
schema = @Schema(implementation = Basic.class))),
@ApiResponse(responseCode = "404", description = "코드를 찾을 수 없음", content = @Content),
@ApiResponse(responseCode = "500", description = "서버 오류", content = @Content)
})
public ApiResponseDto<ChngDetectMastDto.ResultDto> selectChangeDetectionDtctIdList(
@RequestParam(required = false) String chnDtctId) {
return ApiResponseDto.ok(gukYuinApiService.listChnDtctId(chnDtctId));
}
@Operation(summary = "탐지결과 등록목록 조회(1건 조회)", description = "탐지결과 등록목록 조회")
@GetMapping("/chn/mast/list/{chnDtctMstId}")
@ApiResponses(
value = {
@ApiResponse(
responseCode = "200",
description = "목록 성공",
content =
@Content(
mediaType = "application/json",
schema = @Schema(implementation = Basic.class))),
@ApiResponse(responseCode = "404", description = "코드를 찾을 수 없음", content = @Content),
@ApiResponse(responseCode = "500", description = "서버 오류", content = @Content)
})
public ApiResponseDto<ChngDetectMastDto.ResultDto> selectChangeDetectionDetail(
@PathVariable String chnDtctMstId) {
return ApiResponseDto.ok(gukYuinApiService.detail(chnDtctMstId));
}
@Operation(summary = "국유in연동 가능여부 확인", description = "국유in연동 가능여부 확인")
@@ -125,4 +173,193 @@ public class GukYuinApiController {
UUID uuid) {
return ApiResponseDto.ok(gukYuinApiService.getIsLinkGukYuin(uuid));
}
@Operation(summary = "탐지객체 조회 (탐지객체)", description = "탐지객체 조회 (탐지객체)")
@GetMapping("/chn/cont/{chnDtctId}")
@ApiResponses(
value = {
@ApiResponse(
responseCode = "200",
description = "목록 성공",
content =
@Content(
mediaType = "application/json",
schema = @Schema(implementation = Basic.class))),
@ApiResponse(responseCode = "404", description = "코드를 찾을 수 없음", content = @Content),
@ApiResponse(responseCode = "500", description = "서버 오류", content = @Content)
})
public ApiResponseDto<ChngDetectContDto.ResultContDto> findChnContList(
@PathVariable String chnDtctId,
@RequestParam(defaultValue = "0") Integer pageIndex,
@RequestParam(defaultValue = "10") Integer pageSize) {
return ApiResponseDto.ok(gukYuinApiService.findChnContList(chnDtctId, pageIndex, pageSize));
}
@Operation(summary = "탐지객체 조회 (탐지객체 1건 조회)", description = "탐지객체 조회 (탐지객체 1건 조회)")
@GetMapping("/chn/cont/{chnDtctId}/objt/{chnDtctObjtId}")
@ApiResponses(
value = {
@ApiResponse(
responseCode = "200",
description = "목록 성공",
content =
@Content(
mediaType = "application/json",
schema = @Schema(implementation = Basic.class))),
@ApiResponse(responseCode = "404", description = "코드를 찾을 수 없음", content = @Content),
@ApiResponse(responseCode = "500", description = "서버 오류", content = @Content)
})
public ApiResponseDto<ChngDetectContDto.ResultContDto> findChnPnuToContObject(
@PathVariable String chnDtctId,
@PathVariable String chnDtctObjtId,
@RequestParam(defaultValue = "0") Integer pageIndex,
@RequestParam(defaultValue = "10") Integer pageSize) {
return ApiResponseDto.ok(
gukYuinApiService.findChnPnuToContObject(chnDtctId, chnDtctObjtId, pageIndex, pageSize));
}
@Operation(summary = "탐지객체 조회 (PNU에 해당하는 탐지객체)", description = "탐지객체 조회 (PNU에 해당하는 탐지객체)")
@GetMapping("/chn/cont/{chnDtctId}/pnu/{pnu}")
@ApiResponses(
value = {
@ApiResponse(
responseCode = "200",
description = "목록 성공",
content =
@Content(
mediaType = "application/json",
schema = @Schema(implementation = Basic.class))),
@ApiResponse(responseCode = "404", description = "코드를 찾을 수 없음", content = @Content),
@ApiResponse(responseCode = "500", description = "서버 오류", content = @Content)
})
public ApiResponseDto<ChngDetectContDto.ResultContDto> findChnPnuToContList(
@PathVariable String chnDtctId, @PathVariable String pnu) {
return ApiResponseDto.ok(gukYuinApiService.findChnPnuToContList(chnDtctId, pnu));
}
@Operation(summary = "탐지객체 조회 (탐지객체와 교차하는 PNU)", description = "탐지객체 조회 (탐지객체와 교차하는 PNU)")
@GetMapping("/chn/pnu/{chnDtctId}/objt/{chnDtctObjtId}")
@ApiResponses(
value = {
@ApiResponse(
responseCode = "200",
description = "목록 성공",
content =
@Content(
mediaType = "application/json",
schema = @Schema(implementation = Basic.class))),
@ApiResponse(responseCode = "404", description = "코드를 찾을 수 없음", content = @Content),
@ApiResponse(responseCode = "500", description = "서버 오류", content = @Content)
})
public ApiResponseDto<ChngDetectContDto.ResultPnuDto> findPnuObjMgmtList(
@PathVariable String chnDtctId, @PathVariable String chnDtctObjtId) {
return ApiResponseDto.ok(gukYuinApiService.findPnuObjMgmtList(chnDtctId, 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}/lbl/{lblYn}")
public ApiResponseDto<ChngDetectContDto.ResultLabelDto> updateChnDtctObjtLabelingYn(
@PathVariable String chnDtctObjtId, @PathVariable String lblYn) {
return ApiResponseDto.ok(gukYuinApiService.updateChnDtctObjtLabelingYn(chnDtctObjtId, lblYn));
}
@Operation(summary = "국유in연동 등록", description = "국유in연동 등록")
@PostMapping("/mast/reg/{uuid}")
public ApiResponseDto<ResponseObj> connectChnMastRegist(
@Parameter(description = "uuid", example = "7a593d0e-76a8-4b50-8978-9af1fbe871af")
@PathVariable
UUID uuid) {
return ApiResponseDto.okObject(gukYuinApiService.connectChnMastRegist(uuid));
}
@Operation(summary = "라벨 전송 완료 리스트", description = "라벨 전송 완료 리스트")
@GetMapping("/label/send-list")
public ApiResponseDto<List<LabelSendDto>> findLabelingCompleteSendList(
@Parameter(description = "어제 날짜", example = "2026-01-29") LocalDate yesterday) {
return ApiResponseDto.ok(gukYuinApiService.findLabelingCompleteSendList(yesterday));
}
@Operation(summary = "탐지객체 적합여부 조회 (리스트조회)", description = "탐지객체 적합여부 조회 (리스트조회)")
@GetMapping("/rlb/dtct/{chnDtctId}")
@ApiResponses(
value = {
@ApiResponse(
responseCode = "200",
description = "목록 성공",
content =
@Content(
mediaType = "application/json",
schema = @Schema(implementation = Basic.class))),
@ApiResponse(responseCode = "404", description = "코드를 찾을 수 없음", content = @Content),
@ApiResponse(responseCode = "500", description = "서버 오류", content = @Content)
})
public ApiResponseDto<ChngDetectMastDto.RlbDtctDto> findRlbDtctList(
@PathVariable String chnDtctId,
@Parameter(description = "날짜(기본은 어제 날짜)") @RequestParam(defaultValue = "20260205")
String yyyymmdd) {
return ApiResponseDto.ok(gukYuinApiService.findRlbDtctList(chnDtctId, yyyymmdd));
}
@Operation(summary = "탐지객체 적합여부 조회 (객체별 조회)", description = "탐지객체 적합여부 조회 (객체별 조회)")
@GetMapping("/rlb/objt/{chnDtctObjtId}")
@ApiResponses(
value = {
@ApiResponse(
responseCode = "200",
description = "목록 성공",
content =
@Content(
mediaType = "application/json",
schema = @Schema(implementation = Basic.class))),
@ApiResponse(responseCode = "404", description = "코드를 찾을 수 없음", content = @Content),
@ApiResponse(responseCode = "500", description = "서버 오류", content = @Content)
})
public ApiResponseDto<ChngDetectMastDto.RlbDtctDto> findRlbDtctObject(
@PathVariable String chnDtctObjtId) {
return ApiResponseDto.ok(gukYuinApiService.findRlbDtctObject(chnDtctObjtId));
}
@Hidden
@Operation(summary = "job test pnu", description = "job test pnu")
@GetMapping("/job-test/pnu")
public ApiResponseDto<Void> findGukYuinContListPnuUpdate() {
gukYuinApiPnuJobService.findGukYuinContListPnuUpdate();
return ApiResponseDto.ok(null);
}
@Hidden
@Operation(summary = "job test status", description = "job test status")
@GetMapping("/job-test/status")
public ApiResponseDto<Void> findGukYuinMastCompleteYn() {
gukYuinApiStatusJobService.findGukYuinMastCompleteYn();
return ApiResponseDto.ok(null);
}
@Hidden
@Operation(summary = "job test label", description = "job test label")
@GetMapping("/job-test/label")
public ApiResponseDto<Void> findLabelingCompleteSend() {
gukYuinApiLabelJobService.findLabelingCompleteSend();
return ApiResponseDto.ok(null);
}
@Hidden
@Operation(summary = "job test stblt", description = "job test stblt")
@GetMapping("/job-test/stblt")
public ApiResponseDto<Void> findGukYuinEligibleForSurvey() {
gukYuinApiStbltJobService.findGukYuinEligibleForSurvey();
return ApiResponseDto.ok(null);
}
}

View File

@@ -0,0 +1,140 @@
package com.kamco.cd.kamcoback.gukyuin.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import java.util.List;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
public class ChngDetectContDto {
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public static class ContBasic {
private String chnDtctMstId; // 탐지콘텐츠아이디
private String chnDtctContId; // 탐지마스타아이디
private String cprsYr; // 비교년도 2023
private String crtrYr; // 기준년도 2024
private String chnDtctSno; // 차수 (1 | 2 | ...)
private String mpqdNo; // 도엽번호
private String chnDtctId; // 탐지아이디. UUID를 기반으로 '-'를 제거하고 대문자/숫자로 구성
private String chnDtctObjtId; // 탐지객체아이디. UUID를 기반으로 '-'를 제거하고 대문자/숫자로 구성
private String chnDtctPolygon; // 탐지객체폴리곤
private String chnDtctSqms; // 탐지객체면적
private String chnCd; // 변화코드
private String chnDtctJson; // 변화탐지JSON
private String chnDtctProb; // 변화탐지정확도
private String bfClsCd; // 이전부류코드
private String bfClsProb; // 이전분류정확도
private String afClsCd; // 이후분류코드
private String afClsProb; // 이후분류정확도
private String crtDt; // 생성일시
private String crtEpno; // 생성사원번호
private String crtIp; // 생성사원아이피
private String delYn; // 삭제여부
private String[] pnuList; // pnuList
private String reqEpno; // 요청사원번호
private String reqIp; // 요청사원아이피
}
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public static class ChnDetectContReqDto {
private String cprsYr; // 비교년도 2023
private String crtrYr; // 기준년도 2024
private String chnDtctSno; // 차수 (1 | 2 | ...)
private String mpqdNo; // 도엽번호
private String chnDtctId; // 탐지아이디. UUID를 기반으로 '-'를 제거하고 대문자/숫자로 구성
private String chnDtctObjtId; // 탐지객체아이디. UUID를 기반으로 '-'를 제거하고 대문자/숫자로 구성
private String reqEpno; // 사원번호
private String reqIp;
}
@Schema(name = "ResReturn", description = "수행 후 리턴")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public static class ResReturn {
private String flag;
private String message;
}
@Schema(name = "ResultContDto", description = "cont list 리턴 형태")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public static class ResultContDto {
private Integer code;
private String message;
private List<ContBasic> result;
private Boolean success;
}
@Schema(name = "DtoPnuDetectMpng", description = "PNU 결과 형태")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public static class DtoPnuDetectMpng {
private String pnuDtctId;
private String lrmYmd;
private String pnu;
private String pnuSqms;
private String pnuDtctSqms;
private String chnDtctSqms;
private String chnDtctMstId;
private String chnDtctContId;
private String chnDtctId;
private String chnDtctObjtId;
private String crtDt;
}
@Schema(name = "ResultPnuDto", description = "pnu list 리턴 형태")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public static class ResultPnuDto {
private Integer code;
private String message;
private List<DtoPnuDetectMpng> result;
private Boolean success;
}
@Schema(name = "ResultLabelDto", description = "ResultLabelDto list 리턴 형태")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public static class ResultLabelDto {
private Integer code;
private String message;
private DtoPnuDetectMpng result;
private Boolean success;
}
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public static class ReqInfo {
private String reqIp;
private String reqEpno;
}
}

View File

@@ -1,6 +1,7 @@
package com.kamco.cd.kamcoback.gukyuin.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import java.time.ZonedDateTime;
import java.util.List;
import lombok.AllArgsConstructor;
import lombok.Getter;
@@ -66,13 +67,30 @@ public class ChngDetectMastDto {
@AllArgsConstructor
public static class ChnDetectMastReqDto {
private String cprsYr; // 비교년도 2023
private String crtrYr; // 기준년도 2024
private String chnDtctSno; // 차수 (1 | 2 | ...)
private String chnDtctId; // 탐지아이디. UUID를 기반으로 '-'를 제거하고 대문자/숫자로 구성
private String pathNm; // 탐지결과 절대경로명 /kamco_nas/export/{chnDtctId}
private String reqEpno; // 사원번호
private String reqIp; // 사원아이피
@Schema(description = "비교년도", example = "2023")
private String cprsYr;
@Schema(description = "기준년도", example = "2024")
private String crtrYr;
@Schema(description = "차수", example = "1")
private String chnDtctSno;
@Schema(
description = "탐지아이디, UUID를 기반으로 '-'를 제거하고 대문자/숫자로 구성",
example = "D5F192EC76D34F6592035BE63A84F591")
private String chnDtctId;
@Schema(
description = "탐지결과 절대경로명 /kamco_nas/export/{chnDtctId}",
example = "/kamco-nfs/dataset/export/D5F192EC76D34F6592035BE63A84F591")
private String pathNm;
@Schema(description = "사원번호", example = "123456")
private String reqEpno;
@Schema(description = "사원아이피", example = "127.0.0.1")
private String reqIp;
}
@Getter
@@ -129,7 +147,7 @@ public class ChngDetectMastDto {
@AllArgsConstructor
public static class ChngDetectMastSearchDto {
private String chnDtctId;
// private String chnDtctId;
private String cprsYr;
private String crtrYr;
private String chnDtctSno;
@@ -145,4 +163,142 @@ public class ChngDetectMastDto {
private String flag;
private String message;
}
@Schema(name = "ResultDto", description = "mast list 리턴 형태")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public static class ResultDto {
private Integer code;
private String message;
private List<Basic> result;
private Boolean success;
}
@Schema(name = "RegistResDto", description = "reg 등록 후 리턴 형태")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public static class RegistResDto {
private Integer code;
private String message;
private Basic result;
private Boolean success;
}
@Schema(name = "LearnKeyDto", description = "learn 엔티티 key 정보")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public static class LearnKeyDto {
private Long id;
private String uid;
private String chnDtctMstId;
}
@Schema(name = "LabelSendDto", description = "라벨링 전송한 목록")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public static class LabelSendDto {
private String chnDtctObjtId;
private String labelerId;
private ZonedDateTime labelerWorkDttm;
private String reviewerId;
private ZonedDateTime reviewerWorkDttm;
private ZonedDateTime labelSendDttm;
}
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public static class ErrorResDto {
private String timestamp;
private Integer status;
private String error;
private String path;
}
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public static class RlbDtctDto {
private Integer code;
private String message;
private List<RlbDtctMastDto> result;
private Boolean success;
}
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public static class RlbDtctMastDto {
private String pnuDtctId;
private String pnu;
private String lrmSyncYmd;
private String pnuSyncYmd;
private String mpqdNo; // 도엽번호
private String cprsYr; // 비교년도
private String crtrYr; // 기준년도
private String chnDtctSno; // 회차
private String chnDtctId;
private String chnDtctMstId;
private String chnDtctObjtId;
private String chnDtctContId;
private String chnCd;
private String chnDtctProb;
private String bfClsCd; // 이전분류코드
private String bfClsProb; // 이전분류정확도
private String afClsCd; // 이후분류코드
private String afClsProb; // 이후분류정확도
private String pnuSqms;
private String pnuDtctSqms;
private String chnDtctSqms;
private String stbltYn;
private String incyCd;
private String incyRsnCont;
private String lockYn;
private String lblYn;
private String chgYn;
private String rsatctNo;
private String rmk;
private String crtDt; // 생성일시
private String crtEpno; // 생성사원번호
private String crtIp; // 생성사원아이피
private String chgDt;
private String chgEpno;
private String chgIp;
private String delYn; // 삭제여부
}
@Schema(name = "RemoveResDto", description = "remove 후 리턴 형태")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public static class RemoveResDto {
private Integer code;
private String message;
private Boolean result;
private Boolean success;
}
}

View File

@@ -1,21 +1,13 @@
package com.kamco.cd.kamcoback.gukyuin.dto;
import com.kamco.cd.kamcoback.common.utils.enums.EnumType;
import java.util.UUID;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
public class GukYuinDto {
@Getter
@Setter
public static class GukYuinLinkableRes {
private boolean linkable;
// private GukYuinLinkFailCode code;
private String message;
}
/** 실패 코드 enum */
@Getter
@AllArgsConstructor
@@ -39,10 +31,47 @@ public class GukYuinDto {
}
}
@Getter
@Setter
public static class GukYuinLinkableRes {
private boolean linkable;
// private GukYuinLinkFailCode code;
private String message;
}
// Repository가 반환할 Fact(조회 결과)
public record GukYuinLinkFacts(
boolean existsLearn,
boolean isPartScope,
boolean hasRunningInference,
boolean hasOtherUnfinishedGukYuin) {}
@Getter
@Setter
@AllArgsConstructor
public static class LearnInfo {
private Long id;
private UUID uuid;
private Integer compareYyyy;
private Integer targetYyyy;
private Integer stage;
private String uid;
private String applyStatus;
private Boolean applyYn;
public Boolean getApplyYn() {
return this.applyYn != null && this.applyYn;
}
}
@Getter
@Setter
@AllArgsConstructor
public static class GeomUidDto {
private Long geoUid;
private String resultUid;
}
}

View File

@@ -8,8 +8,12 @@ import lombok.Getter;
@AllArgsConstructor
public enum GukYuinStatus implements EnumType {
PENDING("대기"),
IN_PROGRESS("사용"),
COMPLETED("완료");
IN_PROGRESS("진행중"),
GUK_COMPLETED("국유인 매핑 완료"),
PNU_COMPLETED("PNU 싱크 완료"),
PNU_FAILED("PNU 싱크 중 에러"),
END("종료"),
CANCELED("취소");
private final String desc;

View File

@@ -1,99 +1,223 @@
package com.kamco.cd.kamcoback.gukyuin.service;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.kamco.cd.kamcoback.common.utils.NetUtils;
import com.kamco.cd.kamcoback.common.utils.UserUtil;
import com.kamco.cd.kamcoback.config.api.ApiLogFunction;
import com.kamco.cd.kamcoback.config.api.ApiResponseDto.ApiResponseCode;
import com.kamco.cd.kamcoback.config.api.ApiResponseDto.ResponseObj;
import com.kamco.cd.kamcoback.config.resttemplate.ExternalHttpClient;
import com.kamco.cd.kamcoback.config.resttemplate.ExternalHttpClient.ExternalCallResult;
import com.kamco.cd.kamcoback.gukyuin.dto.ChngDetectContDto;
import com.kamco.cd.kamcoback.gukyuin.dto.ChngDetectContDto.ContBasic;
import com.kamco.cd.kamcoback.gukyuin.dto.ChngDetectContDto.ReqInfo;
import com.kamco.cd.kamcoback.gukyuin.dto.ChngDetectContDto.ResultContDto;
import com.kamco.cd.kamcoback.gukyuin.dto.ChngDetectContDto.ResultPnuDto;
import com.kamco.cd.kamcoback.gukyuin.dto.ChngDetectMastDto;
import com.kamco.cd.kamcoback.gukyuin.dto.ChngDetectMastDto.ResReturn;
import com.kamco.cd.kamcoback.gukyuin.dto.ChngDetectMastDto.ChnDetectMastReqDto;
import com.kamco.cd.kamcoback.gukyuin.dto.ChngDetectMastDto.ErrorResDto;
import com.kamco.cd.kamcoback.gukyuin.dto.ChngDetectMastDto.LabelSendDto;
import com.kamco.cd.kamcoback.gukyuin.dto.ChngDetectMastDto.ResultDto;
import com.kamco.cd.kamcoback.gukyuin.dto.ChngDetectMastDto.RlbDtctDto;
import com.kamco.cd.kamcoback.gukyuin.dto.GukYuinDto.GukYuinLinkFacts;
import com.kamco.cd.kamcoback.gukyuin.dto.GukYuinDto.GukYuinLinkFailCode;
import com.kamco.cd.kamcoback.gukyuin.dto.GukYuinDto.GukYuinLinkableRes;
import com.kamco.cd.kamcoback.gukyuin.dto.GukYuinDto.LearnInfo;
import com.kamco.cd.kamcoback.log.dto.EventStatus;
import com.kamco.cd.kamcoback.log.dto.EventType;
import com.kamco.cd.kamcoback.postgres.core.GukYuinCoreService;
import java.util.ArrayList;
import com.kamco.cd.kamcoback.postgres.entity.AuditLogEntity;
import com.kamco.cd.kamcoback.postgres.repository.log.AuditLogRepository;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.LocalDate;
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.HttpMethod;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@Slf4j
@Service
@Transactional(readOnly = true)
@Transactional
@RequiredArgsConstructor
public class GukYuinApiService {
private final GukYuinCoreService gukyuinCoreService;
private final ExternalHttpClient externalHttpClient;
private final NetUtils netUtils = new NetUtils();
private final UserUtil userUtil;
private final AuditLogRepository auditLogRepository;
private final ObjectMapper objectMapper;
private final String myip = netUtils.getLocalIP();
@Value("${spring.profiles.active:local}")
private String profile;
@Value("${gukyuin.url}")
private String gukyuinUrl;
@Value("${gukyuin.mast}")
private String gukyuinMastUrl;
@Value("${gukyuin.cdi}")
private String gukyuinCdiUrl;
private final GukYuinCoreService gukyuinCoreService;
private final ExternalHttpClient externalHttpClient;
private final NetUtils netUtils = new NetUtils();
@Value("${file.dataset-dir}")
private String datasetDir;
@Transactional
public ChngDetectMastDto.Basic regist(ChngDetectMastDto.ChnDetectMastReqDto chnDetectMastReq) {
public ChngDetectMastDto.RegistResDto regist(
ChngDetectMastDto.ChnDetectMastReqDto chnDetectMastReq) {
ChngDetectMastDto.Basic basic = new ChngDetectMastDto.Basic();
String url = gukyuinCdiUrl + "/chn/mast/regist";
String url = gukyuinMastUrl + "/regist";
// url = "http://localhost:8080/api/kcd/cdi/detect/mast/regist";
String myip = netUtils.getLocalIP();
chnDetectMastReq.setReqIp(myip);
chnDetectMastReq.setReqEpno(userUtil.getEmployeeNo());
System.out.println("url == " + url);
System.out.println("url == " + myip);
ExternalCallResult<String> result =
ExternalCallResult<ChngDetectMastDto.RegistResDto> result =
externalHttpClient.call(
url, HttpMethod.POST, chnDetectMastReq, netUtils.jsonHeaders(), String.class);
url,
HttpMethod.POST,
chnDetectMastReq,
netUtils.jsonHeaders(),
ChngDetectMastDto.RegistResDto.class);
System.out.println("result == " + result);
ChngDetectMastDto.RegistResDto resultBody = result.body();
boolean success = false;
if (resultBody != null && resultBody.getSuccess() != null) {
ChngDetectMastDto.Basic registRes = resultBody.getResult();
return basic;
success = resultBody.getSuccess();
// 이미 등록한 경우에는 result가 없음
if (resultBody.getResult() == null) {
return resultBody;
}
// 추론 회차에 applyStatus, applyStatusDttm 업데이트
gukyuinCoreService.updateGukYuinMastRegResult(registRes);
// anal_inference 에도 국유인 반영여부, applyDttm 업데이트
gukyuinCoreService.updateAnalInferenceApplyDttm(registRes);
} else {
String errBody = result.errBody();
ErrorResDto error = null;
try {
error = objectMapper.readValue(errBody, ErrorResDto.class);
return new ChngDetectMastDto.RegistResDto(error.getStatus(), error.getError(), null, false);
} catch (JsonProcessingException e) {
log.error("에러 응답 파싱 실패. rawBody={}", errBody, e);
return new ChngDetectMastDto.RegistResDto(
result.statusCode(), // HTTP status
errBody, // 원문 그대로
null,
false);
}
}
this.insertGukyuinAuditLog(
EventType.ADDED.getId(),
myip,
userUtil.getId(),
url.replace(gukyuinUrl, ""),
chnDetectMastReq,
success);
return resultBody;
}
@Transactional
public ResReturn remove(ChngDetectMastDto.ChnDetectMastReqDto chnDetectMastReq) {
ChngDetectMastDto.Basic basic = new ChngDetectMastDto.Basic();
public ChngDetectMastDto.RemoveResDto remove(
ChngDetectMastDto.ChnDetectMastReqDto chnDetectMastReq) {
String url = gukyuinCdiUrl + "/chn/mast/remove";
String url = gukyuinMastUrl + "/remove";
// url = "http://localhost:8080/api/kcd/cdi/detect/mast/remove";
String myip = netUtils.getLocalIP();
chnDetectMastReq.setReqIp(myip);
chnDetectMastReq.setReqEpno(userUtil.getEmployeeNo());
System.out.println("url == " + url);
System.out.println("url == " + myip);
ExternalCallResult<String> result =
boolean success = false;
ExternalCallResult<ChngDetectMastDto.RemoveResDto> result =
externalHttpClient.call(
url, HttpMethod.POST, chnDetectMastReq, netUtils.jsonHeaders(), String.class);
url,
HttpMethod.POST,
chnDetectMastReq,
netUtils.jsonHeaders(),
ChngDetectMastDto.RemoveResDto.class);
System.out.println("result == " + result);
ChngDetectMastDto.RemoveResDto resultBody = result.body();
if (resultBody != null && resultBody.getSuccess() != null) {
return new ResReturn("success", "탐지결과 삭제 되었습니다.");
success = resultBody.getSuccess();
if (resultBody.getSuccess()) {
gukyuinCoreService.updateGukYuinMastRegRemove(chnDetectMastReq.getChnDtctId());
}
}
@Transactional
public List<ChngDetectMastDto.Basic> list(ChngDetectMastDto.ChngDetectMastSearchDto searchDto) {
List<ChngDetectMastDto.Basic> masterList = new ArrayList<>();
this.insertGukyuinAuditLog(
EventType.REMOVE.getId(),
myip,
userUtil.getId(),
url.replace(gukyuinUrl, ""),
chnDetectMastReq,
success);
return resultBody;
}
// 등록목록 1개 확인
public ChngDetectMastDto.ResultDto detail(String chnDtctMstId) {
String url =
gukyuinCdiUrl
+ "/chn/mast/list/"
+ chnDtctMstId
+ "?reqIp="
+ myip
+ "&reqEpno="
+ userUtil.getEmployeeNo();
ExternalCallResult<ChngDetectMastDto.ResultDto> result =
externalHttpClient.call(
url, HttpMethod.GET, null, netUtils.jsonHeaders(), ChngDetectMastDto.ResultDto.class);
this.insertGukyuinAuditLog(
EventType.DETAIL.getId(),
netUtils.getLocalIP(),
userUtil.getId(),
url.replace(gukyuinUrl, ""),
null,
result.body().getSuccess());
return result.body();
}
// 등록목록 비교년도,기준년도,차수 조합해서 n개 확인
public ChngDetectMastDto.ResultDto listYearStage(
ChngDetectMastDto.ChngDetectMastSearchDto searchDto) {
String queryString = netUtils.dtoToQueryString(searchDto, null);
String url = gukyuinMastUrl + queryString;
String url =
gukyuinCdiUrl
+ "/chn/mast"
+ queryString
+ "&reqIp="
+ myip
+ "&reqEpno="
+ userUtil.getEmployeeNo();
ExternalCallResult<String> result =
externalHttpClient.call(url, HttpMethod.GET, null, netUtils.jsonHeaders(), String.class);
ExternalCallResult<ChngDetectMastDto.ResultDto> result =
externalHttpClient.call(
url, HttpMethod.GET, null, netUtils.jsonHeaders(), ChngDetectMastDto.ResultDto.class);
System.out.println("list result == " + result);
return masterList;
this.insertGukyuinAuditLog(
EventType.LIST.getId(),
netUtils.getLocalIP(),
userUtil.getId(),
url.replace(gukyuinUrl, ""),
null,
result.body().getSuccess());
return result.body();
}
/**
@@ -133,4 +257,325 @@ public class GukYuinApiService {
return GukYuinLinkFailCode.OK;
}
// 탐지객체 리스트 조회
public ResultContDto findChnContList(String chnDtctId, Integer pageIndex, Integer pageSize) {
String url =
gukyuinCdiUrl
+ "/chn/cont/"
+ chnDtctId
+ "?pageIndex="
+ pageIndex
+ "&pageSize="
+ pageSize
+ "&reqIp="
+ myip
+ "&reqEpno="
+ userUtil.getEmployeeNo();
ExternalCallResult<ChngDetectContDto.ResultContDto> result =
externalHttpClient.call(
url,
HttpMethod.GET,
null,
netUtils.jsonHeaders(),
ChngDetectContDto.ResultContDto.class);
List<ContBasic> contList = result.body().getResult();
if (contList == null || contList.isEmpty()) {
return new ResultContDto(
result.body().getCode(),
result.body().getMessage(),
result.body().getResult(),
result.body().getSuccess());
}
this.insertGukyuinAuditLog(
EventType.LIST.getId(),
netUtils.getLocalIP(),
userUtil.getId(),
url.replace(gukyuinUrl, ""),
null,
result.body().getSuccess());
return result.body();
}
public ResultPnuDto findPnuObjMgmtList(String chnDtctId, String chnDtctObjtId) {
String url =
gukyuinCdiUrl
+ "/chn/pnu/"
+ chnDtctId
+ "/objt/"
+ chnDtctObjtId
+ "?reqIp="
+ myip
+ "&reqEpno="
+ userUtil.getEmployeeNo();
ExternalCallResult<ChngDetectContDto.ResultPnuDto> result =
externalHttpClient.call(
url,
HttpMethod.GET,
null,
netUtils.jsonHeaders(),
ChngDetectContDto.ResultPnuDto.class);
this.insertGukyuinAuditLog(
EventType.DETAIL.getId(),
netUtils.getLocalIP(),
userUtil.getId(),
url.replace(gukyuinUrl, ""),
null,
result.body().getSuccess());
return result.body();
}
public ChngDetectContDto.ResultLabelDto updateChnDtctObjtLabelingYn(
String chnDtctObjtId, String lblYn) {
String url = gukyuinCdiUrl + "/rlb/objt/" + chnDtctObjtId + "/lbl/" + lblYn;
ReqInfo info = new ReqInfo();
info.setReqIp(myip);
info.setReqEpno(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 ResultContDto findChnPnuToContList(String chnDtctId, String pnu) {
String url =
gukyuinCdiUrl
+ "/chn/cont/"
+ chnDtctId
+ "/pnu/"
+ pnu
+ "?reqIp="
+ myip
+ "&reqEpno="
+ userUtil.getEmployeeNo();
ExternalCallResult<ChngDetectContDto.ResultContDto> result =
externalHttpClient.call(
url,
HttpMethod.GET,
null,
netUtils.jsonHeaders(),
ChngDetectContDto.ResultContDto.class);
this.insertGukyuinAuditLog(
EventType.LIST.getId(),
netUtils.getLocalIP(),
userUtil.getId(),
url.replace(gukyuinUrl, ""),
null,
result.body().getSuccess());
return result.body();
}
public ResultDto listChnDtctId(String chnDtctId) {
String url =
gukyuinCdiUrl
+ "/chn/mast/"
+ chnDtctId
+ "?reqIp="
+ myip
+ "&reqEpno="
+ userUtil.getEmployeeNo();
ExternalCallResult<ChngDetectMastDto.ResultDto> result =
externalHttpClient.call(
url, HttpMethod.GET, null, netUtils.jsonHeaders(), ChngDetectMastDto.ResultDto.class);
this.insertGukyuinAuditLog(
EventType.DETAIL.getId(),
netUtils.getLocalIP(),
userUtil.getId(),
url.replace(gukyuinUrl, ""),
null,
result.body().getSuccess());
return result.body();
}
@Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = false)
public void insertGukyuinAuditLog(
String actionType,
String myIp,
Long userUid,
String requestUri,
Object requestBody,
boolean successFail) {
try {
AuditLogEntity log =
new AuditLogEntity(
userUid,
EventType.fromName(actionType),
successFail ? EventStatus.SUCCESS : EventStatus.FAILED,
"GUKYUIN", // 메뉴도 국유인으로 하나 따기
myIp,
requestUri,
requestBody == null ? null : ApiLogFunction.cutRequestBody(requestBody.toString()),
null,
null,
null);
auditLogRepository.save(log);
} catch (Exception e) {
log.error(e.getMessage());
throw e;
}
}
public ResponseObj connectChnMastRegist(UUID uuid) {
// uuid로 추론 회차 조회
LearnInfo info = gukyuinCoreService.findMapSheetLearnInfo(uuid);
if (info.getApplyYn() != null && info.getApplyYn()) {
return new ResponseObj(ApiResponseCode.DUPLICATE_DATA, "이미 국유인 연동을 한 회차입니다.");
}
// 비교년도,기준년도로 전송한 데이터 있는지 확인 후 회차 번호 생성
Integer maxStage =
gukyuinCoreService.findMapSheetLearnYearStage(info.getCompareYyyy(), info.getTargetYyyy());
// 1회차를 종료 상태로 처리하고 2회차를 보내야 함
// 추론(learn), 학습데이터(inference) 둘 다 종료 처리
if (maxStage > 0) {
Long learnId =
gukyuinCoreService.findMapSheetLearnInfoByYyyy(
info.getCompareYyyy(), info.getTargetYyyy(), maxStage);
gukyuinCoreService.updateMapSheetLearnGukyuinEndStatus(learnId);
gukyuinCoreService.updateMapSheetInferenceLabelEndStatus(learnId);
}
// reqDto 셋팅
ChnDetectMastReqDto reqDto = new ChnDetectMastReqDto();
reqDto.setCprsYr(String.valueOf(info.getCompareYyyy()));
reqDto.setCrtrYr(String.valueOf(info.getTargetYyyy()));
reqDto.setChnDtctSno(String.valueOf(maxStage + 1));
reqDto.setChnDtctId(info.getUid());
reqDto.setPathNm("/kamco-nfs/dataset/export/" + info.getUid());
if (!Files.isDirectory(Path.of("/kamco-nfs/dataset/export/" + info.getUid()))) {
return new ResponseObj(
ApiResponseCode.NOT_FOUND_DATA, "파일 경로에 회차 실행 파일이 생성되지 않았습니다. 확인 부탁드립니다.");
}
// 국유인 /chn/mast/regist 전송
ChngDetectMastDto.RegistResDto result = this.regist(reqDto);
if (result.getSuccess()) {
return new ResponseObj(ApiResponseCode.OK, "연동되었습니다.");
} else {
return new ResponseObj(ApiResponseCode.INTERNAL_SERVER_ERROR, result.getMessage());
}
}
public List<LabelSendDto> findLabelingCompleteSendList(LocalDate yesterday) {
return gukyuinCoreService.findLabelingCompleteSendList(yesterday);
}
public ResultContDto findChnPnuToContObject(
String chnDtctId, String chnDtctObjtId, Integer pageIndex, Integer pageSize) {
String url =
gukyuinCdiUrl
+ "/chn/cont/"
+ chnDtctId
+ "/chnDtctObjtId/"
+ chnDtctObjtId
+ "?pageIndex="
+ pageIndex
+ "&pageSize="
+ pageSize
+ "&reqIp="
+ myip
+ "&reqEpno="
+ userUtil.getEmployeeNo();
ExternalCallResult<ChngDetectContDto.ResultContDto> result =
externalHttpClient.call(
url,
HttpMethod.GET,
null,
netUtils.jsonHeaders(),
ChngDetectContDto.ResultContDto.class);
this.insertGukyuinAuditLog(
EventType.DETAIL.getId(),
netUtils.getLocalIP(),
userUtil.getId(),
url.replace(gukyuinUrl, ""),
null,
result.body() != null && result.body().getSuccess());
return result.body();
}
public ChngDetectMastDto.RlbDtctDto findRlbDtctList(String chnDtctId, String yyyymmdd) {
String url =
gukyuinCdiUrl
+ "/rlb/dtct/"
+ chnDtctId
+ "?reqIp="
+ myip
+ "&reqEpno="
+ userUtil.getEmployeeNo()
+ "&yyyymmdd="
+ yyyymmdd;
ExternalCallResult<ChngDetectMastDto.RlbDtctDto> result =
externalHttpClient.call(
url, HttpMethod.GET, null, netUtils.jsonHeaders(), ChngDetectMastDto.RlbDtctDto.class);
this.insertGukyuinAuditLog(
EventType.LIST.getId(),
netUtils.getLocalIP(),
userUtil.getId(),
url.replace(gukyuinUrl, ""),
null,
result.body() != null && result.body().getSuccess());
return result.body();
}
public RlbDtctDto findRlbDtctObject(String chnDtctObjtId) {
String url =
gukyuinCdiUrl
+ "/rlb/objt/"
+ chnDtctObjtId
+ "?reqIp="
+ myip
+ "&reqEpno="
+ userUtil.getEmployeeNo();
ExternalCallResult<ChngDetectMastDto.RlbDtctDto> result =
externalHttpClient.call(
url, HttpMethod.GET, null, netUtils.jsonHeaders(), ChngDetectMastDto.RlbDtctDto.class);
this.insertGukyuinAuditLog(
EventType.DETAIL.getId(),
netUtils.getLocalIP(),
userUtil.getId(),
url.replace(gukyuinUrl, ""),
null,
result.body() != null && result.body().getSuccess());
return result.body();
}
}

View File

@@ -1,5 +1,7 @@
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;
@@ -10,20 +12,28 @@ 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(
@@ -52,4 +62,29 @@ public class InferenceResultShpApiController {
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);
}
}

View File

@@ -534,6 +534,10 @@ public class InferenceDetailDto {
throw new RuntimeException(e);
}
}
public Boolean getApplyYn() {
return this.applyYn != null && this.applyYn;
}
}
@Getter

View File

@@ -309,6 +309,15 @@ public class InferenceResultDto {
@Schema(description = "모델3 분석 대기")
private Integer m3PendingJobs;
@Schema(description = "모델1 분석 진행중")
private Integer m1RunningJobs;
@Schema(description = "모델2 분석 진행중")
private Integer m2RunningJobs;
@Schema(description = "모델3 분석 진행중")
private Integer m3RunningJobs;
@Schema(description = "모델1 분석 완료")
private Integer m1CompletedJobs;
@@ -386,11 +395,18 @@ public class InferenceResultDto {
@Schema(description = "모델3 버전")
private String modelVer3;
@Schema(description = "탑지 도엽 수")
@JsonIgnore
private Long totalJobs;
public InferenceStatusDetailDto(
Long detectingCnt,
Integer m1PendingJobs,
Integer m2PendingJobs,
Integer m3PendingJobs,
Integer m1RunningJobs,
Integer m2RunningJobs,
Integer m3RunningJobs,
Integer m1CompletedJobs,
Integer m2CompletedJobs,
Integer m3CompletedJobs,
@@ -413,11 +429,15 @@ public class InferenceResultDto {
String mapSheetScope,
String modelVer1,
String modelVer2,
String modelVer3) {
String modelVer3,
Long totalJobs) {
this.detectingCnt = detectingCnt;
this.m1PendingJobs = m1PendingJobs;
this.m2PendingJobs = m2PendingJobs;
this.m3PendingJobs = m3PendingJobs;
this.m1RunningJobs = m1RunningJobs;
this.m2RunningJobs = m2RunningJobs;
this.m3RunningJobs = m3RunningJobs;
this.m1CompletedJobs = m1CompletedJobs;
this.m2CompletedJobs = m2CompletedJobs;
this.m3CompletedJobs = m3CompletedJobs;
@@ -441,12 +461,13 @@ public class InferenceResultDto {
this.modelVer1 = modelVer1;
this.modelVer2 = modelVer2;
this.modelVer3 = modelVer3;
this.totalJobs = totalJobs;
}
@Schema(description = "진행률")
@JsonProperty("progress")
private int getProgress() {
long tiles = this.detectingCnt; // 도엽수
long tiles = this.totalJobs == null ? 0L : this.totalJobs; // 도엽수
int models = 3; // 모델 개수
int completed =
this.m1CompletedJobs

View File

@@ -1,15 +1,20 @@
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.InferenceResultShpDto;
import com.kamco.cd.kamcoback.postgres.core.InferenceResultCoreService;
import com.kamco.cd.kamcoback.postgres.core.InferenceResultShpCoreService;
import com.kamco.cd.kamcoback.postgres.core.MapSheetMngCoreService;
import com.kamco.cd.kamcoback.scheduler.service.ShpPipelineService;
import java.util.List;
import java.util.Objects;
import java.util.UUID;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import lombok.RequiredArgsConstructor;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -19,9 +24,11 @@ import org.springframework.transaction.annotation.Transactional;
@Transactional(readOnly = true)
public class InferenceResultShpService {
private static final Logger log = LogManager.getLogger(InferenceResultShpService.class);
private final InferenceResultShpCoreService coreService;
private final InferenceResultCoreService inferenceResultCoreService;
private final ShpPipelineService shpPipelineService;
private final MapSheetMngCoreService mapSheetMngCoreService;
@Value("${mapsheet.shp.baseurl}")
private String baseDir;
@@ -59,4 +66,21 @@ public class InferenceResultShpService {
// shp 파일 비동기 생성
shpPipelineService.runPipeline(jarPath, datasetDir, batchId, 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;
}
}

View File

@@ -0,0 +1,6 @@
package com.kamco.cd.kamcoback.inference.service;
import org.springframework.stereotype.Service;
@Service
public class InferenceStatusService {}

View File

@@ -205,6 +205,9 @@ public class LabelWorkDto {
@Schema(description = "1일전처리개수")
private Long day1AgoDoneCnt;
@Schema(description = "계정 상태")
private String memberStatus;
public Long getRemainCnt() {
return this.assignedCnt - this.doneCnt;
}

View File

@@ -0,0 +1,276 @@
package com.kamco.cd.kamcoback.layer;
import com.kamco.cd.kamcoback.config.api.ApiResponseDto;
import com.kamco.cd.kamcoback.layer.dto.LayerDto;
import com.kamco.cd.kamcoback.layer.dto.LayerDto.IsMapYn;
import com.kamco.cd.kamcoback.layer.dto.LayerDto.LayerMapDto;
import com.kamco.cd.kamcoback.layer.dto.LayerDto.OrderReq;
import com.kamco.cd.kamcoback.layer.dto.LayerDto.SearchReq;
import com.kamco.cd.kamcoback.layer.service.LayerService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.ArraySchema;
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.UUID;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.DeleteMapping;
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.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@Tag(name = "레이어 관리", description = "레이어 관리 API")
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/layer")
public class LayerApiController {
private final LayerService layerService;
@Operation(summary = "지도 레이어 관리 목록", description = "지도 레이어 관리 목록 api")
@ApiResponses(
value = {
@ApiResponse(
responseCode = "200",
description = "검색 성공",
content =
@Content(
mediaType = "application/json",
schema = @Schema(implementation = LayerDto.Basic.class))),
@ApiResponse(responseCode = "400", description = "잘못된 검색 조건", content = @Content),
@ApiResponse(responseCode = "500", description = "서버 오류", content = @Content)
})
@GetMapping("/list")
public ApiResponseDto<List<LayerDto.Basic>> getLayers(
@RequestParam(required = false) String tag) {
LayerDto.SearchReq searchReq = new SearchReq();
searchReq.setTag(tag);
List<LayerDto.Basic> layers = layerService.getLayers(searchReq);
return ApiResponseDto.ok(layers);
}
/**
* 레이어 등록
*
* @param layerType
* @param dto
* @return
*/
@Operation(summary = "레이어 등록", description = "레이어 등록 api")
@ApiResponses(
value = {
@ApiResponse(
responseCode = "201",
description = "등록 성공",
content =
@Content(
mediaType = "application/json",
schema = @Schema(implementation = UUID.class))),
@ApiResponse(responseCode = "500", description = "서버 오류", content = @Content)
})
@PostMapping("/save/{layerType}")
public ApiResponseDto<UUID> save(
@Schema(description = "TILE,GEOJSON,WMS,WMTS", example = "GEOJSON") @PathVariable
String layerType,
@RequestBody LayerDto.AddReq dto) {
return ApiResponseDto.ok(layerService.saveLayers(layerType, dto));
}
@Operation(summary = "순서 변경", description = "순서 변경 api")
@ApiResponses(
value = {
@ApiResponse(
responseCode = "201",
description = "등록 성공",
content =
@Content(
mediaType = "application/json",
schema = @Schema(implementation = Void.class))),
@ApiResponse(responseCode = "500", description = "서버 오류", content = @Content)
})
@PutMapping("/order")
public ApiResponseDto<Void> updateOrder(@RequestBody List<OrderReq> dto) {
layerService.orderUpdate(dto);
return ApiResponseDto.ok(null);
}
@Operation(summary = "상세 조회", description = "상세 조회 api")
@ApiResponses(
value = {
@ApiResponse(
responseCode = "200",
description = "검색 성공",
content =
@Content(
mediaType = "application/json",
schema = @Schema(implementation = LayerDto.Detail.class))),
@ApiResponse(responseCode = "400", description = "잘못된 검색 조건", content = @Content),
@ApiResponse(responseCode = "500", description = "서버 오류", content = @Content)
})
@GetMapping("/detail/{uuid}")
public ApiResponseDto<LayerDto.Detail> getDetail(@PathVariable UUID uuid) {
return ApiResponseDto.ok(layerService.getDetail(uuid));
}
/**
* 레이어 삭제
*
* @param uuid
* @return
*/
@Operation(summary = "레이어 삭제", description = "레이어 삭제 api")
@ApiResponses(
value = {
@ApiResponse(
responseCode = "201",
description = "삭제 성공",
content =
@Content(
mediaType = "application/json",
schema = @Schema(implementation = Void.class))),
@ApiResponse(responseCode = "500", description = "서버 오류", content = @Content)
})
@DeleteMapping("/delete/{uuid}")
public ApiResponseDto<Void> delete(@PathVariable UUID uuid) {
layerService.delete(uuid);
return ApiResponseDto.ok(null);
}
/**
* 레이어 수정
*
* @param uuid
* @return
*/
@Operation(summary = "레이어 수정", description = "레이어 수정 api")
@ApiResponses(
value = {
@ApiResponse(
responseCode = "201",
description = "수정 성공",
content =
@Content(
mediaType = "application/json",
schema = @Schema(implementation = Void.class))),
@ApiResponse(responseCode = "500", description = "서버 오류", content = @Content)
})
@PutMapping("/update/{uuid}")
public ApiResponseDto<Void> update(@PathVariable UUID uuid, @RequestBody LayerDto.Detail dto) {
layerService.update(uuid, dto);
return ApiResponseDto.ok(null);
}
@Operation(summary = "맵 노출여부 수정", description = "맵 노출여부 수정 api")
@ApiResponses(
value = {
@ApiResponse(
responseCode = "201",
description = "수정 성공",
content =
@Content(
mediaType = "application/json",
schema = @Schema(implementation = Void.class))),
@ApiResponse(responseCode = "500", description = "서버 오류", content = @Content)
})
@PutMapping("/update-map/{uuid}")
public ApiResponseDto<Void> updateIsMap(@PathVariable UUID uuid, @RequestBody IsMapYn isMapYn) {
layerService.updateIsMap(uuid, isMapYn);
return ApiResponseDto.ok(null);
}
@Operation(summary = "wmts tile 조회", description = "wmts tile 조회 api")
@ApiResponses(
value = {
@ApiResponse(
responseCode = "200",
description = "검색 성공",
content =
@Content(
mediaType = "application/json",
array = @ArraySchema(schema = @Schema(implementation = String.class)))),
@ApiResponse(responseCode = "400", description = "잘못된 검색 조건", content = @Content),
@ApiResponse(responseCode = "500", description = "서버 오류", content = @Content)
})
@GetMapping("/wmts/tile")
public ApiResponseDto<List<String>> getWmtsTile() {
return ApiResponseDto.ok(layerService.getWmtsTile());
}
@Operation(summary = "wms tile 조회", description = "wms tile 조회 api")
@ApiResponses(
value = {
@ApiResponse(
responseCode = "200",
description = "검색 성공",
content =
@Content(
mediaType = "application/json",
array = @ArraySchema(schema = @Schema(implementation = String.class)))),
@ApiResponse(responseCode = "400", description = "잘못된 검색 조건", content = @Content),
@ApiResponse(responseCode = "500", description = "서버 오류", content = @Content)
})
@GetMapping("/wms/tile")
public ApiResponseDto<List<String>> getWmsTile() {
return ApiResponseDto.ok(layerService.getWmsTitle());
}
@Operation(summary = "변화지도 레이어 조회", description = "변화지도 레이어 조회")
@ApiResponses(
value = {
@ApiResponse(
responseCode = "200",
description = "검색 성공",
content =
@Content(
mediaType = "application/json",
array = @ArraySchema(schema = @Schema(implementation = String.class)))),
@ApiResponse(responseCode = "400", description = "잘못된 검색 조건", content = @Content),
@ApiResponse(responseCode = "500", description = "서버 오류", content = @Content)
})
@GetMapping("/map/change-detection")
public ApiResponseDto<List<LayerMapDto>> changeDetectionMap() {
return ApiResponseDto.ok(layerService.findLayerMapList("change-detection"));
}
@Operation(summary = "라벨링 툴 레이어 조회", description = "라벨링 툴 레이어 조회")
@ApiResponses(
value = {
@ApiResponse(
responseCode = "200",
description = "검색 성공",
content =
@Content(
mediaType = "application/json",
array = @ArraySchema(schema = @Schema(implementation = String.class)))),
@ApiResponse(responseCode = "400", description = "잘못된 검색 조건", content = @Content),
@ApiResponse(responseCode = "500", description = "서버 오류", content = @Content)
})
@GetMapping("/map/labeling")
public ApiResponseDto<List<LayerMapDto>> labelingMap() {
return ApiResponseDto.ok(layerService.findLayerMapList("labeling"));
}
@Operation(summary = "년도별 tile Url(before,after 모두 조회)", description = "년도별 tile Url")
@GetMapping("/tile-url")
public ApiResponseDto<LayerDto.YearTileDto> getChangeDetectionTileUrl(
@Parameter(description = "이전 년도", example = "2023") @RequestParam Integer beforeYear,
@Parameter(description = "이후 년도", example = "2024") @RequestParam Integer afterYear) {
return ApiResponseDto.ok(layerService.getChangeDetectionTileUrl(beforeYear, afterYear));
}
@Operation(summary = "년도별 tile Url(년도 1개만 조회)", description = "년도별 tile Url")
@GetMapping("/tile-url-year")
public ApiResponseDto<LayerDto.TileUrlDto> getChangeDetectionTileOneYearUrl(
@Parameter(description = "년도", example = "2023") @RequestParam Integer year) {
return ApiResponseDto.ok(layerService.getChangeDetectionTileOneYearUrl(year));
}
}

View File

@@ -0,0 +1,18 @@
package com.kamco.cd.kamcoback.layer.dto;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
public class GeoJsonDto {
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public static class GeoJsonAddReqDto {
private String url;
private String description;
private String tag;
}
}

View File

@@ -0,0 +1,432 @@
package com.kamco.cd.kamcoback.layer.dto;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.kamco.cd.kamcoback.common.utils.interfaces.JsonFormatDttm;
import io.swagger.v3.oas.annotations.media.Schema;
import java.math.BigDecimal;
import java.time.ZonedDateTime;
import java.util.UUID;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
public class LayerDto {
public enum MapType {
CHANGE_MAP,
LABELING_MAP
}
@Getter
@Setter
@AllArgsConstructor
@Schema(name = "LayerBasic")
public static class Basic {
@Schema(description = "uuid")
private UUID uuid;
@Schema(example = "WMTS", description = "유형 (TILE/GEOJSON/WMTS/WMS)")
private String layerType;
@Schema(description = "설명")
private String description;
@Schema(description = "태그")
private String tag;
@Schema(description = "순서")
private Long order;
@Schema(description = "변화지도 여부")
private Boolean isChangeMap;
@Schema(description = "라벨링지도 여부")
private Boolean isLabelingMap;
@JsonFormatDttm
@Schema(description = "등록일시")
private ZonedDateTime createdDttm;
}
@Getter
@Setter
@AllArgsConstructor
@Schema(name = "LayerDetail")
public static class Detail {
@Schema(description = "uuid")
private UUID uuid;
@Schema(description = "유형 (TILE/GEOJSON/WMTS/WMS)")
private String layerType;
@Schema(description = "title")
private String title;
@Schema(description = "설명")
private String description;
@Schema(description = "태그")
private String tag;
@Schema(description = "순서")
private Long order;
@Schema(description = "변화지도 여부")
private Boolean isChangeMap;
@Schema(description = "라벨링지도 여부")
private Boolean isLabelingMap;
@Schema(description = "url")
private String url;
@Schema(description = "좌측상단 경도", example = "126.0")
private BigDecimal minLon;
@Schema(description = "좌측상단 위도", example = "34.0")
private BigDecimal minLat;
@Schema(description = "우측하단 경도", example = "130.0")
private BigDecimal maxLon;
@Schema(description = "우측하단 위도", example = "38.5")
private BigDecimal maxLat;
@Schema(description = "zoom min", example = "5")
private Short min;
@Schema(description = "zoom max", example = "18")
private Short max;
@JsonFormatDttm
@Schema(description = "등록일시")
private ZonedDateTime createdDttm;
@Schema(description = "좌표계")
private String crs;
}
@Getter
@Setter
@AllArgsConstructor
@Schema(name = "LayerAddReq")
public static class AddReq {
@Schema(description = "title WMS, WMTS 선택한 tile")
private String title;
@Schema(description = "설명")
private String description;
@Schema(description = "태그")
private String tag;
@Schema(description = "url")
private String url;
@Schema(description = "좌측상단 경도", example = "126.0")
private BigDecimal minLon;
@Schema(description = "좌측상단 위도", example = "34.0")
private BigDecimal minLat;
@Schema(description = "우측하단 경도", example = "130.0")
private BigDecimal maxLon;
@Schema(description = "우측하단 위도", example = "38.5")
private BigDecimal maxLat;
@Schema(description = "zoom min", example = "5")
private Short min;
@Schema(description = "zoom max", example = "18")
private Short max;
@Schema(description = "좌표계", example = "EPSG_3857")
private String crs;
}
@Getter
@Setter
@AllArgsConstructor
@Schema(name = "LayerOrderReq")
public static class OrderReq {
@Schema(description = "uuid")
private UUID uuid;
@Schema(description = "레이어 순서")
private Long order;
}
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public static class SearchReq {
private String tag;
private String layerType;
}
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public static class TileAddReqDto {
@Schema(description = "설명", example = "배경지도 입니다.")
private String description;
@Schema(description = "url", example = "http://test.gs.dabeeo.com/tile/{z}/{x}/{y}.png")
private String url;
@Schema(description = "태그", example = "변화지도")
private String tag;
@Schema(description = "좌측상단 경도", example = "126.0")
private BigDecimal minLon;
@Schema(description = "좌측상단 위도", example = "34.0")
private BigDecimal minLat;
@Schema(description = "우측하단 경도", example = "130.0")
private BigDecimal maxLon;
@Schema(description = "우측하단 위도", example = "38.5")
private BigDecimal maxLat;
@Schema(description = "zoom min", example = "5")
private Short min;
@Schema(description = "zoom max", example = "18")
private Short max;
}
@Getter
@Setter
@Schema(name = "LayerMapDto")
public static class LayerMapDto {
@Schema(example = "WMTS", description = "유형 (TILE/GEOJSON/WMTS/WMS)")
private String layerType;
@Schema(description = "title")
private String title;
@Schema(description = "설명")
private String description;
@Schema(description = "태그")
private String tag;
@Schema(description = "순서")
private Long sortOrder;
@Schema(description = "url")
private String url;
@Schema(description = "좌측상단 경도", example = "126.0")
private BigDecimal minLon;
@Schema(description = "좌측상단 위도", example = "34.0")
private BigDecimal minLat;
@Schema(description = "우측하단 경도", example = "130.0")
private BigDecimal maxLon;
@Schema(description = "우측하단 위도", example = "38.5")
private BigDecimal maxLat;
@Schema(description = "zoom min", example = "5")
private Short minZoom;
@Schema(description = "zoom max", example = "18")
private Short maxZoom;
@Schema(description = "bbox")
private JsonNode bbox;
@JsonIgnore private String bboxGeometry;
@Schema(description = "uuid")
private UUID uuid;
@JsonIgnore private String rawJsonString;
@Schema(description = "rawJson")
private JsonNode rawJson;
@Schema(description = "crs")
private String crs;
public LayerMapDto(
String layerType,
String tag,
Long sortOrder,
String url,
BigDecimal minLon,
BigDecimal minLat,
BigDecimal maxLon,
BigDecimal maxLat,
Short minZoom,
Short maxZoom,
String bboxGeometry,
UUID uuid,
String rawJsonString,
String crs) {
this.layerType = layerType;
this.tag = tag;
this.sortOrder = sortOrder;
this.url = url;
this.minLon = minLon;
this.minLat = minLat;
this.maxLon = maxLon;
this.maxLat = maxLat;
this.minZoom = minZoom;
this.maxZoom = maxZoom;
this.bboxGeometry = bboxGeometry;
this.uuid = uuid;
this.rawJsonString = rawJsonString;
JsonNode geoJson = null;
JsonNode rawJson = null;
ObjectMapper mapper = new ObjectMapper();
if (bboxGeometry != null) {
try {
geoJson = mapper.readTree(bboxGeometry);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
if (rawJsonString != null) {
try {
rawJson = mapper.readTree(rawJsonString);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
this.rawJson = rawJson;
this.bbox = geoJson;
this.crs = crs;
}
@JsonProperty("workspace")
public String getWorkSpace() {
return "cd";
}
}
@Schema(name = "TileUrlDto", description = "Tile Url 정보")
@Getter
@Setter
@NoArgsConstructor
public static class TileUrlDto {
@Schema(description = "mngYyyy")
private Integer mngYyyy;
@Schema(description = "url")
private String url;
@Schema(description = "태그")
private String tag;
@Schema(description = "좌측상단 경도", example = "126.0")
private BigDecimal minLon;
@Schema(description = "좌측상단 위도", example = "34.0")
private BigDecimal minLat;
@Schema(description = "우측하단 경도", example = "130.0")
private BigDecimal maxLon;
@Schema(description = "우측하단 위도", example = "38.5")
private BigDecimal maxLat;
@Schema(description = "zoom min", example = "5")
private Short minZoom;
@Schema(description = "zoom max", example = "18")
private Short maxZoom;
@Schema(description = "bbox")
private JsonNode bbox;
@JsonIgnore private String bboxGeometry;
private String crs;
public TileUrlDto(
Integer mngYyyy,
String url,
String tag,
BigDecimal minLon,
BigDecimal minLat,
BigDecimal maxLon,
BigDecimal maxLat,
Short minZoom,
Short maxZoom,
String bboxGeometry,
String crs) {
this.mngYyyy = mngYyyy;
this.url = url;
this.tag = tag;
this.minLon = minLon;
this.minLat = minLat;
this.maxLon = maxLon;
this.maxLat = maxLat;
this.minZoom = minZoom;
this.maxZoom = maxZoom;
this.bboxGeometry = bboxGeometry;
JsonNode geoJson = null;
if (bboxGeometry != null) {
ObjectMapper mapper = new ObjectMapper();
try {
geoJson = mapper.readTree(bboxGeometry);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
this.bbox = geoJson;
this.crs = crs;
}
}
@Schema(name = "TileUrlDto", description = "Tile Url 정보")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public static class YearTileDto {
private TileUrlDto before;
private TileUrlDto after;
}
@Schema(name = "맵 노출 여부", description = "맵 노출 여부")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public static class IsMapYn {
@Schema(description = "CHANGE_MAP(변화지도), LABELING_MAP(라벨링지도)", example = "CHANGE_MAP")
private String mapType;
@Schema(description = "노출여부 true, false", example = "true")
private Boolean isMapYn;
}
}

View File

@@ -0,0 +1,30 @@
package com.kamco.cd.kamcoback.layer.dto;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
public class WmsDto {
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public static class WmsAddReqDto {
private String title;
private String description;
private String tag;
}
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public static class WmsAddDto {
private WmsLayerInfo wmsLayerInfo;
private String title;
private String description;
private String tag;
}
}

View File

@@ -0,0 +1,47 @@
package com.kamco.cd.kamcoback.layer.dto;
import java.util.ArrayList;
import java.util.List;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/** WMS 레이어 정보를 담는 DTO 클래스 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class WmsLayerInfo {
private String name;
private String title;
private String abstractText;
private List<String> keywords = new ArrayList<>();
private BoundingBox boundingBox;
/** 지원하는 좌표계 목록 */
private List<String> crs = new ArrayList<>();
/* ===== convenience methods ===== */
public void addKeyword(String keyword) {
this.keywords.add(keyword);
}
public void addCrs(String crsValue) {
this.crs.add(crsValue);
}
/** BoundingBox 정보를 담는 내부 클래스 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public static class BoundingBox {
private String crs;
private double minX;
private double minY;
private double maxX;
private double maxY;
}
}

View File

@@ -0,0 +1,30 @@
package com.kamco.cd.kamcoback.layer.dto;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
public class WmtsDto {
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public static class WmtsAddReqDto {
private String title;
private String description;
private String tag;
}
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public static class WmtsAddDto {
private WmtsLayerInfo wmtsLayerInfo;
private String title;
private String description;
private String tag;
}
}

View File

@@ -0,0 +1,118 @@
package com.kamco.cd.kamcoback.layer.dto;
import java.util.ArrayList;
import java.util.List;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/** WMTS 레이어 정보를 담는 DTO 클래스 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class WmtsLayerInfo {
private String identifier;
private String title;
private String abstractText;
private List<String> keywords = new ArrayList<>();
private BoundingBox boundingBox;
private List<String> formats = new ArrayList<>();
private List<TileMatrixSetLink> tileMatrixSetLinks = new ArrayList<>();
private List<ResourceUrl> resourceUrls = new ArrayList<>();
private List<Style> styles = new ArrayList<>();
private List<String> matrixIds = new ArrayList<>(); // 20250130
private String workspace; // 20250130
private List<TileMatric> tileMatrices = new ArrayList<>();
// (선택) 기존 add 메서드 유지하고 싶으면 남겨도 됨
public void addTileMatric(TileMatric tileMatric) {
this.tileMatrices.add(tileMatric);
}
public void addMatrixId(String matrixId) {
this.matrixIds.add(matrixId);
}
public void addKeyword(String keyword) {
this.keywords.add(keyword);
}
public void addFormat(String format) {
this.formats.add(format);
}
public void addTileMatrixSetLink(TileMatrixSetLink tileMatrixSetLink) {
this.tileMatrixSetLinks.add(tileMatrixSetLink);
}
public void addResourceUrl(ResourceUrl resourceUrl) {
this.resourceUrls.add(resourceUrl);
}
public void addStyle(Style style) {
this.styles.add(style);
}
/** BoundingBox 정보를 담는 내부 클래스 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public static class BoundingBox {
private String crs;
private double lowerCornerX;
private double lowerCornerY;
private double upperCornerX;
private double upperCornerY;
}
/** ResourceURL 정보를 담는 내부 클래스 (타일 URL 템플릿) */
@Data
@NoArgsConstructor
@AllArgsConstructor
public static class ResourceUrl {
private String format;
private String resourceType;
private String template;
}
/** Style 정보를 담는 내부 클래스 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public static class Style {
private String identifier;
private String title;
private boolean isDefault;
}
/** TileMatrix 정보를 담는 내부 클래스 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public static class TileMatric {
private String identifier;
private String scaleDenominator;
private String topLeftCorner;
// private String tileWidth;
// private String tileHeight;
// private String matrixWidth;
// private String matrixHeight;
}
/** TileMatrixSetLink 정보를 담는 내부 클래스 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public static class TileMatrixSetLink {
private String tileMatrixSet;
private List<String> zoomLevels = new ArrayList<>();
public void addZoomLevel(String zoomLevel) {
this.zoomLevels.add(zoomLevel);
}
}
}

View File

@@ -0,0 +1,3 @@
package com.kamco.cd.kamcoback.layer.service;
public class GeojsonService {}

View File

@@ -0,0 +1,221 @@
package com.kamco.cd.kamcoback.layer.service;
import com.kamco.cd.kamcoback.common.enums.LayerType;
import com.kamco.cd.kamcoback.common.exception.CustomApiException;
import com.kamco.cd.kamcoback.layer.dto.LayerDto;
import com.kamco.cd.kamcoback.layer.dto.LayerDto.Basic;
import com.kamco.cd.kamcoback.layer.dto.LayerDto.IsMapYn;
import com.kamco.cd.kamcoback.layer.dto.LayerDto.LayerMapDto;
import com.kamco.cd.kamcoback.layer.dto.LayerDto.OrderReq;
import com.kamco.cd.kamcoback.layer.dto.LayerDto.TileUrlDto;
import com.kamco.cd.kamcoback.layer.dto.WmsDto.WmsAddDto;
import com.kamco.cd.kamcoback.layer.dto.WmsDto.WmsAddReqDto;
import com.kamco.cd.kamcoback.layer.dto.WmsLayerInfo;
import com.kamco.cd.kamcoback.layer.dto.WmtsDto.WmtsAddDto;
import com.kamco.cd.kamcoback.layer.dto.WmtsLayerInfo;
import com.kamco.cd.kamcoback.postgres.core.MapLayerCoreService;
import java.util.List;
import java.util.UUID;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@RequiredArgsConstructor
@Service
@Transactional(readOnly = true)
public class LayerService {
private final MapLayerCoreService mapLayerCoreService;
private final WmtsService wmtsService;
private final WmsService wmsService;
@Value("${layer.geoserver-url}")
private String geoserverUrl;
@Value("${layer.wms-path}")
private String wmsPath;
@Value("${layer.wmts-path}")
private String wmtsPath;
/**
* 지도 레이어 관리 목록
*
* @return
*/
public List<Basic> getLayers(LayerDto.SearchReq searchReq) {
return mapLayerCoreService.getLayers(searchReq);
}
/**
* 레이어 저장
*
* @param type
* @param dto
* @return
*/
@Transactional
public UUID saveLayers(String type, LayerDto.AddReq dto) {
LayerType layerType =
LayerType.from(type)
.orElseThrow(() -> new CustomApiException("BAD_REQUEST", HttpStatus.BAD_REQUEST));
switch (layerType) {
case TILE -> {
return mapLayerCoreService.saveTile(dto);
}
case GEOJSON -> {
return mapLayerCoreService.saveGeoJson(dto);
}
case WMTS -> {
WmtsLayerInfo info = wmtsService.getDetail(dto.getTitle());
WmtsAddDto addDto = new WmtsAddDto();
addDto.setWmtsLayerInfo(info);
addDto.setDescription(dto.getDescription());
addDto.setTitle(dto.getTitle());
addDto.setTag(dto.getTag());
return mapLayerCoreService.saveWmts(addDto);
}
case WMS -> {
WmsLayerInfo info = wmsService.getDetail(dto.getTitle());
WmsAddDto addDto = new WmsAddDto();
addDto.setWmsLayerInfo(info);
addDto.setDescription(dto.getDescription());
addDto.setTitle(dto.getTitle());
addDto.setTag(dto.getTag());
return mapLayerCoreService.saveWms(addDto);
}
default -> throw new CustomApiException("BAD_REQUEST", HttpStatus.BAD_REQUEST);
}
}
/**
* 순서 수정
*
* @param dtoList
*/
@Transactional
public void orderUpdate(List<OrderReq> dtoList) {
mapLayerCoreService.orderUpdate(dtoList);
}
/**
* 지도 레이어 관리 상세
*
* @param uuid
* @return
*/
public LayerDto.Detail getDetail(UUID uuid) {
return mapLayerCoreService.getLayers(uuid);
}
/**
* 삭제
*
* @param uuid
*/
@Transactional
public void delete(UUID uuid) {
mapLayerCoreService.delete(uuid);
}
/**
* 수정
*
* @param uuid
*/
@Transactional
public void update(UUID uuid, LayerDto.Detail dto) {
mapLayerCoreService.update(uuid, dto);
}
/**
* 맵 노출 여부 수정
*
* @param uuid
* @param isMapYn
*/
@Transactional
public void updateIsMap(UUID uuid, IsMapYn isMapYn) {
mapLayerCoreService.updateIsMap(uuid, isMapYn);
}
/**
* wmts tile 조회
*
* @return List<String>
*/
public List<String> getWmtsTile() {
return wmtsService.getTile();
}
/**
* wms title 조회
*
* @return
*/
public List<String> getWmsTitle() {
return wmsService.getTile();
}
/**
* wms 저장
*
* @param dto
* @return
*/
@Transactional
public UUID saveWms(WmsAddReqDto dto) {
// 선택한 tile 상세정보 조회
WmsLayerInfo info = wmsService.getDetail(dto.getTitle());
WmsAddDto addDto = new WmsAddDto();
addDto.setWmsLayerInfo(info);
addDto.setDescription(dto.getDescription());
addDto.setTitle(dto.getTitle());
addDto.setTag(dto.getTag());
return mapLayerCoreService.saveWms(addDto);
}
public List<LayerMapDto> findLayerMapList(String type) {
List<LayerMapDto> layerMapDtoList = mapLayerCoreService.findLayerMapList(type);
layerMapDtoList.forEach(
dto -> {
if (dto.getLayerType().equals("WMS")) {
dto.setUrl(
String.format(
"%s/%s/%s",
trimSlash(geoserverUrl), trimSlash(wmsPath), dto.getLayerType().toLowerCase()));
} else if (dto.getLayerType().equals("WMTS")) {
dto.setUrl(
String.format(
"%s/%s/%s",
trimSlash(geoserverUrl),
trimSlash(wmtsPath),
dto.getLayerType().toLowerCase()));
}
});
return layerMapDtoList;
}
private String trimSlash(String s) {
if (s == null) {
return "";
}
return s.replaceAll("/+$", "").replaceAll("^/+", "");
}
public LayerDto.YearTileDto getChangeDetectionTileUrl(Integer beforeYear, Integer afterYear) {
return mapLayerCoreService.getChangeDetectionTileUrl(beforeYear, afterYear);
}
public TileUrlDto getChangeDetectionTileOneYearUrl(Integer year) {
return mapLayerCoreService.getChangeDetectionTileOneYearUrl(year);
}
}

View File

@@ -0,0 +1,254 @@
package com.kamco.cd.kamcoback.layer.service;
import com.kamco.cd.kamcoback.common.exception.CustomApiException;
import com.kamco.cd.kamcoback.layer.dto.WmsLayerInfo;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
@Service
@Transactional(rollbackFor = Exception.class)
@RequiredArgsConstructor
public class WmsService {
@Value("${layer.geoserver-url}")
private String geoserverUrl;
@Value("${layer.workspace}")
private String workspace;
public List<String> getTile() {
List<WmsLayerInfo> layers;
try {
layers = getAllLayers(geoserverUrl, workspace);
} catch (IOException | ParserConfigurationException | SAXException e) {
throw new CustomApiException("INTERNAL_SERVER_ERROR", HttpStatus.INTERNAL_SERVER_ERROR);
}
List<String> titles = new ArrayList<>();
for (WmsLayerInfo layer : layers) {
if (StringUtils.hasText(layer.getTitle())) {
titles.add(layer.getTitle());
}
}
return titles;
}
public WmsLayerInfo getDetail(String title) {
try {
// 특정 title로 레이어 찾기
WmsLayerInfo layerInfo = getLayerByTitle(geoserverUrl, workspace, title);
if (layerInfo != null) {
return layerInfo;
} else {
throw new CustomApiException("NOT_FOUND_DATA", HttpStatus.NOT_FOUND);
}
} catch (Exception e) {
throw new CustomApiException("INTERNAL_SERVER_ERROR", HttpStatus.INTERNAL_SERVER_ERROR);
}
}
public List<WmsLayerInfo> getAllLayers(String baseUrl, String workspace)
throws IOException, ParserConfigurationException, SAXException {
String capabilitiesUrl = buildGetCapabilitiesUrl(baseUrl, workspace);
Document doc = fetchAndParseXml(capabilitiesUrl);
List<WmsLayerInfo> layers = new ArrayList<>();
NodeList layerNodes = doc.getElementsByTagName("Layer");
for (int i = 0; i < layerNodes.getLength(); i++) {
Element layerElement = (Element) layerNodes.item(i);
// Name이 있는 레이어만 추가 (실제 레이어, 그룹 레이어 제외)
String name = getElementTextContent(layerElement, "Name");
if (name != null && !name.isEmpty()) {
layers.add(extractLayerInfo(layerElement));
}
}
return layers;
}
/** GetCapabilities URL 생성 */
private String buildGetCapabilitiesUrl(String baseUrl, String workspace) {
// URL 끝의 슬래시 처리
String cleanBaseUrl =
baseUrl.endsWith("/") ? baseUrl.substring(0, baseUrl.length() - 1) : baseUrl;
return String.format(
"%s/geoserver/%s/wms?service=WMS&request=GetCapabilities", cleanBaseUrl, workspace);
}
/** URL에서 XML을 가져와 Document 객체로 파싱 */
private Document fetchAndParseXml(String urlString)
throws IOException, ParserConfigurationException, SAXException {
URL url = new URL(urlString);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setConnectTimeout(10000); // 10초 타임아웃
conn.setReadTimeout(10000);
try (InputStream inputStream = conn.getInputStream()) {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(true);
DocumentBuilder builder = factory.newDocumentBuilder();
return builder.parse(inputStream);
} finally {
conn.disconnect();
}
}
/** Element에서 특정 태그명의 텍스트 내용 추출 */
private String getElementTextContent(Element parent, String tagName) {
NodeList nodeList = parent.getElementsByTagName(tagName);
if (nodeList.getLength() > 0) {
return nodeList.item(0).getTextContent();
}
return null;
}
/** Layer Element에서 WmsLayerInfo 객체 생성 */
private WmsLayerInfo extractLayerInfo(Element layerElement) {
WmsLayerInfo layerInfo = new WmsLayerInfo();
// Name
layerInfo.setName(getElementTextContent(layerElement, "Name"));
// Title
layerInfo.setTitle(getElementTextContent(layerElement, "Title"));
// Abstract
layerInfo.setAbstractText(getElementTextContent(layerElement, "Abstract"));
// Keywords
NodeList keywordNodes = layerElement.getElementsByTagName("Keyword");
for (int i = 0; i < keywordNodes.getLength(); i++) {
String keyword = keywordNodes.item(i).getTextContent();
if (keyword != null && !keyword.trim().isEmpty()) {
layerInfo.addKeyword(keyword.trim());
}
}
// CRS (좌표계)
NodeList crsNodes = layerElement.getElementsByTagName("CRS");
if (crsNodes.getLength() == 0) {
// WMS 1.1.1의 경우 SRS 사용
crsNodes = layerElement.getElementsByTagName("SRS");
}
for (int i = 0; i < crsNodes.getLength(); i++) {
String crs = crsNodes.item(i).getTextContent();
if (crs != null && !crs.trim().isEmpty()) {
layerInfo.addCrs(crs.trim());
}
}
// BoundingBox
NodeList bboxNodes = layerElement.getElementsByTagName("BoundingBox");
if (bboxNodes.getLength() > 0) {
Element bboxElement = (Element) bboxNodes.item(0);
String crs = bboxElement.getAttribute("CRS");
if (crs == null || crs.isEmpty()) {
crs = bboxElement.getAttribute("SRS"); // WMS 1.1.1 호환
}
try {
double minX = Double.parseDouble(bboxElement.getAttribute("minx"));
double minY = Double.parseDouble(bboxElement.getAttribute("miny"));
double maxX = Double.parseDouble(bboxElement.getAttribute("maxx"));
double maxY = Double.parseDouble(bboxElement.getAttribute("maxy"));
WmsLayerInfo.BoundingBox bbox = new WmsLayerInfo.BoundingBox(crs, minX, minY, maxX, maxY);
layerInfo.setBoundingBox(bbox);
} catch (NumberFormatException e) {
System.err.println("BoundingBox 파싱 오류: " + e.getMessage());
}
}
// EX_GeographicBoundingBox도 확인 (전역 범위)
if (layerInfo.getBoundingBox() == null) {
NodeList geoBboxNodes = layerElement.getElementsByTagName("EX_GeographicBoundingBox");
if (geoBboxNodes.getLength() > 0) {
Element geoBboxElement = (Element) geoBboxNodes.item(0);
try {
double westBound =
Double.parseDouble(getElementTextContent(geoBboxElement, "westBoundLongitude"));
double eastBound =
Double.parseDouble(getElementTextContent(geoBboxElement, "eastBoundLongitude"));
double southBound =
Double.parseDouble(getElementTextContent(geoBboxElement, "southBoundLatitude"));
double northBound =
Double.parseDouble(getElementTextContent(geoBboxElement, "northBoundLatitude"));
WmsLayerInfo.BoundingBox bbox =
new WmsLayerInfo.BoundingBox(
"EPSG:4326", westBound, southBound, eastBound, northBound);
layerInfo.setBoundingBox(bbox);
} catch (NumberFormatException e) {
System.err.println("GeographicBoundingBox 파싱 오류: " + e.getMessage());
}
}
}
return layerInfo;
}
/**
* GetCapabilities를 호출하고 title로 레이어 정보를 찾아 반환
*
* @param baseUrl GeoServer 기본 URL (예: http://localhost:8080)
* @param workspace 워크스페이스 이름
* @param targetTitle 찾고자 하는 레이어의 title
* @return WmsLayerInfo 객체, 찾지 못하면 null
* @throws Exception 네트워크 또는 파싱 오류 시
*/
public WmsLayerInfo getLayerByTitle(String baseUrl, String workspace, String targetTitle)
throws Exception {
// GetCapabilities URL 구성
String capabilitiesUrl = buildGetCapabilitiesUrl(baseUrl, workspace);
// GetCapabilities 요청 및 XML 파싱
Document doc = fetchAndParseXml(capabilitiesUrl);
// title로 레이어 찾기
return findLayerByTitle(doc, targetTitle);
}
/** XML Document에서 title로 레이어를 찾아 WmsLayerInfo 객체로 변환 */
private WmsLayerInfo findLayerByTitle(Document doc, String targetTitle) {
// Layer 요소들 찾기
NodeList layerNodes = doc.getElementsByTagName("Layer");
for (int i = 0; i < layerNodes.getLength(); i++) {
Element layerElement = (Element) layerNodes.item(i);
// Title 찾기
String title = getElementTextContent(layerElement, "Title");
// Title이 일치하면 레이어 정보 추출
if (title != null && title.equals(targetTitle)) {
return extractLayerInfo(layerElement);
}
}
return null; // 찾지 못한 경우
}
}

View File

@@ -0,0 +1,465 @@
package com.kamco.cd.kamcoback.layer.service;
import com.kamco.cd.kamcoback.layer.dto.WmtsLayerInfo;
import com.kamco.cd.kamcoback.postgres.core.MapLayerCoreService;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathFactory;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
@Service
@Log4j2
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class WmtsService {
private final MapLayerCoreService mapLayerCoreService;
@Value("${layer.geoserver-url}")
private String geoserverUrl;
@Value("${layer.workspace}")
private String workspace;
private static final String WMTS_GEOSERVER_URL = "/geoserver/";
private static final String WMTS_CAPABILITIES_URL = "/gwc/service/wmts?REQUEST=GetCapabilities";
public List<String> getTile() {
List<WmtsLayerInfo> layers = getAllLayers(geoserverUrl, workspace);
List<String> titles = new ArrayList<>();
for (WmtsLayerInfo layer : layers) {
titles.add(layer.getTitle()); // ✅ getter로 변경
}
return titles;
}
public WmtsLayerInfo getDetail(String tile) {
return getLayerInfoByTitle(geoserverUrl, workspace, tile);
}
/**
* WMTS Capabilities URL에서 모든 레이어 정보를 가져옵니다.
*
* @param geoserverUrl 예: http://localhost:8080
* @param workspace 워크스페이스 이름
* @return 모든 레이어 정보 리스트
*/
public List<WmtsLayerInfo> getAllLayers(String geoserverUrl, String workspace) {
List<WmtsLayerInfo> layers = new ArrayList<>();
try {
// 1. XML 문서 로드 및 파싱
String capabilitiesUrl =
geoserverUrl + WMTS_GEOSERVER_URL + workspace + WMTS_CAPABILITIES_URL;
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(true);
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.parse(new URL(capabilitiesUrl).openStream());
XPathFactory xPathFactory = XPathFactory.newInstance();
XPath xpath = xPathFactory.newXPath();
// 2. 모든 Layer 노드 검색
String expression = "//*[local-name()='Layer']";
NodeList layerNodes =
(NodeList) xpath.compile(expression).evaluate(doc, XPathConstants.NODESET);
// 3. 모든 레이어를 파싱하여 리스트에 추가
for (int i = 0; i < layerNodes.getLength(); i++) {
Node layerNode = layerNodes.item(i);
String title = getChildValue(layerNode, "Title");
if (title != null && !title.trim().isEmpty()) {
WmtsLayerInfo layerInfo = parseLayerNode(workspace, doc, layerNode, title);
layers.add(layerInfo);
}
}
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("WMTS 정보 조회 중 오류 발생: " + e.getMessage());
}
return layers;
}
/**
* WMTS Capabilities URL에서 특정 타이틀의 레이어 정보를 가져옵니다. // * @param capabilitiesUrl 예:
* http://localhost:8080/geoserver/gwc/service/wmts?REQUEST=GetCapabilities
*
* @param geoserverUrl 예: http://localhost:8080
* @param targetTitle 찾고자 하는 레이어의 Title (예: "My Maps")
*/
public WmtsLayerInfo getLayerInfoByTitle(
String geoserverUrl, String workspace, String targetTitle) {
try {
// 1. XML 문서 로드 및 파싱
String capabilitiesUrl =
geoserverUrl + WMTS_GEOSERVER_URL + workspace + WMTS_CAPABILITIES_URL;
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(true); // 네임스페이스 인식
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.parse(new URL(capabilitiesUrl).openStream());
XPathFactory xPathFactory = XPathFactory.newInstance();
XPath xpath = xPathFactory.newXPath();
// 2. 모든 Layer 노드 검색 (네임스페이스 무시하고 local-name으로 검색)
// GeoServer WMTS에서 Layer는 <Contents> -> <Layer> 구조임
String expression = "//*[local-name()='Layer']";
NodeList layerNodes =
(NodeList) xpath.compile(expression).evaluate(doc, XPathConstants.NODESET);
for (int i = 0; i < layerNodes.getLength(); i++) {
Node layerNode = layerNodes.item(i);
// 3. Title 확인
String title = getChildValue(layerNode, "Title");
// 타이틀이 일치하면 객체 매핑 시작
if (title != null && title.trim().equals(targetTitle)) {
return parseLayerNode(workspace, doc, layerNode, title);
}
}
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("WMTS 정보 조회 중 오류 발생: " + e.getMessage());
}
return null; // 찾지 못한 경우
}
// 레이어 노드를 Java 객체로 변환 20250130
private WmtsLayerInfo parseLayerNode(
String workspace, Document doc, Node layerNode, String title) {
WmtsLayerInfo info = new WmtsLayerInfo();
info.setWorkspace(workspace); // 20250130
info.setTitle(title);
info.setIdentifier(getChildValue(layerNode, "Identifier"));
info.setAbstractText(getChildValue(layerNode, "Abstract"));
// Keywords 파싱
info.setKeywords(getChildValues(layerNode, "Keywords", "Keyword"));
// BoundingBox 파싱 (WGS84BoundingBox 기준)
info.setBoundingBox(parseBoundingBox(layerNode));
// Formats 파싱
info.setFormats(getChildValuesDirect(layerNode, "Format"));
// TileMatrixSetLink 파싱 (TileMatrixSet + Zoom Levels)
info.setTileMatrixSetLinks(parseTileMatrixSetLinks(layerNode));
// TileMatrixSetLimits에서 줌 레벨 추출 (개별 zoom 리스트)
info.setMatrixIds(parseMatrixIds(layerNode)); // 20260130
// ResourceURL 파싱
info.setResourceUrls(parseResourceUrls(layerNode));
// Styles 파싱
info.setStyles(parseStyles(layerNode));
// TileMatrixSet의 TileMatrix 정보 파싱
List<String> tileMatrixSetNames = new ArrayList<>();
if (info.getTileMatrixSetLinks() != null) {
for (WmtsLayerInfo.TileMatrixSetLink link : info.getTileMatrixSetLinks()) {
tileMatrixSetNames.add(link.getTileMatrixSet());
}
}
info.setTileMatrices(parseTileMatrices(doc, tileMatrixSetNames));
return info;
}
// --- Helper Methods ---
private WmtsLayerInfo.BoundingBox parseBoundingBox(Node layerNode) {
// 보통 <ows:WGS84BoundingBox>를 찾음
Node bboxNode = findChildNode(layerNode, "WGS84BoundingBox");
if (bboxNode == null) bboxNode = findChildNode(layerNode, "BoundingBox");
if (bboxNode != null) {
WmtsLayerInfo.BoundingBox bbox = new WmtsLayerInfo.BoundingBox();
// WGS84는 CRS 속성이 없을 수 있음
bbox.setCrs(getAttributeValue(bboxNode, "crs"));
String lowerCorner = getChildValue(bboxNode, "LowerCorner");
String upperCorner = getChildValue(bboxNode, "UpperCorner");
if (lowerCorner != null) {
String[] coords = lowerCorner.split(" ");
bbox.setLowerCornerX(Double.parseDouble(coords[0]));
bbox.setLowerCornerY(Double.parseDouble(coords[1]));
}
if (upperCorner != null) {
String[] coords = upperCorner.split(" ");
bbox.setUpperCornerX(Double.parseDouble(coords[0]));
bbox.setUpperCornerY(Double.parseDouble(coords[1]));
}
return bbox;
}
return null;
}
private List<WmtsLayerInfo.ResourceUrl> parseResourceUrls(Node layerNode) {
List<WmtsLayerInfo.ResourceUrl> list = new ArrayList<>();
NodeList children = layerNode.getChildNodes();
for (int i = 0; i < children.getLength(); i++) {
Node node = children.item(i);
if (node.getNodeName().contains("ResourceURL")) { // local-name check simplification
WmtsLayerInfo.ResourceUrl url = new WmtsLayerInfo.ResourceUrl();
url.setFormat(getAttributeValue(node, "format"));
url.setResourceType(getAttributeValue(node, "resourceType"));
url.setTemplate(getAttributeValue(node, "template"));
list.add(url);
}
}
return list;
}
private List<WmtsLayerInfo.Style> parseStyles(Node layerNode) {
List<WmtsLayerInfo.Style> styles = new ArrayList<>();
NodeList children = layerNode.getChildNodes();
for (int i = 0; i < children.getLength(); i++) {
Node node = children.item(i);
if (node.getNodeName().endsWith("Style")) {
WmtsLayerInfo.Style style = new WmtsLayerInfo.Style();
style.setDefault(Boolean.parseBoolean(getAttributeValue(node, "isDefault")));
style.setIdentifier(getChildValue(node, "Identifier"));
style.setTitle(getChildValue(node, "Title"));
styles.add(style);
}
}
return styles;
}
/**
* TileMatrixSetLimits에서 줌 레벨을 추출합니다. 예: "EPSG:4326:0" → "0", "EPSG:4326:1" → "1"
*
* @param layerNode Layer 노드
* @return 줌 레벨 문자열 리스트
*/
private List<String> parseMatrixIds(Node layerNode) {
List<String> matrixIds = new ArrayList<>();
NodeList children = layerNode.getChildNodes();
// 모든 TileMatrixSetLink 찾기
for (int i = 0; i < children.getLength(); i++) {
Node node = children.item(i);
if (node.getNodeName().contains("TileMatrixSetLink")) {
// TileMatrixSetLimits 찾기
Node limitsNode = findChildNode(node, "TileMatrixSetLimits");
if (limitsNode != null) {
NodeList limitsList = limitsNode.getChildNodes();
// 각 TileMatrixLimits 처리
for (int j = 0; j < limitsList.getLength(); j++) {
Node limitNode = limitsList.item(j);
if (limitNode.getNodeName().contains("TileMatrixLimits")) {
// TileMatrix 또는 Identifier 값 추출
String identifier = getChildValue(limitNode, "TileMatrix");
if (identifier == null) {
identifier = getChildValue(limitNode, "Identifier");
}
// 마지막 콜론 이후 값(줌 레벨) 추출
if (identifier != null && identifier.contains(":")) {
String[] parts = identifier.split(":");
String zoomLevel = parts[parts.length - 1];
matrixIds.add(zoomLevel);
}
}
}
}
}
}
return matrixIds;
}
/**
* TileMatrixSetLink 정보를 파싱합니다. 각 TileMatrixSetLink에서 TileMatrixSet 이름과 줌 레벨들을 추출합니다.
*
* @param layerNode Layer 노드
* @return TileMatrixSetLink 객체 리스트
*/
private List<WmtsLayerInfo.TileMatrixSetLink> parseTileMatrixSetLinks(Node layerNode) {
List<WmtsLayerInfo.TileMatrixSetLink> links = new ArrayList<>();
NodeList children = layerNode.getChildNodes();
// 모든 TileMatrixSetLink 찾기
for (int i = 0; i < children.getLength(); i++) {
Node node = children.item(i);
if (node.getNodeName().contains("TileMatrixSetLink")) {
// TileMatrixSet 이름 추출
String tileMatrixSet = getChildValue(node, "TileMatrixSet");
// 줌 레벨들 추출
List<String> zoomLevels = new ArrayList<>();
Node limitsNode = findChildNode(node, "TileMatrixSetLimits");
if (limitsNode != null) {
NodeList limitsList = limitsNode.getChildNodes();
for (int j = 0; j < limitsList.getLength(); j++) {
Node limitNode = limitsList.item(j);
if (limitNode.getNodeName().contains("TileMatrixLimits")) {
// TileMatrix 또는 Identifier 값 추출
String identifier = getChildValue(limitNode, "TileMatrix");
if (identifier == null) {
identifier = getChildValue(limitNode, "Identifier");
}
// 마지막 콜론 이후 값(줌 레벨) 추출
if (identifier != null && identifier.contains(":")) {
String[] parts = identifier.split(":");
String zoomLevel = parts[parts.length - 1];
zoomLevels.add(zoomLevel);
}
}
}
}
// TileMatrixSetLink 객체 생성 및 추가
if (tileMatrixSet != null) {
WmtsLayerInfo.TileMatrixSetLink link =
new WmtsLayerInfo.TileMatrixSetLink(tileMatrixSet, zoomLevels);
links.add(link);
}
}
}
return links;
}
/**
* Document에서 TileMatrixSet의 TileMatrix 정보를 파싱합니다.
*
* @param doc WMTS Capabilities Document
* @param tileMatrixSetNames 조회할 TileMatrixSet 이름 리스트
* @return TileMatric 객체 리스트
*/
private List<WmtsLayerInfo.TileMatric> parseTileMatrices(
Document doc, List<String> tileMatrixSetNames) {
List<WmtsLayerInfo.TileMatric> allMatrices = new ArrayList<>();
try {
XPathFactory xPathFactory = XPathFactory.newInstance();
XPath xpath = xPathFactory.newXPath();
// 각 TileMatrixSet 이름에 대해 TileMatrix 찾기
for (String tileMatrixSetName : tileMatrixSetNames) {
// TileMatrixSet 찾기
String expression = "//*[local-name()='TileMatrixSet']";
NodeList tileMatrixSetNodes =
(NodeList) xpath.compile(expression).evaluate(doc, XPathConstants.NODESET);
for (int i = 0; i < tileMatrixSetNodes.getLength(); i++) {
Node tileMatrixSetNode = tileMatrixSetNodes.item(i);
String identifier = getChildValue(tileMatrixSetNode, "Identifier");
// 일치하는 TileMatrixSet 찾으면 TileMatrix 파싱
if (tileMatrixSetName.equals(identifier)) {
NodeList children = tileMatrixSetNode.getChildNodes();
for (int j = 0; j < children.getLength(); j++) {
Node node = children.item(j);
if (node.getNodeName().contains("TileMatrix")) {
WmtsLayerInfo.TileMatric tileMatric = new WmtsLayerInfo.TileMatric();
// TileMatrix 정보 추출
tileMatric.setScaleDenominator(getChildValue(node, "ScaleDenominator"));
tileMatric.setTopLeftCorner(getChildValue(node, "TopLeftCorner"));
tileMatric.setIdentifier(getChildValue(node, "Identifier"));
/* 미사용 정보 주석처리함
tileMatric.setTileWidth(getChildValue(node, "TileWidth"));
tileMatric.setTileHeight(getChildValue(node, "TileHeight"));
tileMatric.setMatrixWidth(getChildValue(node, "MatrixWidth"));
tileMatric.setMatrixHeight(getChildValue(node, "MatrixHeight"));
*/
allMatrices.add(tileMatric);
}
}
break; // 일치하는 TileMatrixSet 찾았으므로 다음으로
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
return allMatrices;
}
// 특정 노드 아래의 자식 태그 값 추출 (예: <Title>값)
private String getChildValue(Node parent, String childName) {
Node child = findChildNode(parent, childName);
return (child != null) ? child.getTextContent() : null;
}
// 특정 노드 아래의 반복되는 자식 구조 값 추출 (예: Keywords -> Keyword)
private List<String> getChildValues(Node parent, String wrapperName, String childName) {
List<String> results = new ArrayList<>();
Node wrapper = findChildNode(parent, wrapperName);
if (wrapper != null) {
NodeList children = wrapper.getChildNodes();
for (int i = 0; i < children.getLength(); i++) {
Node node = children.item(i);
if (node.getNodeName().endsWith(childName)) {
results.add(node.getTextContent());
}
}
}
return results;
}
// Wrapper 없이 바로 반복되는 값 추출 (예: Format)
private List<String> getChildValuesDirect(Node parent, String childName) {
List<String> results = new ArrayList<>();
NodeList children = parent.getChildNodes();
for (int i = 0; i < children.getLength(); i++) {
Node node = children.item(i);
if (node.getNodeName().endsWith(childName)) {
results.add(node.getTextContent());
}
}
return results;
}
// 이름으로 자식 노드 찾기 (Local Name 기준)
private Node findChildNode(Node parent, String localName) {
NodeList children = parent.getChildNodes();
for (int i = 0; i < children.getLength(); i++) {
Node node = children.item(i);
// 네임스페이스 접두사(ows:, wmts:)를 무시하고 태그 이름 확인
if (node.getNodeName().endsWith(":" + localName) || node.getNodeName().equals(localName)) {
return node;
}
}
return null;
}
private String getAttributeValue(Node node, String attrName) {
if (node.hasAttributes()) {
Node attr = node.getAttributes().getNamedItem(attrName);
if (attr != null) return attr.getNodeValue();
}
return null;
}
}

View File

@@ -1,5 +1,6 @@
package com.kamco.cd.kamcoback.log.dto;
import com.kamco.cd.kamcoback.common.utils.enums.CodeExpose;
import com.kamco.cd.kamcoback.common.utils.enums.EnumType;
import io.swagger.v3.oas.annotations.media.Schema;
import java.time.LocalDate;
@@ -13,6 +14,30 @@ import org.springframework.data.domain.Sort;
public class ErrorLogDto {
@CodeExpose
@Getter
public enum LogErrorLevel implements EnumType {
WARNING("Warning"),
ERROR("Error"),
CRITICAL("Critical");
private final String desc;
LogErrorLevel(String desc) {
this.desc = desc;
}
@Override
public String getId() {
return name();
}
@Override
public String getText() {
return desc;
}
}
@Schema(name = "ErrorLogBasic", description = "에러로그 기본 정보")
@Getter
@Setter
@@ -76,26 +101,4 @@ public class ErrorLogDto {
return PageRequest.of(page, size);
}
}
public enum LogErrorLevel implements EnumType {
WARNING("Warning"),
ERROR("Error"),
CRITICAL("Critical");
private final String desc;
LogErrorLevel(String desc) {
this.desc = desc;
}
@Override
public String getId() {
return name();
}
@Override
public String getText() {
return desc;
}
}
}

View File

@@ -21,6 +21,7 @@ import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import java.io.IOException;
import java.util.List;
import java.util.UUID;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page;
@@ -204,12 +205,12 @@ public class MapSheetMngApiController {
@PostMapping(value = "/upload-pair", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public ApiResponseDto<MapSheetMngDto.DmlReturn> uploadPair(
@RequestPart("tfw") MultipartFile tfwFile,
@RequestPart("tif") MultipartFile tifFile,
@RequestParam(value = "hstUid", required = false) Long hstUid,
@RequestParam(value = "overwrite", required = false) boolean overwrite) {
@RequestParam(value = "tifFileName") String tifFile,
@RequestParam(value = "tifFileSize") Long tifFileSize,
@RequestParam(value = "hstUid", required = false) Long hstUid) {
return ApiResponseDto.createOK(
mapSheetMngService.uploadPair(tfwFile, tifFile, hstUid, overwrite));
mapSheetMngService.uploadPair(tfwFile, tifFile, hstUid, tifFileSize));
}
@Operation(summary = "영상관리 > 파일조회", description = "영상관리 > 파일조회")
@@ -322,6 +323,7 @@ public class MapSheetMngApiController {
@PostMapping(value = "/file-chunk-upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public ApiResponseDto<ModelUploadResDto> fileChunkUpload(
@RequestParam("hstUid") Long hstUid,
@RequestParam("uuid") UUID uuid,
@RequestParam("fileName") String fileName,
@RequestParam("fileSize") long fileSize,
@RequestParam("chunkIndex") Integer chunkIndex,
@@ -338,6 +340,7 @@ public class MapSheetMngApiController {
upAddReqDto.setChunkIndex(chunkIndex);
upAddReqDto.setChunkTotalIndex(chunkTotalIndex);
upAddReqDto.setUploadDivi(uploadDivi);
upAddReqDto.setUuid(uuid);
upAddReqDto.setFinalPath(syncRootDir);
upAddReqDto.setTempPath(syncRootTmpDir);

View File

@@ -7,6 +7,7 @@ import com.kamco.cd.kamcoback.common.utils.enums.EnumType;
import com.kamco.cd.kamcoback.common.utils.enums.Enums;
import com.kamco.cd.kamcoback.common.utils.interfaces.JsonFormatDttm;
import io.swagger.v3.oas.annotations.media.Schema;
import java.math.BigDecimal;
import java.time.ZonedDateTime;
import java.util.List;
import java.util.UUID;
@@ -77,6 +78,34 @@ public class MapSheetMngDto {
@Schema(description = "선택폴더경로", example = "D:\\app\\original-images\\2022")
private String mngPath;
// Tile 등록 로직
@Schema(description = "url")
private String url;
@Schema(description = "좌측상단 경도", example = "126.0")
private BigDecimal minLon;
@Schema(description = "좌측상단 위도", example = "34.0")
private BigDecimal minLat;
@Schema(description = "우측하단 경도", example = "130.0")
private BigDecimal maxLon;
@Schema(description = "우측하단 위도", example = "38.5")
private BigDecimal maxLat;
@Schema(description = "zoom min", example = "5")
private Short minZoom;
@Schema(description = "zoom max", example = "18")
private Short maxZoom;
@Schema(description = "tag")
private String tag;
@Schema(description = "crs 좌표계")
private String crs;
@JsonIgnore private Long createdUid;
}
@@ -155,11 +184,14 @@ public class MapSheetMngDto {
}
public long getSyncErrorTotCnt() {
return this.syncNotPaireCnt + this.syncDuplicateCnt + this.syncFaultCnt;
return this.syncNotPaireCnt + this.syncDuplicateCnt + this.syncFaultCnt + this.syncNoFileCnt;
}
public long getSyncErrorExecTotCnt() {
return this.syncNotPaireExecCnt + this.syncDuplicateExecCnt + this.syncFaultExecCnt;
return this.syncNotPaireExecCnt
+ this.syncDuplicateExecCnt
+ this.syncFaultExecCnt
+ this.syncNoFileExecCnt;
}
public String getMngState() {
@@ -258,6 +290,7 @@ public class MapSheetMngDto {
private String mapSheetPath;
private UUID uuid;
private String uploadId;
// private List<MngFIleDto> fileArray;
@@ -278,7 +311,8 @@ public class MapSheetMngDto {
String errorCheckTfwFileName,
String errorCheckTifFileName,
String mapSheetPath,
UUID uuid) {
UUID uuid,
String uploadId) {
this.hstUid = hstUid;
this.mngYyyy = mngYyyy;
this.mapSheetNum = mapSheetNum;
@@ -298,6 +332,7 @@ public class MapSheetMngDto {
this.errorCheckTifFileName = errorCheckTifFileName;
this.mapSheetPath = mapSheetPath;
this.uuid = uuid;
this.uploadId = uploadId;
}
private String getSyncStateName(String enumId) {

View File

@@ -30,12 +30,14 @@ import java.nio.file.StandardCopyOption;
import java.util.Comparator;
import java.util.List;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
@Slf4j
@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
@@ -107,7 +109,7 @@ public class MapSheetMngService {
@Transactional
public DmlReturn uploadPair(
MultipartFile tfwFile, MultipartFile tifFile, Long hstUid, Boolean overwrite) {
MultipartFile tfwFile, String tifFile, Long hstUid, Long tifFileSize) {
String rootPath = syncRootDir;
String tmpPath = syncTmpDir;
@@ -134,7 +136,7 @@ public class MapSheetMngService {
MngDto mngDto = mapSheetMngCoreService.findMapSheetMng(errDto.getMngYyyy());
String targetYearDir = mngDto.getMngPath();
// 중복체크 -> 도엽/uuid 경로에 업로드 할 거라 overwrite 되지 않음
// 중복체크 -> 도엽50k/uuid 경로에 업로드 할 거라 overwrite 되지 않음
// if (!overwrite) {
// dmlReturn =
// this.duplicateFile(
@@ -146,7 +148,7 @@ public class MapSheetMngService {
// 멀티파트 파일 tmp폴더 저장(파일형식 체크를 위해)
String tfwTmpPath = tmpPath + tfwFile.getOriginalFilename();
String tifTmpPath = tmpPath + tifFile.getOriginalFilename();
// String tifTmpPath = tmpPath + tifFile;
if (!FIleChecker.multipartSaveTo(tfwFile, tfwTmpPath)) {
return new DmlReturn("fail", "UPLOAD ERROR");
@@ -166,7 +168,7 @@ public class MapSheetMngService {
List<MngFilesDto> mngFiles = mapSheetMngCoreService.findByHstUidMapSheetFileList(hstUid);
String uploadPath = "";
for (MngFilesDto dto : mngFiles) {
uploadPath = dto.getFilePath() + "/" + errDto.getUuid();
uploadPath = dto.getFilePath();
break;
}
@@ -180,12 +182,26 @@ public class MapSheetMngService {
MngFilesDto filesDto =
mapSheetMngCoreService.findYyyyToMapSheetFilePathRefer(errDto.getMngYyyy());
String referPath = filesDto.getFilePath();
Path path = Paths.get(referPath);
boolean isFiveDigitNumber =
path.getFileName() != null && path.getFileName().toString().matches("\\d{5}");
log.info("isFiveDigitNumber : " + isFiveDigitNumber);
if (isFiveDigitNumber) {
uploadPath =
Paths.get(referPath).getParent().toString()
+ "/"
+ errDto.getRefMapSheetNum()
+ "/"
+ errDto.getUuid();
} else {
uploadPath =
Paths.get(referPath).getParent().getParent().toString()
+ "/"
+ errDto.getRefMapSheetNum()
+ "/"
+ errDto.getUuid();
}
}
// 업로드 경로 확인(없으면 생성)
@@ -194,8 +210,10 @@ public class MapSheetMngService {
}
tfwTargetPath = Paths.get(uploadPath).resolve(tfwFile.getOriginalFilename());
tifTargetPath = Paths.get(uploadPath).resolve(tifFile.getOriginalFilename());
tifTargetPath = Paths.get(uploadPath).resolve(tifFile);
log.info("tfwTargetPath : " + tfwTargetPath.toString());
log.info("tifTargetPath : " + tifTargetPath.toString());
if (!Files.exists(tifTargetPath)) {
return new DmlReturn("fail", "TIF 파일이 정상적으로 업로드 되지 않았습니다. 확인해주세요.");
}
@@ -213,7 +231,7 @@ public class MapSheetMngService {
updReqSyncCheckState.setHstUid(hstUid);
updReqSyncCheckState.setFilePath(uploadPath);
updReqSyncCheckState.setSyncCheckTfwFileName(tfwFile.getOriginalFilename());
updReqSyncCheckState.setSyncCheckTifFileName(tifFile.getOriginalFilename());
updReqSyncCheckState.setSyncCheckTifFileName(tifFile);
updReqSyncCheckState.setSyncCheckState("DONE");
mapSheetMngCoreService.updateMapSheetMngHstSyncCheckState(updReqSyncCheckState);
// 파일정보 업데이트
@@ -233,11 +251,14 @@ public class MapSheetMngService {
mapSheetMngCoreService.mngFileSave(addReq);
addReq.setFileName(tifFile.getOriginalFilename());
addReq.setFileName(tifFile);
addReq.setFileExt("tif");
addReq.setFileSize(tifFile.getSize());
addReq.setFileSize(tifFileSize);
mapSheetMngCoreService.mngFileSave(addReq);
// 사용할 수 있는 이전 년도 도엽 테이블 저장
mapSheetMngCoreService.saveSheetMngYear();
return new DmlReturn("success", "파일 업로드 완료되었습니다.");
}
@@ -279,14 +300,12 @@ public class MapSheetMngService {
return new DmlReturn("success", fileUids.size() + "개 파일이 사용설정되었습니다.");
}
public DmlReturn validationFile(MultipartFile tfwFile, MultipartFile tifFile) {
public DmlReturn validationFile(MultipartFile tfwFile, String tifFile) {
if (!FIleChecker.validationMultipart(tfwFile)) {
return new DmlReturn("fail", "TFW SIZE 오류");
} else if (!FIleChecker.validationMultipart(tifFile)) {
return new DmlReturn("fail", "TFW SIZE 오류");
} else if (!FIleChecker.checkExtensions(tfwFile.getOriginalFilename(), "tfw")) {
return new DmlReturn("fail", "TFW FILENAME ERROR");
} else if (!FIleChecker.checkExtensions(tifFile.getOriginalFilename(), "tif")) {
} else if (!FIleChecker.checkExtensions(tifFile, "tif")) {
return new DmlReturn("fail", "TIF FILENAME ERROR");
}
@@ -322,12 +341,14 @@ public class MapSheetMngService {
String dirPath = syncRootDir + srchDto.getDirPath();
String sortType = "name desc";
List<FIleChecker.Folder> folderList = FIleChecker.getFolderAll(dirPath);
List<FIleChecker.Folder> folderList =
FIleChecker.getFolderAll(dirPath).stream()
.filter(dir -> dir.getIsValid().equals(true))
.toList();
int folderTotCnt = folderList.size();
int folderErrTotCnt =
(int)
folderList.stream().filter(dto -> dto.getIsValid().toString().equals("false")).count();
(int) folderList.stream().filter(dto -> dto.getIsValid().equals(false)).count();
return new FoldersDto(dirPath, folderTotCnt, folderErrTotCnt, folderList);
}
@@ -390,10 +411,21 @@ public class MapSheetMngService {
MngFilesDto filesDto =
mapSheetMngCoreService.findYyyyToMapSheetFilePathRefer(errDto.getMngYyyy());
String referPath = filesDto.getFilePath();
Path path = Paths.get(referPath);
boolean isFiveDigitNumber =
path.getFileName() != null && path.getFileName().toString().matches("\\d{5}");
log.info("isFiveDigitNumber : " + isFiveDigitNumber);
if (isFiveDigitNumber) {
uploadPath = Paths.get(referPath).getParent().toString() + "/" + errDto.getRefMapSheetNum();
} else {
uploadPath =
Paths.get(referPath).getParent().getParent().toString()
+ "/"
+ errDto.getRefMapSheetNum();
}
}
upAddReqDto.setUuid(errDto.getUuid());
upAddReqDto.setFinalPath(uploadPath + "/");
upAddReqDto.setTempPath(upAddReqDto.getTempPath() + "/");
@@ -402,7 +434,7 @@ public class MapSheetMngService {
ModelUploadResDto modelUploadResDto = new ModelUploadResDto();
// 병합 다 했는데 gdalinfo 가 fail 이면 삭제?
if (upRes.getRes().equals("MERGED")) {
if (upAddReqDto.getStatus().equals("MERGED")) {
if (!FIleChecker.cmmndGdalInfo(upRes.getFilePath() + "/" + upRes.getFileName())) {
Path filePath = Paths.get(upRes.getFilePath());
Files.deleteIfExists(filePath);
@@ -410,6 +442,10 @@ public class MapSheetMngService {
modelUploadResDto.setResMsg("TIF TYPE ERROR");
return modelUploadResDto;
}
// upload_id, uuid 를 update
mapSheetMngCoreService.updateMapSheetMngHstUploadId(
hstUid, upAddReqDto.getUuid(), upRes.getUploadId());
}
modelUploadResDto.setRes(upRes.getRes());

View File

@@ -111,8 +111,8 @@ public class MembersDto {
@EnumValid(enumClass = RoleType.class, message = "userRole은 ADMIN, LABELER, REVIEWER 만 가능합니다.")
private String userRole;
@Schema(description = "사번", example = "K20251212001")
@Size(max = 50)
@Schema(description = "사번", example = "123456")
@Size(max = 6)
private String employeeNo;
@Schema(description = "이름", example = "홍길동")

View File

@@ -11,7 +11,7 @@ import lombok.ToString;
@ToString(exclude = "password")
public class SignInRequest {
@Schema(description = "사용자 ID", example = "1234567")
@Schema(description = "사용자 ID", example = "123456")
private String username;
@Schema(description = "비밀번호", example = "qwe123!@#")

View File

@@ -228,9 +228,11 @@ public class ModelMngDto {
private String clsModelFileName;
private int chunkIndex;
private int chunkTotalIndex;
@JsonIgnore private String jsonPath;
@JsonIgnore private String jsonFileName;
public double getUploadRate() {
if (this.chunkTotalIndex == 0) {
if (chunkIndex < 0 || chunkTotalIndex < 0) {
return 0.0;
}
return (double) (this.chunkIndex + 1) / (this.chunkTotalIndex + 1) * 100.0;

View File

@@ -15,16 +15,19 @@ import com.kamco.cd.kamcoback.upload.service.UploadService;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDate;
import java.util.List;
import java.util.UUID;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
@Service
@Log4j2
@RequiredArgsConstructor
public class ModelMngService {
@@ -53,6 +56,12 @@ public class ModelMngService {
@Value("${file.model-tmp-dir}")
private String modelTmpDir;
@Value("${file.pt-path}")
private String ptPath;
@Value("${file.pt-FileName}")
private String ptFileName;
public Page<ModelMngDto.ModelList> findModelMgmtList(
ModelMngDto.searchReq searchReq,
LocalDate startDate,
@@ -71,11 +80,18 @@ public class ModelMngService {
}
public ApiResponseDto.ResponseObj insertModel(ModelMngDto.AddReq addReq) {
UUID uuid = UUID.randomUUID();
addReq.setUuid(uuid);
addReq.setClsModelVersion(addReq.getModelVer());
addReq.setPriority(0d);
log.info(
"insertModel start: uuid={}, fileName={}, filePath={}",
uuid,
addReq.getFileName(),
addReq.getFilePath());
ModelUploadResDto modelUploadResDto = new ModelUploadResDto();
try {
FIleChecker.unzip(addReq.getFileName(), addReq.getFilePath());
@@ -86,10 +102,21 @@ public class ModelMngService {
|| modelUploadResDto.getCdModelConfigFileName() == null
|| modelUploadResDto.getCdModelConfigFileName().isEmpty()
|| modelUploadResDto.getClsModelFileName() == null
|| modelUploadResDto.getClsModelFileName().isEmpty()) {
|| modelUploadResDto.getClsModelFileName().isEmpty()
|| modelUploadResDto.getJsonFileName() == null
|| modelUploadResDto.getJsonFileName().isEmpty()) {
log.warn(
"model file missing: uuid={}, path={}, cdModel={}, cdConfig={}, clsModel={}, jsonFileName={}",
uuid,
addReq.getFilePath(),
modelUploadResDto.getCdModelFileName(),
modelUploadResDto.getCdModelConfigFileName(),
modelUploadResDto.getClsModelFileName(),
modelUploadResDto.getJsonFileName());
return new ApiResponseDto.ResponseObj(
ApiResponseCode.NOT_FOUND, "pth,json,py파일이 모두 존재하지 않습니다");
ApiResponseCode.NOT_FOUND, "pth,pt,py,json파일이 모두 존재하지 않습니다");
}
addReq.setCdModelPath(modelUploadResDto.getCdModelPath());
@@ -101,12 +128,20 @@ public class ModelMngService {
} catch (IOException e) {
// throw new RuntimeException(e);
log.error(
"unzip or scan failed: uuid={}, fileName={}, filePath={}",
uuid,
addReq.getFileName(),
addReq.getFilePath(),
e);
return new ApiResponseDto.ResponseObj(ApiResponseCode.NOT_FOUND, "파일이 존재하지 않습니다");
}
Long modelUid = modelMngCoreService.insertModel(addReq);
log.info(
"model inserted: uuid={}, modelUid={}, modelVer={}", uuid, modelUid, addReq.getModelVer());
ModelMetricAddReq modelMetricAddReq = new ModelMetricAddReq();
modelMetricAddReq.setModelUid(modelUid);
modelMetricAddReq.setModelVerUid(modelUid);
@@ -117,7 +152,7 @@ public class ModelMngService {
modelMetricAddReq.setIou(0);
ObjectMapper mapper = new ObjectMapper();
String filePath = modelUploadResDto.getClsModelPath() + modelUploadResDto.getClsModelFileName();
String filePath = modelUploadResDto.getJsonPath() + modelUploadResDto.getJsonFileName();
String dataJson = null;
try {
dataJson = Files.readString(Path.of(filePath));
@@ -136,14 +171,28 @@ public class ModelMngService {
modelMetricAddReq.setIou(iou);
} catch (IOException e) {
log.error("metric json parse failed: uuid={}, jsonPath={}", uuid, filePath, e);
return new ApiResponseDto.ResponseObj(ApiResponseCode.NOT_FOUND, "JSON파일 오류");
}
modelMngCoreService.insertModelResultMetric(modelMetricAddReq);
log.info(
"model metric inserted: modelUid={}, f1={}, precision={}, recall={}, iou={}",
modelUid,
modelMetricAddReq.getF1Score(),
modelMetricAddReq.getPrecision(),
modelMetricAddReq.getRecall(),
modelMetricAddReq.getIou());
String zipFilePath = addReq.getFilePath() + "/" + addReq.getFileName();
boolean isDeleted = FIleChecker.deleteFile(zipFilePath);
if (!isDeleted) {
log.warn("zip file delete failed: uuid={}, path={}", uuid, zipFilePath);
}
log.info("insertModel success: uuid={}, modelUid={}", uuid, modelUid);
return new ApiResponseDto.ResponseObj(ApiResponseDto.ApiResponseCode.OK, "등록되었습니다.");
}
@@ -174,6 +223,14 @@ public class ModelMngService {
}
*/
log.info(
"end uploadChunkModelFile: res={}, resMsg={}, uuid={}, filePath={}, fileName={}",
modelUploadResDto.getRes(),
modelUploadResDto.getResMsg(),
modelUploadResDto.getUuid(),
modelUploadResDto.getFilePath(),
modelUploadResDto.getFileName());
return modelUploadResDto;
}
@@ -190,16 +247,31 @@ public class ModelMngService {
// 예: 파일명 출력 및 추가 작업
String foldNm = dto.getFullPath().replace(dto.getFileNm(), "");
if (dto.getExtension().equals("pth")) {
switch (dto.getExtension()) {
case "pth" -> {
modelUploadResDto.setCdModelPath(foldNm);
modelUploadResDto.setCdModelFileName(dto.getFileNm());
} else if (dto.getExtension().equals("py")) {
}
case "py" -> {
modelUploadResDto.setCdModelConfigPath(foldNm);
modelUploadResDto.setCdModelConfigFileName(dto.getFileNm());
} else if (dto.getExtension().equals("json")) {
modelUploadResDto.setClsModelPath(foldNm);
modelUploadResDto.setClsModelFileName(dto.getFileNm());
}
case "json" -> {
modelUploadResDto.setJsonPath(foldNm);
modelUploadResDto.setJsonFileName(dto.getFileNm());
}
}
}
// cls model 적용
String defaultPath = ptPath;
String defaultFileName = ptFileName;
Path ptPath = Paths.get(defaultPath, defaultFileName);
if (Files.exists(ptPath)) {
modelUploadResDto.setClsModelPath(defaultPath);
modelUploadResDto.setClsModelFileName(defaultFileName);
}
// int fileListPos = 0;

View File

@@ -1,7 +1,13 @@
package com.kamco.cd.kamcoback.postgres.core;
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.GukYuinDto.GukYuinLinkFacts;
import com.kamco.cd.kamcoback.gukyuin.dto.GukYuinDto.LearnInfo;
import com.kamco.cd.kamcoback.postgres.repository.Inference.MapSheetLearnRepository;
import com.kamco.cd.kamcoback.postgres.repository.gukyuin.GukYuinRepository;
import java.time.LocalDate;
import java.util.List;
import java.util.UUID;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
@@ -11,6 +17,7 @@ import org.springframework.stereotype.Service;
public class GukYuinCoreService {
private final MapSheetLearnRepository mapSheetLearnRepository;
private final GukYuinRepository gukYuinRepository;
/**
* 국유in연동 가능여부 확인
@@ -21,4 +28,53 @@ public class GukYuinCoreService {
public GukYuinLinkFacts findLinkFacts(UUID uuid) {
return mapSheetLearnRepository.findLinkFacts(uuid);
}
public void updateGukYuinMastRegResult(Basic resultBody) {
gukYuinRepository.updateGukYuinMastRegResult(resultBody);
}
public void updateGukYuinMastRegRemove(String chnDtctId) {
gukYuinRepository.updateGukYuinMastRegRemove(chnDtctId);
}
public void updateInferenceGeomDataPnuCnt(String chnDtctObjtId, long pnuCnt) {
gukYuinRepository.updateInferenceGeomDataPnuCnt(chnDtctObjtId, pnuCnt);
}
public Long findMapSheetAnalDataInferenceGeomUid(String chnDtctObjtId) {
return gukYuinRepository.findMapSheetAnalDataInferenceGeomUid(chnDtctObjtId);
}
public void insertGeoUidPnuData(Long geoUid, String[] pnuList, String chnDtctObjtId) {
gukYuinRepository.insertGeoUidPnuData(geoUid, pnuList, chnDtctObjtId);
}
public LearnInfo findMapSheetLearnInfo(UUID uuid) {
return gukYuinRepository.findMapSheetLearnInfo(uuid);
}
public Integer findMapSheetLearnYearStage(Integer compareYyyy, Integer targetYyyy) {
return gukYuinRepository.findMapSheetLearnYearStage(compareYyyy, targetYyyy);
}
public void updateAnalInferenceApplyDttm(Basic registRes) {
gukYuinRepository.updateAnalInferenceApplyDttm(registRes);
}
public List<LabelSendDto> findLabelingCompleteSendList(LocalDate yesterday) {
return gukYuinRepository.findLabelingCompleteSendList(yesterday);
}
public Long findMapSheetLearnInfoByYyyy(
Integer compareYyyy, Integer targetYyyy, Integer maxStage) {
return gukYuinRepository.findMapSheetLearnInfoByYyyy(compareYyyy, targetYyyy, maxStage);
}
public void updateMapSheetLearnGukyuinEndStatus(Long learnId) {
gukYuinRepository.updateMapSheetLearnGukyuinEndStatus(learnId);
}
public void updateMapSheetInferenceLabelEndStatus(Long learnId) {
gukYuinRepository.updateMapSheetInferenceLabelEndStatus(learnId);
}
}

View File

@@ -0,0 +1,27 @@
package com.kamco.cd.kamcoback.postgres.core;
import com.kamco.cd.kamcoback.gukyuin.dto.ChngDetectMastDto.LearnKeyDto;
import com.kamco.cd.kamcoback.gukyuin.dto.GukYuinStatus;
import com.kamco.cd.kamcoback.postgres.repository.gukyuin.GukYuinRepository;
import java.util.List;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class GukYuinJobCoreService {
private final GukYuinRepository gukYuinRepository;
public GukYuinJobCoreService(GukYuinRepository gukYuinRepository) {
this.gukYuinRepository = gukYuinRepository;
}
@Transactional
public void updateGukYuinApplyStateComplete(Long id, GukYuinStatus status) {
gukYuinRepository.updateGukYuinApplyStateComplete(id, status);
}
public List<LearnKeyDto> findGukyuinApplyStatusUidList(List<String> gukYuinStatus) {
return gukYuinRepository.findGukyuinApplyStatusUidList(gukYuinStatus);
}
}

View File

@@ -0,0 +1,26 @@
package com.kamco.cd.kamcoback.postgres.core;
import com.kamco.cd.kamcoback.gukyuin.dto.GukYuinDto.GeomUidDto;
import com.kamco.cd.kamcoback.postgres.repository.gukyuin.GukYuinLabelJobRepository;
import java.util.List;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class GukYuinLabelJobCoreService {
private final GukYuinLabelJobRepository gukYuinLabelRepository;
public GukYuinLabelJobCoreService(GukYuinLabelJobRepository gukYuinLabelRepository) {
this.gukYuinLabelRepository = gukYuinLabelRepository;
}
public List<GeomUidDto> findYesterdayLabelingCompleteList() {
return gukYuinLabelRepository.findYesterdayLabelingCompleteList();
}
@Transactional
public void updateAnalDataInferenceGeomSendDttm(Long geoUid) {
gukYuinLabelRepository.updateAnalDataInferenceGeomSendDttm(geoUid);
}
}

View File

@@ -0,0 +1,44 @@
package com.kamco.cd.kamcoback.postgres.core;
import com.kamco.cd.kamcoback.gukyuin.dto.ChngDetectMastDto.LearnKeyDto;
import com.kamco.cd.kamcoback.gukyuin.dto.GukYuinStatus;
import com.kamco.cd.kamcoback.postgres.repository.gukyuin.GukYuinPnuJobRepository;
import java.util.List;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class GukYuinPnuJobCoreService {
private final GukYuinPnuJobRepository gukYuinPnuRepository;
public GukYuinPnuJobCoreService(GukYuinPnuJobRepository gukYuinPnuRepository) {
this.gukYuinPnuRepository = gukYuinPnuRepository;
}
public void updateGukYuinApplyStateComplete(Long id, GukYuinStatus status) {
gukYuinPnuRepository.updateGukYuinApplyStateComplete(id, status);
}
public List<LearnKeyDto> findGukyuinApplyStatusUidList(List<String> gukYuinStatus) {
return gukYuinPnuRepository.findGukyuinApplyStatusUidList(gukYuinStatus);
}
public long upsertMapSheetDataAnalGeomPnu(String chnDtctObjtId, String[] pnuList) {
return gukYuinPnuRepository.upsertMapSheetDataAnalGeomPnu(chnDtctObjtId, pnuList);
}
@Transactional
public void updateInferenceGeomDataPnuCnt(String chnDtctObjtId, long pnuCnt) {
gukYuinPnuRepository.updateInferenceGeomDataPnuCnt(chnDtctObjtId, pnuCnt);
}
public Long findMapSheetAnalDataInferenceGeomUid(String chnDtctObjtId) {
return gukYuinPnuRepository.findMapSheetAnalDataInferenceGeomUid(chnDtctObjtId);
}
@Transactional
public void insertGeoUidPnuData(Long geoUid, String[] pnuList, String chnDtctObjtId) {
gukYuinPnuRepository.insertGeoUidPnuData(geoUid, pnuList, chnDtctObjtId);
}
}

View File

@@ -0,0 +1,75 @@
package com.kamco.cd.kamcoback.postgres.core;
import com.kamco.cd.kamcoback.gukyuin.dto.ChngDetectMastDto.LearnKeyDto;
import com.kamco.cd.kamcoback.gukyuin.dto.ChngDetectMastDto.RlbDtctMastDto;
import com.kamco.cd.kamcoback.postgres.entity.PnuEntity;
import com.kamco.cd.kamcoback.postgres.repository.gukyuin.GukYuinStbltJobRepository;
import java.time.ZonedDateTime;
import java.util.List;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class GukYuinStbltJobCoreService {
private final GukYuinStbltJobRepository gukYuinStbltRepository;
public GukYuinStbltJobCoreService(GukYuinStbltJobRepository gukYuinStbltRepository) {
this.gukYuinStbltRepository = gukYuinStbltRepository;
}
public List<LearnKeyDto> findGukYuinEligibleForSurveyList(String status) {
return gukYuinStbltRepository.findGukYuinEligibleForSurveyList(status);
}
@Transactional
public void updateGukYuinEligibleForSurvey(String resultUid, RlbDtctMastDto stbltDto) {
String chnDtctObjtId = "";
PnuEntity entity =
gukYuinStbltRepository.findPnuEntityByResultUid(resultUid, stbltDto.getPnu());
if (entity != null) {
chnDtctObjtId = resultUid;
entity.setPnuDtctId(stbltDto.getPnuDtctId());
entity.setPnu(stbltDto.getPnu());
entity.setLrmSyncYmd(stbltDto.getLrmSyncYmd());
entity.setPnuSyncYmd(stbltDto.getPnuSyncYmd());
entity.setMpqdNo(stbltDto.getMpqdNo());
entity.setCprsYr(stbltDto.getCprsYr());
entity.setCrtrYr(stbltDto.getCrtrYr());
entity.setChnDtctSno(stbltDto.getChnDtctSno());
entity.setChnDtctId(stbltDto.getChnDtctId());
entity.setChnDtctMstId(stbltDto.getChnDtctMstId());
entity.setChnDtctObjtId(stbltDto.getChnDtctObjtId());
entity.setChnDtctContId(stbltDto.getChnDtctContId());
entity.setChnCd(stbltDto.getChnCd());
entity.setBfClsCd(stbltDto.getBfClsCd());
entity.setBfClsProb(stbltDto.getBfClsProb());
entity.setAfClsCd(stbltDto.getAfClsCd());
entity.setAfClsProb(stbltDto.getAfClsProb());
entity.setPnuSqms(stbltDto.getPnuSqms());
entity.setPnuDtctSqms(stbltDto.getPnuDtctSqms());
entity.setChnDtctSqms(stbltDto.getChnDtctSqms());
entity.setStbltYn(stbltDto.getStbltYn());
entity.setIncyCd(stbltDto.getIncyCd());
entity.setIncyRsnCont(stbltDto.getIncyRsnCont());
entity.setLockYn(stbltDto.getLockYn());
entity.setLblYn(stbltDto.getLblYn());
entity.setChgYn(stbltDto.getChgYn());
entity.setRsatctNo(stbltDto.getRsatctNo());
entity.setRmk(stbltDto.getRmk());
entity.setCrtDt(stbltDto.getCrtDt());
entity.setCrtEpno(stbltDto.getCrtEpno());
entity.setCrtIp(stbltDto.getCrtIp());
entity.setChgDt(stbltDto.getChgDt());
entity.setChgIp(stbltDto.getChgIp());
entity.setDelYn(stbltDto.getDelYn().equals("Y"));
entity.setCreatedDttm(ZonedDateTime.now());
gukYuinStbltRepository.save(entity);
//
}
}
}

View File

@@ -37,10 +37,12 @@ import jakarta.persistence.EntityNotFoundException;
import jakarta.validation.constraints.NotNull;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Objects;
import java.util.UUID;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
@@ -81,23 +83,29 @@ public class InferenceResultCoreService {
* @param req
*/
public UUID saveInferenceInfo(InferenceResultDto.RegReq req, List<MngListDto> targetList) {
String firstMapSheetName = null;
String mapSheetName = "";
int detectingCnt = 0;
for (MngListDto dto : targetList) {
if (detectingCnt == 0) {
firstMapSheetName = dto.getMapSheetName();
}
detectingCnt++;
}
List<MngListDto> distinctList =
targetList.stream()
.filter(dto -> dto.getMapSheetName() != null && !dto.getMapSheetName().isBlank())
.collect(
Collectors.toMap(
MngListDto::getMapSheetName,
dto -> dto,
(existing, duplicate) -> existing,
LinkedHashMap::new))
.values()
.stream()
.toList();
int detectingCnt = distinctList.size();
String mapSheetName;
if (detectingCnt == 0) {
mapSheetName = "";
} else if (detectingCnt == 1) {
mapSheetName = firstMapSheetName + " 1건";
mapSheetName = distinctList.get(0).getMapSheetName() + " 1건";
} else {
mapSheetName = firstMapSheetName + "" + (detectingCnt - 1) + "";
mapSheetName = distinctList.get(0).getMapSheetName() + "" + (detectingCnt - 1) + "";
}
MapSheetLearnEntity mapSheetLearnEntity = new MapSheetLearnEntity();
@@ -113,6 +121,8 @@ public class InferenceResultCoreService {
mapSheetLearnEntity.setCreatedUid(userUtil.getId());
mapSheetLearnEntity.setMapSheetCnt(mapSheetName);
mapSheetLearnEntity.setDetectingCnt(0L);
mapSheetLearnEntity.setTotalJobs((long) targetList.size());
// 회차는 국유인 반영할때 update로 변경됨
// mapSheetLearnEntity.setStage(
// mapSheetLearnRepository.getLearnStage(req.getCompareYyyy(), req.getTargetYyyy()));

View File

@@ -0,0 +1,332 @@
package com.kamco.cd.kamcoback.postgres.core;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.kamco.cd.kamcoback.common.enums.LayerType;
import com.kamco.cd.kamcoback.common.exception.CustomApiException;
import com.kamco.cd.kamcoback.common.utils.UserUtil;
import com.kamco.cd.kamcoback.layer.dto.LayerDto;
import com.kamco.cd.kamcoback.layer.dto.LayerDto.IsMapYn;
import com.kamco.cd.kamcoback.layer.dto.LayerDto.LayerMapDto;
import com.kamco.cd.kamcoback.layer.dto.LayerDto.OrderReq;
import com.kamco.cd.kamcoback.layer.dto.LayerDto.TileUrlDto;
import com.kamco.cd.kamcoback.layer.dto.WmsDto.WmsAddDto;
import com.kamco.cd.kamcoback.layer.dto.WmtsDto.WmtsAddDto;
import com.kamco.cd.kamcoback.postgres.entity.MapLayerEntity;
import com.kamco.cd.kamcoback.postgres.repository.layer.MapLayerRepository;
import java.time.ZonedDateTime;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;
import java.util.function.Function;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
@Service
@RequiredArgsConstructor
public class MapLayerCoreService {
private final MapLayerRepository mapLayerRepository;
private final UserUtil userUtil;
private final ObjectMapper objectMapper;
/**
* 지도 레이어 관리 목록
*
* @return
*/
public List<LayerDto.Basic> getLayers(LayerDto.SearchReq searchReq) {
return mapLayerRepository.findAllLayer(searchReq);
}
/**
* 지도 레이어 상세 목록
*
* @param uuid
* @return
*/
public LayerDto.Detail getLayers(UUID uuid) {
MapLayerEntity entity =
mapLayerRepository
.findDetailByUuid(uuid)
.orElseThrow(() -> new CustomApiException("NOT_FOUND_DATA", HttpStatus.NOT_FOUND));
return entity.toDto();
}
/**
* 삭제
*
* @param uuid
*/
public void delete(UUID uuid) {
MapLayerEntity entity =
mapLayerRepository
.findDetailByUuid(uuid)
.orElseThrow(() -> new CustomApiException("NOT_FOUND_DATA", HttpStatus.NOT_FOUND));
entity.setIsDeleted(true);
entity.setUpdatedUid(userUtil.getId());
entity.setUpdatedDttm(ZonedDateTime.now());
}
/**
* 수정
*
* @param uuid
*/
public void update(UUID uuid, LayerDto.Detail dto) {
MapLayerEntity entity =
mapLayerRepository
.findDetailByUuid(uuid)
.orElseThrow(() -> new CustomApiException("NOT_FOUND_DATA", HttpStatus.NOT_FOUND));
if (dto.getDescription() != null) {
entity.setDescription(dto.getDescription());
}
if (dto.getUrl() != null) {
entity.setUrl(dto.getUrl());
}
if (dto.getTag() != null) {
entity.setTag(dto.getTag());
}
if (dto.getMinLon() != null) {
entity.setMinLon(dto.getMinLon());
}
if (dto.getMaxLon() != null) {
entity.setMaxLon(dto.getMaxLon());
}
if (dto.getMinLat() != null) {
entity.setMinLat(dto.getMinLat());
}
if (dto.getMaxLat() != null) {
entity.setMaxLat(dto.getMaxLat());
}
if (dto.getMin() != null) {
entity.setMinZoom(dto.getMin());
}
if (dto.getMax() != null) {
entity.setMaxZoom(dto.getMax());
}
if (dto.getIsChangeMap() != null) {
entity.setIsChangeMap(dto.getIsChangeMap());
}
if (dto.getIsLabelingMap() != null) {
entity.setIsLabelingMap(dto.getIsLabelingMap());
}
if (dto.getCrs() != null) {
entity.setCrs(dto.getCrs());
}
entity.setUpdatedUid(userUtil.getId());
entity.setUpdatedDttm(ZonedDateTime.now());
}
/**
* 맵 노출 여부
*
* @param uuid
* @param isMapYn
*/
public void updateIsMap(UUID uuid, IsMapYn isMapYn) {
MapLayerEntity entity =
mapLayerRepository
.findDetailByUuid(uuid)
.orElseThrow(() -> new CustomApiException("NOT_FOUND_DATA", HttpStatus.NOT_FOUND));
LayerDto.MapType mapType;
try {
mapType = LayerDto.MapType.valueOf(isMapYn.getMapType());
} catch (IllegalArgumentException e) {
throw new CustomApiException("BAD_REQUEST", HttpStatus.BAD_REQUEST);
}
switch (mapType) {
case CHANGE_MAP -> entity.setIsChangeMap(isMapYn.getIsMapYn());
case LABELING_MAP -> entity.setIsLabelingMap(isMapYn.getIsMapYn());
}
}
/**
* 순서 수정
*
* @param dtoList
*/
public void orderUpdate(List<OrderReq> dtoList) {
if (dtoList == null || dtoList.isEmpty()) {
return;
}
List<UUID> uuids =
dtoList.stream().map(OrderReq::getUuid).filter(Objects::nonNull).distinct().toList();
if (uuids.isEmpty()) {
throw new CustomApiException("BAD_REQUEST", HttpStatus.BAD_REQUEST);
}
List<MapLayerEntity> entities = mapLayerRepository.findAllByUuidIn(uuids);
Map<UUID, MapLayerEntity> entityMap =
entities.stream().collect(Collectors.toMap(MapLayerEntity::getUuid, Function.identity()));
List<UUID> notFound = uuids.stream().filter(u -> !entityMap.containsKey(u)).toList();
if (!notFound.isEmpty()) {
throw new CustomApiException("NOT_FOUND_DATA", HttpStatus.NOT_FOUND);
}
Long uid = userUtil.getId();
ZonedDateTime now = ZonedDateTime.now();
for (OrderReq dto : dtoList) {
if (dto.getOrder() == null) {
throw new CustomApiException("INVALID_REQUEST", HttpStatus.BAD_REQUEST);
}
MapLayerEntity entity = entityMap.get(dto.getUuid());
entity.setOrder(dto.getOrder());
entity.setUpdatedUid(uid);
entity.setUpdatedDttm(now);
}
}
/**
* Tile 저장
*
* @param dto
*/
public UUID saveTile(LayerDto.AddReq dto) {
Long order = mapLayerRepository.findSortOrderDesc();
MapLayerEntity mapLayerEntity = new MapLayerEntity();
mapLayerEntity.setDescription(dto.getDescription());
mapLayerEntity.setUrl(dto.getUrl());
mapLayerEntity.setTag(dto.getTag());
mapLayerEntity.setMinLon(dto.getMinLon());
mapLayerEntity.setMinLat(dto.getMinLat());
mapLayerEntity.setMaxLon(dto.getMaxLon());
mapLayerEntity.setMaxLat(dto.getMaxLat());
mapLayerEntity.setMinZoom(dto.getMin());
mapLayerEntity.setMaxZoom(dto.getMax());
mapLayerEntity.setCrs(dto.getCrs());
mapLayerEntity.setCreatedUid(userUtil.getId());
mapLayerEntity.setIsChangeMap(true);
mapLayerEntity.setIsLabelingMap(true);
mapLayerEntity.setOrder(order + 1);
mapLayerEntity.setLayerType(LayerType.TILE.getId());
mapLayerEntity.setUpdatedDttm(ZonedDateTime.now());
return mapLayerRepository.save(mapLayerEntity).getUuid();
}
/**
* GeoJson 저장
*
* @param addDto
* @return
*/
public UUID saveGeoJson(LayerDto.AddReq addDto) {
Long order = mapLayerRepository.findSortOrderDesc();
MapLayerEntity mapLayerEntity = new MapLayerEntity();
mapLayerEntity.setDescription(addDto.getDescription());
mapLayerEntity.setUrl(addDto.getUrl());
mapLayerEntity.setTag(addDto.getTag());
mapLayerEntity.setCreatedUid(userUtil.getId());
mapLayerEntity.setIsChangeMap(true);
mapLayerEntity.setIsLabelingMap(true);
mapLayerEntity.setLayerType(LayerType.GEOJSON.getId());
mapLayerEntity.setCrs(addDto.getCrs());
mapLayerEntity.setUpdatedDttm(ZonedDateTime.now());
mapLayerEntity.setOrder(order + 1);
return mapLayerRepository.save(mapLayerEntity).getUuid();
}
/**
* wmts 저장
*
* @param addDto
*/
public UUID saveWmts(WmtsAddDto addDto) {
Long order = mapLayerRepository.findSortOrderDesc();
String rawJson = "";
try {
rawJson = objectMapper.writeValueAsString(addDto.getWmtsLayerInfo()); // data 없는 형태로 저장
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
MapLayerEntity mapLayerEntity = new MapLayerEntity();
mapLayerEntity.setTitle(addDto.getTitle());
mapLayerEntity.setDescription(addDto.getDescription());
mapLayerEntity.setCreatedUid(userUtil.getId());
mapLayerEntity.setRawJson(rawJson);
mapLayerEntity.setIsChangeMap(true);
mapLayerEntity.setIsLabelingMap(true);
mapLayerEntity.setOrder(order + 1);
mapLayerEntity.setLayerType(LayerType.WMTS.getId());
mapLayerEntity.setUpdatedDttm(ZonedDateTime.now());
mapLayerEntity.setTag(addDto.getTag());
return mapLayerRepository.save(mapLayerEntity).getUuid();
}
/**
* wms 저장
*
* @param addDto
* @return
*/
public UUID saveWms(WmsAddDto addDto) {
Long order = mapLayerRepository.findSortOrderDesc();
String rawJson = "";
try {
rawJson = objectMapper.writeValueAsString(addDto.getWmsLayerInfo()); // data 없는 형태로 저장
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
MapLayerEntity mapLayerEntity = new MapLayerEntity();
mapLayerEntity.setTitle(addDto.getTitle());
mapLayerEntity.setDescription(addDto.getDescription());
mapLayerEntity.setCreatedUid(userUtil.getId());
mapLayerEntity.setRawJson(rawJson);
mapLayerEntity.setIsChangeMap(true);
mapLayerEntity.setIsLabelingMap(true);
mapLayerEntity.setOrder(order + 1);
mapLayerEntity.setLayerType(LayerType.WMS.getId());
mapLayerEntity.setUpdatedDttm(ZonedDateTime.now());
mapLayerEntity.setTag(addDto.getTag());
return mapLayerRepository.save(mapLayerEntity).getUuid();
}
public List<LayerMapDto> findLayerMapList(String type) {
return mapLayerRepository.findLayerMapList(type);
}
public LayerDto.YearTileDto getChangeDetectionTileUrl(Integer beforeYear, Integer afterYear) {
return mapLayerRepository.getChangeDetectionTileUrl(beforeYear, afterYear);
}
public TileUrlDto getChangeDetectionTileOneYearUrl(Integer year) {
return mapLayerRepository.getChangeDetectionTileOneYearUrl(year);
}
}

View File

@@ -16,6 +16,7 @@ import com.kamco.cd.kamcoback.postgres.entity.MapSheetMngEntity;
import com.kamco.cd.kamcoback.postgres.entity.MapSheetMngHstEntity;
import com.kamco.cd.kamcoback.postgres.entity.YearEntity;
import com.kamco.cd.kamcoback.postgres.repository.mapsheet.MapSheetMngRepository;
import com.kamco.cd.kamcoback.postgres.repository.mapsheet.MapSheetMngYearRepository;
import jakarta.persistence.EntityNotFoundException;
import jakarta.validation.Valid;
import java.io.IOException;
@@ -29,6 +30,7 @@ import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.UUID;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
@@ -43,6 +45,7 @@ import org.springframework.transaction.annotation.Transactional;
public class MapSheetMngCoreService {
private final MapSheetMngRepository mapSheetMngRepository;
private final MapSheetMngYearRepository mapSheetMngYearRepository;
@Value("${spring.profiles.active}")
private String activeEnv;
@@ -159,6 +162,9 @@ public class MapSheetMngCoreService {
saved.getMngYyyy(), saved.getMngPath());
mapSheetMngRepository.updateYearState(saved.getMngYyyy(), "DONE");
// 년도별 Tile 정보 등록
mapSheetMngRepository.insertMapSheetMngTile(addReq);
return hstCnt;
}
@@ -309,15 +315,23 @@ public class MapSheetMngCoreService {
return mapSheetMngRepository.findByHstMapSheetTargetList(mngYyyy, mapIds);
}
public void updateMapSheetMngHstUploadId(Long hstUid, UUID uuid, String uploadId) {
mapSheetMngRepository.updateMapSheetMngHstUploadId(hstUid, uuid, uploadId);
}
/** 변화탐지 실행 가능 비교년도 저장 */
public void saveSheetMngYear() {
mapSheetMngYearRepository.saveFileInfo();
}
/**
* 변화탐지 실행 가능 비교년도 조회
*
* @param mngYyyy
* @param mapId
* @return
* @param mngYyyy 비교년도
* @param mapId 5k 도엽번호
* @return List<MngListCompareDto>
*/
public List<MngListCompareDto> getByHstMapSheetCompareList(int mngYyyy, List<String> mapId) {
return mapSheetMngRepository.findByHstMapSheetCompareList(mngYyyy, mapId);
return mapSheetMngYearRepository.findByHstMapSheetCompareList(mngYyyy, mapId);
}
;
}

View File

@@ -1,6 +1,8 @@
package com.kamco.cd.kamcoback.postgres.core;
import com.kamco.cd.kamcoback.common.enums.CommonUseStatus;
import com.kamco.cd.kamcoback.postgres.entity.MapSheetMngFileEntity;
import com.kamco.cd.kamcoback.postgres.repository.mapsheet.MapSheetMngYearRepository;
import com.kamco.cd.kamcoback.postgres.repository.scheduler.MapSheetMngFileJobRepository;
import com.kamco.cd.kamcoback.scheduler.dto.MapSheetMngDto;
import com.kamco.cd.kamcoback.scheduler.dto.MapSheetMngDto.MngHstDto;
@@ -15,6 +17,7 @@ import org.springframework.stereotype.Service;
public class MapSheetMngFileJobCoreService {
private final MapSheetMngFileJobRepository mapSheetMngFileJobRepository;
private final MapSheetMngYearRepository mapSheetMngYearRepository;
public Page<MapSheetMngDto.MngDto> findMapSheetMngList(
MapSheetMngDto.@Valid MngSearchReq searchReq) {
@@ -69,7 +72,11 @@ public class MapSheetMngFileJobCoreService {
strtYyyy, endYyyy, mapSheetNum);
}
public void updateException5kMapSheet(String mapSheetNum) {
mapSheetMngFileJobRepository.updateException5kMapSheet(mapSheetNum);
public void updateException5kMapSheet(String mapSheetNum, CommonUseStatus commonUseStatus) {
mapSheetMngFileJobRepository.updateException5kMapSheet(mapSheetNum, commonUseStatus);
}
public void saveSheetMngYear() {
mapSheetMngYearRepository.saveFileInfo();
}
}

View File

@@ -0,0 +1,36 @@
package com.kamco.cd.kamcoback.postgres.core;
import com.kamco.cd.kamcoback.postgres.repository.scheduler.TrainingDataLabelJobRepository;
import com.kamco.cd.kamcoback.scheduler.dto.TrainingDataReviewJobDto.InspectorPendingDto;
import com.kamco.cd.kamcoback.scheduler.dto.TrainingDataReviewJobDto.Tasks;
import java.util.List;
import java.util.UUID;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
@Service
@RequiredArgsConstructor
public class TrainingDataLabelJobCoreService {
private final TrainingDataLabelJobRepository trainingDataLabelJobRepository;
public List<Tasks> findCompletedYesterdayUnassigned() {
return trainingDataLabelJobRepository.findCompletedYesterdayUnassigned();
}
public void assignReviewerBatch(List<UUID> assignmentUids, String reviewerId) {
trainingDataLabelJobRepository.assignReviewerBatch(assignmentUids, reviewerId);
}
public List<InspectorPendingDto> findInspectorPendingByRound(Long analUid) {
return trainingDataLabelJobRepository.findInspectorPendingByRound(analUid);
}
public void lockInspectors(Long analUid, List<String> reviewerIds) {
trainingDataLabelJobRepository.lockInspectors(analUid, reviewerIds);
}
public void updateGeomUidTestState(List<Long> geomUids) {
trainingDataLabelJobRepository.updateGeomUidTestState(geomUids);
}
}

View File

@@ -4,10 +4,7 @@ import com.kamco.cd.kamcoback.postgres.repository.scheduler.TrainingDataReviewJo
import com.kamco.cd.kamcoback.scheduler.dto.TrainingDataReviewJobDto.AnalCntInfo;
import com.kamco.cd.kamcoback.scheduler.dto.TrainingDataReviewJobDto.AnalMapSheetList;
import com.kamco.cd.kamcoback.scheduler.dto.TrainingDataReviewJobDto.CompleteLabelData;
import com.kamco.cd.kamcoback.scheduler.dto.TrainingDataReviewJobDto.InspectorPendingDto;
import com.kamco.cd.kamcoback.scheduler.dto.TrainingDataReviewJobDto.Tasks;
import java.util.List;
import java.util.UUID;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
@@ -17,34 +14,6 @@ public class TrainingDataReviewJobCoreService {
private final TrainingDataReviewJobRepository trainingDataReviewJobRepository;
public List<Tasks> findCompletedYesterdayUnassigned() {
return trainingDataReviewJobRepository.findCompletedYesterdayUnassigned();
}
public void assignReviewer(UUID assignmentUid, String reviewerId) {
trainingDataReviewJobRepository.assignReviewer(assignmentUid, reviewerId);
}
public void assignReviewerBatch(List<UUID> assignmentUids, String reviewerId) {
trainingDataReviewJobRepository.assignReviewerBatch(assignmentUids, reviewerId);
}
public Tasks findAssignmentTask(String assignmentUid) {
return trainingDataReviewJobRepository.findAssignmentTask(assignmentUid);
}
public List<InspectorPendingDto> findInspectorPendingByRound(Long analUid) {
return trainingDataReviewJobRepository.findInspectorPendingByRound(analUid);
}
public void lockInspectors(Long analUid, List<String> reviewerIds) {
trainingDataReviewJobRepository.lockInspectors(analUid, reviewerIds);
}
public void updateGeomUidTestState(List<Long> geomUids) {
trainingDataReviewJobRepository.updateGeomUidTestState(geomUids);
}
public List<CompleteLabelData> findCompletedYesterdayLabelingList(
Long analUid, String mapSheetNum) {
return trainingDataReviewJobRepository.findCompletedYesterdayLabelingList(analUid, mapSheetNum);

View File

@@ -0,0 +1,129 @@
package com.kamco.cd.kamcoback.postgres.entity;
import com.kamco.cd.kamcoback.layer.dto.LayerDto;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.SequenceGenerator;
import jakarta.persistence.Table;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import java.math.BigDecimal;
import java.time.ZonedDateTime;
import java.util.UUID;
import lombok.Getter;
import lombok.Setter;
import org.hibernate.annotations.ColumnDefault;
import org.hibernate.annotations.JdbcTypeCode;
import org.hibernate.type.SqlTypes;
@Getter
@Setter
@Entity
@Table(name = "tb_map_layer")
public class MapLayerEntity {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "tb_map_layer_id_gen")
@SequenceGenerator(
name = "tb_map_layer_id_gen",
sequenceName = "tb_map_layer_seq",
allocationSize = 1)
@Column(name = "id", nullable = false)
private Long id;
@Size(max = 20)
@NotNull
@Column(name = "layer_type", nullable = false, length = 20)
private String layerType;
@Size(max = 200)
@Column(name = "title", length = 200)
private String title;
@Column(name = "description", length = Integer.MAX_VALUE)
private String description;
@Column(name = "url", length = Integer.MAX_VALUE)
private String url;
@Column(name = "min_lon", precision = 10, scale = 7)
private BigDecimal minLon;
@Column(name = "min_lat", precision = 10, scale = 7)
private BigDecimal minLat;
@Column(name = "max_lon", precision = 10, scale = 7)
private BigDecimal maxLon;
@Column(name = "max_lat", precision = 10, scale = 7)
private BigDecimal maxLat;
@Column(name = "min_zoom")
private Short minZoom;
@Column(name = "max_zoom")
private Short maxZoom;
@Column(name = "raw_json", columnDefinition = "jsonb")
@JdbcTypeCode(SqlTypes.JSON)
private String rawJson;
@NotNull
@ColumnDefault("now()")
@Column(name = "created_dttm", nullable = false)
private ZonedDateTime createdDttm = ZonedDateTime.now();
@Column(name = "updated_dttm")
private ZonedDateTime updatedDttm;
@Column(name = "uuid")
private UUID uuid = UUID.randomUUID();
@Column(name = "created_uid")
private Long createdUid;
@Column(name = "updated_uid")
private Long updatedUid;
@Column(name = "is_change_map")
private Boolean isChangeMap;
@Column(name = "is_labeling_map")
private Boolean isLabelingMap;
@Column(name = "sort_order")
private Long order;
@Column(name = "tag")
private String tag;
@Column(name = "is_deleted")
private Boolean isDeleted = false;
@Column(name = "crs")
private String crs;
public LayerDto.Detail toDto() {
return new LayerDto.Detail(
this.uuid,
this.layerType,
this.title,
this.description,
this.tag,
this.order,
this.isChangeMap,
this.isLabelingMap,
this.url,
this.minLon,
this.minLat,
this.maxLon,
this.maxLat,
this.minZoom,
this.maxZoom,
this.createdDttm,
this.crs);
}
}

View File

@@ -156,6 +156,12 @@ public class MapSheetAnalDataInferenceGeomEntity {
@JoinColumn(name = "map_5k_id", referencedColumnName = "fid")
private MapInkx5kEntity map5k;
@Column(name = "label_send_dttm")
private ZonedDateTime labelSendDttm;
@Column(name = "lock_yn")
private String lockYn;
public InferenceDetailDto.DetailListEntity toEntity() {
DetectionClassification classification = DetectionClassification.fromString(classBeforeCd);
Clazzes comparedClazz = new Clazzes(classification, classBeforeProb);

View File

@@ -196,6 +196,12 @@ public class MapSheetLearnEntity {
@Column(name = "uid", nullable = false)
private String uid = UUID.randomUUID().toString().replace("-", "").toUpperCase();
@Column(name = "total_jobs")
private Long totalJobs;
@Column(name = "chn_dtct_mst_id")
private String chnDtctMstId;
public InferenceResultDto.ResultList toDto() {
return new InferenceResultDto.ResultList(
this.uuid,

View File

@@ -163,6 +163,9 @@ public class MapSheetMngHstEntity extends CommonDateEntity {
@Column(name = "uuid")
private UUID uuid;
@Column(name = "upload_id")
private String uploadId;
// 파일정보 업데이트
public void updateFileInfos(Long tifSizeBytes, Long tfwSizeBytes) {
tifSizeBytes = tifSizeBytes == null ? 0L : tifSizeBytes;

View File

@@ -0,0 +1,58 @@
package com.kamco.cd.kamcoback.postgres.entity;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import jakarta.validation.constraints.NotNull;
import java.math.BigDecimal;
import java.time.ZonedDateTime;
import lombok.Getter;
import lombok.Setter;
import org.hibernate.annotations.ColumnDefault;
@Getter
@Setter
@Entity
@Table(name = "tb_map_sheet_mng_tile")
public class MapSheetMngTileEntity {
@Id
@Column(name = "mng_yyyy", nullable = false)
private Integer mngYyyy;
@Column(name = "url", length = Integer.MAX_VALUE)
private String url;
@Column(name = "min_lon", precision = 10, scale = 7)
private BigDecimal minLon;
@Column(name = "min_lat", precision = 10, scale = 7)
private BigDecimal minLat;
@Column(name = "max_lon", precision = 10, scale = 7)
private BigDecimal maxLon;
@Column(name = "max_lat", precision = 10, scale = 7)
private BigDecimal maxLat;
@Column(name = "min_zoom")
private Short minZoom;
@Column(name = "max_zoom")
private Short maxZoom;
@Column(name = "tag")
private String tag;
@Column(name = "crs")
private String crs;
@NotNull
@ColumnDefault("now()")
@Column(name = "created_dttm", nullable = false)
private ZonedDateTime createdDttm = ZonedDateTime.now();
@Column(name = "updated_dttm")
private ZonedDateTime updatedDttm;
}

View File

@@ -0,0 +1,34 @@
package com.kamco.cd.kamcoback.postgres.entity;
import jakarta.persistence.Column;
import jakarta.persistence.EmbeddedId;
import jakarta.persistence.Entity;
import jakarta.persistence.Table;
import jakarta.validation.constraints.NotNull;
import java.time.ZonedDateTime;
import lombok.Getter;
import lombok.Setter;
import org.hibernate.annotations.ColumnDefault;
@Getter
@Setter
@Entity
@Table(name = "tb_map_sheet_mng_year_yn")
public class MapSheetMngYearYnEntity {
@EmbeddedId private MapSheetMngYearYnEntityId id;
@NotNull
@Column(name = "yn", nullable = false, length = Integer.MAX_VALUE)
private String yn;
@NotNull
@ColumnDefault("now()")
@Column(name = "created_dttm", nullable = false)
private ZonedDateTime createdDttm;
@NotNull
@ColumnDefault("now()")
@Column(name = "updated_dttm", nullable = false)
private ZonedDateTime updatedDttm;
}

View File

@@ -0,0 +1,46 @@
package com.kamco.cd.kamcoback.postgres.entity;
import jakarta.persistence.Column;
import jakarta.persistence.Embeddable;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import java.io.Serializable;
import java.util.Objects;
import lombok.Getter;
import lombok.Setter;
import org.hibernate.Hibernate;
@Getter
@Setter
@Embeddable
public class MapSheetMngYearYnEntityId implements Serializable {
private static final long serialVersionUID = 6282262062316057898L;
@Size(max = 20)
@NotNull
@Column(name = "map_sheet_num", nullable = false, length = 20)
private String mapSheetNum;
@NotNull
@Column(name = "mng_yyyy", nullable = false)
private Integer mngYyyy;
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || Hibernate.getClass(this) != Hibernate.getClass(o)) {
return false;
}
MapSheetMngYearYnEntityId entity = (MapSheetMngYearYnEntityId) o;
return Objects.equals(this.mngYyyy, entity.mngYyyy)
&& Objects.equals(this.mapSheetNum, entity.mapSheetNum);
}
@Override
public int hashCode() {
return Objects.hash(mngYyyy, mapSheetNum);
}
}

View File

@@ -12,7 +12,7 @@ import jakarta.persistence.SequenceGenerator;
import jakarta.persistence.Table;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import java.time.OffsetDateTime;
import java.time.ZonedDateTime;
import lombok.Getter;
import lombok.Setter;
import org.hibernate.annotations.ColumnDefault;
@@ -39,7 +39,7 @@ public class PnuEntity {
private String pnu;
@Column(name = "created_dttm")
private OffsetDateTime createdDttm;
private ZonedDateTime createdDttm;
@Column(name = "created_uid")
private Long createdUid;
@@ -47,4 +47,140 @@ public class PnuEntity {
@ColumnDefault("false")
@Column(name = "del_yn")
private Boolean delYn;
@Size(max = 40)
@Column(name = "pnu_dtct_id", length = 40)
private String pnuDtctId;
@Size(max = 10)
@Column(name = "lrm_sync_ymd", length = 10)
private String lrmSyncYmd;
@Size(max = 10)
@Column(name = "pnu_sync_ymd", length = 10)
private String pnuSyncYmd;
@Size(max = 20)
@Column(name = "mpqd_no", length = 20)
private String mpqdNo;
@Size(max = 10)
@Column(name = "cprs_yr", length = 10)
private String cprsYr;
@Size(max = 10)
@Column(name = "crtr_yr", length = 10)
private String crtrYr;
@Size(max = 255)
@Column(name = "chn_dtct_id")
private String chnDtctId;
@Size(max = 10)
@Column(name = "chn_dtct_mst_id", length = 10)
private String chnDtctMstId;
@Size(max = 255)
@Column(name = "chn_dtct_objt_id")
private String chnDtctObjtId;
@Size(max = 255)
@Column(name = "chn_dtct_cont_id")
private String chnDtctContId;
@Size(max = 50)
@Column(name = "chn_cd", length = 50)
private String chnCd;
@Size(max = 50)
@Column(name = "chn_dtct_prob", length = 50)
private String chnDtctProb;
@Size(max = 50)
@Column(name = "bf_cls_cd", length = 50)
private String bfClsCd;
@Size(max = 50)
@Column(name = "bf_cls_prob", length = 50)
private String bfClsProb;
@Size(max = 50)
@Column(name = "af_cls_cd", length = 50)
private String afClsCd;
@Size(max = 50)
@Column(name = "af_cls_prob", length = 50)
private String afClsProb;
@Size(max = 100)
@Column(name = "pnu_sqms", length = 100)
private String pnuSqms;
@Size(max = 100)
@Column(name = "pnu_dtct_sqms", length = 100)
private String pnuDtctSqms;
@Size(max = 100)
@Column(name = "chn_dtct_sqms", length = 100)
private String chnDtctSqms;
@Size(max = 1)
@Column(name = "stblt_yn", length = 1)
private String stbltYn;
@Size(max = 30)
@Column(name = "incy_cd", length = 30)
private String incyCd;
@Size(max = 255)
@Column(name = "incy_rsn_cont")
private String incyRsnCont;
@Size(max = 1)
@Column(name = "lock_yn", length = 1)
private String lockYn;
@Size(max = 1)
@Column(name = "lbl_yn", length = 1)
private String lblYn;
@Size(max = 1)
@Column(name = "chg_yn", length = 1)
private String chgYn;
@Size(max = 50)
@Column(name = "rsatct_no", length = 50)
private String rsatctNo;
@Size(max = 100)
@Column(name = "rmk", length = 100)
private String rmk;
@Size(max = 20)
@Column(name = "crt_dt", length = 20)
private String crtDt;
@Size(max = 20)
@Column(name = "crt_epno", length = 20)
private String crtEpno;
@Size(max = 20)
@Column(name = "crt_ip", length = 20)
private String crtIp;
@Size(max = 20)
@Column(name = "chg_dt", length = 20)
private String chgDt;
@Size(max = 20)
@Column(name = "chg_epno", length = 20)
private String chgEpno;
@Size(max = 20)
@Column(name = "chg_ip", length = 20)
private String chgIp;
@Size(max = 10)
@Column(name = "chn_dtct_sno", length = 10)
private String chnDtctSno;
}

View File

@@ -205,6 +205,9 @@ public class MapSheetLearnRepositoryImpl implements MapSheetLearnRepositoryCusto
mapSheetLearnEntity.m1PendingJobs,
mapSheetLearnEntity.m2PendingJobs,
mapSheetLearnEntity.m3PendingJobs,
mapSheetLearnEntity.m1RunningJobs,
mapSheetLearnEntity.m2RunningJobs,
mapSheetLearnEntity.m3RunningJobs,
mapSheetLearnEntity.m1CompletedJobs,
mapSheetLearnEntity.m2CompletedJobs,
mapSheetLearnEntity.m3CompletedJobs,
@@ -227,7 +230,8 @@ public class MapSheetLearnRepositoryImpl implements MapSheetLearnRepositoryCusto
mapSheetLearnEntity.mapSheetScope,
m1Model.modelVer.as("model1Ver"),
m2Model.modelVer.as("model2Ver"),
m3Model.modelVer.as("model3Ver")))
m3Model.modelVer.as("model3Ver"),
mapSheetLearnEntity.totalJobs))
.from(mapSheetLearnEntity)
.leftJoin(m1Model)
.on(m1Model.uuid.eq(mapSheetLearnEntity.m1ModelUuid))

View File

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

View File

@@ -0,0 +1,11 @@
package com.kamco.cd.kamcoback.postgres.repository.gukyuin;
import com.kamco.cd.kamcoback.gukyuin.dto.GukYuinDto.GeomUidDto;
import java.util.List;
public interface GukYuinLabelJobRepositoryCustom {
List<GeomUidDto> findYesterdayLabelingCompleteList();
void updateAnalDataInferenceGeomSendDttm(Long geoUid);
}

View File

@@ -0,0 +1,77 @@
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.QMapSheetAnalDataInferenceGeomEntity.mapSheetAnalDataInferenceGeomEntity;
import static com.kamco.cd.kamcoback.postgres.entity.QMapSheetAnalInferenceEntity.mapSheetAnalInferenceEntity;
import static com.kamco.cd.kamcoback.postgres.entity.QMapSheetLearnEntity.mapSheetLearnEntity;
import com.kamco.cd.kamcoback.gukyuin.dto.GukYuinDto.GeomUidDto;
import com.kamco.cd.kamcoback.gukyuin.dto.GukYuinStatus;
import com.kamco.cd.kamcoback.label.dto.LabelAllocateDto.InspectState;
import com.querydsl.core.types.Projections;
import com.querydsl.core.types.dsl.BooleanExpression;
import com.querydsl.jpa.impl.JPAQueryFactory;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Repository;
@Repository
@RequiredArgsConstructor
public class GukYuinLabelJobRepositoryImpl implements GukYuinLabelJobRepositoryCustom {
private final JPAQueryFactory queryFactory;
@PersistenceContext private EntityManager em;
@Override
public List<GeomUidDto> findYesterdayLabelingCompleteList() {
ZoneId zone = ZoneId.of("Asia/Seoul");
ZonedDateTime todayStart = ZonedDateTime.now(zone).toLocalDate().atStartOfDay(zone);
ZonedDateTime tomorrowStart = todayStart.plusDays(1);
ZonedDateTime yesterdayStart = todayStart.minusDays(1);
// BooleanExpression isYesterday =
// labelingAssignmentEntity
// .inspectStatDttm
// .goe(yesterdayStart)
// .and(labelingAssignmentEntity.inspectStatDttm.lt(todayStart));
BooleanExpression isYesterday =
labelingAssignmentEntity
.inspectStatDttm
.goe(todayStart)
.and(labelingAssignmentEntity.inspectStatDttm.lt(tomorrowStart));
return queryFactory
.select(
Projections.constructor(
GeomUidDto.class,
labelingAssignmentEntity.inferenceGeomUid,
mapSheetAnalDataInferenceGeomEntity.resultUid))
.from(labelingAssignmentEntity)
.innerJoin(mapSheetAnalDataInferenceGeomEntity)
.on(
labelingAssignmentEntity.inferenceGeomUid.eq(
mapSheetAnalDataInferenceGeomEntity.geoUid))
.innerJoin(mapSheetAnalInferenceEntity)
.on(labelingAssignmentEntity.analUid.eq(mapSheetAnalInferenceEntity.id))
.innerJoin(mapSheetLearnEntity)
.on(
mapSheetAnalInferenceEntity.learnId.eq(mapSheetLearnEntity.id),
mapSheetLearnEntity.applyStatus.in(
GukYuinStatus.GUK_COMPLETED.getId(), GukYuinStatus.PNU_COMPLETED.getId()))
.where(labelingAssignmentEntity.inspectState.eq(InspectState.COMPLETE.getId()), isYesterday)
.fetch();
}
@Override
public void updateAnalDataInferenceGeomSendDttm(Long geoUid) {
queryFactory
.update(mapSheetAnalDataInferenceGeomEntity)
.set(mapSheetAnalDataInferenceGeomEntity.labelSendDttm, ZonedDateTime.now())
.where(mapSheetAnalDataInferenceGeomEntity.geoUid.eq(geoUid))
.execute();
}
}

View File

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

View File

@@ -0,0 +1,20 @@
package com.kamco.cd.kamcoback.postgres.repository.gukyuin;
import com.kamco.cd.kamcoback.gukyuin.dto.ChngDetectMastDto.LearnKeyDto;
import com.kamco.cd.kamcoback.gukyuin.dto.GukYuinStatus;
import java.util.List;
public interface GukYuinPnuJobRepositoryCustom {
void updateInferenceGeomDataPnuCnt(String chnDtctObjtId, long pnuCnt);
Long findMapSheetAnalDataInferenceGeomUid(String chnDtctObjtId);
void insertGeoUidPnuData(Long geoUid, String[] pnuList, String chnDtctObjtId);
void updateGukYuinApplyStateComplete(Long id, GukYuinStatus status);
List<LearnKeyDto> findGukyuinApplyStatusUidList(List<String> gukYuinStatus);
long upsertMapSheetDataAnalGeomPnu(String uid, String[] pnuList);
}

View File

@@ -0,0 +1,123 @@
package com.kamco.cd.kamcoback.postgres.repository.gukyuin;
import static com.kamco.cd.kamcoback.postgres.entity.QMapSheetAnalDataInferenceGeomEntity.mapSheetAnalDataInferenceGeomEntity;
import static com.kamco.cd.kamcoback.postgres.entity.QMapSheetLearnEntity.mapSheetLearnEntity;
import static com.kamco.cd.kamcoback.postgres.entity.QPnuEntity.pnuEntity;
import com.kamco.cd.kamcoback.gukyuin.dto.ChngDetectMastDto.LearnKeyDto;
import com.kamco.cd.kamcoback.gukyuin.dto.GukYuinStatus;
import com.kamco.cd.kamcoback.postgres.entity.PnuEntity;
import com.querydsl.core.types.Projections;
import com.querydsl.jpa.impl.JPAQueryFactory;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import jakarta.transaction.Transactional;
import java.time.ZonedDateTime;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Repository;
@Repository
@RequiredArgsConstructor
public class GukYuinPnuJobRepositoryImpl implements GukYuinPnuJobRepositoryCustom {
private final JPAQueryFactory queryFactory;
@PersistenceContext private EntityManager em;
@Override
public void updateInferenceGeomDataPnuCnt(String chnDtctObjtId, long pnuCnt) {
queryFactory
.update(mapSheetAnalDataInferenceGeomEntity)
.set(mapSheetAnalDataInferenceGeomEntity.pnu, pnuCnt)
.where(mapSheetAnalDataInferenceGeomEntity.resultUid.eq(chnDtctObjtId))
.execute();
}
@Override
public Long findMapSheetAnalDataInferenceGeomUid(String chnDtctObjtId) {
return queryFactory
.select(mapSheetAnalDataInferenceGeomEntity.geoUid)
.from(mapSheetAnalDataInferenceGeomEntity)
.where(mapSheetAnalDataInferenceGeomEntity.resultUid.eq(chnDtctObjtId))
.fetchOne();
}
@Override
public void insertGeoUidPnuData(Long geoUid, String[] pnuList, String chnDtctObjtId) {
for (String pnu : pnuList) {
PnuEntity entity =
queryFactory
.selectFrom(pnuEntity)
.where(pnuEntity.pnu.eq(pnu), pnuEntity.chnDtctObjtId.eq(chnDtctObjtId))
.fetchOne();
if (entity == null) {
queryFactory
.insert(pnuEntity)
.columns(
pnuEntity.geo.geoUid, pnuEntity.pnu, pnuEntity.createdDttm, pnuEntity.chnDtctObjtId)
.values(geoUid, pnu, ZonedDateTime.now(), chnDtctObjtId)
.execute();
}
}
}
@Override
public List<LearnKeyDto> findGukyuinApplyStatusUidList(List<String> status) {
return queryFactory
.select(
Projections.constructor(
LearnKeyDto.class,
mapSheetLearnEntity.id,
mapSheetLearnEntity.uid,
mapSheetLearnEntity.chnDtctMstId))
.from(mapSheetLearnEntity)
.where(mapSheetLearnEntity.applyStatus.in(status))
.fetch();
}
@Override
public long upsertMapSheetDataAnalGeomPnu(String chnDtctObjtId, String[] pnuList) {
long length = pnuList.length;
queryFactory
.update(mapSheetAnalDataInferenceGeomEntity)
.set(mapSheetAnalDataInferenceGeomEntity.pnu, length)
.where(mapSheetAnalDataInferenceGeomEntity.resultUid.eq(chnDtctObjtId))
.execute();
Long geoUid =
queryFactory
.select(mapSheetAnalDataInferenceGeomEntity.geoUid)
.from(mapSheetAnalDataInferenceGeomEntity)
.where(mapSheetAnalDataInferenceGeomEntity.resultUid.eq(chnDtctObjtId))
.fetchOne();
long succCnt = 0;
for (String pnu : pnuList) {
long result =
queryFactory
.insert(pnuEntity)
.columns(
pnuEntity.geo.geoUid,
pnuEntity.pnu,
pnuEntity.createdDttm,
pnuEntity.chnDtctObjtId)
.values(geoUid, pnu, ZonedDateTime.now(), chnDtctObjtId)
.execute();
if (result > 0) {
succCnt++;
}
}
return succCnt;
}
@Override
@Transactional
public void updateGukYuinApplyStateComplete(Long id, GukYuinStatus status) {
queryFactory
.update(mapSheetLearnEntity)
.set(mapSheetLearnEntity.applyStatus, status.getId())
.set(mapSheetLearnEntity.applyStatusDttm, ZonedDateTime.now())
.where(mapSheetLearnEntity.id.eq(id))
.execute();
}
}

View File

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

View File

@@ -0,0 +1,48 @@
package com.kamco.cd.kamcoback.postgres.repository.gukyuin;
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.LearnKeyDto;
import com.kamco.cd.kamcoback.gukyuin.dto.GukYuinDto.GeomUidDto;
import com.kamco.cd.kamcoback.gukyuin.dto.GukYuinDto.LearnInfo;
import com.kamco.cd.kamcoback.gukyuin.dto.GukYuinStatus;
import java.time.LocalDate;
import java.util.List;
import java.util.UUID;
public interface GukYuinRepositoryCustom {
void updateGukYuinMastRegResult(Basic resultBody);
void updateGukYuinMastRegRemove(String chnDtctId);
void updateInferenceGeomDataPnuCnt(String chnDtctObjtId, long pnuCnt);
Long findMapSheetAnalDataInferenceGeomUid(String chnDtctObjtId);
void insertGeoUidPnuData(Long geoUid, String[] pnuList, String chnDtctObjtId);
void updateGukYuinApplyStateComplete(Long id, GukYuinStatus status);
List<LearnKeyDto> findGukyuinApplyStatusUidList(List<String> gukYuinStatus);
long upsertMapSheetDataAnalGeomPnu(String uid, String[] pnuList);
LearnInfo findMapSheetLearnInfo(UUID uuid);
Integer findMapSheetLearnYearStage(Integer compareYyyy, Integer targetYyyy);
void updateAnalInferenceApplyDttm(Basic registRes);
List<GeomUidDto> findYesterdayLabelingCompleteList();
void updateAnalDataInferenceGeomSendDttm(Long geoUid);
List<LabelSendDto> findLabelingCompleteSendList(LocalDate yesterday);
Long findMapSheetLearnInfoByYyyy(Integer compareYyyy, Integer targetYyyy, Integer maxStage);
void updateMapSheetLearnGukyuinEndStatus(Long learnId);
void updateMapSheetInferenceLabelEndStatus(Long learnId);
}

View File

@@ -0,0 +1,331 @@
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.QMapSheetAnalDataInferenceGeomEntity.mapSheetAnalDataInferenceGeomEntity;
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.QPnuEntity.pnuEntity;
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.LearnKeyDto;
import com.kamco.cd.kamcoback.gukyuin.dto.GukYuinDto.GeomUidDto;
import com.kamco.cd.kamcoback.gukyuin.dto.GukYuinDto.LearnInfo;
import com.kamco.cd.kamcoback.gukyuin.dto.GukYuinStatus;
import com.kamco.cd.kamcoback.label.dto.LabelAllocateDto.InspectState;
import com.kamco.cd.kamcoback.label.dto.LabelAllocateDto.LabelMngState;
import com.kamco.cd.kamcoback.postgres.entity.PnuEntity;
import com.querydsl.core.types.Projections;
import com.querydsl.core.types.dsl.BooleanExpression;
import com.querydsl.core.types.dsl.Expressions;
import com.querydsl.core.types.dsl.NumberExpression;
import com.querydsl.jpa.impl.JPAQueryFactory;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import jakarta.transaction.Transactional;
import java.time.LocalDate;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.List;
import java.util.UUID;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Repository;
@Repository
@RequiredArgsConstructor
public class GukYuinRepositoryImpl implements GukYuinRepositoryCustom {
private final JPAQueryFactory queryFactory;
@PersistenceContext private EntityManager em;
@Override
public void updateGukYuinMastRegResult(Basic resultBody) {
int excnPgrt = Integer.parseInt(resultBody.getExcnPgrt());
int stage = Integer.parseInt(resultBody.getChnDtctSno());
GukYuinStatus status = GukYuinStatus.IN_PROGRESS;
if (excnPgrt == 100) {
status = GukYuinStatus.GUK_COMPLETED;
}
queryFactory
.update(mapSheetLearnEntity)
.set(mapSheetLearnEntity.stage, stage)
.set(mapSheetLearnEntity.applyStatus, status.getId())
.set(mapSheetLearnEntity.applyStatusDttm, ZonedDateTime.now())
.set(mapSheetLearnEntity.chnDtctMstId, resultBody.getChnDtctMstId())
.set(mapSheetLearnEntity.applyYn, true)
.set(mapSheetLearnEntity.applyDttm, ZonedDateTime.now())
.where(mapSheetLearnEntity.uid.eq(resultBody.getChnDtctId()))
.execute();
}
@Override
public void updateGukYuinMastRegRemove(String chnDtctId) {
queryFactory
.update(mapSheetLearnEntity)
.set(mapSheetLearnEntity.applyStatus, GukYuinStatus.CANCELED.getId())
.set(mapSheetLearnEntity.applyStatusDttm, ZonedDateTime.now())
.set(mapSheetLearnEntity.applyYn, false)
.where(mapSheetLearnEntity.uid.eq(chnDtctId))
.execute();
}
@Override
public void updateInferenceGeomDataPnuCnt(String chnDtctObjtId, long pnuCnt) {
queryFactory
.update(mapSheetAnalDataInferenceGeomEntity)
.set(mapSheetAnalDataInferenceGeomEntity.pnu, pnuCnt)
.where(mapSheetAnalDataInferenceGeomEntity.resultUid.eq(chnDtctObjtId))
.execute();
}
@Override
public Long findMapSheetAnalDataInferenceGeomUid(String chnDtctObjtId) {
return queryFactory
.select(mapSheetAnalDataInferenceGeomEntity.geoUid)
.from(mapSheetAnalDataInferenceGeomEntity)
.where(mapSheetAnalDataInferenceGeomEntity.resultUid.eq(chnDtctObjtId))
.fetchOne();
}
@Override
public void insertGeoUidPnuData(Long geoUid, String[] pnuList, String chnDtctObjtId) {
for (String pnu : pnuList) {
PnuEntity entity =
queryFactory
.selectFrom(pnuEntity)
.where(
pnuEntity.geo.geoUid.eq(geoUid),
pnuEntity.pnu.eq(pnu),
pnuEntity.chnDtctObjtId.eq(chnDtctObjtId))
.fetchOne();
if (entity == null) {
queryFactory
.insert(pnuEntity)
.columns(
pnuEntity.geo.geoUid, pnuEntity.pnu, pnuEntity.createdDttm, pnuEntity.chnDtctObjtId)
.values(geoUid, pnu, ZonedDateTime.now(), chnDtctObjtId)
.execute();
}
}
}
@Override
public List<LearnKeyDto> findGukyuinApplyStatusUidList(List<String> status) {
return queryFactory
.select(
Projections.constructor(
LearnKeyDto.class,
mapSheetLearnEntity.id,
mapSheetLearnEntity.uid,
mapSheetLearnEntity.chnDtctMstId))
.from(mapSheetLearnEntity)
.where(mapSheetLearnEntity.applyStatus.in(status))
.fetch();
}
@Override
public long upsertMapSheetDataAnalGeomPnu(String chnDtctObjtId, String[] pnuList) {
long length = pnuList.length;
queryFactory
.update(mapSheetAnalDataInferenceGeomEntity)
.set(mapSheetAnalDataInferenceGeomEntity.pnu, length)
.where(mapSheetAnalDataInferenceGeomEntity.resultUid.eq(chnDtctObjtId))
.execute();
Long geoUid =
queryFactory
.select(mapSheetAnalDataInferenceGeomEntity.geoUid)
.from(mapSheetAnalDataInferenceGeomEntity)
.where(mapSheetAnalDataInferenceGeomEntity.resultUid.eq(chnDtctObjtId))
.fetchOne();
long succCnt = 0;
for (String pnu : pnuList) {
long result =
queryFactory
.insert(pnuEntity)
.columns(pnuEntity.geo.geoUid, pnuEntity.pnu, pnuEntity.createdDttm)
.values(geoUid, pnu, ZonedDateTime.now())
.execute();
if (result > 0) {
succCnt++;
}
}
return succCnt;
}
@Override
public LearnInfo findMapSheetLearnInfo(UUID uuid) {
return queryFactory
.select(
Projections.constructor(
LearnInfo.class,
mapSheetLearnEntity.id,
mapSheetLearnEntity.uuid,
mapSheetLearnEntity.compareYyyy,
mapSheetLearnEntity.targetYyyy,
mapSheetLearnEntity.stage,
mapSheetLearnEntity.uid,
mapSheetLearnEntity.applyStatus,
mapSheetLearnEntity.applyYn))
.from(mapSheetLearnEntity)
.where(mapSheetLearnEntity.uuid.eq(uuid))
.fetchOne();
}
@Override
public Integer findMapSheetLearnYearStage(Integer compareYyyy, Integer targetYyyy) {
NumberExpression<Integer> stageExpr =
Expressions.numberTemplate(Integer.class, "coalesce({0}, 0)", mapSheetLearnEntity.stage);
return queryFactory
.select(stageExpr.max().coalesce(0))
.from(mapSheetLearnEntity)
.where(
mapSheetLearnEntity.compareYyyy.eq(compareYyyy),
mapSheetLearnEntity.targetYyyy.eq(targetYyyy),
mapSheetLearnEntity.applyStatus.isNotNull(),
mapSheetLearnEntity.applyStatus.ne(GukYuinStatus.PENDING.getId()))
.fetchOne();
}
@Override
public void updateAnalInferenceApplyDttm(Basic registRes) {
Long learnId =
queryFactory
.select(mapSheetLearnEntity.id)
.from(mapSheetLearnEntity)
.where(mapSheetLearnEntity.uid.eq(registRes.getChnDtctId()))
.fetchOne();
queryFactory
.update(mapSheetAnalInferenceEntity)
.set(mapSheetAnalInferenceEntity.gukyuinUsed, "Y")
.set(mapSheetAnalInferenceEntity.gukyuinApplyDttm, ZonedDateTime.now())
.set(mapSheetAnalInferenceEntity.stage, Integer.parseInt(registRes.getChnDtctSno()))
.where(mapSheetAnalInferenceEntity.learnId.eq(learnId))
.execute();
}
@Override
public List<GeomUidDto> findYesterdayLabelingCompleteList() {
ZoneId zone = ZoneId.of("Asia/Seoul");
ZonedDateTime todayStart = ZonedDateTime.now(zone).toLocalDate().atStartOfDay(zone);
ZonedDateTime yesterdayStart = todayStart.minusDays(1);
BooleanExpression isYesterday =
labelingAssignmentEntity
.inspectStatDttm
.goe(yesterdayStart)
.and(labelingAssignmentEntity.inspectStatDttm.lt(todayStart));
return queryFactory
.select(
Projections.constructor(
GeomUidDto.class,
labelingAssignmentEntity.inferenceGeomUid,
mapSheetAnalDataInferenceGeomEntity.resultUid))
.from(labelingAssignmentEntity)
.innerJoin(mapSheetAnalDataInferenceGeomEntity)
.on(
labelingAssignmentEntity.inferenceGeomUid.eq(
mapSheetAnalDataInferenceGeomEntity.geoUid))
.innerJoin(mapSheetAnalInferenceEntity)
.on(labelingAssignmentEntity.analUid.eq(mapSheetAnalInferenceEntity.id))
.innerJoin(mapSheetLearnEntity)
.on(
mapSheetAnalInferenceEntity.learnId.eq(mapSheetLearnEntity.id),
mapSheetLearnEntity.applyStatus.in(
GukYuinStatus.GUK_COMPLETED.getId(), GukYuinStatus.PNU_COMPLETED.getId()))
.where(labelingAssignmentEntity.inspectState.eq(InspectState.COMPLETE.getId()), isYesterday)
.fetch();
}
@Override
public void updateAnalDataInferenceGeomSendDttm(Long geoUid) {
queryFactory
.update(mapSheetAnalDataInferenceGeomEntity)
.set(mapSheetAnalDataInferenceGeomEntity.labelSendDttm, ZonedDateTime.now())
.where(mapSheetAnalDataInferenceGeomEntity.geoUid.eq(geoUid))
.execute();
}
@Override
public List<LabelSendDto> findLabelingCompleteSendList(LocalDate yesterday) {
ZoneId zone = ZoneId.of("Asia/Seoul");
ZonedDateTime from = yesterday.atStartOfDay(zone);
ZonedDateTime to = from.plusDays(1);
BooleanExpression isYesterday =
labelingAssignmentEntity
.inspectStatDttm
.goe(from)
.and(labelingAssignmentEntity.inspectStatDttm.lt(to));
return queryFactory
.select(
Projections.constructor(
LabelSendDto.class,
mapSheetAnalDataInferenceGeomEntity.resultUid,
labelingAssignmentEntity.workerUid,
labelingAssignmentEntity.workStatDttm,
labelingAssignmentEntity.inspectorUid,
labelingAssignmentEntity.inspectStatDttm,
mapSheetAnalDataInferenceGeomEntity.labelSendDttm))
.from(labelingAssignmentEntity)
.innerJoin(mapSheetAnalDataInferenceGeomEntity)
.on(
labelingAssignmentEntity.inferenceGeomUid.eq(
mapSheetAnalDataInferenceGeomEntity.geoUid))
.where(labelingAssignmentEntity.inspectState.eq(InspectState.COMPLETE.getId()), isYesterday)
.fetch();
}
@Override
public Long findMapSheetLearnInfoByYyyy(
Integer compareYyyy, Integer targetYyyy, Integer maxStage) {
return queryFactory
.select(mapSheetLearnEntity.id)
.from(mapSheetLearnEntity)
.where(
mapSheetLearnEntity.compareYyyy.eq(compareYyyy),
mapSheetLearnEntity.targetYyyy.eq(targetYyyy),
mapSheetLearnEntity.stage.eq(maxStage))
.fetchOne();
}
@Override
public void updateMapSheetLearnGukyuinEndStatus(Long learnId) {
queryFactory
.update(mapSheetLearnEntity)
.set(mapSheetLearnEntity.applyStatus, GukYuinStatus.END.getId())
.set(mapSheetLearnEntity.applyStatusDttm, ZonedDateTime.now())
.where(mapSheetLearnEntity.id.eq(learnId))
.execute();
}
@Override
public void updateMapSheetInferenceLabelEndStatus(Long learnId) {
queryFactory
.update(mapSheetAnalInferenceEntity)
.set(mapSheetAnalInferenceEntity.analState, LabelMngState.FINISH.getId())
.set(mapSheetAnalInferenceEntity.updatedDttm, ZonedDateTime.now())
.where(mapSheetAnalInferenceEntity.learnId.eq(learnId))
.execute();
}
@Override
@Transactional
public void updateGukYuinApplyStateComplete(Long id, GukYuinStatus status) {
queryFactory
.update(mapSheetLearnEntity)
.set(mapSheetLearnEntity.applyStatus, status.getId())
.set(mapSheetLearnEntity.applyStatusDttm, ZonedDateTime.now())
.where(mapSheetLearnEntity.id.eq(id))
.execute();
}
}

View File

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

View File

@@ -0,0 +1,15 @@
package com.kamco.cd.kamcoback.postgres.repository.gukyuin;
import com.kamco.cd.kamcoback.gukyuin.dto.ChngDetectMastDto.LearnKeyDto;
import com.kamco.cd.kamcoback.gukyuin.dto.ChngDetectMastDto.RlbDtctMastDto;
import com.kamco.cd.kamcoback.postgres.entity.PnuEntity;
import java.util.List;
public interface GukYuinStbltJobRepositoryCustom {
List<LearnKeyDto> findGukYuinEligibleForSurveyList(String status);
void updateGukYuinEligibleForSurvey(String resultUid, RlbDtctMastDto stbltDto);
PnuEntity findPnuEntityByResultUid(String resultUid, String pnu);
}

View File

@@ -0,0 +1,93 @@
package com.kamco.cd.kamcoback.postgres.repository.gukyuin;
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.QMapSheetAnalInferenceEntity.mapSheetAnalInferenceEntity;
import static com.kamco.cd.kamcoback.postgres.entity.QMapSheetLearnEntity.mapSheetLearnEntity;
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.LearnKeyDto;
import com.kamco.cd.kamcoback.gukyuin.dto.ChngDetectMastDto.RlbDtctMastDto;
import com.kamco.cd.kamcoback.gukyuin.dto.GukYuinStatus;
import com.kamco.cd.kamcoback.postgres.entity.MapSheetAnalDataInferenceGeomEntity;
import com.kamco.cd.kamcoback.postgres.entity.PnuEntity;
import com.querydsl.core.types.Projections;
import com.querydsl.jpa.impl.JPAQueryFactory;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import java.time.ZonedDateTime;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Repository;
@Repository
@RequiredArgsConstructor
public class GukYuinStbltJobRepositoryImpl implements GukYuinStbltJobRepositoryCustom {
private final JPAQueryFactory queryFactory;
@PersistenceContext private EntityManager em;
@Override
public List<LearnKeyDto> findGukYuinEligibleForSurveyList(String status) {
return queryFactory
.select(
Projections.constructor(
LearnKeyDto.class,
mapSheetLearnEntity.id,
mapSheetLearnEntity.uid,
mapSheetLearnEntity.chnDtctMstId))
.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.applyStatus.eq(GukYuinStatus.PNU_COMPLETED.getId()),
mapSheetAnalDataInferenceGeomEntity.pnu.gt(0),
mapSheetAnalDataInferenceGeomEntity.fitState.isNull())
.groupBy(mapSheetLearnEntity.id, mapSheetLearnEntity.uid, mapSheetLearnEntity.chnDtctMstId)
.having(mapSheetAnalDataInferenceGeomEntity.geoUid.count().gt(1L))
.fetch();
}
@Override
public void updateGukYuinEligibleForSurvey(String resultUid, RlbDtctMastDto stbltDto) {
MapSheetAnalDataInferenceGeomEntity geomEntity =
queryFactory
.selectFrom(mapSheetAnalDataInferenceGeomEntity)
.where(mapSheetAnalDataInferenceGeomEntity.resultUid.eq(resultUid))
.fetchOne();
if (geomEntity != null) {
PnuEntity pnuEt =
queryFactory
.selectFrom(pnuEntity)
.where(pnuEntity.chnDtctObjtId.eq(resultUid))
.fetchFirst();
queryFactory
.update(mapSheetAnalDataInferenceGeomEntity)
.set(
mapSheetAnalDataInferenceGeomEntity.fitState,
pnuEt.getStbltYn().equals("Y")
? ImageryFitStatus.UNFIT.getId()
: ImageryFitStatus.FIT.getId()) // 적합여부가 Y 이면 부적합인 것, N 이면 적합한 것이라고 함
.set(mapSheetAnalDataInferenceGeomEntity.fitStateDttm, ZonedDateTime.now())
.set(mapSheetAnalDataInferenceGeomEntity.lockYn, stbltDto.getLockYn())
.where(mapSheetAnalDataInferenceGeomEntity.resultUid.eq(resultUid))
.execute();
}
}
@Override
public PnuEntity findPnuEntityByResultUid(String resultUid, String pnu) {
return queryFactory
.selectFrom(pnuEntity)
.where(pnuEntity.pnu.eq(pnu), pnuEntity.chnDtctObjtId.eq(resultUid))
.fetchOne();
}
}

View File

@@ -8,6 +8,7 @@ import static com.kamco.cd.kamcoback.postgres.entity.QMapSheetAnalDataInferenceG
import static com.kamco.cd.kamcoback.postgres.entity.QMapSheetAnalInferenceEntity.mapSheetAnalInferenceEntity;
import static com.kamco.cd.kamcoback.postgres.entity.QMemberEntity.memberEntity;
import com.kamco.cd.kamcoback.common.enums.ImageryFitStatus;
import com.kamco.cd.kamcoback.common.enums.StatusType;
import com.kamco.cd.kamcoback.label.dto.LabelAllocateDto;
import com.kamco.cd.kamcoback.label.dto.LabelAllocateDto.AllocateInfoDto;
@@ -82,7 +83,9 @@ public class LabelAllocateRepositoryImpl implements LabelAllocateRepositoryCusto
.innerJoin(mapSheetAnalDataInferenceGeomEntity)
.on(
mapSheetAnalDataInferenceEntity.id.eq(mapSheetAnalDataInferenceGeomEntity.dataUid),
mapSheetAnalDataInferenceGeomEntity.pnu.isNotNull(),
// mapSheetAnalDataInferenceGeomEntity.pnu.isNotNull(),
mapSheetAnalDataInferenceGeomEntity.pnu.gt(0),
mapSheetAnalDataInferenceGeomEntity.fitState.eq(ImageryFitStatus.UNFIT.getId()),
mapSheetAnalDataInferenceGeomEntity.labelState.isNull())
.where(
mapSheetAnalInferenceEntity.uuid.eq(uuid),
@@ -126,8 +129,8 @@ public class LabelAllocateRepositoryImpl implements LabelAllocateRepositoryCusto
"""
insert into tb_labeling_assignment
(assignment_uid, inference_geom_uid, worker_uid,
work_state, assign_group_id, anal_uid, pnu)
values (?, ?, ?, ?, ?, ?, ?)
work_state, assign_group_id, anal_uid)
values (?, ?, ?, ?, ?, ?)
""";
try (PreparedStatement ps = connection.prepareStatement(sql)) {
@@ -140,7 +143,6 @@ public class LabelAllocateRepositoryImpl implements LabelAllocateRepositoryCusto
ps.setString(4, LabelState.ASSIGNED.getId());
ps.setString(5, String.valueOf(info.getMapSheetNum()));
ps.setLong(6, analEntity.getId());
ps.setLong(7, info.getPnu());
ps.addBatch();
batchSize++;
@@ -190,7 +192,9 @@ public class LabelAllocateRepositoryImpl implements LabelAllocateRepositoryCusto
.innerJoin(mapSheetAnalDataInferenceGeomEntity)
.on(
mapSheetAnalDataInferenceEntity.id.eq(mapSheetAnalDataInferenceGeomEntity.dataUid),
mapSheetAnalDataInferenceGeomEntity.pnu.isNotNull(),
// mapSheetAnalDataInferenceGeomEntity.pnu.isNotNull(),
mapSheetAnalDataInferenceGeomEntity.pnu.gt(0),
mapSheetAnalDataInferenceGeomEntity.fitState.eq(ImageryFitStatus.UNFIT.getId()),
mapSheetAnalDataInferenceGeomEntity.labelState.isNull())
.where(mapSheetAnalInferenceEntity.uuid.eq(uuid))
.fetchOne();
@@ -381,9 +385,10 @@ public class LabelAllocateRepositoryImpl implements LabelAllocateRepositoryCusto
mapSheetAnalDataInferenceGeomEntity.compareYyyy.eq(analEntity.getCompareYyyy()),
mapSheetAnalDataInferenceGeomEntity.targetYyyy.eq(analEntity.getTargetYyyy()),
mapSheetAnalDataInferenceGeomEntity.stage.eq(analEntity.getStage()),
mapSheetAnalDataInferenceGeomEntity.pnu.isNotNull()
// mapSheetAnalDataInferenceGeomEntity.pnu.gt(0L)
// mapSheetAnalDataInferenceGeomEntity.passYn.isFalse() //TODO:
// mapSheetAnalDataInferenceGeomEntity.pnu.isNotNull()
mapSheetAnalDataInferenceGeomEntity.pnu.gt(0L),
mapSheetAnalDataInferenceGeomEntity.fitState.eq(
ImageryFitStatus.UNFIT.getId()) // TODO:
// 추후 라벨링 대상 조건 수정하기
)
.fetchOne();
@@ -555,11 +560,9 @@ public class LabelAllocateRepositoryImpl implements LabelAllocateRepositoryCusto
mapSheetAnalDataInferenceGeomEntity.dataUid))
.where(
mapSheetAnalInferenceEntity.uuid.eq(targetUuid),
mapSheetAnalDataInferenceGeomEntity.pnu.isNotNull()
// mapSheetAnalDataInferenceGeomEntity.pnu.gt(0L),
// mapSheetAnalDataInferenceGeomEntity.passYn.isFalse() //TODO: 추후 라벨링
// 대상 조건 수정하기
)
// mapSheetAnalDataInferenceGeomEntity.pnu.isNotNull()
mapSheetAnalDataInferenceGeomEntity.pnu.gt(0L),
mapSheetAnalDataInferenceGeomEntity.fitState.eq(ImageryFitStatus.UNFIT.getId()))
.fetchOne();
}
@@ -576,8 +579,7 @@ public class LabelAllocateRepositoryImpl implements LabelAllocateRepositoryCusto
queryFactory
.select(labelingAssignmentEntity.count())
.from(labelingAssignmentEntity)
.where(
analUidCondition, labelingAssignmentEntity.workState.in("ASSIGNED", "SKIP", "DONE"))
.where(analUidCondition, labelingAssignmentEntity.workState.in("SKIP", "DONE"))
.fetchOne();
Long skipCount =
@@ -602,6 +604,13 @@ public class LabelAllocateRepositoryImpl implements LabelAllocateRepositoryCusto
.where(analUidCondition, labelingAssignmentEntity.inspectState.eq("COMPLETE"))
.fetchOne();
Long inspectionExcept =
queryFactory
.select(labelingAssignmentEntity.count())
.from(labelingAssignmentEntity)
.where(analUidCondition, labelingAssignmentEntity.inspectState.eq("EXCEPT"))
.fetchOne();
Long inspectorCount =
queryFactory
.select(labelingAssignmentEntity.inspectorUid.countDistinct())
@@ -614,6 +623,7 @@ public class LabelAllocateRepositoryImpl implements LabelAllocateRepositoryCusto
long labelCompleted = labelingCompleted != null ? labelingCompleted : 0L;
long inspectCompleted = inspectionCompleted != null ? inspectionCompleted : 0L;
long skipped = skipCount != null ? skipCount : 0L;
long inspectExcepted = inspectionExcept != null ? inspectionExcept : 0L;
long labelingRemaining = labelingTotal - labelCompleted - skipped;
if (labelingRemaining < 0) {
@@ -621,7 +631,7 @@ public class LabelAllocateRepositoryImpl implements LabelAllocateRepositoryCusto
}
long inspectionTotal = labelingTotal;
long inspectionRemaining = inspectionTotal - inspectCompleted - skipped;
long inspectionRemaining = inspectionTotal - inspectCompleted - inspectExcepted;
if (inspectionRemaining < 0) {
inspectionRemaining = 0;
}
@@ -658,7 +668,7 @@ public class LabelAllocateRepositoryImpl implements LabelAllocateRepositoryCusto
.inspectionStatus(inspectionStatus)
.inspectionTotalCount(inspectionTotal)
.inspectionCompletedCount(inspectCompleted)
.inspectionSkipCount(skipped) // TODO
.inspectionSkipCount(inspectExcepted)
.inspectionRemainingCount(inspectionRemaining)
.inspectorCount(inspectorCount != null ? inspectorCount : 0L)
.progressRate(labelingRate)
@@ -740,11 +750,9 @@ public class LabelAllocateRepositoryImpl implements LabelAllocateRepositoryCusto
mapSheetAnalInferenceEntity.targetYyyy.eq(
mapSheetAnalDataInferenceGeomEntity.targetYyyy),
mapSheetAnalInferenceEntity.stage.eq(mapSheetAnalDataInferenceGeomEntity.stage),
mapSheetAnalDataInferenceGeomEntity.pnu.isNotNull()
// mapSheetAnalDataInferenceGeomEntity.pnu.gt(0),
// mapSheetAnalDataInferenceGeomEntity.passYn.isFalse() //TODO: 추후 라벨링 대상 조건
// 수정하기
)
// mapSheetAnalDataInferenceGeomEntity.pnu.isNotNull()
mapSheetAnalDataInferenceGeomEntity.pnu.gt(0),
mapSheetAnalDataInferenceGeomEntity.fitState.eq(ImageryFitStatus.UNFIT.getId()))
.where(mapSheetAnalInferenceEntity.id.eq(analEntity.getId()))
.groupBy(
mapSheetAnalInferenceEntity.analTitle,
@@ -1510,10 +1518,10 @@ public class LabelAllocateRepositoryImpl implements LabelAllocateRepositoryCusto
labelingAssignmentEntity.analUid.eq(analEntity.getId()),
labelingAssignmentEntity.workerUid.ne(userId))
.groupBy(memberEntity.userRole, memberEntity.employeeNo, memberEntity.name)
.having(
completeCnt
.multiply(2)
.goe(totalCnt)) // 진행률 평균 이상인 것들만 조회 => percent 를 바로 쓰면 having절에 무리가 갈 수 있다고 함
// .having(
// completeCnt
// .multiply(2)
// .goe(totalCnt)) // 진행률 평균 이상인 것들만 조회 => 조건 제거
.orderBy(
completeCnt
.desc()) // TODO: 현재는 잔여건수가 제일 적은(=완료건수가 높은) 순서로 desc, 추후 도엽번호? PNU? 로 정렬하여

View File

@@ -2,6 +2,7 @@ package com.kamco.cd.kamcoback.postgres.repository.label;
import static com.kamco.cd.kamcoback.postgres.entity.QMapSheetLearnEntity.mapSheetLearnEntity;
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.LabelState;
import com.kamco.cd.kamcoback.label.dto.LabelWorkDto;
@@ -294,14 +295,14 @@ public class LabelWorkRepositoryImpl implements LabelWorkRepositoryCustom {
if (searchReq.getSearchVal() != null && !searchReq.getSearchVal().isEmpty()) {
whereSubBuilder.and(
Expressions.stringTemplate("{0}", memberEntity.userId)
Expressions.stringTemplate("{0}", memberEntity.employeeNo)
.likeIgnoreCase("%" + searchReq.getSearchVal() + "%")
.or(
Expressions.stringTemplate("{0}", memberEntity.name)
.likeIgnoreCase("%" + searchReq.getSearchVal() + "%")));
}
whereSubBuilder.and(labelingAssignmentEntity.workerUid.eq(memberEntity.userId));
whereSubBuilder.and(labelingAssignmentEntity.workerUid.eq(memberEntity.employeeNo));
// 공통 조건 추출
BooleanExpression doneStateCondition =
@@ -344,13 +345,14 @@ public class LabelWorkRepositoryImpl implements LabelWorkRepositoryCustom {
WorkerState.class,
memberEntity.userRole,
memberEntity.name,
memberEntity.userId,
memberEntity.employeeNo,
assignedCnt.as("assignedCnt"),
doneCnt.as("doneCnt"),
skipCnt.as("skipCnt"),
day3AgoDoneCnt.as("day3AgoDoneCnt"),
day2AgoDoneCnt.as("day2AgoDoneCnt"),
day1AgoDoneCnt.as("day1AgoDoneCnt")))
day1AgoDoneCnt.as("day1AgoDoneCnt"),
memberEntity.status))
.from(labelingAssignmentEntity)
.innerJoin(mapSheetAnalInferenceEntity)
.on(
@@ -361,7 +363,11 @@ public class LabelWorkRepositoryImpl implements LabelWorkRepositoryCustom {
.innerJoin(memberEntity)
.on(whereSubBuilder)
.where(whereBuilder)
.groupBy(memberEntity.userRole, memberEntity.name, memberEntity.userId)
.groupBy(
memberEntity.userRole,
memberEntity.name,
memberEntity.employeeNo,
memberEntity.status)
.orderBy(orderSpecifiers.toArray(new OrderSpecifier[0]))
.offset(pageable.getOffset())
.limit(pageable.getPageSize())
@@ -439,14 +445,14 @@ public class LabelWorkRepositoryImpl implements LabelWorkRepositoryCustom {
if (searchReq.getSearchVal() != null && !searchReq.getSearchVal().isEmpty()) {
whereSubBuilder.and(
Expressions.stringTemplate("{0}", memberEntity.userId)
Expressions.stringTemplate("{0}", memberEntity.employeeNo)
.likeIgnoreCase("%" + searchReq.getSearchVal() + "%")
.or(
Expressions.stringTemplate("{0}", memberEntity.name)
.likeIgnoreCase("%" + searchReq.getSearchVal() + "%")));
}
whereSubBuilder.and(labelingAssignmentEntity.inspectorUid.eq(memberEntity.userId));
whereSubBuilder.and(labelingAssignmentEntity.inspectorUid.eq(memberEntity.employeeNo));
// 공통 조건 추출
BooleanExpression doneStateCondition =
@@ -490,13 +496,14 @@ public class LabelWorkRepositoryImpl implements LabelWorkRepositoryCustom {
WorkerState.class,
memberEntity.userRole,
memberEntity.name,
memberEntity.userId,
memberEntity.employeeNo,
assignedCnt.as("assignedCnt"),
doneCnt.as("doneCnt"),
skipCnt.as("skipCnt"),
day3AgoDoneCnt.as("day3AgoDoneCnt"),
day2AgoDoneCnt.as("day2AgoDoneCnt"),
day1AgoDoneCnt.as("day1AgoDoneCnt")))
day1AgoDoneCnt.as("day1AgoDoneCnt"),
memberEntity.status))
.from(labelingAssignmentEntity)
.innerJoin(mapSheetAnalInferenceEntity)
.on(
@@ -507,7 +514,11 @@ public class LabelWorkRepositoryImpl implements LabelWorkRepositoryCustom {
.innerJoin(memberEntity)
.on(whereSubBuilder)
.where(whereBuilder)
.groupBy(memberEntity.userRole, memberEntity.name, memberEntity.userId)
.groupBy(
memberEntity.userRole,
memberEntity.name,
memberEntity.employeeNo,
memberEntity.status)
.orderBy(orderSpecifiers.toArray(new OrderSpecifier[0]))
.offset(pageable.getOffset())
.limit(pageable.getPageSize())
@@ -545,7 +556,19 @@ public class LabelWorkRepositoryImpl implements LabelWorkRepositoryCustom {
@Override
public LabelWorkMngDetail findLabelWorkMngDetail(UUID uuid) {
NumberExpression<Long> labelTotCnt = mapSheetAnalDataInferenceGeomEntity.geoUid.count();
NumberExpression<Long> labelTotCnt =
new CaseBuilder()
// .when(mapSheetAnalDataInferenceGeomEntity.pnu.isNotNull())
.when(
mapSheetAnalDataInferenceGeomEntity
.pnu
.gt(0)
.and(
mapSheetAnalDataInferenceGeomEntity.fitState.eq(
ImageryFitStatus.UNFIT.getId())))
.then(1L)
.otherwise(0L)
.sum();
NumberExpression<Long> labelerCnt = labelingAssignmentEntity.workerUid.count();
NumberExpression<Long> reviewerCnt = labelingAssignmentEntity.inspectorUid.count();

View File

@@ -0,0 +1,7 @@
package com.kamco.cd.kamcoback.postgres.repository.layer;
import com.kamco.cd.kamcoback.postgres.entity.MapLayerEntity;
import org.springframework.data.jpa.repository.JpaRepository;
public interface MapLayerRepository
extends JpaRepository<MapLayerEntity, Long>, MapLayerRepositoryCustom {}

View File

@@ -0,0 +1,27 @@
package com.kamco.cd.kamcoback.postgres.repository.layer;
import com.kamco.cd.kamcoback.layer.dto.LayerDto;
import com.kamco.cd.kamcoback.layer.dto.LayerDto.LayerMapDto;
import com.kamco.cd.kamcoback.layer.dto.LayerDto.TileUrlDto;
import com.kamco.cd.kamcoback.postgres.entity.MapLayerEntity;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
public interface MapLayerRepositoryCustom {
Long findSortOrderDesc();
List<LayerDto.Basic> findAllLayer(LayerDto.SearchReq searchReq);
Optional<MapLayerEntity> findDetailByUuid(UUID uuid);
List<MapLayerEntity> findAllByUuidIn(Collection<UUID> uuids);
List<LayerMapDto> findLayerMapList(String type);
LayerDto.YearTileDto getChangeDetectionTileUrl(Integer beforeYear, Integer afterYear);
TileUrlDto getChangeDetectionTileOneYearUrl(Integer year);
}

View File

@@ -0,0 +1,228 @@
package com.kamco.cd.kamcoback.postgres.repository.layer;
import static com.kamco.cd.kamcoback.postgres.entity.QMapLayerEntity.mapLayerEntity;
import static com.kamco.cd.kamcoback.postgres.entity.QMapSheetMngTileEntity.mapSheetMngTileEntity;
import com.kamco.cd.kamcoback.layer.dto.LayerDto;
import com.kamco.cd.kamcoback.layer.dto.LayerDto.LayerMapDto;
import com.kamco.cd.kamcoback.layer.dto.LayerDto.TileUrlDto;
import com.kamco.cd.kamcoback.postgres.entity.MapLayerEntity;
import com.querydsl.core.BooleanBuilder;
import com.querydsl.core.types.Projections;
import com.querydsl.core.types.dsl.BooleanExpression;
import com.querydsl.core.types.dsl.Expressions;
import com.querydsl.core.types.dsl.NumberExpression;
import com.querydsl.jpa.impl.JPAQueryFactory;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Repository;
@Repository
@RequiredArgsConstructor
public class MapLayerRepositoryImpl implements MapLayerRepositoryCustom {
private final JPAQueryFactory queryFactory;
@Override
public Long findSortOrderDesc() {
return queryFactory
.select(mapLayerEntity.order.max().coalesce(0L))
.from(mapLayerEntity)
.where(mapLayerEntity.order.isNotNull())
.fetchOne();
}
@Override
public List<LayerDto.Basic> findAllLayer(LayerDto.SearchReq searchReq) {
BooleanBuilder whereBuilder = new BooleanBuilder();
whereBuilder.and(mapLayerEntity.isDeleted.isFalse());
if (searchReq != null) {
if (searchReq.getTag() != null) {
whereBuilder.and(mapLayerEntity.tag.toLowerCase().eq(searchReq.getTag().toLowerCase()));
}
if (searchReq.getLayerType() != null) {
whereBuilder.and(
mapLayerEntity.layerType.toLowerCase().eq(searchReq.getLayerType().toLowerCase()));
}
}
return queryFactory
.select(
Projections.constructor(
LayerDto.Basic.class,
mapLayerEntity.uuid,
mapLayerEntity.layerType,
mapLayerEntity.description,
mapLayerEntity.tag,
mapLayerEntity.order,
mapLayerEntity.isChangeMap,
mapLayerEntity.isLabelingMap,
mapLayerEntity.createdDttm))
.from(mapLayerEntity)
.where(whereBuilder)
.orderBy(mapLayerEntity.order.asc())
.fetch();
}
@Override
public Optional<MapLayerEntity> findDetailByUuid(UUID uuid) {
return Optional.ofNullable(
queryFactory
.select(mapLayerEntity)
.from(mapLayerEntity)
.where(mapLayerEntity.uuid.eq(uuid).and(mapLayerEntity.isDeleted.isFalse()))
.orderBy(mapLayerEntity.order.asc())
.fetchOne());
}
@Override
public List<MapLayerEntity> findAllByUuidIn(Collection<UUID> uuids) {
return queryFactory
.select(mapLayerEntity)
.from(mapLayerEntity)
.where(mapLayerEntity.uuid.in(uuids).and(mapLayerEntity.isDeleted.isFalse()))
.orderBy(mapLayerEntity.order.asc())
.fetch();
}
@Override
public List<LayerMapDto> findLayerMapList(String type) {
NumberExpression<Integer> crsInt =
Expressions.numberTemplate(
Integer.class, "cast(replace({0}, 'EPSG_', '') as integer)", mapLayerEntity.crs);
return queryFactory
.select(
Projections.constructor(
LayerMapDto.class,
mapLayerEntity.layerType,
mapLayerEntity.tag,
mapLayerEntity.order,
mapLayerEntity.url,
mapLayerEntity.minLon,
mapLayerEntity.minLat,
mapLayerEntity.maxLon,
mapLayerEntity.maxLat,
mapLayerEntity.minZoom,
mapLayerEntity.maxZoom,
Expressions.stringTemplate(
"ST_AsGeoJSON(ST_Transform(ST_MakeEnvelope({0}, {1}, {2}, {3}, 4326), {4}))",
mapLayerEntity.minLon,
mapLayerEntity.minLat,
mapLayerEntity.maxLon,
mapLayerEntity.maxLat,
crsInt),
mapLayerEntity.uuid,
Expressions.stringTemplate("cast({0} as text)", mapLayerEntity.rawJson),
mapLayerEntity.crs))
.from(mapLayerEntity)
.where(layerTypeCondition(type), mapLayerEntity.isDeleted.isFalse())
.orderBy(mapLayerEntity.order.asc())
.fetch();
}
@Override
public LayerDto.YearTileDto getChangeDetectionTileUrl(Integer beforeYear, Integer afterYear) {
NumberExpression<Integer> crsInt =
Expressions.numberTemplate(
Integer.class, "cast(replace({0}, 'EPSG_', '') as integer)", mapSheetMngTileEntity.crs);
LayerDto.TileUrlDto before =
queryFactory
.select(
Projections.constructor(
LayerDto.TileUrlDto.class,
mapSheetMngTileEntity.mngYyyy,
mapSheetMngTileEntity.tag,
mapSheetMngTileEntity.url,
mapSheetMngTileEntity.minLon,
mapSheetMngTileEntity.minLat,
mapSheetMngTileEntity.maxLon,
mapSheetMngTileEntity.maxLat,
mapSheetMngTileEntity.minZoom,
mapSheetMngTileEntity.maxZoom,
Expressions.stringTemplate(
"ST_AsGeoJSON(ST_Transform(ST_MakeEnvelope({0}, {1}, {2}, {3}, 4326), {4}))",
mapSheetMngTileEntity.minLon,
mapSheetMngTileEntity.minLat,
mapSheetMngTileEntity.maxLon,
mapSheetMngTileEntity.maxLat,
crsInt),
mapSheetMngTileEntity.crs))
.from(mapSheetMngTileEntity)
.where(mapSheetMngTileEntity.mngYyyy.eq(beforeYear))
.fetchOne();
LayerDto.TileUrlDto after =
queryFactory
.select(
Projections.constructor(
LayerDto.TileUrlDto.class,
mapSheetMngTileEntity.mngYyyy,
mapSheetMngTileEntity.tag,
mapSheetMngTileEntity.url,
mapSheetMngTileEntity.minLon,
mapSheetMngTileEntity.minLat,
mapSheetMngTileEntity.maxLon,
mapSheetMngTileEntity.maxLat,
mapSheetMngTileEntity.minZoom,
mapSheetMngTileEntity.maxZoom,
Expressions.stringTemplate(
"ST_AsGeoJSON(ST_Transform(ST_MakeEnvelope({0}, {1}, {2}, {3}, 4326), {4}))",
mapSheetMngTileEntity.minLon,
mapSheetMngTileEntity.minLat,
mapSheetMngTileEntity.maxLon,
mapSheetMngTileEntity.maxLat,
crsInt),
mapSheetMngTileEntity.crs))
.from(mapSheetMngTileEntity)
.where(mapSheetMngTileEntity.mngYyyy.eq(afterYear))
.fetchOne();
return new LayerDto.YearTileDto(before, after);
}
@Override
public TileUrlDto getChangeDetectionTileOneYearUrl(Integer year) {
NumberExpression<Integer> crsInt =
Expressions.numberTemplate(
Integer.class, "cast(replace({0}, 'EPSG_', '') as integer)", mapSheetMngTileEntity.crs);
return queryFactory
.select(
Projections.constructor(
LayerDto.TileUrlDto.class,
mapSheetMngTileEntity.mngYyyy,
mapSheetMngTileEntity.tag,
mapSheetMngTileEntity.url,
mapSheetMngTileEntity.minLon,
mapSheetMngTileEntity.minLat,
mapSheetMngTileEntity.maxLon,
mapSheetMngTileEntity.maxLat,
mapSheetMngTileEntity.minZoom,
mapSheetMngTileEntity.maxZoom,
Expressions.stringTemplate(
"ST_AsGeoJSON(ST_Transform(ST_MakeEnvelope({0}, {1}, {2}, {3}, 4326), {4}))",
mapSheetMngTileEntity.minLon,
mapSheetMngTileEntity.minLat,
mapSheetMngTileEntity.maxLon,
mapSheetMngTileEntity.maxLat,
crsInt),
mapSheetMngTileEntity.crs))
.from(mapSheetMngTileEntity)
.where(mapSheetMngTileEntity.mngYyyy.eq(year))
.fetchOne();
}
private BooleanExpression layerTypeCondition(String type) {
return type.equals("change-detection")
? mapLayerEntity.isChangeMap.isTrue()
: mapLayerEntity.isLabelingMap.isTrue();
}
}

View File

@@ -244,7 +244,7 @@ public class AuditLogRepositoryImpl extends QuerydslRepositorySupport
menuEntity.menuNm.as("menuName"),
auditLogEntity.eventType.as("eventType"),
Expressions.stringTemplate(
"to_char({0}, 'YYYY-MM-DD HH:mm')", auditLogEntity.createdDate)
"to_char({0}, 'YYYY-MM-DD HH24:MI')", auditLogEntity.createdDate)
.as("logDateTime"),
Projections.constructor(
AuditLogDto.LogDetail.class,
@@ -308,7 +308,7 @@ public class AuditLogRepositoryImpl extends QuerydslRepositorySupport
AuditLogDto.MenuDetail.class,
auditLogEntity.id.as("logId"),
Expressions.stringTemplate(
"to_char({0}, 'YYYY-MM-DD HH:mm')", auditLogEntity.createdDate)
"to_char({0}, 'YYYY-MM-DD HH24:MI')", auditLogEntity.createdDate)
.as("logDateTime"),
memberEntity.name.as("userName"),
memberEntity.employeeNo.as("loginId"),
@@ -375,7 +375,7 @@ public class AuditLogRepositoryImpl extends QuerydslRepositorySupport
AuditLogDto.UserDetail.class,
auditLogEntity.id.as("logId"),
Expressions.stringTemplate(
"to_char({0}, 'YYYY-MM-DD HH:mm')", auditLogEntity.createdDate)
"to_char({0}, 'YYYY-MM-DD HH24:MI')", auditLogEntity.createdDate)
.as("logDateTime"),
menuEntity.menuNm.as("menuName"),
auditLogEntity.eventType.as("eventType"),

View File

@@ -56,7 +56,7 @@ public class ErrorLogRepositoryImpl extends QuerydslRepositorySupport
errorLogEntity.errorMessage.as("errorMessage"),
errorLogEntity.stackTrace.as("errorDetail"),
Expressions.stringTemplate(
"to_char({0}, 'YYYY-MM-DD')", errorLogEntity.createdDate)))
"to_char({0}, 'YYYY-MM-DD HH24:MI:SS.FF3')", errorLogEntity.createdDate)))
.from(errorLogEntity)
.leftJoin(auditLogEntity)
.on(errorLogEntity.id.eq(auditLogEntity.errorLogUid))

View File

@@ -3,7 +3,7 @@ package com.kamco.cd.kamcoback.postgres.repository.mapsheet;
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.MngListCompareDto;
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.YearSearchReq;
import com.kamco.cd.kamcoback.postgres.entity.MapSheetMngHstEntity;
@@ -11,6 +11,7 @@ import com.kamco.cd.kamcoback.postgres.entity.YearEntity;
import jakarta.validation.Valid;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import org.springframework.data.domain.Page;
public interface MapSheetMngRepositoryCustom {
@@ -66,8 +67,6 @@ public interface MapSheetMngRepositoryCustom {
List<MngListDto> findByHstMapSheetTargetList(int mngYyyy, List<String> mapIds);
List<MngListCompareDto> findByHstMapSheetCompareList(int mngYyyy, List<String> mapId);
MapSheetMngDto.MngFilesDto findByFileUidMapSheetFile(Long fileUid);
void updateHstFileSizes(Long hstUid, long tifSizeBytes, long tfwSizeBytes, long totalSizeBytes);
@@ -77,4 +76,8 @@ public interface MapSheetMngRepositoryCustom {
Page<YearEntity> getYears(YearSearchReq req);
List<ImageFeature> getSceneInference(String yyyy, List<String> mapSheetNums);
void updateMapSheetMngHstUploadId(Long hstUid, UUID uuid, String uploadId);
void insertMapSheetMngTile(@Valid AddReq addReq);
}

View File

@@ -4,6 +4,7 @@ import static com.kamco.cd.kamcoback.postgres.entity.QMapInkx5kEntity.mapInkx5kE
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.QMapSheetMngHstEntity.mapSheetMngHstEntity;
import static com.kamco.cd.kamcoback.postgres.entity.QMapSheetMngTileEntity.mapSheetMngTileEntity;
import static com.kamco.cd.kamcoback.postgres.entity.QYearEntity.yearEntity;
import static com.querydsl.core.types.dsl.Expressions.nullExpression;
@@ -12,7 +13,7 @@ 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.MngListCompareDto;
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.YearSearchReq;
import com.kamco.cd.kamcoback.postgres.entity.MapSheetMngHstEntity;
@@ -34,6 +35,7 @@ import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.UUID;
import org.hibernate.query.Query;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
@@ -100,48 +102,55 @@ public class MapSheetMngRepositoryImpl extends QuerydslRepositorySupport
.then(1L)
.otherwise(0L)
.sum(),
new CaseBuilder()
.when(mapSheetMngHstEntity.syncState.eq("NOTPAIR"))
.then(1L)
.otherwise(0L)
.sum(),
new CaseBuilder()
.when(
mapSheetMngHstEntity
.syncCheckState
.eq("DONE")
mapInkx5kEntity
.useInference
.eq(CommonUseStatus.USE)
.and(mapSheetMngHstEntity.syncState.eq("NOTPAIR")))
.then(1L)
.otherwise(0L)
.sum(),
new CaseBuilder()
.when(mapSheetMngHstEntity.syncState.eq("DUPLICATE"))
.when(
mapInkx5kEntity
.useInference
.eq(CommonUseStatus.USE)
.and(
mapSheetMngHstEntity
.syncCheckState
.eq("DONE")
.and(mapSheetMngHstEntity.syncState.eq("NOTPAIR"))))
.then(1L)
.otherwise(0L)
.sum(),
new CaseBuilder()
.when(
mapSheetMngHstEntity
.syncCheckState
.eq("DONE")
mapInkx5kEntity
.useInference
.eq(CommonUseStatus.USE)
.and(mapSheetMngHstEntity.syncState.eq("DUPLICATE")))
.then(1L)
.otherwise(0L)
.sum(),
new CaseBuilder()
.when(
mapInkx5kEntity
.useInference
.eq(CommonUseStatus.USE)
.and(
mapSheetMngHstEntity
.syncState
.eq("TYPEERROR")
.or(mapSheetMngHstEntity.syncState.eq("SIZEERROR")))
.syncCheckState
.eq("DONE")
.and(mapSheetMngHstEntity.syncState.eq("DUPLICATE"))))
.then(1L)
.otherwise(0L)
.sum(),
new CaseBuilder()
.when(
mapSheetMngHstEntity
.syncCheckState
.eq("DONE")
mapInkx5kEntity
.useInference
.eq(CommonUseStatus.USE)
.and(
mapSheetMngHstEntity
.syncState
@@ -151,16 +160,43 @@ public class MapSheetMngRepositoryImpl extends QuerydslRepositorySupport
.otherwise(0L)
.sum(),
new CaseBuilder()
.when(mapSheetMngHstEntity.syncState.eq("NOFILE"))
.when(
mapInkx5kEntity
.useInference
.eq(CommonUseStatus.USE)
.and(
mapSheetMngHstEntity
.syncCheckState
.eq("DONE")
.and(
mapSheetMngHstEntity
.syncState
.eq("TYPEERROR")
.or(
mapSheetMngHstEntity.syncState.eq(
"SIZEERROR")))))
.then(1L)
.otherwise(0L)
.sum(),
new CaseBuilder()
.when(
mapInkx5kEntity
.useInference
.eq(CommonUseStatus.USE)
.and(mapSheetMngHstEntity.syncState.eq("NOFILE")))
.then(1L)
.otherwise(0L)
.sum(),
new CaseBuilder()
.when(
mapInkx5kEntity
.useInference
.eq(CommonUseStatus.USE)
.and(
mapSheetMngHstEntity
.syncCheckState
.eq("DONE")
.and(mapSheetMngHstEntity.syncState.eq("NOFILE")))
.and(mapSheetMngHstEntity.syncState.eq("NOFILE"))))
.then(1L)
.otherwise(0L)
.sum(),
@@ -169,7 +205,9 @@ public class MapSheetMngRepositoryImpl extends QuerydslRepositorySupport
.from(mapSheetMngEntity)
.leftJoin(mapSheetMngHstEntity)
.on(mapSheetMngEntity.mngYyyy.eq(mapSheetMngHstEntity.mngYyyy))
.where(whereBuilder)
.leftJoin(mapInkx5kEntity)
.on(mapSheetMngHstEntity.mapSheetNum.eq(mapInkx5kEntity.mapidcdNo))
.where(mapInkx5kEntity.useInference.eq(CommonUseStatus.USE))
// .offset(pageable.getOffset())
// .limit(pageable.getPageSize())
.orderBy(mapSheetMngEntity.mngYyyy.desc())
@@ -216,7 +254,9 @@ public class MapSheetMngRepositoryImpl extends QuerydslRepositorySupport
public MapSheetMngDto.MngDto findMapSheetMng(int mngYyyy) {
BooleanBuilder whereBuilder = new BooleanBuilder();
whereBuilder.and(mapSheetMngEntity.mngYyyy.eq(mngYyyy));
whereBuilder
.and(mapSheetMngEntity.mngYyyy.eq(mngYyyy))
.and(mapInkx5kEntity.useInference.eq(CommonUseStatus.USE));
MapSheetMngDto.MngDto foundContent =
queryFactory
@@ -244,48 +284,55 @@ public class MapSheetMngRepositoryImpl extends QuerydslRepositorySupport
.then(1L)
.otherwise(0L)
.sum(),
new CaseBuilder()
.when(mapSheetMngHstEntity.syncState.eq("NOTPAIR"))
.then(1L)
.otherwise(0L)
.sum(),
new CaseBuilder()
.when(
mapSheetMngHstEntity
.syncCheckState
.eq("DONE")
mapInkx5kEntity
.useInference
.eq(CommonUseStatus.USE)
.and(mapSheetMngHstEntity.syncState.eq("NOTPAIR")))
.then(1L)
.otherwise(0L)
.sum(),
new CaseBuilder()
.when(mapSheetMngHstEntity.syncState.eq("DUPLICATE"))
.when(
mapInkx5kEntity
.useInference
.eq(CommonUseStatus.USE)
.and(
mapSheetMngHstEntity
.syncCheckState
.eq("DONE")
.and(mapSheetMngHstEntity.syncState.eq("NOTPAIR"))))
.then(1L)
.otherwise(0L)
.sum(),
new CaseBuilder()
.when(
mapSheetMngHstEntity
.syncCheckState
.eq("DONE")
mapInkx5kEntity
.useInference
.eq(CommonUseStatus.USE)
.and(mapSheetMngHstEntity.syncState.eq("DUPLICATE")))
.then(1L)
.otherwise(0L)
.sum(),
new CaseBuilder()
.when(
mapInkx5kEntity
.useInference
.eq(CommonUseStatus.USE)
.and(
mapSheetMngHstEntity
.syncState
.eq("TYPEERROR")
.or(mapSheetMngHstEntity.syncState.eq("SIZEERROR")))
.syncCheckState
.eq("DONE")
.and(mapSheetMngHstEntity.syncState.eq("DUPLICATE"))))
.then(1L)
.otherwise(0L)
.sum(),
new CaseBuilder()
.when(
mapSheetMngHstEntity
.syncCheckState
.eq("DONE")
mapInkx5kEntity
.useInference
.eq(CommonUseStatus.USE)
.and(
mapSheetMngHstEntity
.syncState
@@ -295,16 +342,43 @@ public class MapSheetMngRepositoryImpl extends QuerydslRepositorySupport
.otherwise(0L)
.sum(),
new CaseBuilder()
.when(mapSheetMngHstEntity.syncState.eq("NOFILE"))
.when(
mapInkx5kEntity
.useInference
.eq(CommonUseStatus.USE)
.and(
mapSheetMngHstEntity
.syncCheckState
.eq("DONE")
.and(
mapSheetMngHstEntity
.syncState
.eq("TYPEERROR")
.or(
mapSheetMngHstEntity.syncState.eq(
"SIZEERROR")))))
.then(1L)
.otherwise(0L)
.sum(),
new CaseBuilder()
.when(
mapInkx5kEntity
.useInference
.eq(CommonUseStatus.USE)
.and(mapSheetMngHstEntity.syncState.eq("NOFILE")))
.then(1L)
.otherwise(0L)
.sum(),
new CaseBuilder()
.when(
mapInkx5kEntity
.useInference
.eq(CommonUseStatus.USE)
.and(
mapSheetMngHstEntity
.syncCheckState
.eq("DONE")
.and(mapSheetMngHstEntity.syncState.eq("NOFILE")))
.and(mapSheetMngHstEntity.syncState.eq("NOFILE"))))
.then(1L)
.otherwise(0L)
.sum(),
@@ -313,6 +387,8 @@ public class MapSheetMngRepositoryImpl extends QuerydslRepositorySupport
.from(mapSheetMngEntity)
.leftJoin(mapSheetMngHstEntity)
.on(mapSheetMngEntity.mngYyyy.eq(mapSheetMngHstEntity.mngYyyy))
.leftJoin(mapInkx5kEntity)
.on(mapSheetMngHstEntity.mapSheetNum.eq(mapInkx5kEntity.mapidcdNo))
.where(whereBuilder)
.groupBy(mapSheetMngEntity.mngYyyy)
.fetchOne();
@@ -388,7 +464,8 @@ public class MapSheetMngRepositoryImpl extends QuerydslRepositorySupport
mapSheetMngHstEntity.syncCheckTfwFileName,
mapSheetMngHstEntity.syncCheckTifFileName,
mapSheetMngHstEntity.mapSheetPath,
mapSheetMngHstEntity.uuid))
mapSheetMngHstEntity.uuid,
mapSheetMngHstEntity.uploadId))
.from(mapSheetMngHstEntity)
.innerJoin(mapInkx5kEntity)
.on(
@@ -454,7 +531,8 @@ public class MapSheetMngRepositoryImpl extends QuerydslRepositorySupport
mapSheetMngHstEntity.syncCheckTfwFileName,
mapSheetMngHstEntity.syncCheckTifFileName,
mapSheetMngHstEntity.mapSheetPath,
mapSheetMngHstEntity.uuid))
mapSheetMngHstEntity.uuid,
mapSheetMngHstEntity.uploadId))
.from(mapSheetMngHstEntity)
.innerJoin(mapInkx5kEntity)
.on(mapSheetMngHstEntity.mapSheetNum.eq(mapInkx5kEntity.mapidcdNo))
@@ -573,53 +651,6 @@ public class MapSheetMngRepositoryImpl extends QuerydslRepositorySupport
.fetch();
}
/**
* 변화탐지 실행 가능 비교년도 조회
*
* @param mngYyyy
* @param mapIds
* @return
*/
@Override
public List<MngListCompareDto> findByHstMapSheetCompareList(int mngYyyy, List<String> mapIds) {
String sql =
"""
SELECT
t.map_sheet_num,
t.map_years,
COALESCE(s.target_year, 0) AS before_year
FROM public.tb_map_sheet_years_map t
LEFT JOIN LATERAL (
SELECT x::int AS target_year
FROM unnest(string_to_array(t.map_years, '>')) AS x
WHERE x::int <= :mngYyyy
ORDER BY x::int DESC
LIMIT 1
) s ON true
INNER JOIN tb_map_inkx_5k tmik
ON t.map_sheet_num = tmik.mapidcd_no AND tmik.use_inference = 'USE'
WHERE t.map_sheet_num = ANY(:mapIds)
""";
@SuppressWarnings("unchecked")
List<Object[]> rows =
em.createNativeQuery(sql)
.setParameter("mngYyyy", mngYyyy)
.setParameter("mapIds", mapIds.toArray(new String[0]))
.getResultList();
return rows.stream()
.map(
r ->
new MngListCompareDto(
(String) r[1], // map_years
(String) r[0], // map_sheet_num
((Number) r[2]).intValue() // before_year
))
.toList();
}
@Override
public List<MapSheetMngDto.MngFilesDto> findHstUidToMapSheetFileList(Long hstUid) {
BooleanBuilder whereBuilder = new BooleanBuilder();
@@ -1003,6 +1034,46 @@ public class MapSheetMngRepositoryImpl extends QuerydslRepositorySupport
.fetch();
}
@Override
public void updateMapSheetMngHstUploadId(Long hstUid, UUID uuid, String uploadId) {
queryFactory
.update(mapSheetMngHstEntity)
.set(mapSheetMngHstEntity.uploadId, uploadId)
.set(mapSheetMngHstEntity.uuid, uuid)
.where(mapSheetMngHstEntity.hstUid.eq(hstUid))
.execute();
}
@Override
public void insertMapSheetMngTile(AddReq addReq) {
long execute =
queryFactory
.insert(mapSheetMngTileEntity)
.columns(
mapSheetMngTileEntity.mngYyyy,
mapSheetMngTileEntity.url,
mapSheetMngTileEntity.minLon,
mapSheetMngTileEntity.minLat,
mapSheetMngTileEntity.maxLon,
mapSheetMngTileEntity.maxLat,
mapSheetMngTileEntity.minZoom,
mapSheetMngTileEntity.maxZoom,
mapSheetMngTileEntity.tag,
mapSheetMngTileEntity.crs)
.values(
addReq.getMngYyyy(),
addReq.getUrl(),
addReq.getMinLon(),
addReq.getMinLat(),
addReq.getMaxLon(),
addReq.getMaxLat(),
addReq.getMinZoom(),
addReq.getMaxZoom(),
addReq.getTag(),
addReq.getCrs())
.execute();
}
private BooleanExpression eqYearStatus(QYearEntity years, String status) {
if (status == null) {
return null;

View File

@@ -0,0 +1,9 @@
package com.kamco.cd.kamcoback.postgres.repository.mapsheet;
import com.kamco.cd.kamcoback.postgres.entity.MapSheetMngYearYnEntity;
import com.kamco.cd.kamcoback.postgres.entity.MapSheetMngYearYnEntityId;
import org.springframework.data.jpa.repository.JpaRepository;
public interface MapSheetMngYearRepository
extends JpaRepository<MapSheetMngYearYnEntity, MapSheetMngYearYnEntityId>,
MapSheetMngYearRepositoryCustom {}

View File

@@ -0,0 +1,10 @@
package com.kamco.cd.kamcoback.postgres.repository.mapsheet;
import com.kamco.cd.kamcoback.mapsheet.dto.MapSheetMngDto.MngListCompareDto;
import java.util.List;
public interface MapSheetMngYearRepositoryCustom {
void saveFileInfo();
List<MngListCompareDto> findByHstMapSheetCompareList(int mngYyyy, List<String> mapIds);
}

View File

@@ -0,0 +1,101 @@
package com.kamco.cd.kamcoback.postgres.repository.mapsheet;
import com.kamco.cd.kamcoback.mapsheet.dto.MapSheetMngDto.MngListCompareDto;
import com.kamco.cd.kamcoback.postgres.entity.QMapSheetMngYearYnEntity;
import com.querydsl.core.types.Projections;
import com.querydsl.core.types.dsl.Expressions;
import com.querydsl.core.types.dsl.StringExpression;
import com.querydsl.jpa.impl.JPAQueryFactory;
import jakarta.persistence.EntityManager;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Repository;
@Repository
@RequiredArgsConstructor
public class MapSheetMngYearRepositoryImpl implements MapSheetMngYearRepositoryCustom {
private final JPAQueryFactory queryFactory;
private final EntityManager em;
/** 변화탐지 실행 가능 비교년도 저장 */
@Override
public void saveFileInfo() {
em.createNativeQuery("TRUNCATE TABLE tb_map_sheet_mng_year_yn").executeUpdate();
String sql =
"""
WITH bounds AS (
SELECT
map_sheet_num,
MIN(mng_yyyy::int) AS min_y,
MAX(mng_yyyy::int) AS max_y
FROM tb_map_sheet_mng_files
GROUP BY map_sheet_num
),
years AS (
SELECT
b.map_sheet_num,
gs.y AS mng_yyyy
FROM bounds b
CROSS JOIN LATERAL generate_series(b.min_y, b.max_y) AS gs(y)
),
exist AS (
SELECT DISTINCT
map_sheet_num,
mng_yyyy::int AS mng_yyyy
FROM tb_map_sheet_mng_files
),
src AS (
SELECT
y.map_sheet_num,
y.mng_yyyy,
CASE
WHEN e.map_sheet_num IS NULL THEN 'N'
ELSE 'Y'
END AS yn
FROM years y
LEFT JOIN exist e
ON e.map_sheet_num = y.map_sheet_num
AND e.mng_yyyy = y.mng_yyyy
)
INSERT INTO tb_map_sheet_mng_year_yn
(map_sheet_num, mng_yyyy, yn)
SELECT
map_sheet_num,
mng_yyyy,
yn
FROM src
ON CONFLICT (map_sheet_num, mng_yyyy)
DO UPDATE SET
yn = EXCLUDED.yn,
updated_dttm = now()
""";
em.createNativeQuery(sql).executeUpdate();
}
/**
* 변화탐지 실행 가능 비교년도 조회
*
* @param mngYyyy
* @param mapIds
* @return
*/
@Override
public List<MngListCompareDto> findByHstMapSheetCompareList(int mngYyyy, List<String> mapIds) {
QMapSheetMngYearYnEntity y = QMapSheetMngYearYnEntity.mapSheetMngYearYnEntity;
StringExpression mngYyyyStr = Expressions.stringTemplate("concat({0}, '')", mngYyyy);
return queryFactory
.select(
Projections.constructor(
MngListCompareDto.class, mngYyyyStr, y.id.mapSheetNum, y.id.mngYyyy.max()))
.from(y)
.where(y.id.mapSheetNum.in(mapIds), y.yn.eq("Y"), y.id.mngYyyy.loe(mngYyyy))
.groupBy(y.id.mapSheetNum)
.fetch();
}
}

View File

@@ -21,7 +21,7 @@ public class MemberInactiveJobRepositoryImpl implements MemberInactiveJobReposit
@Override
public List<MemberInfo> findInactiveLabelerReviewer() {
ZonedDateTime checkTime = ZonedDateTime.now(ZoneId.of("Asia/Seoul")).minusDays(14);
ZonedDateTime checkTime = ZonedDateTime.now(ZoneId.of("Asia/Seoul")).minusDays(28);
return queryFactory
.select(Projections.constructor(MemberInfo.class, memberEntity.id, memberEntity.employeeNo))
.from(memberEntity)

View File

@@ -1,5 +1,6 @@
package com.kamco.cd.kamcoback.postgres.repository.scheduler;
import com.kamco.cd.kamcoback.common.enums.CommonUseStatus;
import com.kamco.cd.kamcoback.scheduler.dto.MapSheetMngDto;
import com.kamco.cd.kamcoback.scheduler.dto.MapSheetMngDto.MngHstDto;
import java.util.List;
@@ -21,5 +22,5 @@ public interface MapSheetMngFileJobRepositoryCustom {
public Long findByHstMapSheetBeforeYyyyListCount(int strtYyyy, int endYyyy, String mapSheetNum);
public void updateException5kMapSheet(String mapSheetNum);
public void updateException5kMapSheet(String mapSheetNum, CommonUseStatus commonUseStatus);
}

View File

@@ -257,11 +257,12 @@ public class MapSheetMngFileJobRepositoryImpl extends QuerydslRepositorySupport
}
@Override
public void updateException5kMapSheet(String mapSheetNum) {
public void updateException5kMapSheet(String mapSheetNum, CommonUseStatus commonUseStatus) {
long updateCount =
queryFactory
.update(mapInkx5kEntity)
.set(mapInkx5kEntity.useInference, CommonUseStatus.AUTO_EXCEPT)
.set(mapInkx5kEntity.useInference, commonUseStatus)
.set(mapInkx5kEntity.modifiedDate, ZonedDateTime.now())
.where(mapInkx5kEntity.mapidcdNo.eq(mapSheetNum))
.execute();
}

View File

@@ -0,0 +1,23 @@
package com.kamco.cd.kamcoback.postgres.repository.scheduler;
import com.kamco.cd.kamcoback.postgres.entity.LabelingInspectorEntity;
import jakarta.persistence.LockModeType;
import java.util.List;
import java.util.UUID;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Lock;
import org.springframework.data.jpa.repository.Query;
public interface TrainingDataLabelJobRepository
extends JpaRepository<LabelingInspectorEntity, UUID>, TrainingDataLabelJobRepositoryCustom {
@Lock(LockModeType.PESSIMISTIC_WRITE)
@Query(
"""
select r
from LabelingInspectorEntity r
where r.analUid = :analUid
and r.inspectorUid in :inspectorUids
""")
List<LabelingInspectorEntity> lockInspectors(Long analUid, List<String> inspectorUids);
}

View File

@@ -0,0 +1,17 @@
package com.kamco.cd.kamcoback.postgres.repository.scheduler;
import com.kamco.cd.kamcoback.scheduler.dto.TrainingDataReviewJobDto.InspectorPendingDto;
import com.kamco.cd.kamcoback.scheduler.dto.TrainingDataReviewJobDto.Tasks;
import java.util.List;
import java.util.UUID;
public interface TrainingDataLabelJobRepositoryCustom {
List<Tasks> findCompletedYesterdayUnassigned();
List<InspectorPendingDto> findInspectorPendingByRound(Long analUid);
void assignReviewerBatch(List<UUID> assignmentUids, String reviewerId);
void updateGeomUidTestState(List<Long> geomUids);
}

View File

@@ -0,0 +1,119 @@
package com.kamco.cd.kamcoback.postgres.repository.scheduler;
import static com.kamco.cd.kamcoback.postgres.entity.QLabelingAssignmentEntity.labelingAssignmentEntity;
import static com.kamco.cd.kamcoback.postgres.entity.QLabelingInspectorEntity.labelingInspectorEntity;
import static com.kamco.cd.kamcoback.postgres.entity.QMapSheetAnalDataInferenceGeomEntity.mapSheetAnalDataInferenceGeomEntity;
import com.kamco.cd.kamcoback.label.dto.LabelAllocateDto.InspectState;
import com.kamco.cd.kamcoback.label.dto.LabelAllocateDto.LabelState;
import com.kamco.cd.kamcoback.postgres.entity.LabelingAssignmentEntity;
import com.kamco.cd.kamcoback.scheduler.dto.TrainingDataReviewJobDto.InspectorPendingDto;
import com.kamco.cd.kamcoback.scheduler.dto.TrainingDataReviewJobDto.Tasks;
import com.querydsl.core.types.Projections;
import com.querydsl.core.types.dsl.BooleanExpression;
import com.querydsl.core.types.dsl.Expressions;
import com.querydsl.core.types.dsl.StringExpression;
import com.querydsl.jpa.impl.JPAQueryFactory;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.List;
import java.util.UUID;
import org.springframework.data.jpa.repository.support.QuerydslRepositorySupport;
import org.springframework.stereotype.Repository;
@Repository
public class TrainingDataLabelJobRepositoryImpl extends QuerydslRepositorySupport
implements TrainingDataLabelJobRepositoryCustom {
private final JPAQueryFactory queryFactory;
private final StringExpression NULL_STRING = Expressions.stringTemplate("cast(null as text)");
public TrainingDataLabelJobRepositoryImpl(JPAQueryFactory queryFactory) {
super(LabelingAssignmentEntity.class);
this.queryFactory = queryFactory;
}
@Override
public List<Tasks> findCompletedYesterdayUnassigned() {
ZoneId zone = ZoneId.of("Asia/Seoul");
ZonedDateTime todayStart = ZonedDateTime.now(zone).toLocalDate().atStartOfDay(zone);
ZonedDateTime yesterdayStart = todayStart.minusDays(1);
BooleanExpression isYesterday =
labelingAssignmentEntity
.workStatDttm
.goe(yesterdayStart)
.and(labelingAssignmentEntity.workStatDttm.lt(todayStart));
return queryFactory
.select(
Projections.constructor(
Tasks.class,
labelingAssignmentEntity.assignmentUid,
labelingAssignmentEntity.inferenceGeomUid,
labelingAssignmentEntity.analUid))
.from(labelingAssignmentEntity)
.where(
labelingAssignmentEntity.workState.in(LabelState.SKIP.getId(), LabelState.DONE.getId()),
labelingAssignmentEntity.inspectorUid.isNull(),
isYesterday)
.orderBy(
labelingAssignmentEntity.analUid.asc(),
labelingAssignmentEntity.assignGroupId.asc(),
labelingAssignmentEntity.inferenceGeomUid.asc())
.fetch();
}
/**
* 해당 회차에 라벨링 할당받은 검수자별 완료 건수 count(), 완료한 게 적은 순으로 해야 일이 한 사람에게 몰리지 않음
*
* @param analUid
* @return
*/
@Override
public List<InspectorPendingDto> findInspectorPendingByRound(Long analUid) {
return queryFactory
.select(
Projections.constructor(
InspectorPendingDto.class,
labelingInspectorEntity.inspectorUid,
labelingAssignmentEntity.assignmentUid.count()))
.from(labelingInspectorEntity)
.leftJoin(labelingAssignmentEntity)
.on(
labelingInspectorEntity.inspectorUid.eq(labelingAssignmentEntity.inspectorUid),
labelingAssignmentEntity.inspectState.in(
InspectState.EXCEPT.getId(), InspectState.COMPLETE.getId()))
.where(labelingInspectorEntity.analUid.eq(analUid))
.groupBy(labelingInspectorEntity.inspectorUid)
.orderBy(labelingAssignmentEntity.assignmentUid.count().asc())
.fetch();
}
/**
* 배치용 여러 건 update
*
* @param assignmentUids
* @param reviewerId
*/
@Override
public void assignReviewerBatch(List<UUID> assignmentUids, String reviewerId) {
queryFactory
.update(labelingAssignmentEntity)
.set(labelingAssignmentEntity.inspectorUid, reviewerId)
.set(labelingAssignmentEntity.inspectState, InspectState.UNCONFIRM.getId())
.set(labelingAssignmentEntity.modifiedDate, ZonedDateTime.now())
.where(labelingAssignmentEntity.assignmentUid.in(assignmentUids))
.execute();
}
@Override
public void updateGeomUidTestState(List<Long> geomUids) {
queryFactory
.update(mapSheetAnalDataInferenceGeomEntity)
.set(mapSheetAnalDataInferenceGeomEntity.testState, InspectState.UNCONFIRM.getId())
.set(mapSheetAnalDataInferenceGeomEntity.updatedDttm, ZonedDateTime.now())
.where(mapSheetAnalDataInferenceGeomEntity.geoUid.in(geomUids))
.execute();
}
}

View File

@@ -3,25 +3,10 @@ package com.kamco.cd.kamcoback.postgres.repository.scheduler;
import com.kamco.cd.kamcoback.scheduler.dto.TrainingDataReviewJobDto.AnalCntInfo;
import com.kamco.cd.kamcoback.scheduler.dto.TrainingDataReviewJobDto.AnalMapSheetList;
import com.kamco.cd.kamcoback.scheduler.dto.TrainingDataReviewJobDto.CompleteLabelData;
import com.kamco.cd.kamcoback.scheduler.dto.TrainingDataReviewJobDto.InspectorPendingDto;
import com.kamco.cd.kamcoback.scheduler.dto.TrainingDataReviewJobDto.Tasks;
import java.util.List;
import java.util.UUID;
public interface TrainingDataReviewJobRepositoryCustom {
List<Tasks> findCompletedYesterdayUnassigned();
List<InspectorPendingDto> findInspectorPendingByRound(Long analUid);
void assignReviewer(UUID assignmentUid, String reviewerId);
void assignReviewerBatch(List<UUID> assignmentUids, String reviewerId);
Tasks findAssignmentTask(String assignmentUid);
void updateGeomUidTestState(List<Long> geomUids);
List<CompleteLabelData> findCompletedYesterdayLabelingList(Long analUid, String mapSheetNum);
List<AnalMapSheetList> findCompletedAnalMapSheetList(Long analUid);

View File

@@ -1,24 +1,18 @@
package com.kamco.cd.kamcoback.postgres.repository.scheduler;
import static com.kamco.cd.kamcoback.postgres.entity.QLabelingAssignmentEntity.labelingAssignmentEntity;
import static com.kamco.cd.kamcoback.postgres.entity.QLabelingInspectorEntity.labelingInspectorEntity;
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.QMapSheetLearnDataGeomEntity.mapSheetLearnDataGeomEntity;
import static com.kamco.cd.kamcoback.postgres.entity.QMapSheetLearnEntity.mapSheetLearnEntity;
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.postgres.entity.LabelingAssignmentEntity;
import com.kamco.cd.kamcoback.scheduler.dto.TrainingDataReviewJobDto.AnalCntInfo;
import com.kamco.cd.kamcoback.scheduler.dto.TrainingDataReviewJobDto.AnalMapSheetList;
import com.kamco.cd.kamcoback.scheduler.dto.TrainingDataReviewJobDto.CompleteLabelData;
import com.kamco.cd.kamcoback.scheduler.dto.TrainingDataReviewJobDto.CompleteLabelData.Properties;
import com.kamco.cd.kamcoback.scheduler.dto.TrainingDataReviewJobDto.InspectorPendingDto;
import com.kamco.cd.kamcoback.scheduler.dto.TrainingDataReviewJobDto.Tasks;
import com.querydsl.core.types.Projections;
import com.querydsl.core.types.dsl.BooleanExpression;
import com.querydsl.core.types.dsl.CaseBuilder;
import com.querydsl.core.types.dsl.Expressions;
import com.querydsl.core.types.dsl.NumberExpression;
@@ -29,9 +23,10 @@ import java.time.LocalDate;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.List;
import java.util.UUID;
import org.springframework.data.jpa.repository.support.QuerydslRepositorySupport;
import org.springframework.stereotype.Repository;
@Repository
public class TrainingDataReviewJobRepositoryImpl extends QuerydslRepositorySupport
implements TrainingDataReviewJobRepositoryCustom {
@@ -43,121 +38,6 @@ public class TrainingDataReviewJobRepositoryImpl extends QuerydslRepositorySuppo
this.queryFactory = queryFactory;
}
@Override
public List<Tasks> findCompletedYesterdayUnassigned() {
ZoneId zone = ZoneId.of("Asia/Seoul");
ZonedDateTime todayStart = ZonedDateTime.now(zone).toLocalDate().atStartOfDay(zone);
ZonedDateTime yesterdayStart = todayStart.minusDays(1);
BooleanExpression isYesterday =
labelingAssignmentEntity
.workStatDttm
.goe(yesterdayStart)
.and(labelingAssignmentEntity.workStatDttm.lt(todayStart));
return queryFactory
.select(
Projections.constructor(
Tasks.class,
labelingAssignmentEntity.assignmentUid,
labelingAssignmentEntity.inferenceGeomUid,
labelingAssignmentEntity.analUid))
.from(labelingAssignmentEntity)
.where(
labelingAssignmentEntity.workState.in(LabelState.SKIP.getId(), LabelState.DONE.getId()),
labelingAssignmentEntity.inspectorUid.isNull(),
isYesterday)
.orderBy(
labelingAssignmentEntity.analUid.asc(),
labelingAssignmentEntity.assignGroupId.asc(),
labelingAssignmentEntity.inferenceGeomUid.asc())
.fetch();
}
/**
* 해당 회차에 라벨링 할당받은 검수자별 완료 건수 count(), 완료한 게 적은 순으로 해야 일이 한 사람에게 몰리지 않음
*
* @param analUid
* @return
*/
@Override
public List<InspectorPendingDto> findInspectorPendingByRound(Long analUid) {
return queryFactory
.select(
Projections.constructor(
InspectorPendingDto.class,
labelingInspectorEntity.inspectorUid,
labelingAssignmentEntity.assignmentUid.count()))
.from(labelingInspectorEntity)
.leftJoin(labelingAssignmentEntity)
.on(
labelingInspectorEntity.inspectorUid.eq(labelingAssignmentEntity.inspectorUid),
labelingAssignmentEntity.inspectState.in(
InspectState.EXCEPT.getId(), InspectState.COMPLETE.getId()))
.where(labelingInspectorEntity.analUid.eq(analUid))
.groupBy(labelingInspectorEntity.inspectorUid)
.orderBy(labelingAssignmentEntity.assignmentUid.count().asc())
.fetch();
}
/**
* 실시간 분배용 1건 update
*
* @param assignmentUid
* @param reviewerId
*/
@Override
public void assignReviewer(UUID assignmentUid, String reviewerId) {
queryFactory
.update(labelingAssignmentEntity)
.set(labelingAssignmentEntity.inspectorUid, reviewerId)
.set(labelingAssignmentEntity.inspectState, InspectState.UNCONFIRM.getId())
.set(labelingAssignmentEntity.modifiedDate, ZonedDateTime.now())
.where(labelingAssignmentEntity.assignmentUid.eq(assignmentUid))
.execute();
}
/**
* 배치용 여러 건 update
*
* @param assignmentUids
* @param reviewerId
*/
@Override
public void assignReviewerBatch(List<UUID> assignmentUids, String reviewerId) {
queryFactory
.update(labelingAssignmentEntity)
.set(labelingAssignmentEntity.inspectorUid, reviewerId)
.set(labelingAssignmentEntity.inspectState, InspectState.UNCONFIRM.getId())
.set(labelingAssignmentEntity.modifiedDate, ZonedDateTime.now())
.where(labelingAssignmentEntity.assignmentUid.in(assignmentUids))
.execute();
}
@Override
public Tasks findAssignmentTask(String assignmentUid) {
return queryFactory
.select(
Projections.constructor(
Tasks.class,
labelingAssignmentEntity.assignmentUid,
labelingAssignmentEntity.inferenceGeomUid,
labelingAssignmentEntity.analUid))
.from(labelingAssignmentEntity)
.where(labelingAssignmentEntity.assignmentUid.eq(UUID.fromString(assignmentUid)))
.fetchOne();
}
@Override
public void updateGeomUidTestState(List<Long> geomUids) {
queryFactory
.update(mapSheetAnalDataInferenceGeomEntity)
.set(mapSheetAnalDataInferenceGeomEntity.testState, InspectState.UNCONFIRM.getId())
.set(mapSheetAnalDataInferenceGeomEntity.updatedDttm, ZonedDateTime.now())
.where(mapSheetAnalDataInferenceGeomEntity.geoUid.in(geomUids))
.execute();
}
@Override
public List<CompleteLabelData> findCompletedYesterdayLabelingList(
Long analUid, String mapSheetNum) {

View File

@@ -6,6 +6,7 @@ import static com.kamco.cd.kamcoback.postgres.entity.QMapInkx5kEntity.mapInkx5kE
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.QMapSheetLearnDataGeomEntity.mapSheetLearnDataGeomEntity;
import static com.kamco.cd.kamcoback.postgres.entity.QPnuEntity.pnuEntity;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
@@ -466,6 +467,14 @@ public class TrainingDataLabelRepositoryImpl extends QuerydslRepositorySupport
}
// 5. DTO 생성
// pnu list 조회
List<String> pnuList =
queryFactory
.select(pnuEntity.pnu)
.from(pnuEntity)
.where(pnuEntity.geo.geoUid.eq(mapSheetAnalDataInferenceGeomEntityEntity.getGeoUid()))
.fetch();
var changeDetectionInfo =
ChangeDetectionInfo.builder()
.mapSheetInfo(mapSheetEntity != null ? mapSheetEntity.getMapidNm() : "")
@@ -507,10 +516,7 @@ public class TrainingDataLabelRepositoryImpl extends QuerydslRepositorySupport
mapSheetAnalDataInferenceGeomEntityEntity.getCdProb() != null
? mapSheetAnalDataInferenceGeomEntityEntity.getCdProb()
: 0.0)
.pnu(
mapSheetAnalDataInferenceGeomEntityEntity.getPnu() != null
? mapSheetAnalDataInferenceGeomEntityEntity.getPnu()
: 0L)
.pnu(pnuList)
.mapSheetNum(
mapSheetAnalDataInferenceGeomEntityEntity.getMapSheetNum() != null
? mapSheetAnalDataInferenceGeomEntityEntity.getMapSheetNum()

View File

@@ -7,6 +7,7 @@ import static com.kamco.cd.kamcoback.postgres.entity.QMapSheetAnalDataInferenceG
import static com.kamco.cd.kamcoback.postgres.entity.QMapSheetAnalInferenceEntity.mapSheetAnalInferenceEntity;
import static com.kamco.cd.kamcoback.postgres.entity.QMapSheetLearnDataGeomEntity.mapSheetLearnDataGeomEntity;
import static com.kamco.cd.kamcoback.postgres.entity.QMemberEntity.memberEntity;
import static com.kamco.cd.kamcoback.postgres.entity.QPnuEntity.pnuEntity;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
@@ -494,6 +495,14 @@ public class TrainingDataReviewRepositoryImpl extends QuerydslRepositorySupport
}
// 5. DTO 생성
// pnu list 조회
List<String> pnuList =
queryFactory
.select(pnuEntity.pnu)
.from(pnuEntity)
.where(pnuEntity.geo.geoUid.eq(mapSheetAnalDataInferenceGeomEntityEntity.getGeoUid()))
.fetch();
var changeDetectionInfo =
ChangeDetectionInfo.builder()
.mapSheetInfo(mapSheetEntity != null ? mapSheetEntity.getMapidNm() : "")
@@ -535,10 +544,7 @@ public class TrainingDataReviewRepositoryImpl extends QuerydslRepositorySupport
mapSheetAnalDataInferenceGeomEntityEntity.getCdProb() != null
? mapSheetAnalDataInferenceGeomEntityEntity.getCdProb()
: 0.0)
.pnu(
mapSheetAnalDataInferenceGeomEntityEntity.getPnu() != null
? mapSheetAnalDataInferenceGeomEntityEntity.getPnu()
: 0L)
.pnu(pnuList)
.mapSheetNum(
mapSheetAnalDataInferenceGeomEntityEntity.getMapSheetNum() != null
? mapSheetAnalDataInferenceGeomEntityEntity.getMapSheetNum()

View File

@@ -13,7 +13,7 @@ public class MapSheetMngFileJobController {
private final MapSheetMngFileJobService mapSheetMngFileJobService;
// 현재 상태 확인용 Getter
@Getter private boolean isSchedulerEnabled = true;
@Getter private boolean isSchedulerEnabled = false;
@Getter private boolean isFileSyncSchedulerEnabled = false;
@Getter private int mngSyncPageSize = 20;

View File

@@ -0,0 +1,55 @@
package com.kamco.cd.kamcoback.scheduler.service;
import com.kamco.cd.kamcoback.gukyuin.dto.ChngDetectContDto;
import com.kamco.cd.kamcoback.gukyuin.dto.GukYuinDto.GeomUidDto;
import com.kamco.cd.kamcoback.gukyuin.service.GukYuinApiService;
import com.kamco.cd.kamcoback.postgres.core.GukYuinLabelJobCoreService;
import java.util.List;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
@Log4j2
@Service
@RequiredArgsConstructor
public class GukYuinApiLabelJobService {
private final GukYuinLabelJobCoreService gukYuinLabelJobCoreService;
private final GukYuinApiService gukYuinApiService;
@Value("${spring.profiles.active}")
private String profile;
/**
* 실행중인 profile
*
* @return
*/
private boolean isLocalProfile() {
return "local".equalsIgnoreCase(profile);
}
/** 어제 라벨링 검수 완료된 것 -> 국유인에 전송 */
@Scheduled(cron = "0 0 2 * * *")
public void findLabelingCompleteSend() {
if (isLocalProfile()) {
return;
}
List<GeomUidDto> list = gukYuinLabelJobCoreService.findYesterdayLabelingCompleteList();
if (list.isEmpty()) {
return;
}
for (GeomUidDto gto : list) {
ChngDetectContDto.ResultLabelDto dto =
gukYuinApiService.updateChnDtctObjtLabelingYn(gto.getResultUid(), "Y");
if (dto.getSuccess()) {
// inference_geom 에 label_send_dttm 업데이트 하기
gukYuinLabelJobCoreService.updateAnalDataInferenceGeomSendDttm(gto.getGeoUid());
}
}
}
}

View File

@@ -0,0 +1,110 @@
package com.kamco.cd.kamcoback.scheduler.service;
import com.kamco.cd.kamcoback.gukyuin.dto.ChngDetectContDto.ContBasic;
import com.kamco.cd.kamcoback.gukyuin.dto.ChngDetectContDto.ResultContDto;
import com.kamco.cd.kamcoback.gukyuin.dto.ChngDetectMastDto;
import com.kamco.cd.kamcoback.gukyuin.dto.ChngDetectMastDto.LearnKeyDto;
import com.kamco.cd.kamcoback.gukyuin.dto.ChngDetectMastDto.ResultDto;
import com.kamco.cd.kamcoback.gukyuin.dto.GukYuinStatus;
import com.kamco.cd.kamcoback.gukyuin.service.GukYuinApiService;
import com.kamco.cd.kamcoback.postgres.core.GukYuinPnuJobCoreService;
import java.util.List;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
@Log4j2
@Service
@RequiredArgsConstructor
public class GukYuinApiPnuJobService {
private final GukYuinPnuJobCoreService gukYuinPnuJobCoreService;
private final GukYuinApiService gukYuinApiService;
@Value("${spring.profiles.active}")
private String profile;
/**
* 실행중인 profile
*
* @return
*/
private boolean isLocalProfile() {
return "local".equalsIgnoreCase(profile);
}
/** 국유인 등록 완료 후, 탐지객체 조회해서 PNU 업데이트 하는 스케줄링 하루 1번 새벽 1시에 실행 */
@Scheduled(cron = "0 0 1 * * *")
public void findGukYuinContListPnuUpdate() {
if (isLocalProfile()) {
return;
}
List<LearnKeyDto> list =
gukYuinPnuJobCoreService.findGukyuinApplyStatusUidList(
List.of(GukYuinStatus.GUK_COMPLETED.getId(), GukYuinStatus.PNU_FAILED.getId()));
if (list.isEmpty()) {
return;
}
for (LearnKeyDto dto : list) {
try {
processUid(dto.getUid(), dto.getUid());
gukYuinPnuJobCoreService.updateGukYuinApplyStateComplete(
dto.getId(), GukYuinStatus.PNU_COMPLETED);
} catch (Exception e) {
log.error("[GUKYUIN] failed uid={}", dto.getUid(), e);
gukYuinPnuJobCoreService.updateGukYuinApplyStateComplete(
dto.getId(), GukYuinStatus.PNU_FAILED);
}
}
}
private void processUid(String chnDtctId, String uid) {
ResultDto result = gukYuinApiService.listChnDtctId(chnDtctId);
if (result == null || result.getResult() == null || result.getResult().isEmpty()) {
return;
}
ChngDetectMastDto.Basic basic = result.getResult().get(0);
String chnDtctCnt = basic.getChnDtctCnt();
if (chnDtctCnt == null || chnDtctCnt.isEmpty()) {
return;
}
// page 계산
int pageSize = 100;
int totalCount = Integer.parseInt(chnDtctCnt);
int totalPages = (totalCount + pageSize - 1) / pageSize;
for (int page = 0; page < totalPages; page++) {
processPage(uid, page, pageSize);
}
}
private void processPage(String uid, int page, int pageSize) {
ResultContDto resContList = gukYuinApiService.findChnContList(uid, page, pageSize);
if (resContList.getResult() == null || resContList.getResult().isEmpty()) {
return; // 외부 API 이상 방어
}
List<ContBasic> contList = resContList.getResult();
for (ContBasic cont : contList) {
String[] pnuList = cont.getPnuList();
long pnuCnt = pnuList == null ? 0 : pnuList.length;
if (cont.getChnDtctObjtId() != null) {
gukYuinPnuJobCoreService.updateInferenceGeomDataPnuCnt(cont.getChnDtctObjtId(), pnuCnt);
if (pnuCnt > 0) {
Long geoUid =
gukYuinPnuJobCoreService.findMapSheetAnalDataInferenceGeomUid(
cont.getChnDtctObjtId());
gukYuinPnuJobCoreService.insertGeoUidPnuData(geoUid, pnuList, cont.getChnDtctObjtId());
}
}
}
}
}

View File

@@ -0,0 +1,71 @@
package com.kamco.cd.kamcoback.scheduler.service;
import com.kamco.cd.kamcoback.gukyuin.dto.ChngDetectMastDto;
import com.kamco.cd.kamcoback.gukyuin.dto.ChngDetectMastDto.LearnKeyDto;
import com.kamco.cd.kamcoback.gukyuin.dto.GukYuinStatus;
import com.kamco.cd.kamcoback.gukyuin.service.GukYuinApiService;
import com.kamco.cd.kamcoback.postgres.core.GukYuinJobCoreService;
import java.util.List;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
@Log4j2
@Service
@RequiredArgsConstructor
public class GukYuinApiStatusJobService {
private final GukYuinJobCoreService gukYuinJobCoreService;
private final GukYuinApiService gukYuinApiService;
@Value("${spring.profiles.active}")
private String profile;
/**
* 실행중인 profile
*
* @return
*/
private boolean isLocalProfile() {
return "local".equalsIgnoreCase(profile);
}
/** 국유인 연동 후, 100% 되었는지 확인하는 스케줄링 매 10분마다 호출 */
@Scheduled(cron = "0 0/10 * * * *")
public void findGukYuinMastCompleteYn() {
if (isLocalProfile()) {
return;
}
List<LearnKeyDto> list =
gukYuinJobCoreService.findGukyuinApplyStatusUidList(
List.of(GukYuinStatus.IN_PROGRESS.getId()));
if (list.isEmpty()) {
return;
}
for (LearnKeyDto dto : list) {
try {
ChngDetectMastDto.ResultDto result = gukYuinApiService.listChnDtctId(dto.getUid());
if (result == null || result.getResult() == null || result.getResult().isEmpty()) {
log.warn("[GUKYUIN] empty result chnDtctMstId={}", dto.getChnDtctMstId());
continue;
}
ChngDetectMastDto.Basic basic = result.getResult().getFirst();
Integer progress =
basic.getExcnPgrt() == null ? null : Integer.parseInt(basic.getExcnPgrt().trim());
if (progress != null && progress == 100) {
gukYuinJobCoreService.updateGukYuinApplyStateComplete(
dto.getId(), GukYuinStatus.GUK_COMPLETED);
}
} catch (Exception e) {
log.error("[GUKYUIN] failed uid={}", dto.getChnDtctMstId(), e);
}
}
}
}

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