From 28ab6863da819965a6374ca56b50f2b14e1d1782 Mon Sep 17 00:00:00 2001 From: eastargh Date: Thu, 27 Aug 2026 16:54:01 +0900 Subject: [PATCH 01/13] =?UTF-8?q?=EC=9B=B9=ED=9B=85=20=EB=B0=9C=EC=86=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../onl/apim/webhook/webhookSendLogMan.jsp | 5 ++-- .../djb/webhook/service/WebhookService.java | 26 ++++++++++++++++--- 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/WebContent/jsp/onl/apim/webhook/webhookSendLogMan.jsp b/WebContent/jsp/onl/apim/webhook/webhookSendLogMan.jsp index 1007a35..0c88f88 100644 --- a/WebContent/jsp/onl/apim/webhook/webhookSendLogMan.jsp +++ b/WebContent/jsp/onl/apim/webhook/webhookSendLogMan.jsp @@ -89,7 +89,7 @@ mtype: 'POST', url: url, postData: getSearchForJqgrid("cmd", "LIST"), - colNames: ['id', 'No.', '기관명', '이벤트유형', 'Target URL', 'Proxy URL', 'Status', '성공여부', '발송서버', '발송일시'], + colNames: ['id', 'No.', '기관명', '이벤트유형', 'Target URL', 'Proxy URL', 'Status', '성공여부', '재시도', '발송서버', '발송일시'], colModel: [ {name: 'id', align: 'center', key: true, hidden: true}, {name: 'rowNum', align: 'center', width: 45, sortable: false}, @@ -99,11 +99,12 @@ {name: 'proxyUrl', align: 'left', width: 300, formatter: formatTargetUrl}, {name: 'statusCode', align: 'center', width: 60}, {name: 'success', align: 'center', width: 70, formatter: formatSuccess}, + {name: 'retryCount', align: 'center', width: 60}, {name: 'sendBy', align: 'center', width: 100}, {name: 'sentAt', align: 'center', width: 140} ], jsonReader: {repeatitems: false}, - pager: $('#pager'), + pager: $('#pager'), page: '${param.page}', rowNum: '${rmsDefaultRowNum}', autoheight: true, diff --git a/src/main/java/com/eactive/eai/rms/ext/djb/webhook/service/WebhookService.java b/src/main/java/com/eactive/eai/rms/ext/djb/webhook/service/WebhookService.java index c0bfdae..35b54b4 100644 --- a/src/main/java/com/eactive/eai/rms/ext/djb/webhook/service/WebhookService.java +++ b/src/main/java/com/eactive/eai/rms/ext/djb/webhook/service/WebhookService.java @@ -17,7 +17,6 @@ import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; import org.springframework.web.client.HttpClientErrorException; import org.springframework.web.client.HttpServerErrorException; import org.springframework.web.client.RestTemplate; @@ -146,8 +145,12 @@ public class WebhookService { /** * 웹훅 발송 메인 메서드 + * + *

{@code @Transactional}을 두지 않는다 - 재시도(최대 retryCount회, Thread.sleep 포함)가 + * 이 메서드 안에서 동기로 도는데, 메서드 전체를 하나의 트랜잭션으로 감싸면 재시도 도중의 + * 실패 로그 저장(sendWithRetry 내부)이 커밋되지 않고 메서드가 끝날 때까지 대기하게 되어 + * "실패 시 즉시 로그부터 남긴다"는 의도가 무의미해진다. save() 호출마다 개별 커밋되도록 둔다.

*/ - @Transactional public void send(WebhookSendRequest req) { String targetUrl = req.getTargetUrl(); @@ -235,6 +238,8 @@ public class WebhookService { * 재시도 포함 HTTP 발송 * - 4xx: 클라이언트 오류이므로 즉시 throw (재시도 불필요) * - 5xx / 네트워크 오류: 최대 RETRY_COUNT회까지 선형 증가 backoff 재시도 (1s → 2s → 3s → 4s → 5s) + * - 실패할 때마다 즉시 로그를 저장한다 (최초 실패는 INSERT, 이후 재시도 실패는 같은 row를 UPDATE) - + * 재시도 도중 앱이 죽어도 마지막으로 저장된 실패 기록이 남도록 하기 위함 */ private ResponseEntity sendWithRetry(String proxyUrl, HttpEntity request, WebhookSendLog sendLog, int retryCount, int retryTime) throws Exception { Exception lastException = null; @@ -243,21 +248,34 @@ public class WebhookService { if (attempt > 0) { long delayMs = (long) retryTime * attempt; log.info("[Webhook] 재시도 {}/{} - url: {}, delay: {}ms", attempt, retryCount, proxyUrl, delayMs); - sendLog.setRetryCount(attempt); Thread.sleep(delayMs); } return restTemplate.postForEntity(proxyUrl, request, String.class); } catch (HttpClientErrorException e) { //throw e; // 4xx는 재시도 불필요 - lastException = e; //TODO: 재시도 테스트 + lastException = e; //TODO: 재시도 테스트 + recordAttemptFailure(sendLog, attempt, e.getStatusCode().value(), e.getResponseBodyAsString(), e.getMessage()); } catch (Exception e) { lastException = e; log.warn("[Webhook] 발송 실패 (attempt {}/{}) - url: {}, error: {}", attempt, retryCount, proxyUrl, e.getMessage()); + recordAttemptFailure(sendLog, attempt, null, null, e.getMessage()); } } throw lastException; } + // 실패한 시도 하나를 즉시 저장한다. sendLog에 id가 없으면(최초 실패) INSERT, 있으면(재시도 실패) 같은 row를 UPDATE한다. + private void recordAttemptFailure(WebhookSendLog sendLog, int attempt, Integer statusCode, String responseBody, String errorMessage) { + sendLog.setRetryCount(attempt); + sendLog.setSuccess(false); + sendLog.setErrorMessage(errorMessage); + if (statusCode != null) { + sendLog.setStatusCode(statusCode); + sendLog.setResponseBody(responseBody); + } + sendLogRepository.save(sendLog); + } + /** * HMAC-SHA256 서명 생성 * Java 17 미만은 HexFormat 미지원이므로 직접 변환 From 598dc5b1ee8a2078435ffd1ff4c03f305e905e96 Mon Sep 17 00:00:00 2001 From: eastargh Date: Thu, 27 Aug 2026 16:54:27 +0900 Subject: [PATCH 02/13] =?UTF-8?q?=EC=9B=B9=ED=9B=85=20=EB=B0=9C=EC=86=A1?= =?UTF-8?q?=20=EC=9D=B8=EC=A6=9D=20=EC=9A=B0=ED=9A=8C(=EB=82=B4=EB=B6=80?= =?UTF-8?q?=EB=A7=9D=EB=A7=8C=20=EC=82=AC=EC=9A=A9)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../user/sync/RestTemplateConfiguration.java | 27 +++++++++++++++---- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/eactive/eai/rms/ext/user/sync/RestTemplateConfiguration.java b/src/main/java/com/eactive/eai/rms/ext/user/sync/RestTemplateConfiguration.java index 91b96f3..933ce4e 100644 --- a/src/main/java/com/eactive/eai/rms/ext/user/sync/RestTemplateConfiguration.java +++ b/src/main/java/com/eactive/eai/rms/ext/user/sync/RestTemplateConfiguration.java @@ -1,24 +1,41 @@ package com.eactive.eai.rms.ext.user.sync; import org.apache.http.client.HttpClient; +import org.apache.http.conn.ssl.NoopHostnameVerifier; +import org.apache.http.conn.ssl.SSLConnectionSocketFactory; +import org.apache.http.conn.ssl.TrustAllStrategy; import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.ssl.SSLContextBuilder; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; import org.springframework.web.client.RestTemplate; +import javax.net.ssl.SSLContext; + @Configuration public class RestTemplateConfiguration { - + @Bean - public HttpClient httpClient() { - - HttpClient httpClient = + public HttpClient httpClient() throws Exception { + + // 사내망 내부 CA(자체 발급) 인증서 체인을 JVM cacerts가 신뢰하지 못해 PKIX 오류가 나는 문제의 + // 임시 우회. 인증서 체인/호스트명 검증을 모두 끄므로 MITM에 취약해진다 - 내부망 대상으로만 쓰고, + // 가능하면 해당 CA를 JVM 트러스트스토어에 등록하는 방식으로 대체할 것. + SSLContext sslContext = SSLContextBuilder.create() + .loadTrustMaterial(TrustAllStrategy.INSTANCE) + .build(); + + SSLConnectionSocketFactory sslSocketFactory = + new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE); + + HttpClient httpClient = HttpClientBuilder.create() + .setSSLSocketFactory(sslSocketFactory) .setMaxConnTotal(50) .setMaxConnPerRoute(20) .build(); - + return httpClient; } From a2cda26909d6e7e23e5b7a29d9af86fd46f1f60d Mon Sep 17 00:00:00 2001 From: eastargh Date: Fri, 28 Aug 2026 09:48:34 +0900 Subject: [PATCH 03/13] =?UTF-8?q?=EA=B1=B0=EB=9E=98=ED=98=84=ED=99=A9=2090?= =?UTF-8?q?0=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../transaction/online/transactionStatus.jsp | 24 +++++++++---------- .../online/transactionStatusDetail.jsp | 20 ++++++++-------- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/WebContent/jsp/onl/transaction/online/transactionStatus.jsp b/WebContent/jsp/onl/transaction/online/transactionStatus.jsp index cc19dd7..abdd478 100644 --- a/WebContent/jsp/onl/transaction/online/transactionStatus.jsp +++ b/WebContent/jsp/onl/transaction/online/transactionStatus.jsp @@ -55,10 +55,10 @@ $(document).ready(function() { '200', '300', '400', - '900', + /* '900', */ '300', '400', - '900', + /* '900', */ 'IF', '<%= localeMessage.getString("tranStat.recv") %>', '<%= localeMessage.getString("common.total") %>', @@ -75,11 +75,11 @@ $(document).ready(function() { { name : 'E200' , align : 'center' , width:'50' ,formatter:'integer',formatoptions:{thousandsSeparator:","},summaryType:'sum'}, { name : 'E300' , align : 'center' , width:'50' ,formatter:'integer',formatoptions:{thousandsSeparator:","},summaryType:'sum'}, { name : 'E400' , align : 'center' , width:'50' ,formatter:'integer',formatoptions:{thousandsSeparator:","},summaryType:'sum'}, - { name : 'E900' , align : 'center' , width:'50' ,formatter:'integer',formatoptions:{thousandsSeparator:","},summaryType:'sum'}, + /* { name : 'E900' , align : 'center' , width:'50' ,formatter:'integer',formatoptions:{thousandsSeparator:","},summaryType:'sum'}, */ { name : 'E9300' , align : 'center' , width:'50' ,formatter:'integer',formatoptions:{thousandsSeparator:","},summaryType:'sum'}, { name : 'E9400' , align : 'center' , width:'50' ,formatter:'integer',formatoptions:{thousandsSeparator:","},summaryType:'sum'}, - { name : 'E9900' , align : 'center' , width:'50' ,formatter:'integer',formatoptions:{thousandsSeparator:","},summaryType:'sum'}, + /* { name : 'E9900' , align : 'center' , width:'50' ,formatter:'integer',formatoptions:{thousandsSeparator:","},summaryType:'sum'}, */ { name : 'AVGEAI' , align : 'center' , width:'46' ,formatter:'integer',formatoptions:{thousandsSeparator:","},summaryType:'avg'}, { name : 'AVGPSV' , align : 'center' , width:'46' ,formatter:'integer',formatoptions:{thousandsSeparator:","},summaryType:'avg'}, @@ -152,8 +152,8 @@ $(document).ready(function() { useColSpanStyle: true, groupHeaders:[ {startColumnName: 'P100', numberOfColumns: 4, titleText: '<%= localeMessage.getString("tranStat.tranNo") %>'}, - {startColumnName: 'E100', numberOfColumns: 5, titleText: '<%= localeMessage.getString("tranStat.errNo") %>'}, - {startColumnName: 'E9300', numberOfColumns: 3, titleText: '<%= localeMessage.getString("tranStat.timeout") %>'}, + {startColumnName: 'E100', numberOfColumns: 4, titleText: '<%= localeMessage.getString("tranStat.errNo") %>'}, + {startColumnName: 'E9300', numberOfColumns: 2, titleText: '<%= localeMessage.getString("tranStat.timeout") %>'}, {startColumnName: 'AVGEAI', numberOfColumns: 3, titleText: '<%= localeMessage.getString("tranStat.avgTime") %>'} ] }); @@ -210,9 +210,9 @@ $(document).ready(function() { var merge = new Array(); merge.push(makeMerge("업무구분","0","1","0","0")); merge.push(makeMerge("구간별 처리건수","0","0","1","4")); - merge.push(makeMerge("구간별 에러건수","0","0","5","9")); - merge.push(makeMerge("타임아웃","0","0","10","12")); - merge.push(makeMerge("평균처리시간(초)","0","0","13","15")); + merge.push(makeMerge("구간별 에러건수","0","0","5","8")); + merge.push(makeMerge("타임아웃","0","0","9","11")); + merge.push(makeMerge("평균처리시간(초)","0","0","12","14")); gridToExcelSubmit(url,"LIST_GRID_TO_EXCEL",$("#grid"),$("#ajaxForm"),"업무별 거래현황",merge); return false; @@ -224,9 +224,9 @@ $(document).ready(function() { var merge = new Array(); merge.push(makeMerge("업무구분","0","1","0","0")); merge.push(makeMerge("구간별 처리건수","0","0","1","4")); - merge.push(makeMerge("구간별 에러건수","0","0","5","9")); - merge.push(makeMerge("타임아웃","0","0","10","12")); - merge.push(makeMerge("평균처리시간(초)","0","0","13","15")); + merge.push(makeMerge("구간별 에러건수","0","0","5","8")); + merge.push(makeMerge("타임아웃","0","0","9","11")); + merge.push(makeMerge("평균처리시간(초)","0","0","12","14")); gridToExcelSubmit(url_excel,"LIST_GRID_TO_EXCEL",$("#grid"),$("#ajaxForm"),'업무별 거래현황',merge); return false; }); diff --git a/WebContent/jsp/onl/transaction/online/transactionStatusDetail.jsp b/WebContent/jsp/onl/transaction/online/transactionStatusDetail.jsp index a6f0673..5a4fa0a 100644 --- a/WebContent/jsp/onl/transaction/online/transactionStatusDetail.jsp +++ b/WebContent/jsp/onl/transaction/online/transactionStatusDetail.jsp @@ -59,10 +59,10 @@ function w2GridToExcelSubmit(url,cmd,gridObj,formObject,fileName,merge){ records[i]['E200'] = data[i]['E200'].toString(); records[i]['E300'] = data[i]['E300'].toString(); records[i]['E400'] = data[i]['E400'].toString(); - records[i]['E900'] = data[i]['E900'].toString(); + /* records[i]['E900'] = data[i]['E900'].toString(); */ records[i]['E9300'] = data[i]['E9300'].toString(); records[i]['E9400'] = data[i]['E9400'].toString(); - records[i]['E9900'] = data[i]['E9900'].toString(); + /* records[i]['E9900'] = data[i]['E9900'].toString(); */ records[i]['BIZNAME'] = data[i]['BIZNAME']; records[i]['SVCCODE'] = data[i]['SVCCODE']; @@ -245,8 +245,8 @@ $( document ).ready(function() { {span : 4, caption:'<%= localeMessage.getString("tranStat.interface") %>'}, {span : 4, caption:'<%= localeMessage.getString("tranStat.tranNo") %>'}, {span : 3, caption:'<%= localeMessage.getString("tranStat.avgTime") %>'}, - {span : 5, caption:'<%= localeMessage.getString("tranStat.errNo") %>'}, - {span : 3, caption:'<%= localeMessage.getString("tranStat.timeout") %>'} + {span : 4, caption:'<%= localeMessage.getString("tranStat.errNo") %>'}, + {span : 2, caption:'<%= localeMessage.getString("tranStat.timeout") %>'} ], columns: [ @@ -269,11 +269,11 @@ $( document ).ready(function() { { field : 'E200' ,type:'text' ,caption : '200' , size: '65px' ,style:'text-align:right', render:thousandsSeparator }, { field : 'E300' ,type:'text' ,caption : '300' , size: '65px' ,style:'text-align:right', render:thousandsSeparator }, { field : 'E400' ,type:'text' ,caption : '400' , size: '65px' ,style:'text-align:right', render:thousandsSeparator }, - { field : 'E900' ,type:'text' ,caption : '900' , size: '65px' ,style:'text-align:right', render:thousandsSeparator }, + /* { field : 'E900' ,type:'text' ,caption : '900' , size: '65px' ,style:'text-align:right', render:thousandsSeparator }, */ { field : 'E9300' ,type:'text' ,caption : '300' , size: '50px' ,style:'text-align:right', render:thousandsSeparator }, { field : 'E9400' ,type:'text' ,caption : '400' , size: '50px' ,style:'text-align:right', render:thousandsSeparator }, - { field : 'E9900' ,type:'text' ,caption : '900' , size: '50px' ,style:'text-align:right', render:thousandsSeparator } + /* { field : 'E9900' ,type:'text' ,caption : '900' , size: '50px' ,style:'text-align:right', render:thousandsSeparator } */ ] ,summary:[ @@ -346,8 +346,8 @@ $( document ).ready(function() { merge.push(makeMerge("API","0","0","0","3")); merge.push(makeMerge("전문구간별 처리건수","0","0","4","7")); merge.push(makeMerge("평균처리시간(초)","0","0","8","10")); - merge.push(makeMerge("전문구간별 에러건수","0","0","11","15")); - merge.push(makeMerge("타임아웃","0","0","16","18")); + merge.push(makeMerge("전문구간별 에러건수","0","0","11","14")); + merge.push(makeMerge("타임아웃","0","0","15","16")); w2GridToExcelSubmit(url_excel,"LIST_GRID_TO_EXCEL",w2ui['myGrid'],$("#ajaxForm"),"전문별 거래현황",merge); return false; @@ -376,8 +376,8 @@ $( document ).ready(function() { merge.push(makeMerge("API","0","0","0","3")); merge.push(makeMerge("구간별 처리건수","0","0","4","7")); merge.push(makeMerge("평균처리시간(초)","0","0","8","10")); - merge.push(makeMerge("구간별 에러건수","0","0","11","15")); - merge.push(makeMerge("타임아웃","0","0","16","18")); + merge.push(makeMerge("구간별 에러건수","0","0","11","14")); + merge.push(makeMerge("타임아웃","0","0","15","16")); w2GridToExcelSubmit(url_excel,"LIST_GRID_TO_EXCEL",w2ui['myGrid'],$("#ajaxForm"),"전문별 거래현황",merge); return false; From 258bb70a9349342cc8a463d0101433bb3a778e5f Mon Sep 17 00:00:00 2001 From: eastargh Date: Fri, 28 Aug 2026 13:57:41 +0900 Subject: [PATCH 04/13] =?UTF-8?q?eCams=20=EC=97=B0=EB=8F=99=EC=8B=9C=20?= =?UTF-8?q?=EB=A1=9C=EA=B7=B8=20=EC=A0=80=EC=9E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../onl/admin/rule/layoutSyncHistoryMan.jsp | 18 +- .../history/LayoutSyncHistoryService.java | 6 + .../loader/controller/LoaderController.java | 241 ++++++++++-------- .../eai/rms/onl/loader/dao/LoaderDao.java | 5 + .../eai/rms/onl/loader/dao/LoaderDaoImpl.java | 9 +- .../service/TransactionLoaderService.java | 12 +- .../service/TransactionLoaderServiceImpl.java | 30 +++ .../history/ui/LayoutSyncHistoryUISearch.java | 6 + 8 files changed, 214 insertions(+), 113 deletions(-) diff --git a/WebContent/jsp/onl/admin/rule/layoutSyncHistoryMan.jsp b/WebContent/jsp/onl/admin/rule/layoutSyncHistoryMan.jsp index d02f75c..1ab6a22 100644 --- a/WebContent/jsp/onl/admin/rule/layoutSyncHistoryMan.jsp +++ b/WebContent/jsp/onl/admin/rule/layoutSyncHistoryMan.jsp @@ -47,7 +47,7 @@ mtype: 'POST', url: url, postData: getSearchForJqgrid("cmd", "LIST"), - colNames: ['No', 'Service Name', 'Command', '레이아웃명', '연동시간', '결과', '결과메세지'], + colNames: ['No', '서비스명', 'Command', '연동구분명', '연동시간', '결과', '결과메세지'], colModel: [ {name: 'seqno', align: 'center', width: 50, sortable: false}, {name: 'serviceName', align: 'center', width: 100, sortable: false}, @@ -123,8 +123,8 @@
-
전문레이아웃 연동 이력IIM에서 연동한 인터페이스 및 전문 레이아웃 이력을 조회합니다.
-
+
연동 배포 이력IIM 레이아웃 연동 이력과 eCams 인터페이스 배포 이력을 조회합니다.
+ @@ -133,8 +133,6 @@ - - + + + + + + + + +
레이아웃명 연동시간 @@ -143,6 +141,14 @@ + 서비스명Command
연동구분명 결과
@@ -153,6 +159,8 @@
diff --git a/src/main/java/com/eactive/eai/rms/data/entity/onl/layoutsync/history/LayoutSyncHistoryService.java b/src/main/java/com/eactive/eai/rms/data/entity/onl/layoutsync/history/LayoutSyncHistoryService.java index 68c4ffa..7fa2d18 100644 --- a/src/main/java/com/eactive/eai/rms/data/entity/onl/layoutsync/history/LayoutSyncHistoryService.java +++ b/src/main/java/com/eactive/eai/rms/data/entity/onl/layoutsync/history/LayoutSyncHistoryService.java @@ -31,6 +31,12 @@ public class LayoutSyncHistoryService extends AbstractDataService optSpecInfo = apiSpecInfoService.findById(interfaceId); + if (optSpecInfo.isPresent()) { + ApiSpecInfoUI specInfo = apiSpecUIMapper.mapToUI(optSpecInfo.get()); + specInfo.setCreatedBy(null); + specInfo.setCreatedDate(null); + specInfo.setLastModifiedBy(null); + specInfo.setLastModifiedDate(null); + interfaceDeployPack.setSpecInfo(specInfo); + } + + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.registerModule(new JavaTimeModule()); + objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); + ObjectWriter writer = objectMapper.writerWithDefaultPrettyPrinter(); + String bizCode = eaiMessageDeploy.getEaibzwkdstcd(); + String ioMapPath = monitoringContext.getStringProperty(MonitoringContext.IOMAP_DOWNLOAD_PATH); + String fileDir = FilenameUtils.concat(ioMapPath, bizCode); + String fileFullPath = FilenameUtils.concat(fileDir, interfaceId+".json"); + + FileWriteUtil.makeDirWithGroupPermission(fileDir); + File file = new File(fileFullPath); + json = writer.writeValueAsString(interfaceDeployPack); + FileUtils.writeStringToFile(file, json, StandardCharsets.UTF_8); + + // TSEAITR10 배포 이력 저장 (SERVICENAME=eCams, COMMAND=downloadfull, LOUTNAME=interfaceId, RECVDATA=json) + loaderService.insertDeploySyncLog(dt, "downloadfull", interfaceId, json); + + Map result = new HashMap<>(); + result.put("status", "success"); + result.put("message", fileFullPath+" write done."); + + return new ModelAndView(resultView, "result", result); + } catch (Exception e) { + logger.error("interface downloadfull error - interfaceId=" + interfaceId, e); + loaderService.insertDeployFailLog(dt, "downloadfull", interfaceId, json, e.toString()); + + Map result = new HashMap<>(); + result.put("status", "fail"); + result.put("message", e.toString()); + + return new ModelAndView(resultView, "result", result); } - - // 4. API 스펙 정보(ptl_api_spec_info) 추가 (감사(audit) 컬럼은 제외) - Optional optSpecInfo = apiSpecInfoService.findById(interfaceId); - if (optSpecInfo.isPresent()) { - ApiSpecInfoUI specInfo = apiSpecUIMapper.mapToUI(optSpecInfo.get()); - specInfo.setCreatedBy(null); - specInfo.setCreatedDate(null); - specInfo.setLastModifiedBy(null); - specInfo.setLastModifiedDate(null); - interfaceDeployPack.setSpecInfo(specInfo); - } - - ObjectMapper objectMapper = new ObjectMapper(); - objectMapper.registerModule(new JavaTimeModule()); - objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); - ObjectWriter writer = objectMapper.writerWithDefaultPrettyPrinter(); - String bizCode = eaiMessageDeploy.getEaibzwkdstcd(); - String ioMapPath = monitoringContext.getStringProperty(MonitoringContext.IOMAP_DOWNLOAD_PATH); - String fileDir = FilenameUtils.concat(ioMapPath, bizCode); - String fileFullPath = FilenameUtils.concat(fileDir, interfaceId+".json"); - - FileWriteUtil.makeDirWithGroupPermission(fileDir); - File file = new File(fileFullPath); - writer.writeValue(file, interfaceDeployPack); - - Map result = new HashMap<>(); - String status = "success"; - result.put("status", status); - result.put("message", fileFullPath+" write done."); - - return new ModelAndView(resultView, "result", result); } /** @@ -1533,57 +1548,73 @@ public class LoaderController implements InterceptorSkipController { * @throws Exception */ @RequestMapping( params ={"cmd=interface","control=uploadfull"}) - public ModelAndView interfaceLoadFully(String serviceType, String filePath) throws Exception { + public ModelAndView interfaceLoadFully(String serviceType, String filePath) { DataSourceType dt = DataSourceTypeManager.getDataSourceType(serviceType); DataSourceContextHolder.setDataSourceType(dt); - String ioMapPath = monitoringContext.getStringProperty(MonitoringContext.IOMAP_UPLOAD_PATH); - String fileFullPath ; - if(StringUtils.isBlank(ioMapPath)) { - fileFullPath = filePath; - } else { - fileFullPath = FilenameUtils.concat(ioMapPath, filePath); - } - - File file = new File(fileFullPath); - InputStream inputStream = new FileInputStream(file); - // ObjectMapper 생성 및 설정 - ObjectMapper objectMapper = new ObjectMapper(); - objectMapper.registerModule(new JavaTimeModule()); - InterfaceDeployPack interfaceDeployPack = objectMapper.readValue(inputStream, InterfaceDeployPack.class); - - EAIMessageDeploy eaiMessageDeploy = interfaceDeployPack.getEaiMessageDeploy(); - - interfaceDeployService.saveDeploy(eaiMessageDeploy); - agentUtilService.broadcast(new CommonCommand("com.ext.eai.agent.stdmessage.ReloadSTDMessageCommand", "ALL")); - agentUtilService.broadcast(new CommonCommand("com.eactive.eai.agent.eaimessage.ReloadEAIMessageCommand", eaiMessageDeploy.getEaisvcname())); - - for(LayoutDeploy layoutDeploy: interfaceDeployPack.getLayoutDeployList()){ - layoutDeployService.saveDeploy(layoutDeploy); - agentUtilService.broadcast(new CommonCommand( "com.eactive.eai.agent.transformer.ReloadLayoutCommand", layoutDeploy.getLoutname())); - } - - for(TransformDeploy transformDeploy : interfaceDeployPack.getTransformDeployList()){ - transformDeployService.saveDeploy(transformDeploy); - agentUtilService.broadcast(new CommonCommand( "com.eactive.eai.agent.transformer.ReloadTransformCommand", transformDeploy.getCnvsnname())); - } - - ApiSpecInfoUI specInfoUI = interfaceDeployPack.getSpecInfo(); - if (specInfoUI != null && StringUtils.isNotBlank(specInfoUI.getApiId())) { - ApiSpecInfo specInfo = apiSpecUIMapper.mapToEntity(specInfoUI); - if (apiSpecInfoService.findById(specInfo.getApiId()).isPresent()) { - apiSpecInfoService.updateById(specInfo.getApiId(), specInfo); + String json = ""; + String loutName = filePath; + try { + String ioMapPath = monitoringContext.getStringProperty(MonitoringContext.IOMAP_UPLOAD_PATH); + String fileFullPath ; + if(StringUtils.isBlank(ioMapPath)) { + fileFullPath = filePath; } else { - apiSpecInfoService.create(specInfo); + fileFullPath = FilenameUtils.concat(ioMapPath, filePath); } + + File file = new File(fileFullPath); + json = FileUtils.readFileToString(file, StandardCharsets.UTF_8); + // ObjectMapper 생성 및 설정 + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.registerModule(new JavaTimeModule()); + InterfaceDeployPack interfaceDeployPack = objectMapper.readValue(json, InterfaceDeployPack.class); + + EAIMessageDeploy eaiMessageDeploy = interfaceDeployPack.getEaiMessageDeploy(); + loutName = eaiMessageDeploy.getEaisvcname(); + + interfaceDeployService.saveDeploy(eaiMessageDeploy); + agentUtilService.broadcast(new CommonCommand("com.ext.eai.agent.stdmessage.ReloadSTDMessageCommand", "ALL")); + agentUtilService.broadcast(new CommonCommand("com.eactive.eai.agent.eaimessage.ReloadEAIMessageCommand", eaiMessageDeploy.getEaisvcname())); + + for(LayoutDeploy layoutDeploy: interfaceDeployPack.getLayoutDeployList()){ + layoutDeployService.saveDeploy(layoutDeploy); + agentUtilService.broadcast(new CommonCommand( "com.eactive.eai.agent.transformer.ReloadLayoutCommand", layoutDeploy.getLoutname())); + } + + for(TransformDeploy transformDeploy : interfaceDeployPack.getTransformDeployList()){ + transformDeployService.saveDeploy(transformDeploy); + agentUtilService.broadcast(new CommonCommand( "com.eactive.eai.agent.transformer.ReloadTransformCommand", transformDeploy.getCnvsnname())); + } + + ApiSpecInfoUI specInfoUI = interfaceDeployPack.getSpecInfo(); + if (specInfoUI != null && StringUtils.isNotBlank(specInfoUI.getApiId())) { + ApiSpecInfo specInfo = apiSpecUIMapper.mapToEntity(specInfoUI); + if (apiSpecInfoService.findById(specInfo.getApiId()).isPresent()) { + apiSpecInfoService.updateById(specInfo.getApiId(), specInfo); + } else { + apiSpecInfoService.create(specInfo); + } + } + + // TSEAITR10 배포 이력 저장 (SERVICENAME=eCams, COMMAND=uploadfull, LOUTNAME=eaisvcname, RECVDATA=json) + loaderService.insertDeploySyncLog(dt, "uploadfull", loutName, json); + + Map result = new HashMap(); + result.put("status", "success"); + result.put("message", fileFullPath+" deploy done."); + + return new ModelAndView(resultView, "result", result); + } catch (Exception e) { + logger.error("interface uploadfull error - filePath=" + filePath, e); + loaderService.insertDeployFailLog(dt, "uploadfull", loutName, json, e.toString()); + + Map result = new HashMap(); + result.put("status", "fail"); + result.put("message", e.toString()); + + return new ModelAndView(resultView, "result", result); } - - Map result = new HashMap(); - String status = "success"; - result.put("status", status); - result.put("message", fileFullPath+" deploy done."); - - return new ModelAndView(resultView, "result", result); } } diff --git a/src/main/java/com/eactive/eai/rms/onl/loader/dao/LoaderDao.java b/src/main/java/com/eactive/eai/rms/onl/loader/dao/LoaderDao.java index 46f984d..0e4f8ab 100644 --- a/src/main/java/com/eactive/eai/rms/onl/loader/dao/LoaderDao.java +++ b/src/main/java/com/eactive/eai/rms/onl/loader/dao/LoaderDao.java @@ -41,4 +41,9 @@ public interface LoaderDao { public List selectTableDataForListValue( DataSourceType dataSourceType, String tableName, String key1, List param1 ) ; public List selectSnaAdpApList( DataSourceType dataSourceType, String param1 ) ; + + /** + * TSEAITR10(배포/동기화 이력)에 로그 1건을 저장한다. + */ + public int insertSyncLog( DataSourceType dt, HashMap param ) ; } diff --git a/src/main/java/com/eactive/eai/rms/onl/loader/dao/LoaderDaoImpl.java b/src/main/java/com/eactive/eai/rms/onl/loader/dao/LoaderDaoImpl.java index b67908d..8bf429f 100644 --- a/src/main/java/com/eactive/eai/rms/onl/loader/dao/LoaderDaoImpl.java +++ b/src/main/java/com/eactive/eai/rms/onl/loader/dao/LoaderDaoImpl.java @@ -189,10 +189,15 @@ public class LoaderDaoImpl extends SqlMapClientTemplateDao implements LoaderDao // } // }.execute(); } + public int insertSyncLog( DataSourceType dt, HashMap param ) { + param.put("schemaId", dt.getSchema()); + return this.template.update("Layout.insertLayoutSyncLog", param); + } + public List selectSnaAdpApList( DataSourceType dataSourceType, String param1 ) { final HashMap param = new HashMap(); - param.put("param1", param1); - + param.put("param1", param1); + return template.queryForList("Loader.selectSnaAdpApList", param); // return new SpecifiedDataSourceExecutor>( diff --git a/src/main/java/com/eactive/eai/rms/onl/loader/service/TransactionLoaderService.java b/src/main/java/com/eactive/eai/rms/onl/loader/service/TransactionLoaderService.java index 74a1942..ac61e46 100644 --- a/src/main/java/com/eactive/eai/rms/onl/loader/service/TransactionLoaderService.java +++ b/src/main/java/com/eactive/eai/rms/onl/loader/service/TransactionLoaderService.java @@ -157,5 +157,15 @@ public interface TransactionLoaderService{ public HashMap syncResult(Command command ,String fileName ) throws Exception; - public void appendToListfile(String bizCode, String interfaceId) throws Exception ; + public void appendToListfile(String bizCode, String interfaceId) throws Exception ; + + /** + * downloadfull / uploadfull 성공 시 TSEAITR10 에 배포 이력을 저장한다. (PRCSSRSLT=S) + */ + public void insertDeploySyncLog(DataSourceType dt, String command, String interfaceId, String recvData) ; + + /** + * downloadfull / uploadfull 실패 시 TSEAITR10 에 실패 이력을 저장한다. (PRCSSRSLT=F, PRCSSRSLTCMNT=errMsg) + */ + public void insertDeployFailLog(DataSourceType dt, String command, String interfaceId, String recvData, String errMsg) ; } \ No newline at end of file diff --git a/src/main/java/com/eactive/eai/rms/onl/loader/service/TransactionLoaderServiceImpl.java b/src/main/java/com/eactive/eai/rms/onl/loader/service/TransactionLoaderServiceImpl.java index c9fe144..772c3b2 100644 --- a/src/main/java/com/eactive/eai/rms/onl/loader/service/TransactionLoaderServiceImpl.java +++ b/src/main/java/com/eactive/eai/rms/onl/loader/service/TransactionLoaderServiceImpl.java @@ -2,6 +2,7 @@ package com.eactive.eai.rms.onl.loader.service; import com.eactive.eai.agent.command.Command; import com.eactive.eai.agent.command.CommonCommand; +import com.eactive.eai.common.util.UUIDGenerator; import com.eactive.eai.rms.common.base.BaseService; import com.eactive.eai.rms.common.context.MonitoringContext; import com.eactive.eai.rms.common.converter.ElinkObjectMapper; @@ -1483,4 +1484,33 @@ public class TransactionLoaderServiceImpl extends BaseService implements Transac String targetLine = bizCode+"|"+interfaceId+System.lineSeparator(); org.apache.commons.io.FileUtils.writeStringToFile(listFile, targetLine, Charset.defaultCharset(), true); } + + @Override + public void insertDeploySyncLog(DataSourceType dt, String command, String interfaceId, String recvData) { + insertDeployLog(dt, command, interfaceId, recvData, "S", ""); + } + + @Override + public void insertDeployFailLog(DataSourceType dt, String command, String interfaceId, String recvData, String errMsg) { + insertDeployLog(dt, command, interfaceId, recvData, "F", errMsg); + } + + private void insertDeployLog(DataSourceType dt, String command, String interfaceId, String recvData, + String prcssRslt, String prcssRsltCmnt) { + try { + HashMap param = new HashMap(); + param.put("logPrcssSeqno", UUIDGenerator.getUUID()); + param.put("serviceName", "InterfaceDeploy"); + param.put("command", command); + param.put("loutName", interfaceId); + param.put("recvData", recvData == null ? "" : recvData); + param.put("recvAmndHMS", DateUtil.getDateTime("yyyyMMddHHmmssSS").substring(0, 16)); + param.put("prcssRslt", prcssRslt); + param.put("prcssRsltCmnt", StringUtils.left(prcssRsltCmnt, 4000)); // PRCSSRSLTCMNT 길이 보호 + dao.insertSyncLog(dt, param); + } catch (Exception e) { + // 이력 저장 실패가 download/upload 본기능을 중단시키지 않도록 한다. + logger.error("insertDeployLog error (command=" + command + ", interfaceId=" + interfaceId + ")", e); + } + } } \ No newline at end of file diff --git a/src/main/java/com/eactive/eai/rms/onl/manage/rule/layoutsync/history/ui/LayoutSyncHistoryUISearch.java b/src/main/java/com/eactive/eai/rms/onl/manage/rule/layoutsync/history/ui/LayoutSyncHistoryUISearch.java index 7c246bf..0d715fc 100644 --- a/src/main/java/com/eactive/eai/rms/onl/manage/rule/layoutsync/history/ui/LayoutSyncHistoryUISearch.java +++ b/src/main/java/com/eactive/eai/rms/onl/manage/rule/layoutsync/history/ui/LayoutSyncHistoryUISearch.java @@ -7,6 +7,12 @@ public class LayoutSyncHistoryUISearch { private String searchLoutName; + /* 서비스명 (부분일치) */ + private String searchServiceName; + + /* 커맨드 (부분일치) */ + private String searchCommand; + /* 연동시간 기간 검색 (yyyyMMdd, 8자리) */ private String searchStartDate; private String searchEndDate; From 74f7aa5cf9bb46dd903a8b8361bf2ff3ecb1ae20 Mon Sep 17 00:00:00 2001 From: eastargh Date: Fri, 28 Aug 2026 13:58:40 +0900 Subject: [PATCH 05/13] =?UTF-8?q?=EC=9C=A0=EB=9F=89=EC=A0=9C=EC=96=B4?= =?UTF-8?q?=ED=86=A0=ED=81=B0=ED=9A=8D=EB=93=9D=EC=8B=A4=ED=8C=A8=20?= =?UTF-8?q?=EA=B0=90=EC=8B=9C=20=EC=8A=A4=EC=BC=80=EC=A5=B4=20=ED=94=84?= =?UTF-8?q?=EB=9D=BC=ED=8D=BC=ED=8B=B0=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ext/djb/inflow/InflowTokenService.java | 16 ++++----- .../ext/djb/job/InflowTokenMonitorJob.java | 36 ++++++++++--------- 2 files changed, 28 insertions(+), 24 deletions(-) diff --git a/src/main/java/com/eactive/eai/rms/ext/djb/inflow/InflowTokenService.java b/src/main/java/com/eactive/eai/rms/ext/djb/inflow/InflowTokenService.java index 436de74..1f945cd 100644 --- a/src/main/java/com/eactive/eai/rms/ext/djb/inflow/InflowTokenService.java +++ b/src/main/java/com/eactive/eai/rms/ext/djb/inflow/InflowTokenService.java @@ -31,16 +31,16 @@ public class InflowTokenService { private UmsManager ums; public void checkRecentFails(long rangeMinute) { - List rows = inflowTokenRepository.countTokenInsufficient(rangeMinute); - - for (InflowTokenInsufficient info : rows) { - log.debug("유량제어 토큰 획득 실패: {}-{} 최근 {}분 동안 {}건", info.getEaisvcname(), info.getEaisvcdesc(), rangeMinute, info.getCnt()); - String message = "유량제어 토큰 획득 실패가 발생하였습니다 \n" + info.getEaisvcname(); - - if (monitoringContext.getBooleanProperty(MonitoringContext.DJB_UMS_MESSENGER_APIMONITOR_ENABLED, true)) { + if (monitoringContext.getBooleanProperty(MonitoringContext.DJB_UMS_MESSENGER_APIMONITOR_ENABLED, true)) { + List rows = inflowTokenRepository.countTokenInsufficient(rangeMinute); + + for (InflowTokenInsufficient info : rows) { + log.debug("유량제어 토큰 획득 실패: {}-{} 최근 {}분 동안 {}건", info.getEaisvcname(), info.getEaisvcdesc(), rangeMinute, info.getCnt()); + String message = "유량제어 토큰 획득 실패가 발생하였습니다 \n" + info.getEaisvcname(); + HashMap params = new HashMap(); params.put("message", message); - ums.send("api-monitor", MessageCode.INFLOW_TOKEN_FAILED, params); + ums.sendMessenger("api-monitor", MessageCode.INFLOW_TOKEN_FAILED, params); } } } diff --git a/src/main/java/com/eactive/eai/rms/ext/djb/job/InflowTokenMonitorJob.java b/src/main/java/com/eactive/eai/rms/ext/djb/job/InflowTokenMonitorJob.java index 3327ab1..271d6dc 100644 --- a/src/main/java/com/eactive/eai/rms/ext/djb/job/InflowTokenMonitorJob.java +++ b/src/main/java/com/eactive/eai/rms/ext/djb/job/InflowTokenMonitorJob.java @@ -1,9 +1,7 @@ package com.eactive.eai.rms.ext.djb.job; import java.util.Arrays; -import java.util.Date; import java.util.Set; -import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import org.apache.commons.lang3.StringUtils; @@ -13,7 +11,6 @@ import org.quartz.JobDataMap; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import org.quartz.SchedulerException; -import org.quartz.Trigger; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.ApplicationContext; @@ -37,7 +34,7 @@ import com.eactive.eai.rms.onl.common.util.DateUtil; *

Job 파라미터 (JobDataMap):

*
    *
  • - * NONE + * api.inflow.fail.range_minute: 유량제어 토큰 획득 실패 조회 구간 (단위: 분, 기본값: 5) *
  • *
* @@ -48,6 +45,12 @@ public class InflowTokenMonitorJob implements Job { private static final Logger log = LoggerFactory.getLogger(InflowTokenMonitorJob.class); + /** Job 파라미터 키: 유량제어 실패 조회 구간(분) */ + public static final String KEY_API_INFLOW_FAIL_RANGE_MINUTE = "api.inflow.fail.range_minute"; + + /** 유량제어 실패 조회 구간 기본값(분) */ + public static final String DEFAULT_RANGE_MINUTE = "5"; + private transient MonitoringContext monitoringContext; @Override @@ -69,18 +72,19 @@ public class InflowTokenMonitorJob implements Job { log.info("*** START InflowTokenFailMonitorJob run({})", CommonUtil.getToday("yyyy-MM-dd HH:mm")); - long execMinute = 1; //default 배치 실행주기(분) - - Trigger trigger = context.getTrigger(); - Date prevFireTime = trigger.getPreviousFireTime(); - Date nextFireTime = trigger.getNextFireTime(); - - //1회 이상 배치가 실행된 경우, 배치 간격을 구하여 유량제어조회 구간 파라미터로 전달한다 - if (prevFireTime != null && nextFireTime != null) { - long diffMillis = nextFireTime.getTime() - prevFireTime.getTime(); - execMinute = TimeUnit.MILLISECONDS.toMinutes(diffMillis); - log.info("실행 주기: {}분", execMinute); - } + // Job 프로퍼티에서 유량제어 실패 조회 구간(분)을 읽는다. 미지정 시 기본값 사용 + long execMinute; + String rangeMinute = jobDataMap.getString(KEY_API_INFLOW_FAIL_RANGE_MINUTE); + if (StringUtils.isEmpty(rangeMinute)) { + rangeMinute = DEFAULT_RANGE_MINUTE; + } + try { + execMinute = Long.parseLong(rangeMinute.trim()); + } catch (NumberFormatException e) { + log.warn("잘못된 {} 값: {} - 기본값 {} 사용", KEY_API_INFLOW_FAIL_RANGE_MINUTE, rangeMinute, DEFAULT_RANGE_MINUTE); + execMinute = Long.parseLong(DEFAULT_RANGE_MINUTE); + } + log.info("유량제어 실패 조회 구간: {}분", execMinute); ApplicationContext appContext; try { From bc40362c478fc5c9b0032c06a45733ad781a818e Mon Sep 17 00:00:00 2001 From: eastargh Date: Fri, 28 Aug 2026 15:25:17 +0900 Subject: [PATCH 06/13] =?UTF-8?q?SMS=EC=9D=B8=EC=A6=9D=EC=B0=BD=20?= =?UTF-8?q?=EC=98=A4=ED=94=88=EC=8B=9C=20=EC=9D=B8=EC=A6=9D=EB=B2=88?= =?UTF-8?q?=ED=98=B8=20focus?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- WebContent/jsp/common/screen/emergency.jsp | 22 ++++++++++++++++--- .../rms/common/context/MonitoringContext.java | 3 +++ .../eai/rms/common/login/MainController.java | 8 ++++++- 3 files changed, 29 insertions(+), 4 deletions(-) diff --git a/WebContent/jsp/common/screen/emergency.jsp b/WebContent/jsp/common/screen/emergency.jsp index 16ca022..b503f2e 100644 --- a/WebContent/jsp/common/screen/emergency.jsp +++ b/WebContent/jsp/common/screen/emergency.jsp @@ -299,8 +299,12 @@ if (response.smsAuthRequired) { // 비밀번호는 아직 반영되지 않음 - SMS 인증 완료 시 최종 반영됨 smsAuthPurpose = 'PASSWORD_CHANGE'; + // 두 모달이 겹치면 이전 모달의 enforceFocus/backdrop 이 남아 입력이 막히므로 + // #pwdChgModal 이 완전히 닫힌 뒤 SMS 인증 모달을 연다 + $('#pwdChgModal').one('hidden.bs.modal', function() { + showSmsAuthModal(response); + }); $('#pwdChgModal').modal('hide'); - showSmsAuthModal(response); return; } alert(response.message); @@ -369,6 +373,19 @@ verifySmsAuthCode(); } }); + + // 인증번호는 숫자만 허용 (타이핑/붙여넣기/드래그 공통, 백스페이스·방향키는 방해하지 않음) + $("#smsAuthCode").on("input", function(){ + var digits = this.value.replace(/[^0-9]/g, '').slice(0, 6); + if (this.value !== digits) { + this.value = digits; + } + }); + + // SMS 인증 모달이 완전히 열린 뒤 인증번호 입력창에 포커스 + $('#smsAuthModal').on('shown.bs.modal', function() { + $('#smsAuthCode').trigger('focus'); + }); }); function fncSsoLogin() { @@ -487,8 +504,7 @@

인증번호가 로 발송되었습니다.

+ placeholder="인증번호 6자리" maxlength="6" inputmode="numeric" autocomplete="off">

남은 시간: 60

diff --git a/src/main/java/com/eactive/eai/rms/common/context/MonitoringContext.java b/src/main/java/com/eactive/eai/rms/common/context/MonitoringContext.java index 5f9c3b6..5230b33 100644 --- a/src/main/java/com/eactive/eai/rms/common/context/MonitoringContext.java +++ b/src/main/java/com/eactive/eai/rms/common/context/MonitoringContext.java @@ -273,6 +273,9 @@ public interface MonitoringContext { // 비밀번호 변경 시 재사용을 금지할 최근 이력 개수 public static final String RMS_PASSWORD_HISTORY_COUNT = "rms.password.history.count"; + // 비밀번호 변경 시 SMS 2차 인증 사용 여부 (djb.sms_auth.enabled 와 함께 true 여야 동작) + public static final String RMS_PASSWORD_SMS_AUTH_ENABLED = "rms.password.sms_auth.enabled"; + // API 상태변화 스윙챗 발송여부 public static final String DJB_UMS_MESSENGER_APIMONITOR_ENABLED = "djb.ums.messenger.api-monitor.enabled"; diff --git a/src/main/java/com/eactive/eai/rms/common/login/MainController.java b/src/main/java/com/eactive/eai/rms/common/login/MainController.java index bed49f0..a95d81b 100644 --- a/src/main/java/com/eactive/eai/rms/common/login/MainController.java +++ b/src/main/java/com/eactive/eai/rms/common/login/MainController.java @@ -464,6 +464,11 @@ public class MainController implements InterceptorSkipController { MonitoringContext.RMS_PASSWORD_HISTORY_COUNT, DEFAULT_PASSWORD_HISTORY_COUNT); } + // 비밀번호 변경 시 SMS 2차 인증 사용 여부 (djb.sms_auth.enabled 와 rms.password.sms_auth.enabled 가 모두 true 여야 동작) + private boolean isPasswordChangeSmsAuthEnabled() { + return monitoringContext.getBooleanProperty(MonitoringContext.RMS_PASSWORD_SMS_AUTH_ENABLED, false); + } + // 로그인 실패 횟수 증가, 임계치 도달 시 계정 잠금 private int increaseLoginFailCount(UserInfo userInfo) { int failCount = (userInfo.getLoginfailcount() == null ? 0 : userInfo.getLoginfailcount()) + 1; @@ -909,8 +914,9 @@ public class MainController implements InterceptorSkipController { } // 5. SMS 2차 인증 필요 여부 체크 (비상모드는 로그인과 동일하게 우회) + // djb.sms_auth.enabled 와 rms.password.sms_auth.enabled 가 모두 true 일 때만 SMS 인증 수행 Boolean emergencyMode = (Boolean) session.getAttribute("emergencyMode"); - if (!Boolean.TRUE.equals(emergencyMode) && smsAuthService.isEnabled()) { + if (!Boolean.TRUE.equals(emergencyMode) && smsAuthService.isEnabled() && isPasswordChangeSmsAuthEnabled()) { if (!smsAuthService.hasValidPhoneNumber(userInfo)) { UserAccessLogger.log(request, LOG_CATEGORY_LOGIN, "F", "휴대폰번호 미등록으로 비밀번호 변경 불가"); return ChangePasswordResponseDto.builder() From 9cdfad13171023805bcf2c184586bd5674982db5 Mon Sep 17 00:00:00 2001 From: eastargh Date: Mon, 31 Aug 2026 09:45:18 +0900 Subject: [PATCH 07/13] =?UTF-8?q?=EC=84=B8=EC=85=98=ED=83=80=EC=9E=84?= =?UTF-8?q?=EC=95=84=EC=9B=83=201=EC=8B=9C=EA=B0=84=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- WebContent/WEB-INF/web.xml | 2 +- WebContent/WEB-INF/weblogic-web.xml | 2 +- WebContent/WEB-INF/weblogic.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/WebContent/WEB-INF/web.xml b/WebContent/WEB-INF/web.xml index 2370c37..162b93d 100644 --- a/WebContent/WEB-INF/web.xml +++ b/WebContent/WEB-INF/web.xml @@ -175,6 +175,6 @@ - 30 + 60 \ No newline at end of file diff --git a/WebContent/WEB-INF/weblogic-web.xml b/WebContent/WEB-INF/weblogic-web.xml index c8af68a..99a381a 100644 --- a/WebContent/WEB-INF/weblogic-web.xml +++ b/WebContent/WEB-INF/weblogic-web.xml @@ -145,6 +145,6 @@ - 30 + 60 \ No newline at end of file diff --git a/WebContent/WEB-INF/weblogic.xml b/WebContent/WEB-INF/weblogic.xml index 5e2434b..3f97d3c 100644 --- a/WebContent/WEB-INF/weblogic.xml +++ b/WebContent/WEB-INF/weblogic.xml @@ -3,7 +3,7 @@ monitoring - 1800 + 3600 JSESSIONID_EMS replicated_if_clustered From 927ef830ac2f28335539f77b1dee07d8b27483ea Mon Sep 17 00:00:00 2001 From: eastargh Date: Mon, 31 Aug 2026 10:47:15 +0900 Subject: [PATCH 08/13] =?UTF-8?q?API=20=ED=86=B5=EA=B3=84=20=ED=99=94?= =?UTF-8?q?=EB=A9=B4=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../onl/admin/authserver/clientManDetail.jsp | 13 ++- .../jsp/onl/kjb/statistics/apiStatsDayMan.jsp | 9 +- .../onl/kjb/statistics/apiStatsHourMan.jsp | 7 +- .../onl/kjb/statistics/apiStatsMinuteMan.jsp | 9 +- .../onl/kjb/statistics/apiStatsMonthMan.jsp | 27 +++-- .../onl/kjb/statistics/apiStatsYearMan.jsp | 11 +- .../jsp/onl/kjb/statistics/apiUseStatsMan.jsp | 46 ++++++-- .../transaction/online/transactionStatus.jsp | 3 + .../online/transactionStatusDetail.jsp | 3 + .../tracking/trackingManDetail.jsp | 106 ++++++++++++------ .../djb/statistics/ApiUseStatsService.java | 34 +++--- .../kjb/statistics/ApiStatsDayController.java | 35 ++++++ .../statistics/ApiStatsHourController.java | 35 ++++++ .../statistics/ApiStatsMinuteController.java | 35 ++++++ .../statistics/ApiStatsMonthController.java | 35 ++++++ .../statistics/ApiStatsYearController.java | 35 ++++++ .../ext/kjb/statistics/ui/ApiStatsUI.java | 1 + 17 files changed, 354 insertions(+), 90 deletions(-) diff --git a/WebContent/jsp/onl/admin/authserver/clientManDetail.jsp b/WebContent/jsp/onl/admin/authserver/clientManDetail.jsp index 289f994..8461bdb 100644 --- a/WebContent/jsp/onl/admin/authserver/clientManDetail.jsp +++ b/WebContent/jsp/onl/admin/authserver/clientManDetail.jsp @@ -190,6 +190,8 @@ var key = "${param.clientId}"; if (key != "" && key != "null") { isDetail = true; + } else { + $('input[name="grantTypes_"]').prop('checked', true); } init(key, detail); @@ -506,18 +508,17 @@ - <%= localeMessage.getString("clntManDtl.grntTp") %> - (<%= localeMessage.getString("clntManDtl.sprt") %> ,) (*) + <%= localeMessage.getString("clntManDtl.grntTp") %> (*) client_credentials <%-- authorization_code--%> <%-- password--%> - refresh_token + - + REDIRECT URI @@ -538,7 +539,7 @@ - + <%= localeMessage.getString("clntManDtl.rfrshTknExprtnDt") %> @@ -555,7 +556,7 @@ - + Security Key diff --git a/WebContent/jsp/onl/kjb/statistics/apiStatsDayMan.jsp b/WebContent/jsp/onl/kjb/statistics/apiStatsDayMan.jsp index 8da16c3..220fadf 100644 --- a/WebContent/jsp/onl/kjb/statistics/apiStatsDayMan.jsp +++ b/WebContent/jsp/onl/kjb/statistics/apiStatsDayMan.jsp @@ -416,12 +416,13 @@ mtype: 'POST', postData: gridPostData, colNames: [ - 'API명', + 'API ID', 'API 명', '총건수', '성공', 'Timeout', '시스템오류', '평균응답(ms)', '최소응답(ms)', '최대응답(ms)' ], colModel: [ - { name: 'apiName', align: 'left', width: '250', sortable: false }, + { name: 'apiName', align: 'left', width: '150', sortable: false }, + { name: 'apiDesc', align: 'left', width: '250', sortable: false }, { name: 'totalCnt', align: 'right', width: '80', formatter: numberFormatter, sortable: false }, { name: 'successCnt', align: 'right', width: '80', formatter: numberFormatter, sortable: false }, { name: 'timeoutCnt', align: 'right', width: '80', formatter: numberFormatter, sortable: false }, @@ -458,7 +459,7 @@ mtype: 'POST', postData: gridPostData, colNames: [ - '통계시간', 'API명', '인스턴스', '업무구분', '클라이언트ID', + '통계시간', 'API ID', '인스턴스', '업무구분', '클라이언트ID', 'Inbound Adapter', 'Outbound Adapter', '총건수', '성공', 'Timeout', '시스템오류', '평균응답(ms)', '최소응답(ms)', '최대응답(ms)' @@ -602,7 +603,7 @@ - API명 + API ID diff --git a/WebContent/jsp/onl/kjb/statistics/apiStatsHourMan.jsp b/WebContent/jsp/onl/kjb/statistics/apiStatsHourMan.jsp index 0ff3a34..0c70059 100644 --- a/WebContent/jsp/onl/kjb/statistics/apiStatsHourMan.jsp +++ b/WebContent/jsp/onl/kjb/statistics/apiStatsHourMan.jsp @@ -418,12 +418,13 @@ mtype: 'POST', postData: gridPostData, colNames: [ - 'API명', + 'API ID', 'API 명', '총건수', '성공', 'Timeout', '시스템오류', '평균응답(ms)', '최소응답(ms)', '최대응답(ms)' ], colModel: [ - { name: 'apiName', align: 'left', width: '250', sortable: false }, + { name: 'apiName', align: 'left', width: '150', sortable: false }, + { name: 'apiDesc', align: 'left', width: '250', sortable: false }, { name: 'totalCnt', align: 'right', width: '80', formatter: numberFormatter, sortable: false }, { name: 'successCnt', align: 'right', width: '80', formatter: numberFormatter, sortable: false }, { name: 'timeoutCnt', align: 'right', width: '80', formatter: numberFormatter, sortable: false }, @@ -580,7 +581,7 @@ - API명 + API ID diff --git a/WebContent/jsp/onl/kjb/statistics/apiStatsMinuteMan.jsp b/WebContent/jsp/onl/kjb/statistics/apiStatsMinuteMan.jsp index 9f99261..21070c4 100644 --- a/WebContent/jsp/onl/kjb/statistics/apiStatsMinuteMan.jsp +++ b/WebContent/jsp/onl/kjb/statistics/apiStatsMinuteMan.jsp @@ -431,12 +431,13 @@ mtype: 'POST', postData: gridPostData, colNames: [ - 'API명', - '총건수', '성공', 'Timeout', '시스템오류', + 'API ID', 'API 명', + '총건수', '성공', 'Timeout', '시스템오류', '평균응답(ms)', '최소응답(ms)', '최대응답(ms)' ], colModel: [ - { name: 'apiName', align: 'left', width: '250', sortable: false }, + { name: 'apiName', align: 'left', width: '150', sortable: false }, + { name: 'apiDesc', align: 'left', width: '250', sortable: false }, { name: 'totalCnt', align: 'right', width: '80', formatter: numberFormatter, sortable: false }, { name: 'successCnt', align: 'right', width: '80', formatter: numberFormatter, sortable: false }, { name: 'timeoutCnt', align: 'right', width: '80', formatter: numberFormatter, sortable: false }, @@ -609,7 +610,7 @@ (최대 1시간, 초과시 자동 조정) - API명 + API ID diff --git a/WebContent/jsp/onl/kjb/statistics/apiStatsMonthMan.jsp b/WebContent/jsp/onl/kjb/statistics/apiStatsMonthMan.jsp index 9b5a8ed..1e58e84 100644 --- a/WebContent/jsp/onl/kjb/statistics/apiStatsMonthMan.jsp +++ b/WebContent/jsp/onl/kjb/statistics/apiStatsMonthMan.jsp @@ -421,12 +421,13 @@ mtype: 'POST', postData: gridPostData, colNames: [ - 'API명', + 'API ID', 'API 명', '총건수', '성공', 'Timeout', '시스템오류', '평균응답(ms)', '최소응답(ms)', '최대응답(ms)' ], colModel: [ - { name: 'apiName', align: 'left', width: '250', sortable: false }, + { name: 'apiName', align: 'left', width: '150', sortable: false }, + { name: 'apiDesc', align: 'left', width: '250', sortable: false }, { name: 'totalCnt', align: 'right', width: '80', formatter: numberFormatter, sortable: false }, { name: 'successCnt', align: 'right', width: '80', formatter: numberFormatter, sortable: false }, { name: 'timeoutCnt', align: 'right', width: '80', formatter: numberFormatter, sortable: false }, @@ -463,7 +464,7 @@ mtype: 'POST', postData: gridPostData, colNames: [ - '통계시간', 'API명', '인스턴스', '업무구분', '클라이언트ID', + '통계시간', 'API ID', '인스턴스', '업무구분', '클라이언트ID', 'Inbound Adapter', 'Outbound Adapter', '총건수', '성공', 'Timeout', '시스템오류', '평균응답(ms)', '최소응답(ms)', '최대응답(ms)' @@ -510,11 +511,21 @@ $("input[name=searchStartDateTime]").inputmask("9999-99", { 'autoUnmask': true }); $("input[name=searchEndDateTime]").inputmask("9999-99", { 'autoUnmask': true }); - // 기본값 설정 (최근 12개월) + // 기본값 설정: 올해 1월 ~ 지난달 (현재가 1월이면 작년 1월 ~ 작년 12월) var today = new Date(); - var endMonth = today.getFullYear() + '-' + (today.getMonth() + 1 < 10 ? '0' : '') + (today.getMonth() + 1); - var startDate = new Date(today.getFullYear(), today.getMonth() - 11, 1); - var startMonth = startDate.getFullYear() + '-' + (startDate.getMonth() + 1 < 10 ? '0' : '') + (startDate.getMonth() + 1); + var startDate, endDate; + if (today.getMonth() === 0) { // 1월 + startDate = new Date(today.getFullYear() - 1, 0, 1); + endDate = new Date(today.getFullYear() - 1, 11, 1); + } else { + startDate = new Date(today.getFullYear(), 0, 1); + endDate = new Date(today.getFullYear(), today.getMonth() - 1, 1); + } + function toMonthStr(d) { + return d.getFullYear() + '-' + (d.getMonth() + 1 < 10 ? '0' : '') + (d.getMonth() + 1); + } + var startMonth = toMonthStr(startDate); + var endMonth = toMonthStr(endDate); if (!$("input[name=searchStartDateTime]").val()) { $("input[name=searchStartDateTime]").val(startMonth); @@ -593,7 +604,7 @@ - API명 + API ID diff --git a/WebContent/jsp/onl/kjb/statistics/apiStatsYearMan.jsp b/WebContent/jsp/onl/kjb/statistics/apiStatsYearMan.jsp index ad17a8c..70a8bd0 100644 --- a/WebContent/jsp/onl/kjb/statistics/apiStatsYearMan.jsp +++ b/WebContent/jsp/onl/kjb/statistics/apiStatsYearMan.jsp @@ -397,12 +397,13 @@ mtype: 'POST', postData: gridPostData, colNames: [ - 'API명', - '총건수', '성공', 'Timeout', '시스템오류', + 'API ID', 'API 명', + '총건수', '성공', 'Timeout', '시스템오류', '평균응답(ms)', '최소응답(ms)', '최대응답(ms)' ], colModel: [ - { name: 'apiName', align: 'left', width: '250', sortable: false }, + { name: 'apiName', align: 'left', width: '150', sortable: false }, + { name: 'apiDesc', align: 'left', width: '250', sortable: false }, { name: 'totalCnt', align: 'right', width: '80', formatter: numberFormatter, sortable: false }, { name: 'successCnt', align: 'right', width: '80', formatter: numberFormatter, sortable: false }, { name: 'timeoutCnt', align: 'right', width: '80', formatter: numberFormatter, sortable: false }, @@ -439,7 +440,7 @@ mtype: 'POST', postData: gridPostData, colNames: [ - '통계시간', 'API명', '인스턴스', '업무구분', '클라이언트ID', + '통계시간', 'API ID', '인스턴스', '업무구분', '클라이언트ID', 'Inbound Adapter', 'Outbound Adapter', '총건수', '성공', 'Timeout', '시스템오류', '평균응답(ms)', '최소응답(ms)', '최대응답(ms)' @@ -563,7 +564,7 @@ - API명 + API ID diff --git a/WebContent/jsp/onl/kjb/statistics/apiUseStatsMan.jsp b/WebContent/jsp/onl/kjb/statistics/apiUseStatsMan.jsp index e6cdf7a..59acf9c 100644 --- a/WebContent/jsp/onl/kjb/statistics/apiUseStatsMan.jsp +++ b/WebContent/jsp/onl/kjb/statistics/apiUseStatsMan.jsp @@ -65,9 +65,27 @@ } + // 조회구분(searchType)에 따라 '구분' / '구분명' 컬럼의 헤더명·폭·표시여부를 조정 + function applyGridColumnsByType(type) { + var $g = $("#grid"); + if (type === 'API') { + $g.jqGrid('setLabel', 'orgName', 'API ID'); + $g.jqGrid('setLabel', 'apiDesc', 'API명'); + $g.jqGrid('setColProp', 'orgName', { widthOrg: 150, width: 150 }); + $g.jqGrid('setColProp', 'apiDesc', { widthOrg: 250, width: 250 }); + $g.jqGrid('showCol', 'apiDesc'); + } else { + $g.jqGrid('setLabel', 'orgName', (type === 'DATE') ? '사용일' : '제휴사'); + $g.jqGrid('setColProp', 'orgName', { widthOrg: 250, width: 250 }); + $g.jqGrid('hideCol', 'apiDesc'); + } + $g.jqGrid('setGridWidth', $('#content_middle').width(), true); + } + function search() { var postData = getPostData("cmd", "LIST"); if (postData) { + applyGridColumnsByType($('input[name="searchType"]:checked').val()); $("#grid").setGridParam({ url: url, postData: postData }).trigger("reloadGrid"); } } @@ -186,12 +204,13 @@ mtype: 'POST', postData: gridPostData, colNames: [ - '구분', - '총건수', '성공', '성공율(%)', '실패율(%)', 'Timeout', '시스템오류', + '제휴사', 'API명', + '총건수', '성공', '성공율(%)', '실패율(%)', 'Timeout', '시스템오류', '평균응답(ms)', '최소응답(ms)', '최대응답(ms)' ], colModel: [ { name: 'orgName', align: 'left', width: '250', sortable: false }, + { name: 'apiDesc', align: 'left', width: '250', sortable: false, hidden: true }, { name: 'totalCnt', align: 'right', width: '80', formatter: numberFormatter, sortable: false }, { name: 'successCnt', align: 'right', width: '80', formatter: numberFormatter, sortable: false }, { name: 'successRate', align: 'right', width: '80', formatter: decimalFormatter, sortable: false }, @@ -225,10 +244,23 @@ $("input[name=searchStartDate], input[name=searchEndDate]").inputmask("9999-99-99", { 'autoUnmask': true }); $("input[name=searchStartDate], input[name=searchEndDate]").datepicker(); + // 기본값 설정: 이번달 1일 ~ 어제 (오늘이 1일이면 지난달 1일 ~ 지난달 말일) var today = getToday(); - var startDate = today.substring(0,6)+"01"; - var endDate = today; - + var now = new Date(); + function toYmd(d) { + var m = d.getMonth() + 1; + var day = d.getDate(); + return d.getFullYear() + '' + (m < 10 ? '0' : '') + m + (day < 10 ? '0' : '') + day; + } + var startDate, endDate; + if (now.getDate() === 1) { + startDate = toYmd(new Date(now.getFullYear(), now.getMonth() - 1, 1)); + endDate = toYmd(new Date(now.getFullYear(), now.getMonth(), 0)); + } else { + startDate = today.substring(0,6) + "01"; + endDate = toYmd(new Date(now.getFullYear(), now.getMonth(), now.getDate() - 1)); + } + if (!$("input[name=searchStartDate]").val()) { $("input[name=searchStartDate]").val(startDate); @@ -302,11 +334,11 @@ - API명 + API ID - + diff --git a/WebContent/jsp/onl/transaction/online/transactionStatus.jsp b/WebContent/jsp/onl/transaction/online/transactionStatus.jsp index abdd478..cb108e2 100644 --- a/WebContent/jsp/onl/transaction/online/transactionStatus.jsp +++ b/WebContent/jsp/onl/transaction/online/transactionStatus.jsp @@ -281,6 +281,9 @@ $(document).ready(function() { <%-- " level="W" status="DETAIL"/> --%>
<%= localeMessage.getString("tranStat.title") %><%= localeMessage.getString("tranStat.tooltip") %>
+
+ ※ 100 : 송신>APIM   200 : APIM>수신   300 : 수신>APIM   400 : APIM>송신 +
diff --git a/WebContent/jsp/onl/transaction/online/transactionStatusDetail.jsp b/WebContent/jsp/onl/transaction/online/transactionStatusDetail.jsp index 5a4fa0a..1fe1304 100644 --- a/WebContent/jsp/onl/transaction/online/transactionStatusDetail.jsp +++ b/WebContent/jsp/onl/transaction/online/transactionStatusDetail.jsp @@ -437,6 +437,9 @@ $( document ).ready(function() { <%-- " alt="" id="btn_search" level="R" /> --%>
<%= localeMessage.getString("tranStatDetail.title") %><%= localeMessage.getString("tranStat.tooltip") %>
+
+ ※ 100 : 송신>APIM   200 : APIM>수신   300 : 수신>APIM   400 : APIM>송신 +
diff --git a/WebContent/jsp/onl/transaction/tracking/trackingManDetail.jsp b/WebContent/jsp/onl/transaction/tracking/trackingManDetail.jsp index 18e3d05..2b35276 100644 --- a/WebContent/jsp/onl/transaction/tracking/trackingManDetail.jsp +++ b/WebContent/jsp/onl/transaction/tracking/trackingManDetail.jsp @@ -253,7 +253,7 @@ var name = $(this).attr("name"); if ("${rmsMenuAuth}" =="W"){ //2020.07.08 admin 만 버튼보이게 수정 $("#"+name).show(); - $("input[name="+name+"]").css("width","185px"); + $("input[name="+name+"]").css("width","calc(100% - 60px)"); } }); @@ -261,7 +261,7 @@ var name = $(this).attr("name"); if($("input[name="+name+"]").val().trim() != ""){ $("#"+name).show(); - $("input[name="+name+"]").css("width","185px"); + $("input[name="+name+"]").css("width","calc(100% - 60px)"); } }); @@ -977,15 +977,21 @@ html,hbody {
<%=localeMessage.getString("trackingDetail.common")%>
+ + + + + + - - - - + + + +
<%=localeMessage.getString("trackingDetail.eaiSvcSerno")%>
<%=localeMessage.getString("trackingDetail.eaiBzwkDstcd")%> <%=localeMessage.getString("trackingDetail.msgDpstYMS")%> <%=localeMessage.getString("trackingDetail.eaiBzwkDstcd")%> <%=localeMessage.getString("trackingDetail.msgDpstYMS")%>
<%= localeMessage.getString("eaiMessage.eaiSvcName")%> @@ -1019,11 +1025,17 @@ html,hbody {
API 정보
+ + + + + + - - - - + + + + @@ -1145,20 +1157,27 @@ html,hbody {
<%=localeMessage.getString("trackingDetail.inbound")%>
APP(Client) ID APP 명 APP(Client) ID APP 명
법인 ID
+ + + + + + - - + <%-- + --%> - - - - - + <%-- + + + --%> + <%-- @@ -1169,39 +1188,52 @@ html,hbody { - + --%>
<%=localeMessage.getString("trackingDetail.svcMotivUseDstcd")%> <%=localeMessage.getString("trackingDetail.svcMotivUseDstcd")%> <%=localeMessage.getString("trackingDetail.gstatSysAdptrBzwkGroupName")%> + <%-- " alt="" id="gstatSysAdptrBzwkGroupName" class="adapter_btn" level="W"/> --%>
<%=localeMessage.getString("trackingDetail.flowCtrlRoutName")%> <%=localeMessage.getString("trackingDetail.stndMsgUseYn")%>
<%=localeMessage.getString("trackingDetail.flowCtrlRoutName")%>
<%=localeMessage.getString("trackingDetail.svcPrcssDsticName")%> <%=localeMessage.getString("trackingDetail.svcBfClmnLogYn")%>
<%=localeMessage.getString("trackingDetail.gstatSvcDsticName")%> <%=localeMessage.getString("trackingDetail.prsntMsgIdName")%>
<%=localeMessage.getString("trackingDetail.outbound")%>
+ + + + + + - - + <%-- --%> + <%-- --%> - + - - - - - + <%-- + + + --%> + <%-- - + --%>
<%=localeMessage.getString("trackingDetail.psvIntfacDsticName")%> <%=localeMessage.getString("trackingDetail.psvIntfacDsticName")%> <%=localeMessage.getString("trackingDetail.psvSysAdptrBzwkGroupName")%> + <%-- " alt="" id="psvSysAdptrBzwkGroupName" class="adapter_btn" level="W"/> --%> <%=localeMessage.getString("trackingDetail.psvBzwkSysName")%>
<%=localeMessage.getString("trackingDetail.outbndRoutName")%> <%=localeMessage.getString("trackingDetail.toutVal")%>
<%=localeMessage.getString("trackingDetail.psvSysSvcDsticName")%> <%=localeMessage.getString("trackingDetail.psvBzwkSysName")%>
<%=localeMessage.getString("trackingDetail.psvSysSvcDsticName")%>
<%=localeMessage.getString("trackingDetail.rspnsErrFldName")%> <%=localeMessage.getString("trackingDetail.psvSysIdName")%>
<%=localeMessage.getString("trackingDetail.MAPPING")%>
+ + + + + + - - - - + + + @@ -1210,7 +1242,7 @@ html,hbody { @@ -1220,10 +1252,10 @@ html,hbody { - + <%-- - + --%>
<%=localeMessage.getString("trackingDetail.chngYn")%> <%=localeMessage.getString("trackingDetail.chngMsgIdName")%> + <%=localeMessage.getString("trackingDetail.chngYn")%> <%=localeMessage.getString("trackingDetail.chngMsgIdName")%> <%=localeMessage.getString("trackingDetail.bascRspnsChngYn")%> <%=localeMessage.getString("trackingDetail.bascRspnsChngMsgIdName")%> - + <%=localeMessage.getString("trackingDetail.errRspnsChngYn")%> <%=localeMessage.getString("trackingDetail.errRspnsChngMsgIdName")%> - + " alt="" id="errRspnsChngMsgIdName" class="layout_btn" />
<%=localeMessage.getString("trackingDetail.inptMsgIDName")%> @@ -1233,9 +1265,9 @@ html,hbody {
<%=localeMessage.getString("trackingDetail.bascRspnsMsgCmprCtnt")%> <%=localeMessage.getString("trackingDetail.flovrYn")%>
-
<%=localeMessage.getString("trackingDetail.etc")%>
+ <%--
<%=localeMessage.getString("trackingDetail.etc")%>
@@ -1267,7 +1299,7 @@ html,hbody { -
<%=localeMessage.getString("trackingDetail.rspnsErrcdName")%><%=localeMessage.getString("trackingDetail.trackAsisKey3Ctnt")%> <%=localeMessage.getString("trackingDetail.trackAsisKey4Ctnt")%>
+ --%>
diff --git a/src/main/java/com/eactive/eai/rms/ext/djb/statistics/ApiUseStatsService.java b/src/main/java/com/eactive/eai/rms/ext/djb/statistics/ApiUseStatsService.java index 1ad6122..0e6d751 100644 --- a/src/main/java/com/eactive/eai/rms/ext/djb/statistics/ApiUseStatsService.java +++ b/src/main/java/com/eactive/eai/rms/ext/djb/statistics/ApiUseStatsService.java @@ -57,19 +57,19 @@ public class ApiUseStatsService String dataSql = ""; if ("ORG".equals(search.getSearchType())) { - dataSql = "SELECT NVL(NVL(B.ORGNAME, D.ORG_NAME),'미분류') AS ORGNAME"; + dataSql = "SELECT NVL(NVL(B.ORGNAME, D.ORG_NAME),'미분류') AS ORGNAME, '' AS APIDESC"; dataSql += getQueryBody(search); dataSql += " GROUP BY NVL(NVL(B.ORGNAME, D.ORG_NAME),'미분류')"; dataSql += " ORDER BY ORGNAME"; } else if ("API".equals(search.getSearchType())) { - dataSql = "SELECT A.API_NAME "; + dataSql = "SELECT A.API_NAME, A.EAISVCDESC "; dataSql += getQueryBody(search); - dataSql += " GROUP BY A.API_NAME"; + dataSql += " GROUP BY A.API_NAME, A.EAISVCDESC "; dataSql += " ORDER BY API_NAME"; } else if ("DATE".equals(search.getSearchType())) { - dataSql = "SELECT TO_CHAR(STAT_TIME,'YYYY-MM-DD') AS STAT_TIME "; + dataSql = "SELECT TO_CHAR(STAT_TIME,'YYYY-MM-DD') AS STAT_TIME, '' AS APIDESC "; dataSql += getQueryBody(search); dataSql += " GROUP BY TO_CHAR(STAT_TIME,'YYYY-MM-DD')"; dataSql += " ORDER BY STAT_TIME"; @@ -85,7 +85,7 @@ public class ApiUseStatsService StringBuilder where = buildNativeWhere(search); String totalSql; - totalSql = "SELECT '합계' AS ORGNAME "; + totalSql = "SELECT '합계' AS ORGNAME, '' AS APIDESC "; totalSql += getQueryBody(search); Query totalQuery = entityManager.createNativeQuery(totalSql); @@ -109,6 +109,7 @@ public class ApiUseStatsService + " FROM (" + " SELECT A.STAT_TIME " + " , A.API_NAME" + + " , C.EAISVCDESC" + " , A.CLIENT_ID " + " , A.TOTAL_CNT" + " , A.SUCCESS_CNT" @@ -118,13 +119,13 @@ public class ApiUseStatsService + " , A.MIN_RESP_TIME" + " , A.MAX_RESP_TIME" + " , CASE WHEN API_NAME LIKE '%1' THEN SUBSTR(OUTBOUND_ADAPTER,2,3) ELSE SUBSTR(INBOUND_ADAPTER,2,3) END AS ORG_CODE" - + " FROM API_STATS_DAY A " + + " FROM API_STATS_DAY A LEFT OUTER JOIN TSEAIHE01 C ON A.API_NAME = C.EAISVCNAME " + " WHERE A.STAT_TIME >= :searchStartDate " + " AND A.STAT_TIME <= :searchEndDate "; if (StringUtils.isNotBlank(search.getSearchApiName())) { - dataSql += " AND A.API_NAME LIKE :searchApiName "; + dataSql += " AND UPPER(A.API_NAME) LIKE :searchApiName "; } dataSql += " ) A" @@ -160,15 +161,16 @@ public class ApiUseStatsService private ApiStatsUI toVO(Object[] row) { ApiStatsUI vo = new ApiStatsUI(); vo.setOrgName(StringUtils.toString(row[0])); - vo.setTotalCnt(StringUtils.toLong(row[1])); - vo.setSuccessCnt(StringUtils.toLong(row[2])); - vo.setSuccessRate(StringUtils.toDecimal(row[3]).setScale(2, RoundingMode.HALF_UP)); - vo.setFailRate(StringUtils.toDecimal(row[4]).setScale(2, RoundingMode.HALF_UP)); - vo.setTimeoutCnt(StringUtils.toLong(row[5])); - vo.setSystemErrCnt(StringUtils.toLong(row[6])); - vo.setAvgRespTime(StringUtils.toDecimal(row[7])); - vo.setMinRespTime(StringUtils.toDecimal(row[8])); - vo.setMaxRespTime(StringUtils.toDecimal(row[9])); + vo.setApiDesc(StringUtils.toString(row[1])); + vo.setTotalCnt(StringUtils.toLong(row[2])); + vo.setSuccessCnt(StringUtils.toLong(row[3])); + vo.setSuccessRate(StringUtils.toDecimal(row[4]).setScale(2, RoundingMode.HALF_UP)); + vo.setFailRate(StringUtils.toDecimal(row[5]).setScale(2, RoundingMode.HALF_UP)); + vo.setTimeoutCnt(StringUtils.toLong(row[6])); + vo.setSystemErrCnt(StringUtils.toLong(row[7])); + vo.setAvgRespTime(StringUtils.toDecimal(row[8])); + vo.setMinRespTime(StringUtils.toDecimal(row[9])); + vo.setMaxRespTime(StringUtils.toDecimal(row[10])); return vo; } diff --git a/src/main/java/com/eactive/ext/kjb/statistics/ApiStatsDayController.java b/src/main/java/com/eactive/ext/kjb/statistics/ApiStatsDayController.java index 01bba22..a178f00 100644 --- a/src/main/java/com/eactive/ext/kjb/statistics/ApiStatsDayController.java +++ b/src/main/java/com/eactive/ext/kjb/statistics/ApiStatsDayController.java @@ -4,6 +4,8 @@ import java.io.IOException; import java.time.LocalDate; import java.time.format.DateTimeFormatter; import java.util.List; +import java.util.Map; +import java.util.Set; import java.util.stream.Collectors; import javax.servlet.http.HttpServletResponse; @@ -18,7 +20,10 @@ import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; +import com.eactive.eai.rms.common.datasource.DataSourceContextHolder; +import com.eactive.eai.rms.common.datasource.DataSourceTypeManager; import com.eactive.eai.rms.common.vo.GridResponse; +import com.eactive.eai.rms.data.entity.onl.apim.obp.ObpGwMetricJpaDAO; import com.eactive.eai.rms.data.entity.onl.kjb.statistics.ApiStatsDay; import com.eactive.eai.rms.data.entity.onl.kjb.statistics.ApiStatsDayService; import com.eactive.ext.kjb.statistics.mapping.ApiStatsUIMapper; @@ -38,6 +43,7 @@ public class ApiStatsDayController { private final ApiStatsDayService service; private final ApiStatsUIMapper mapper; private final ApiStatsExcelExportService excelExportService; + private final ObpGwMetricJpaDAO apiNameDao; @GetMapping(value = "/onl/kjb/statistics/apiStatsDayMan.view") public String view() { @@ -66,9 +72,38 @@ public class ApiStatsDayController { @PostMapping(value = "/onl/kjb/statistics/apiStatsDayMan.json", params = "cmd=LIST_SUMMARY") public ResponseEntity> selectSummaryList(ApiStatsSearch search, Pageable pageable) { Page page = service.selectSummaryList(search, pageable); + fillApiDesc(page.getContent()); return ResponseEntity.ok(new GridResponse<>(page)); } + /** + * 요약 통계 행의 apiName(=API ID)으로 TSEAIHE01 을 조회하여 API 명(EAISVCDESC)을 채운다. + * 매칭되는 항목이 없으면 공란으로 둔다. + */ + private void fillApiDesc(List rows) { + if (rows == null || rows.isEmpty()) { + return; + } + + Set apiIds = rows.stream() + .map(ApiStatsUI::getApiName) + .filter(id -> id != null && !id.isEmpty()) + .collect(Collectors.toSet()); + + if (apiIds.isEmpty()) { + return; + } + + // API_STATS_DAY 와 동일한 APIGW 스키마에서 조회 + DataSourceContextHolder.setDataSourceType( + DataSourceTypeManager.getDataSourceType(DataSourceTypeManager.APIGW)); + + Map nameMap = apiNameDao.selectApiNames(apiIds); + for (ApiStatsUI row : rows) { + row.setApiDesc(nameMap.get(row.getApiName())); + } + } + @PostMapping(value = "/onl/kjb/statistics/apiStatsDayMan.json", params = "cmd=EXCEL_EXPORT") public void exportToExcel(ApiStatsSearch search, HttpServletResponse response) throws IOException { log.info("Excel export started - search: {}", search); diff --git a/src/main/java/com/eactive/ext/kjb/statistics/ApiStatsHourController.java b/src/main/java/com/eactive/ext/kjb/statistics/ApiStatsHourController.java index 20b2a9b..b02d003 100644 --- a/src/main/java/com/eactive/ext/kjb/statistics/ApiStatsHourController.java +++ b/src/main/java/com/eactive/ext/kjb/statistics/ApiStatsHourController.java @@ -4,6 +4,8 @@ import java.io.IOException; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.List; +import java.util.Map; +import java.util.Set; import java.util.stream.Collectors; import javax.servlet.http.HttpServletResponse; @@ -18,7 +20,10 @@ import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; +import com.eactive.eai.rms.common.datasource.DataSourceContextHolder; +import com.eactive.eai.rms.common.datasource.DataSourceTypeManager; import com.eactive.eai.rms.common.vo.GridResponse; +import com.eactive.eai.rms.data.entity.onl.apim.obp.ObpGwMetricJpaDAO; import com.eactive.eai.rms.data.entity.onl.kjb.statistics.ApiStatsHour; import com.eactive.eai.rms.data.entity.onl.kjb.statistics.ApiStatsHourService; import com.eactive.ext.kjb.statistics.mapping.ApiStatsUIMapper; @@ -38,6 +43,7 @@ public class ApiStatsHourController { private final ApiStatsHourService service; private final ApiStatsUIMapper mapper; private final ApiStatsExcelExportService excelExportService; + private final ObpGwMetricJpaDAO apiNameDao; @GetMapping(value = "/onl/kjb/statistics/apiStatsHourMan.view") public String view() { @@ -66,9 +72,38 @@ public class ApiStatsHourController { @PostMapping(value = "/onl/kjb/statistics/apiStatsHourMan.json", params = "cmd=LIST_SUMMARY") public ResponseEntity> selectSummaryList(ApiStatsSearch search, Pageable pageable) { Page page = service.selectSummaryList(search, pageable); + fillApiDesc(page.getContent()); return ResponseEntity.ok(new GridResponse<>(page)); } + /** + * 요약 통계 행의 apiName(=API ID)으로 TSEAIHE01 을 조회하여 API 명(EAISVCDESC)을 채운다. + * 매칭되는 항목이 없으면 공란으로 둔다. + */ + private void fillApiDesc(List rows) { + if (rows == null || rows.isEmpty()) { + return; + } + + Set apiIds = rows.stream() + .map(ApiStatsUI::getApiName) + .filter(id -> id != null && !id.isEmpty()) + .collect(Collectors.toSet()); + + if (apiIds.isEmpty()) { + return; + } + + // API_STATS_HOUR 와 동일한 APIGW 스키마에서 조회 + DataSourceContextHolder.setDataSourceType( + DataSourceTypeManager.getDataSourceType(DataSourceTypeManager.APIGW)); + + Map nameMap = apiNameDao.selectApiNames(apiIds); + for (ApiStatsUI row : rows) { + row.setApiDesc(nameMap.get(row.getApiName())); + } + } + @PostMapping(value = "/onl/kjb/statistics/apiStatsHourMan.json", params = "cmd=EXCEL_EXPORT") public void exportToExcel(ApiStatsSearch search, HttpServletResponse response) throws IOException { log.info("Excel export started - search: {}", search); diff --git a/src/main/java/com/eactive/ext/kjb/statistics/ApiStatsMinuteController.java b/src/main/java/com/eactive/ext/kjb/statistics/ApiStatsMinuteController.java index a153172..c8bc366 100644 --- a/src/main/java/com/eactive/ext/kjb/statistics/ApiStatsMinuteController.java +++ b/src/main/java/com/eactive/ext/kjb/statistics/ApiStatsMinuteController.java @@ -4,6 +4,8 @@ import java.io.IOException; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.List; +import java.util.Map; +import java.util.Set; import java.util.stream.Collectors; import javax.servlet.http.HttpServletResponse; @@ -18,7 +20,10 @@ import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; +import com.eactive.eai.rms.common.datasource.DataSourceContextHolder; +import com.eactive.eai.rms.common.datasource.DataSourceTypeManager; import com.eactive.eai.rms.common.vo.GridResponse; +import com.eactive.eai.rms.data.entity.onl.apim.obp.ObpGwMetricJpaDAO; import com.eactive.eai.rms.data.entity.onl.kjb.statistics.ApiStatsMinute; import com.eactive.eai.rms.data.entity.onl.kjb.statistics.ApiStatsMinuteService; import com.eactive.ext.kjb.statistics.mapping.ApiStatsUIMapper; @@ -38,6 +43,7 @@ public class ApiStatsMinuteController { private final ApiStatsMinuteService service; private final ApiStatsUIMapper mapper; private final ApiStatsExcelExportService excelExportService; + private final ObpGwMetricJpaDAO apiNameDao; @GetMapping(value = "/onl/kjb/statistics/apiStatsMinuteMan.view") public String view() { @@ -66,9 +72,38 @@ public class ApiStatsMinuteController { @PostMapping(value = "/onl/kjb/statistics/apiStatsMinuteMan.json", params = "cmd=LIST_SUMMARY") public ResponseEntity> selectSummaryList(ApiStatsSearch search, Pageable pageable) { Page page = service.selectSummaryList(search, pageable); + fillApiDesc(page.getContent()); return ResponseEntity.ok(new GridResponse<>(page)); } + /** + * 요약 통계 행의 apiName(=API ID)으로 TSEAIHE01 을 조회하여 API 명(EAISVCDESC)을 채운다. + * 매칭되는 항목이 없으면 공란으로 둔다. + */ + private void fillApiDesc(List rows) { + if (rows == null || rows.isEmpty()) { + return; + } + + Set apiIds = rows.stream() + .map(ApiStatsUI::getApiName) + .filter(id -> id != null && !id.isEmpty()) + .collect(Collectors.toSet()); + + if (apiIds.isEmpty()) { + return; + } + + // API_STATS_MINUTE 와 동일한 APIGW 스키마에서 조회 + DataSourceContextHolder.setDataSourceType( + DataSourceTypeManager.getDataSourceType(DataSourceTypeManager.APIGW)); + + Map nameMap = apiNameDao.selectApiNames(apiIds); + for (ApiStatsUI row : rows) { + row.setApiDesc(nameMap.get(row.getApiName())); + } + } + @PostMapping(value = "/onl/kjb/statistics/apiStatsMinuteMan.json", params = "cmd=EXCEL_EXPORT") public void exportToExcel(ApiStatsSearch search, HttpServletResponse response) throws IOException { log.info("Excel export started - search: {}", search); diff --git a/src/main/java/com/eactive/ext/kjb/statistics/ApiStatsMonthController.java b/src/main/java/com/eactive/ext/kjb/statistics/ApiStatsMonthController.java index 5f18d85..18ce3ea 100644 --- a/src/main/java/com/eactive/ext/kjb/statistics/ApiStatsMonthController.java +++ b/src/main/java/com/eactive/ext/kjb/statistics/ApiStatsMonthController.java @@ -4,6 +4,8 @@ import java.io.IOException; import java.time.YearMonth; import java.time.format.DateTimeFormatter; import java.util.List; +import java.util.Map; +import java.util.Set; import java.util.stream.Collectors; import javax.servlet.http.HttpServletResponse; @@ -18,7 +20,10 @@ import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; +import com.eactive.eai.rms.common.datasource.DataSourceContextHolder; +import com.eactive.eai.rms.common.datasource.DataSourceTypeManager; import com.eactive.eai.rms.common.vo.GridResponse; +import com.eactive.eai.rms.data.entity.onl.apim.obp.ObpGwMetricJpaDAO; import com.eactive.eai.rms.data.entity.onl.kjb.statistics.ApiStatsMonth; import com.eactive.eai.rms.data.entity.onl.kjb.statistics.ApiStatsMonthService; import com.eactive.ext.kjb.statistics.mapping.ApiStatsUIMapper; @@ -38,6 +43,7 @@ public class ApiStatsMonthController { private final ApiStatsMonthService service; private final ApiStatsUIMapper mapper; private final ApiStatsExcelExportService excelExportService; + private final ObpGwMetricJpaDAO apiNameDao; @GetMapping(value = "/onl/kjb/statistics/apiStatsMonthMan.view") public String view() { @@ -66,9 +72,38 @@ public class ApiStatsMonthController { @PostMapping(value = "/onl/kjb/statistics/apiStatsMonthMan.json", params = "cmd=LIST_SUMMARY") public ResponseEntity> selectSummaryList(ApiStatsSearch search, Pageable pageable) { Page page = service.selectSummaryList(search, pageable); + fillApiDesc(page.getContent()); return ResponseEntity.ok(new GridResponse<>(page)); } + /** + * 요약 통계 행의 apiName(=API ID)으로 TSEAIHE01 을 조회하여 API 명(EAISVCDESC)을 채운다. + * 매칭되는 항목이 없으면 공란으로 둔다. + */ + private void fillApiDesc(List rows) { + if (rows == null || rows.isEmpty()) { + return; + } + + Set apiIds = rows.stream() + .map(ApiStatsUI::getApiName) + .filter(id -> id != null && !id.isEmpty()) + .collect(Collectors.toSet()); + + if (apiIds.isEmpty()) { + return; + } + + // API_STATS_MONTH 와 동일한 APIGW 스키마에서 조회 + DataSourceContextHolder.setDataSourceType( + DataSourceTypeManager.getDataSourceType(DataSourceTypeManager.APIGW)); + + Map nameMap = apiNameDao.selectApiNames(apiIds); + for (ApiStatsUI row : rows) { + row.setApiDesc(nameMap.get(row.getApiName())); + } + } + @PostMapping(value = "/onl/kjb/statistics/apiStatsMonthMan.json", params = "cmd=EXCEL_EXPORT") public void exportToExcel(ApiStatsSearch search, HttpServletResponse response) throws IOException { log.info("Excel export started - search: {}", search); diff --git a/src/main/java/com/eactive/ext/kjb/statistics/ApiStatsYearController.java b/src/main/java/com/eactive/ext/kjb/statistics/ApiStatsYearController.java index 8b2fb2a..8915357 100644 --- a/src/main/java/com/eactive/ext/kjb/statistics/ApiStatsYearController.java +++ b/src/main/java/com/eactive/ext/kjb/statistics/ApiStatsYearController.java @@ -3,6 +3,8 @@ package com.eactive.ext.kjb.statistics; import java.io.IOException; import java.time.Year; import java.util.List; +import java.util.Map; +import java.util.Set; import java.util.stream.Collectors; import javax.servlet.http.HttpServletResponse; @@ -17,7 +19,10 @@ import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; +import com.eactive.eai.rms.common.datasource.DataSourceContextHolder; +import com.eactive.eai.rms.common.datasource.DataSourceTypeManager; import com.eactive.eai.rms.common.vo.GridResponse; +import com.eactive.eai.rms.data.entity.onl.apim.obp.ObpGwMetricJpaDAO; import com.eactive.eai.rms.data.entity.onl.kjb.statistics.ApiStatsYear; import com.eactive.eai.rms.data.entity.onl.kjb.statistics.ApiStatsYearService; import com.eactive.ext.kjb.statistics.mapping.ApiStatsUIMapper; @@ -37,6 +42,7 @@ public class ApiStatsYearController { private final ApiStatsYearService service; private final ApiStatsUIMapper mapper; private final ApiStatsExcelExportService excelExportService; + private final ObpGwMetricJpaDAO apiNameDao; @GetMapping(value = "/onl/kjb/statistics/apiStatsYearMan.view") public String view() { @@ -65,9 +71,38 @@ public class ApiStatsYearController { @PostMapping(value = "/onl/kjb/statistics/apiStatsYearMan.json", params = "cmd=LIST_SUMMARY") public ResponseEntity> selectSummaryList(ApiStatsSearch search, Pageable pageable) { Page page = service.selectSummaryList(search, pageable); + fillApiDesc(page.getContent()); return ResponseEntity.ok(new GridResponse<>(page)); } + /** + * 요약 통계 행의 apiName(=API ID)으로 TSEAIHE01 을 조회하여 API 명(EAISVCDESC)을 채운다. + * 매칭되는 항목이 없으면 공란으로 둔다. + */ + private void fillApiDesc(List rows) { + if (rows == null || rows.isEmpty()) { + return; + } + + Set apiIds = rows.stream() + .map(ApiStatsUI::getApiName) + .filter(id -> id != null && !id.isEmpty()) + .collect(Collectors.toSet()); + + if (apiIds.isEmpty()) { + return; + } + + // API_STATS_YEAR 와 동일한 APIGW 스키마에서 조회 + DataSourceContextHolder.setDataSourceType( + DataSourceTypeManager.getDataSourceType(DataSourceTypeManager.APIGW)); + + Map nameMap = apiNameDao.selectApiNames(apiIds); + for (ApiStatsUI row : rows) { + row.setApiDesc(nameMap.get(row.getApiName())); + } + } + @PostMapping(value = "/onl/kjb/statistics/apiStatsYearMan.json", params = "cmd=EXCEL_EXPORT") public void exportToExcel(ApiStatsSearch search, HttpServletResponse response) throws IOException { log.info("Excel export started - search: {}", search); diff --git a/src/main/java/com/eactive/ext/kjb/statistics/ui/ApiStatsUI.java b/src/main/java/com/eactive/ext/kjb/statistics/ui/ApiStatsUI.java index 83ee405..4f8fa2b 100644 --- a/src/main/java/com/eactive/ext/kjb/statistics/ui/ApiStatsUI.java +++ b/src/main/java/com/eactive/ext/kjb/statistics/ui/ApiStatsUI.java @@ -11,6 +11,7 @@ import lombok.Data; public class ApiStatsUI { private String statTime; private String apiName; + private String apiDesc; private String gwInstanceId; private String bizDivCode; private String clientId; From a1d2058bcadd8ef237d25898e4d6931632f8bf14 Mon Sep 17 00:00:00 2001 From: eastargh Date: Mon, 31 Aug 2026 11:12:24 +0900 Subject: [PATCH 09/13] =?UTF-8?q?=EC=9B=B9=ED=9B=85=20=EC=9D=B8=EC=A6=9D?= =?UTF-8?q?=20=EC=9A=B0=ED=9A=8C=20=EB=A1=9C=EA=B7=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../rms/ext/djb/webhook/service/WebhookService.java | 6 ++++++ .../rms/ext/user/sync/RestTemplateConfiguration.java | 11 +++++++++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/eactive/eai/rms/ext/djb/webhook/service/WebhookService.java b/src/main/java/com/eactive/eai/rms/ext/djb/webhook/service/WebhookService.java index 35b54b4..a3f91ac 100644 --- a/src/main/java/com/eactive/eai/rms/ext/djb/webhook/service/WebhookService.java +++ b/src/main/java/com/eactive/eai/rms/ext/djb/webhook/service/WebhookService.java @@ -245,6 +245,12 @@ public class WebhookService { Exception lastException = null; for (int attempt = 0; attempt <= retryCount; attempt++) { try { + if (attempt == 0) { + // 배포 반영 확인용: HttpComponentsClientHttpRequestFactory 여야 SSL 우회 RestTemplate 사용 중. + // SimpleClientHttpRequestFactory 로 찍히면 기본 new RestTemplate() 이 주입된 것 → PKIX 원인. + log.info("[Webhook] POST 시도 - url: {}, requestFactory: {}", proxyUrl, + restTemplate.getRequestFactory().getClass().getSimpleName()); + } if (attempt > 0) { long delayMs = (long) retryTime * attempt; log.info("[Webhook] 재시도 {}/{} - url: {}, delay: {}ms", attempt, retryCount, proxyUrl, delayMs); diff --git a/src/main/java/com/eactive/eai/rms/ext/user/sync/RestTemplateConfiguration.java b/src/main/java/com/eactive/eai/rms/ext/user/sync/RestTemplateConfiguration.java index 933ce4e..f6e8195 100644 --- a/src/main/java/com/eactive/eai/rms/ext/user/sync/RestTemplateConfiguration.java +++ b/src/main/java/com/eactive/eai/rms/ext/user/sync/RestTemplateConfiguration.java @@ -13,6 +13,9 @@ import org.springframework.web.client.RestTemplate; import javax.net.ssl.SSLContext; +import lombok.extern.slf4j.Slf4j; + +@Slf4j @Configuration public class RestTemplateConfiguration { @@ -36,20 +39,24 @@ public class RestTemplateConfiguration { .setMaxConnPerRoute(20) .build(); + // 배포 반영 확인용: 이 줄이 기동 로그에 없으면 수정 전 클래스가 떠 있는 것. + log.info("[RestTemplateConfiguration] httpClient 생성: SSL 인증서 체인/호스트명 검증 비활성화(TrustAllStrategy+NoopHostnameVerifier) 적용"); + return httpClient; } - + @Bean public HttpComponentsClientHttpRequestFactory factory(HttpClient httpClient) { HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(); factory.setHttpClient(httpClient); return factory; } - + @Bean public RestTemplate restTemplate(HttpComponentsClientHttpRequestFactory factory) { RestTemplate restTemplate = new RestTemplate(); restTemplate.setRequestFactory(factory); + log.info("[RestTemplateConfiguration] RestTemplate 빈 생성: requestFactory={}", factory.getClass().getSimpleName()); return restTemplate; } From 5fd8334ea88d9bf8cb058d1a5cae81938611a81d Mon Sep 17 00:00:00 2001 From: eastargh Date: Tue, 1 Sep 2026 10:07:32 +0900 Subject: [PATCH 10/13] =?UTF-8?q?UI=20=EC=A1=B0=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- WebContent/jsp/common/screen/top_04.jsp | 2 +- .../jsp/onl/kjb/statistics/apiStatsDayMan.jsp | 52 +++++++- .../onl/kjb/statistics/apiStatsHourMan.jsp | 49 ++++++- .../onl/kjb/statistics/apiStatsMinuteMan.jsp | 126 +++++++++++++----- .../onl/kjb/statistics/apiStatsMonthMan.jsp | 4 +- .../onl/kjb/statistics/apiStatsYearMan.jsp | 2 +- .../tracking/trackingManDetail.jsp | 7 +- 7 files changed, 200 insertions(+), 42 deletions(-) diff --git a/WebContent/jsp/common/screen/top_04.jsp b/WebContent/jsp/common/screen/top_04.jsp index 6a9ed54..c103f09 100644 --- a/WebContent/jsp/common/screen/top_04.jsp +++ b/WebContent/jsp/common/screen/top_04.jsp @@ -596,7 +596,7 @@
- +
-
API명별 요약 통계
+
API별 요약 통계
diff --git a/WebContent/jsp/onl/kjb/statistics/apiStatsHourMan.jsp b/WebContent/jsp/onl/kjb/statistics/apiStatsHourMan.jsp index 0c70059..6a2512c 100644 --- a/WebContent/jsp/onl/kjb/statistics/apiStatsHourMan.jsp +++ b/WebContent/jsp/onl/kjb/statistics/apiStatsHourMan.jsp @@ -33,6 +33,18 @@ width: 100%; height: 250px; } + /* 달력 팝업이 파이차트 가운데 숫자(z-index:10) 위로 오도록 */ + #ui-datepicker-div { + z-index: 9999 !important; + } + .cssbtn.small { + height: 24px; + border-radius: 2px; + min-width: 24px; + box-shadow: none; + border-color: #ebebec; + font-size: 11px; + } @@ -292,6 +304,25 @@ fetchChartData(); } + // 조회일자를 하루 이동 후 조회 (direction: -1 이전날, 1 이후날) + function shiftSearchDate(direction) { + var v = $("input[name=searchDate]").val().replace(/-/g, ""); + if (!v || v.length !== 8) return; + + var d = new Date( + parseInt(v.substring(0, 4)), + parseInt(v.substring(4, 6)) - 1, + parseInt(v.substring(6, 8)) + ); + d.setDate(d.getDate() + direction); + + function pad(n) { return String(n).padStart(2, '0'); } + $("input[name=searchDate]").val( + d.getFullYear() + '-' + pad(d.getMonth() + 1) + '-' + pad(d.getDate())); + + search(); + } + function exportToExcel(isSummary) { var cmdType = isSummary ? 'EXCEL_EXPORT_SUMMARY' : 'EXCEL_EXPORT'; console.log('[Excel Export] Starting... (type: ' + cmdType + ')'); @@ -460,8 +491,8 @@ datatype: "json", mtype: 'POST', postData: gridPostData, - colNames: [ - '통계시간', 'API명', '인스턴스', '업무구분', '클라이언트ID', + colNames: [ + '통계시간', 'API ID', '인스턴스', '업무구분', '클라이언트ID', 'Inbound Adapter', 'Outbound Adapter', '총건수', '성공', 'Timeout', '시스템오류', '평균응답(ms)', '최소응답(ms)', '최대응답(ms)' @@ -507,6 +538,8 @@ // 날짜 입력 마스크 $("input[name=searchDate]").inputmask("9999-99-99", { 'autoUnmask': true }); + $("input[name=searchDate]").datepicker(); + // 기본값 설정 (오늘) var today = getToday(); if (!$("input[name=searchDate]").val()) { @@ -525,6 +558,14 @@ search(); }); + $("#btnPrevDate").click(function() { + shiftSearchDate(-1); + }); + + $("#btnNextDate").click(function() { + shiftSearchDate(1); + }); + $("#btn_excel_export_summary").click(function() { exportToExcel(true); }); @@ -577,6 +618,8 @@ 조회일자 + + (선택일 00시 ~ 23시 조회) @@ -622,7 +665,7 @@
-
API명별 요약 통계
+
API별 요약 통계
diff --git a/WebContent/jsp/onl/kjb/statistics/apiStatsMinuteMan.jsp b/WebContent/jsp/onl/kjb/statistics/apiStatsMinuteMan.jsp index 21070c4..dd8c252 100644 --- a/WebContent/jsp/onl/kjb/statistics/apiStatsMinuteMan.jsp +++ b/WebContent/jsp/onl/kjb/statistics/apiStatsMinuteMan.jsp @@ -26,6 +26,18 @@ border: 1px solid #ddd; background: #fff; } + .cssbtn.small { + height: 24px; + border-radius: 2px; + min-width: 24px; + box-shadow: none; + border-color: #ebebec; + font-size: 11px; + } + /* 달력 팝업이 파이차트 가운데 숫자(z-index:10) 위로 오도록 */ + #ui-datepicker-div { + z-index: 9999 !important; + } @@ -203,7 +215,6 @@ function validateAndAdjustDateRange() { var startDate = $("input[name=searchStartDate]").val().replace(/-/g, ""); var startTime = $("input[name=searchStartTime]").val().replace(/:/g, ""); - var endDate = $("input[name=searchEndDate]").val().replace(/-/g, ""); var endTime = $("input[name=searchEndTime]").val().replace(/:/g, ""); if (!startDate || !startTime) return null; @@ -219,35 +230,43 @@ ); } - // UI 업데이트 헬퍼 - function updateEndDateTime(dt) { - var d = dt.getFullYear() + + // 종료일시(yyyyMMddHHmm) 포맷 헬퍼 + function formatDateTime(dt) { + return dt.getFullYear() + String(dt.getMonth() + 1).padStart(2, '0') + - String(dt.getDate()).padStart(2, '0'); - var t = String(dt.getHours()).padStart(2, '0') + + String(dt.getDate()).padStart(2, '0') + + String(dt.getHours()).padStart(2, '0') + String(dt.getMinutes()).padStart(2, '0'); - $("input[name=searchEndDate]").val(d.substring(0, 4) + '-' + d.substring(4, 6) + '-' + d.substring(6, 8)); - $("input[name=searchEndTime]").val(t.substring(0, 2) + ':' + t.substring(2, 4)); - return d + t; + } + + // 종료시각 UI 업데이트 헬퍼 + function updateEndTime(dt) { + $("input[name=searchEndTime]").val( + String(dt.getHours()).padStart(2, '0') + ':' + String(dt.getMinutes()).padStart(2, '0')); + return formatDateTime(dt); } var start = parseDateTime(startDate, startTime); var startDateTime = startDate + startTime; var endDateTime; - if (endDate && endTime) { - var end = parseDateTime(endDate, endTime); + if (endTime) { + // 종료일은 시작일 기준. 종료시각이 시작시각보다 이르면 자정을 넘긴 것으로 보고 +1일 + var end = parseDateTime(startDate, endTime); + if (end < start) { + end = new Date(end.getTime() + 24 * 60 * 60 * 1000); + } var diffMinutes = (end - start) / (1000 * 60); if (diffMinutes > 60) { // 1시간 초과 시 시작 + 1시간으로 조정 - endDateTime = updateEndDateTime(new Date(start.getTime() + 60 * 60 * 1000)); + endDateTime = updateEndTime(new Date(start.getTime() + 60 * 60 * 1000)); } else { - endDateTime = endDate + endTime; + endDateTime = formatDateTime(end); } } else { // 종료시간 미입력 시 시작 + 1시간으로 설정 - endDateTime = updateEndDateTime(new Date(start.getTime() + 60 * 60 * 1000)); + endDateTime = updateEndTime(new Date(start.getTime() + 60 * 60 * 1000)); } return { start: startDateTime, end: endDateTime }; @@ -302,6 +321,44 @@ fetchChartData(range); } + // 조회기간을 정시 단위 1시간 구간으로 이동 (direction: -1 이전, 1 이후) + // ex) 시작 08:35, 이전 클릭 -> 08:00~09:00, 다시 -> 07:00~08:00 + // ex) 시작 08:35, 이후 클릭 -> 09:00~10:00, 다시 -> 10:00~11:00 + function shiftTimeRange(direction) { + var startDate = $("input[name=searchStartDate]").val().replace(/-/g, ""); + var startTime = $("input[name=searchStartTime]").val().replace(/:/g, ""); + if (!startDate || !startTime) return; + + var start = new Date( + parseInt(startDate.substring(0, 4)), + parseInt(startDate.substring(4, 6)) - 1, + parseInt(startDate.substring(6, 8)), + parseInt(startTime.substring(0, 2)), + parseInt(startTime.substring(2, 4)) + ); + + if (direction < 0) { + // 분 단위가 있으면 정시로 내림, 이미 정시면 1시간 전 + if (start.getMinutes() === 0) start.setHours(start.getHours() - 1); + else start.setMinutes(0); + } else { + // 분 단위가 있으면 다음 정시로 올림, 이미 정시면 1시간 후 + start.setMinutes(0); + start.setHours(start.getHours() + 1); + } + + var end = new Date(start.getTime() + 60 * 60 * 1000); + + function pad(n) { return String(n).padStart(2, '0'); } + + $("input[name=searchStartDate]").val( + start.getFullYear() + '-' + pad(start.getMonth() + 1) + '-' + pad(start.getDate())); + $("input[name=searchStartTime]").val(pad(start.getHours()) + ':' + pad(start.getMinutes())); + $("input[name=searchEndTime]").val(pad(end.getHours()) + ':' + pad(end.getMinutes())); + + search(); + } + function exportToExcel(isSummary) { var cmdType = isSummary ? 'EXCEL_EXPORT_SUMMARY' : 'EXCEL_EXPORT'; console.log('[Excel Export] Starting... (type: ' + cmdType + ')'); @@ -317,13 +374,10 @@ serviceType: '${param.serviceType}' }; - var startDate = $("input[name=searchStartDate]").val().replace(/-/g, ""); - var startTime = $("input[name=searchStartTime]").val().replace(/:/g, ""); - var endDate = $("input[name=searchEndDate]").val().replace(/-/g, ""); - var endTime = $("input[name=searchEndTime]").val().replace(/:/g, ""); - if (startDate && startTime) { - postData.searchStartDateTime = startDate + startTime; - postData.searchEndDateTime = endDate + endTime; + var range = validateAndAdjustDateRange(); + if (range) { + postData.searchStartDateTime = range.start; + postData.searchEndDateTime = range.end; } console.log('[Excel Export] Request data:', postData); @@ -474,7 +528,7 @@ mtype: 'POST', postData: gridPostData, colNames: [ - '통계시간', 'API명', '인스턴스', '업무구분', '클라이언트ID', + '통계시간', 'API ID', '인스턴스', '업무구분', '클라이언트ID', 'Inbound Adapter', 'Outbound Adapter', '총건수', '성공', 'Timeout', '시스템오류', '평균응답(ms)', '최소응답(ms)', '최대응답(ms)' @@ -518,9 +572,11 @@ $(document).ready(function() { // 날짜/시간 입력 마스크 - $("input[name=searchStartDate], input[name=searchEndDate]").inputmask("9999-99-99", { 'autoUnmask': true }); + $("input[name=searchStartDate]").inputmask("9999-99-99", { 'autoUnmask': true }); $("input[name=searchStartTime], input[name=searchEndTime]").inputmask("99:99", { 'autoUnmask': true }); + $("input[name=searchStartDate]").datepicker(); + // 기본값 설정 (현재 시간(분) 기준 1시간 전 ~ 현재 시간(분)) var now = new Date(); var oneHourAgo = new Date(now.getTime() - 60 * 60 * 1000); @@ -538,8 +594,7 @@ $("input[name=searchStartDate]").val(formatDate(oneHourAgo)); $("input[name=searchStartTime]").val(formatTime(oneHourAgo)); } - if (!$("input[name=searchEndDate]").val()) { - $("input[name=searchEndDate]").val(formatDate(now)); + if (!$("input[name=searchEndTime]").val()) { $("input[name=searchEndTime]").val(formatTime(now)); } @@ -555,6 +610,14 @@ search(); }); + $("#btnPrevTime").click(function() { + shiftTimeRange(-1); + }); + + $("#btnNextTime").click(function() { + shiftTimeRange(1); + }); + $("#btn_excel_export_summary").click(function() { exportToExcel(true); }); @@ -569,8 +632,8 @@ } }); - // 종료일/시간 변경 시 1시간 초과 검증 - $("input[name=searchEndDate], input[name=searchEndTime]").on('change blur', function() { + // 종료시각 변경 시 1시간 초과 검증 + $("input[name=searchEndTime]").on('change blur', function() { validateAndAdjustDateRange(); }); @@ -603,11 +666,12 @@ 조회기간 - - + + ~ - - + + + (최대 1시간, 초과시 자동 조정) API ID diff --git a/WebContent/jsp/onl/kjb/statistics/apiStatsMonthMan.jsp b/WebContent/jsp/onl/kjb/statistics/apiStatsMonthMan.jsp index 1e58e84..8501ecd 100644 --- a/WebContent/jsp/onl/kjb/statistics/apiStatsMonthMan.jsp +++ b/WebContent/jsp/onl/kjb/statistics/apiStatsMonthMan.jsp @@ -643,10 +643,10 @@
- +
-
API명별 요약 통계
+
API별 요약 통계
diff --git a/WebContent/jsp/onl/kjb/statistics/apiStatsYearMan.jsp b/WebContent/jsp/onl/kjb/statistics/apiStatsYearMan.jsp index 70a8bd0..a4d27b6 100644 --- a/WebContent/jsp/onl/kjb/statistics/apiStatsYearMan.jsp +++ b/WebContent/jsp/onl/kjb/statistics/apiStatsYearMan.jsp @@ -605,7 +605,7 @@
-
API명별 요약 통계
+
API별 요약 통계
diff --git a/WebContent/jsp/onl/transaction/tracking/trackingManDetail.jsp b/WebContent/jsp/onl/transaction/tracking/trackingManDetail.jsp index 2b35276..ccfe6dd 100644 --- a/WebContent/jsp/onl/transaction/tracking/trackingManDetail.jsp +++ b/WebContent/jsp/onl/transaction/tracking/trackingManDetail.jsp @@ -23,6 +23,9 @@ white-space: normal; } +.table_row td { padding: 0 5px; } +.table_row input { box-sizing: border-box; } +