From f95b82bf7fac87c72d58ec15c04db2840ccafb38 Mon Sep 17 00:00:00 2001 From: "gayoun.park" Date: Sat, 28 Feb 2026 00:23:48 +0900 Subject: [PATCH] =?UTF-8?q?=EC=8A=A4=EC=BC=80=EC=A4=84=EB=A7=81=EC=97=90?= =?UTF-8?q?=20log=20=EC=B6=94=EA=B0=80=20=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../gukyuin/service/GukYuinApiService.java | 6 ++++ .../service/GukYuinApiLabelJobService.java | 15 +++++--- .../service/GukYuinApiPnuJobService.java | 35 +++++++++++++++++-- .../service/GukYuinApiStbltJobService.java | 4 +-- .../service/TrainingDataLabelJobService.java | 26 +++++++++++--- .../service/TrainingDataReviewJobService.java | 32 ++++++++++++++--- 6 files changed, 99 insertions(+), 19 deletions(-) diff --git a/src/main/java/com/kamco/cd/kamcoback/gukyuin/service/GukYuinApiService.java b/src/main/java/com/kamco/cd/kamcoback/gukyuin/service/GukYuinApiService.java index 92c21076..0489dba4 100644 --- a/src/main/java/com/kamco/cd/kamcoback/gukyuin/service/GukYuinApiService.java +++ b/src/main/java/com/kamco/cd/kamcoback/gukyuin/service/GukYuinApiService.java @@ -281,6 +281,7 @@ public class GukYuinApiService { + "&reqEpno=" + ("Y".equals(batchYn) ? "BATCH" : userUtil.getEmployeeNo()); + log.info("##### API 호출 URL : {}", url); ExternalCallResult result = externalHttpClient.call( url, @@ -289,6 +290,7 @@ public class GukYuinApiService { netUtils.jsonHeaders(), ChngDetectContDto.ResultContDto.class); + log.info("##### API 호출 완료 : {}", result.toString()); List contList = result.body().getResult(); if (contList == null || contList.isEmpty()) { return new ResultContDto( @@ -348,6 +350,7 @@ public class GukYuinApiService { info.setReqIp(myip); info.setReqEpno("Y".equals(batchYn) ? "BATCH" : userUtil.getEmployeeNo()); + log.info("##### API 호출 URL : {}", url); ExternalCallResult result = externalHttpClient.call( url, @@ -355,6 +358,7 @@ public class GukYuinApiService { info, netUtils.jsonHeaders(), ChngDetectContDto.ResultLabelDto.class); + log.info("##### API 호출 완료 : {}", result.toString()); this.insertGukyuinAuditLog( EventType.MODIFIED.getId(), @@ -408,10 +412,12 @@ public class GukYuinApiService { + "&reqEpno=" + ("Y".equals(batchYn) ? "BATCH" : userUtil.getEmployeeNo()); + log.info("##### API 호출 URL : {}", url); ExternalCallResult result = externalHttpClient.call( url, HttpMethod.GET, null, netUtils.jsonHeaders(), ChngDetectMastDto.ResultDto.class); + log.info("##### API 호출 완료 : {}", result.toString()); this.insertGukyuinAuditLog( EventType.DETAIL.getId(), netUtils.getLocalIP(), diff --git a/src/main/java/com/kamco/cd/kamcoback/scheduler/service/GukYuinApiLabelJobService.java b/src/main/java/com/kamco/cd/kamcoback/scheduler/service/GukYuinApiLabelJobService.java index 9d936315..db03d732 100644 --- a/src/main/java/com/kamco/cd/kamcoback/scheduler/service/GukYuinApiLabelJobService.java +++ b/src/main/java/com/kamco/cd/kamcoback/scheduler/service/GukYuinApiLabelJobService.java @@ -31,26 +31,33 @@ public class GukYuinApiLabelJobService { return "local".equalsIgnoreCase(profile); } - // @Scheduled(cron = "0 0 2 * * *") public void runTask() { findLabelingCompleteSend(null); } /** 어제 라벨링 검수 완료된 것 -> 국유인에 전송 */ public void findLabelingCompleteSend(LocalDate baseDate) { - // if (isLocalProfile()) { - // return; - // } + + log.info("[Step 1-1] 어제 검수완료된 라벨링을 검색한다."); + log.info("=== baseDate : {}", baseDate); + log.info("=== baseDate 있으면 해당 일자, 없으면 어제일자로 조회"); List list = gukYuinLabelJobCoreService.findYesterdayLabelingCompleteList(baseDate); + log.info("[Step 1-2] 검수완료된 폴리곤 객체 수 : {}", list == null ? 0 : list.size()); if (list.isEmpty()) { + log.info("[Step 1-3] 객체 없어서 return : 스케줄링 종료"); return; } + log.info("[Step 2-1] 객체 목록으로 라벨링 전송 API 호출 시작"); for (GeomUidDto gto : list) { + log.info("[Step 2-2] 객체ID 확인 gto.getResultUid(): {}", gto.getResultUid()); ChngDetectContDto.ResultLabelDto dto = gukYuinApiService.updateChnDtctObjtLabelingYn(gto.getResultUid(), "Y", "Y"); + log.info("[Step 2-3] 결과 dto.getSuccess(): {}", dto.getSuccess()); if (dto.getSuccess()) { + log.info("[Step 2-4] 결과가 성공일 때 inference_geom에 label_send_dttm 업데이트 하기"); + log.info("==== 업데이트 하는 객체 gto.getGeoUid(): {}", gto.getGeoUid()); // inference_geom 에 label_send_dttm 업데이트 하기 gukYuinLabelJobCoreService.updateAnalDataInferenceGeomSendDttm(gto.getGeoUid()); } diff --git a/src/main/java/com/kamco/cd/kamcoback/scheduler/service/GukYuinApiPnuJobService.java b/src/main/java/com/kamco/cd/kamcoback/scheduler/service/GukYuinApiPnuJobService.java index 720721d7..2f1fc6c6 100644 --- a/src/main/java/com/kamco/cd/kamcoback/scheduler/service/GukYuinApiPnuJobService.java +++ b/src/main/java/com/kamco/cd/kamcoback/scheduler/service/GukYuinApiPnuJobService.java @@ -41,16 +41,24 @@ public class GukYuinApiPnuJobService { // return; // } + log.info("[Step 1-1] 국유인 연동까지 완료된 추론 목록 가져오기"); + log.info("=== apply_status -> 100% 다운 완료: GUK_COMPLETED, PNU매핑 실패: PNU_FAILED"); List list = gukYuinPnuJobCoreService.findGukyuinApplyStatusUidList( List.of(GukYuinStatus.GUK_COMPLETED.getId(), GukYuinStatus.PNU_FAILED.getId())); + + log.info("[Step 1-2] 매핑할 추론 회차 갯수 : {}", list == null ? 0 : list.size()); if (list.isEmpty()) { + log.info("[Step 1-3] 매핑할 추론 회차 갯수 없어서 리턴하고 끝남"); return; } + log.info("[Step 2-1] 추론 회차별 pnu 매핑 for문 시작 "); for (LearnKeyDto dto : list) { try { - processUid(dto.getUid(), dto.getUid()); + log.info("[Step 2-2] 진행하는 추론 Uid: {}", dto.getUid()); + processUid(dto.getUid()); + gukYuinPnuJobCoreService.updateGukYuinApplyStateComplete( dto.getId(), GukYuinStatus.PNU_COMPLETED); } catch (Exception e) { @@ -61,15 +69,21 @@ public class GukYuinApiPnuJobService { } } - private void processUid(String chnDtctId, String uid) { - ResultDto result = gukYuinApiService.listChnDtctId(chnDtctId, "Y"); + private void processUid(String uid) { + log.info("[Step 2-4] 탐지 등록목록 상세 API 호출 시작"); + ResultDto result = gukYuinApiService.listChnDtctId(uid, "Y"); + if (result == null || result.getResult() == null || result.getResult().isEmpty()) { + log.info("[Step 2-5] 결과값 없어서 return"); return; } + log.info("[Step 2-5] 결과값 첫번째 값 가져오기"); ChngDetectMastDto.Basic basic = result.getResult().get(0); String chnDtctCnt = basic.getChnDtctCnt(); + log.info("[Step 2-6] 탐지 객체 전체 갯수 chnDtctCnt = {}", chnDtctCnt); if (chnDtctCnt == null || chnDtctCnt.isEmpty()) { + log.info("[Step 2-76] 탐지 객체 전체 갯수 없어서 return"); return; } @@ -78,29 +92,44 @@ public class GukYuinApiPnuJobService { int totalCount = Integer.parseInt(chnDtctCnt); int totalPages = (totalCount + pageSize - 1) / pageSize; + log.info("[Step 3-1] 탐지 객체 전체 수로 페이지 계산 : {}", totalPages); for (int page = 0; page < totalPages; page++) { + log.info("[Step 3-2] 페이지 별 호출 : {}", page); processPage(uid, page, pageSize); } } private void processPage(String uid, int page, int pageSize) { + log.info("[Step 4-1] 탐지객체 목록 API 호출 시작"); ResultContDto resContList = gukYuinApiService.findChnContList(uid, page, pageSize, "Y"); if (resContList.getResult() == null || resContList.getResult().isEmpty()) { + log.info("[Step 4-2] 탐지객체 목록 결과 없어서 return"); return; // 외부 API 이상 방어 } List contList = resContList.getResult(); + log.info("[Step 4-3] 탐지객체 목록 결과 contList.size : {}", contList == null ? 0 : contList.size()); for (ContBasic cont : contList) { String[] pnuList = cont.getPnuList(); long pnuCnt = pnuList == null ? 0 : pnuList.length; + log.info("[Step 4-4] 객체에 연결된 pnuCnt : {}", pnuCnt); if (cont.getChnDtctObjtId() != null) { + log.info( + "[Step 4-5] inference_geom 에 pnu 갯수 update : cont.getChnDtctObjtId = {}", + cont.getChnDtctObjtId()); + log.info(" === cont.getChnDtctObjtId : {}", cont.getChnDtctObjtId()); + log.info(" === pnuCnt : {}", pnuCnt); gukYuinPnuJobCoreService.updateInferenceGeomDataPnuCnt(cont.getChnDtctObjtId(), pnuCnt); if (pnuCnt > 0) { + log.info("[Step 4-6] 객체 ID로 geoUid 검색 = {}", cont.getChnDtctObjtId()); Long geoUid = gukYuinPnuJobCoreService.findMapSheetAnalDataInferenceGeomUid( cont.getChnDtctObjtId()); + + log.info("[Step 4-7] tb_pnu 에 데이터 upsert 수행"); + log.info("===== geoUid = {}", geoUid); gukYuinPnuJobCoreService.insertGeoUidPnuData(geoUid, pnuList, cont.getChnDtctObjtId()); } } diff --git a/src/main/java/com/kamco/cd/kamcoback/scheduler/service/GukYuinApiStbltJobService.java b/src/main/java/com/kamco/cd/kamcoback/scheduler/service/GukYuinApiStbltJobService.java index 56cb0ae7..08f6b869 100644 --- a/src/main/java/com/kamco/cd/kamcoback/scheduler/service/GukYuinApiStbltJobService.java +++ b/src/main/java/com/kamco/cd/kamcoback/scheduler/service/GukYuinApiStbltJobService.java @@ -51,7 +51,7 @@ public class GukYuinApiStbltJobService { gukYuinStbltJobCoreService.findGukYuinEligibleForSurveyList( GukYuinStatus.PNU_COMPLETED.getId()); - log.info("[Step 1-2] 국유인 연동 PNU 완료된 추론 회차 갯수 : {}", list.size()); + log.info("[Step 1-2] 국유인 연동 PNU 완료된 추론 회차 갯수 : {}", list == null ? 0 : list.size()); if (list.isEmpty()) { log.info("[Step 1-3] 국유인 연동 PNU 완료된 추론 회차 갯수 없어서 return"); return; @@ -83,7 +83,7 @@ public class GukYuinApiStbltJobService { } log.info("[Step 4-1] 국유인 실태조사 적합여부 result 값으로 데이터 업데이트"); - log.info(" === 데이터 갯수 : {}", result.getResult().size()); + log.info(" === 데이터 갯수 : {}", result.getResult() == null ? 0 : result.getResult().size()); for (RlbDtctMastDto stbltDto : result.getResult()) { log.info("[Step 4-2] 국유인 실태조사 적합여부 결과 가져오기"); diff --git a/src/main/java/com/kamco/cd/kamcoback/scheduler/service/TrainingDataLabelJobService.java b/src/main/java/com/kamco/cd/kamcoback/scheduler/service/TrainingDataLabelJobService.java index 59474500..82663b12 100644 --- a/src/main/java/com/kamco/cd/kamcoback/scheduler/service/TrainingDataLabelJobService.java +++ b/src/main/java/com/kamco/cd/kamcoback/scheduler/service/TrainingDataLabelJobService.java @@ -32,7 +32,6 @@ public class TrainingDataLabelJobService { return "local".equalsIgnoreCase(profile); } - // @Scheduled(cron = "0 0 0 * * *") public void runTask() { // 프록시를 통해 호출해야 @Transactional이 적용됨 applicationContext @@ -43,55 +42,72 @@ public class TrainingDataLabelJobService { @Transactional public void assignReviewerYesterdayLabelComplete(LocalDate baseDate) { - // if (isLocalProfile()) { - // return; - // } - try { + log.info("[Step 1-1] 라벨링 완료된 데이터 목록 조회한다."); + log.info("=== baseDate : {}", baseDate); + log.info("=== baseDate 있으면 해당 일자, 없으면 어제일자로 조회"); List tasks = trainingDataLabelJobCoreService.findCompletedYesterdayUnassigned(baseDate); + log.info("[Step 1-2] 목록 객체 건수 count : {}", tasks == null ? 0 : tasks.size()); if (tasks.isEmpty()) { + log.info("[Step 1-3] 조회된 것 없어 return"); return; } // 회차별로 그룹핑 + log.info("[Step 2-1] 회차별로 그룹핑 시작"); Map> taskByRound = tasks.stream().collect(Collectors.groupingBy(Tasks::getAnalUid)); // 회차별 분배 + log.info("[Step 3-1] 회차별로 분배 시작"); for (Map.Entry> entry : taskByRound.entrySet()) { Long analUid = entry.getKey(); List analTasks = entry.getValue(); // pending 계산 + log.info("[Step 3-2] 수행하는 회차 analUid: {}", analUid); + log.info("해당 회차에 라벨링 할당받은 검수자별 완료 건수 count(), 완료한 게 적은 순으로 해야 일이 한 사람에게 몰리지 않음"); List pendings = trainingDataLabelJobCoreService.findInspectorPendingByRound(analUid); + log.info("검수자 수: {}", pendings == null ? 0 : pendings.size()); if (pendings.isEmpty()) { + log.info("[Step 3-3] 할당된 검수자가 없으면 return"); continue; } + log.info("[Step 4-1] 검수자 사번 List 생성"); List reviewerIds = pendings.stream().map(InspectorPendingDto::getInspectorUid).toList(); // Lock 걸릴 수 있기 때문에 엔티티 조회하는 Repository 에서 구현 + log.info("[Step 4-2] 검수자 테이블 lock 걸리지 않게 처리"); trainingDataLabelJobCoreService.lockInspectors(analUid, reviewerIds); // 균등 분배 + log.info("[Step 5-1] 검수자에게 라벨 작업 균등분배 시작"); Map> assignMap = distributeByLeastPending(analTasks, reviewerIds); + log.info("[Step 5-2] 검수자에게 라벨 작업 균등분배 완료"); // reviewer별 batch update + log.info("[Step 5-3] 검수자별 할당 데이터를 batch update 시작"); assignMap.forEach( (reviewerId, assignedTasks) -> { if (assignedTasks.isEmpty()) { + log.info("[Step 5-4] 할당된 데이터 없으면 return"); return; } List assignmentUids = assignedTasks.stream().map(Tasks::getAssignmentUid).toList(); + log.info("[Step 6-1] 할당 작업에 검수자 아이디 update"); + log.info("==== 검수자 사번: {}", reviewerId); + log.info("==== 할당 갯수: {}", assignmentUids == null ? 0 : assignmentUids.size()); trainingDataLabelJobCoreService.assignReviewerBatch(assignmentUids, reviewerId); + log.info("[Step 7-1] geom 테이블에 검수 상태 update"); List geomUids = assignedTasks.stream().map(Tasks::getInferenceUid).toList(); trainingDataLabelJobCoreService.updateGeomUidTestState(geomUids); }); diff --git a/src/main/java/com/kamco/cd/kamcoback/scheduler/service/TrainingDataReviewJobService.java b/src/main/java/com/kamco/cd/kamcoback/scheduler/service/TrainingDataReviewJobService.java index 98923dbb..af929662 100644 --- a/src/main/java/com/kamco/cd/kamcoback/scheduler/service/TrainingDataReviewJobService.java +++ b/src/main/java/com/kamco/cd/kamcoback/scheduler/service/TrainingDataReviewJobService.java @@ -39,52 +39,68 @@ public class TrainingDataReviewJobService { } @Transactional - // @Scheduled(cron = "0 0 2 * * *") public void runTask() { exportGeojsonLabelingGeom(null); } public void exportGeojsonLabelingGeom(LocalDate baseDate) { - // if (isLocalProfile()) { - // return; - // } - // 1) 경로/파일명 결정 String targetDir = "local".equals(profile) ? System.getProperty("user.home") + "/geojson" : trainingDataDir; + log.info("[Step 1-1] geojson 파일 생성할 경로: {}", targetDir); // 2) 진행중인 회차 중, complete_cnt 가 존재하는 회차 목록 가져오기 + log.info("[Step 1-2] 진행중인 회차 중, complete_cnt 가 존재하는 회차 목록 가져오기"); List analList = trainingDataReviewJobCoreService.findAnalCntInfoList(); + log.info("[Step 1-3] 회차 리스트 건수: {}", analList == null ? 0 : analList.size()); + + if (analList.isEmpty()) { + log.info("[Step 1-4] 회차 리스트 없어 return 하고 종료"); + return; + } for (AnalCntInfo info : analList) { + log.info("[Step 2-1] 회차 폴리곤 전체 건수 == 파일 생성 건수 같은지 확인"); + log.info("=== info.getAllCnt(): {}", info.getAllCnt()); + log.info("=== info.getFileCnt(): {}", info.getFileCnt()); + if (Objects.equals(info.getAllCnt(), info.getFileCnt())) { + log.info("[Step 2-2] 회차 폴리곤 전체 건수 == 파일 생성 건수 같아서 파일 생성 진행하지 않음 continue"); continue; } String resultUid = info.getResultUid(); // 회차의 대문자 uid (폴더명으로 사용) // 3) 회차 + 어제까지 검수 완료된 총 데이터의 도엽별 목록 가져오기 + log.info("[Step 3-1] 회차 + 어제까지 검수 완료된 총 데이터의 도엽별 목록 가져오기"); List analMapList = trainingDataReviewJobCoreService.findCompletedAnalMapSheetList( info.getAnalUid(), baseDate); + log.info("=== analMapList cnt: {}", analMapList == null ? 0 : analMapList.size()); if (analMapList.isEmpty()) { + log.info("[Step 3-2] 도엽 목록 조회되지 않아 continue"); continue; } + log.info("[Step 4-1] 도엽별 geom 데이터 가지고 와서 geojson 만들기 시작"); for (AnalMapSheetList mapSheet : analMapList) { // 4) 도엽별 geom 데이터 가지고 와서 geojson 만들기 + log.info("[Step 4-2] 도엽별 검수완료된 폴리곤 데이터 목록 조회"); List completeList = trainingDataReviewJobCoreService.findCompletedYesterdayLabelingList( info.getAnalUid(), mapSheet.getMapSheetNum(), baseDate); + log.info("=== completeList size: {}", completeList == null ? 0 : completeList.size()); if (!completeList.isEmpty()) { + log.info("[Step 4-3] 목록에서 filter로 geoUid List 생성, 폴리곤 feature별 리스트 생성"); List geoUids = completeList.stream().map(CompleteLabelData::getGeoUid).toList(); List features = completeList.stream().map(GeoJsonFeature::from).toList(); // 5) 파일서버에 uid 폴더 생성 후 업로드 하기 + log.info("[Step 5-1] 파일서버에 uid 폴더 생성 후 업로드 하기 시작"); FeatureCollection collection = new FeatureCollection(features); String filename = String.format( @@ -94,15 +110,21 @@ public class TrainingDataReviewJobService { mapSheet.getTargetYyyy(), mapSheet.getMapSheetNum()); + log.info("=== filename: {}", filename); + log.info("=== 회차의 uid: {}", resultUid); Path outputPath = Paths.get(targetDir + "/" + resultUid, filename); + log.info("=== outputPath: {}", outputPath); try { + log.info("[Step 6-1] Uid로 폴더 생성"); Files.createDirectories(outputPath.getParent()); + log.info("[Step 6-2] geoJson 파일 생성"); ObjectMapper objectMapper = new ObjectMapper(); objectMapper.enable(SerializationFeature.INDENT_OUTPUT); objectMapper.writeValue(outputPath.toFile(), collection); // geoUids : file_create_yn = true 로 업데이트 + log.info("[Step 6-3] learn_data_geom 에 file_create_yn = true 로 업데이트"); trainingDataReviewJobCoreService.updateLearnDataGeomFileCreateYn(geoUids); } catch (IOException e) {