GwMetric 테이블 변경

This commit is contained in:
Rinjae
2025-12-17 17:02:53 +09:00
parent 64aeedde2e
commit c4fbd276b9
9 changed files with 172 additions and 63 deletions
+1
View File
@@ -2,5 +2,6 @@
GET {{emsUrl}}/kjb/gw-metrics/after-timeslice/2025-12-02_01:00:00.json
### Pretty Print
GET {{emsUrl}}/kjb/gw-metrics/after-timeslice/2025-12-02_01:00:00.json?pretty=true
+4
View File
@@ -33,6 +33,10 @@
# Spring Bean 설정 및 리소스 실시간 반영 (경로 주의)
-javaagent:C:\path\to\hotswap-agent.jar
-XX:HotswapAgent=fatjar
-Dhotswap.agent.disablePlugin=org.hotswap.agent.plugin.ibatis.IBatisPlugin,org.hotswap.agent.plugin.mybatis.MyBatisPlugin
```
-----
@@ -61,18 +61,39 @@ public class ObpGwMetricDAO extends SqlMapClientTemplateDao {
}
/**
* URI 조회 (HTTP_ADAPTER_EXTRA_LOG)
* URI 및 HTTP Method 조회 결과
*/
public static class UriMethodResult {
private final Map<String, String> uriMap;
private final Map<String, String> methodMap;
public UriMethodResult(Map<String, String> uriMap, Map<String, String> methodMap) {
this.uriMap = uriMap;
this.methodMap = methodMap;
}
public Map<String, String> getUriMap() {
return uriMap;
}
public Map<String, String> getMethodMap() {
return methodMap;
}
}
/**
* URI 및 HTTP Method 조회 (HTTP_ADAPTER_EXTRA_LOG)
* - GUID = TSEAILG1X.EAISVCSERNO와 매핑
* - SERVICE_PROCESS_NUMBER = 100 (인바운드 요청)
*
* @param schemaId DB 스키마 ID
* @param eaiSvcSernos EAI 서비스 일련번호 목록 (GUID)
* @return eaiSvcSerno → URI 맵 (예: "POST /api/v1/transfer")
* @return UriMethodResult (eaiSvcSerno → URI 맵, eaiSvcSerno → METHOD 맵)
*/
@SuppressWarnings("unchecked")
public Map<String, String> selectUris(String schemaId, Set<String> eaiSvcSernos) {
public UriMethodResult selectUrisAndMethods(String schemaId, Set<String> eaiSvcSernos) {
if (eaiSvcSernos == null || eaiSvcSernos.isEmpty()) {
return new HashMap<>();
return new UriMethodResult(new HashMap<>(), new HashMap<>());
}
HashMap<String, Object> paramMap = new HashMap<>();
@@ -81,15 +102,22 @@ public class ObpGwMetricDAO extends SqlMapClientTemplateDao {
List<Map<String, String>> resultList = this.template.queryForList("ObpGwMetric.selectUris", paramMap);
Map<String, String> result = new HashMap<>();
Map<String, String> uriMap = new HashMap<>();
Map<String, String> methodMap = new HashMap<>();
for (Map<String, String> m : resultList) {
String eaiSvcSerno = m.get("EAI_SVC_SERNO");
String uri = m.get("URI");
if (eaiSvcSerno != null && uri != null) {
result.put(eaiSvcSerno, uri);
String method = m.get("METHOD");
if (eaiSvcSerno != null) {
if (uri != null) {
uriMap.put(eaiSvcSerno, uri);
}
if (method != null) {
methodMap.put(eaiSvcSerno, method);
}
}
}
return result;
return new UriMethodResult(uriMap, methodMap);
}
/**
@@ -0,0 +1,101 @@
package com.eactive.eai.rms.onl.apim.common;
import com.eactive.eai.rms.common.interceptor.InterceptorSkipController;
import com.google.gson.GsonBuilder;
import java.nio.charset.StandardCharsets;
/**
* REST API Controller 기본 클래스
*
* <p>기능:</p>
* <ul>
* <li>세션 체크 우회 (InterceptorSkipController)</li>
* <li>JSON 변환 유틸리티 (pretty print 지원)</li>
* <li>응답 크기 포맷팅</li>
* </ul>
*
* <p>사용 예:</p>
* <pre>
* {@code
* @Controller
* public class MyApiController extends BaseRestController {
* @RequestMapping(value = "/api/something.json", produces = JSON_CONTENT_TYPE)
* @ResponseBody
* public String getSomething(@RequestParam(value = "pretty", required = false) Boolean pretty) {
* List<MyDTO> data = service.getData();
* return toJson(data, pretty);
* }
* }
* }
* </pre>
*/
public abstract class BaseRestController implements InterceptorSkipController {
/**
* JSON 응답 Content-Type
*/
protected static final String JSON_CONTENT_TYPE = "application/json;charset=utf-8";
/**
* 객체를 JSON 문자열로 변환
*
* @param object 변환할 객체
* @param pretty true면 들여쓰기 적용 (null이면 false로 처리)
* @return JSON 문자열
*/
protected String toJson(Object object, Boolean pretty) {
GsonBuilder builder = new GsonBuilder().serializeNulls();
if (Boolean.TRUE.equals(pretty)) {
builder.setPrettyPrinting();
}
return builder.create().toJson(object);
}
/**
* 객체를 JSON 문자열로 변환 (기본: 한 줄 출력)
*
* @param object 변환할 객체
* @return JSON 문자열
*/
protected String toJson(Object object) {
return toJson(object, false);
}
/**
* 바이트 크기를 읽기 쉬운 형식으로 변환
* 예: 1024 -> "1.0 KB", 1048576 -> "1.0 MB"
*
* @param bytes 바이트 수
* @return 포맷된 문자열
*/
protected String formatByteSize(long bytes) {
if (bytes < 1024) {
return bytes + " B";
} else if (bytes < 1024 * 1024) {
return String.format("%.1f KB", bytes / 1024.0);
} else {
return String.format("%.1f MB", bytes / (1024.0 * 1024.0));
}
}
/**
* JSON 문자열의 바이트 크기 계산 (UTF-8 기준)
*
* @param json JSON 문자열
* @return 바이트 수
*/
protected int getJsonByteSize(String json) {
return json.getBytes(StandardCharsets.UTF_8).length;
}
/**
* JSON 문자열의 포맷된 바이트 크기 반환
*
* @param json JSON 문자열
* @return 포맷된 크기 문자열
*/
protected String getFormattedJsonSize(String json) {
return formatByteSize(getJsonByteSize(json));
}
}
@@ -16,6 +16,7 @@ import java.time.format.DateTimeFormatter;
* "apiId": "1ba56b82-a9c9-41e2-b3ab-ff9377667d56",
* "apiName": "[해외송금] 가상계좌 입금실패 정보조회",
* "apiRoutingUri": "/api/obs/remittance/remittancefail*",
* "httpMethod": "POST",
* "orgId": "ORG001",
* "partnerCode": "000008-01",
* "appId": "APP001",
@@ -42,6 +43,9 @@ public class ObpGwMetricAfterDTO {
@SerializedName("apiRoutingUri")
private String apiRoutingUri;
@SerializedName("httpMethod")
private String httpMethod;
@SerializedName("orgId")
private String orgId;
@@ -74,6 +78,7 @@ public class ObpGwMetricAfterDTO {
dto.setApiId(entity.getApiId());
dto.setApiName(entity.getApiName());
dto.setApiRoutingUri(entity.getUri());
dto.setHttpMethod(entity.getMethod());
dto.setOrgId(entity.getOrgId());
dto.setPartnerCode(entity.getPartnerCode());
dto.setAppId(entity.getAppId());
@@ -1,8 +1,6 @@
package com.eactive.eai.rms.onl.apim.obp;
import com.eactive.eai.rms.common.interceptor.InterceptorSkipController;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.eactive.eai.rms.onl.apim.common.BaseRestController;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
@@ -11,7 +9,6 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
@@ -20,10 +17,10 @@ import java.util.List;
* ObpGwMetric REST Controller
* API Gateway 시간별 거래량 지표 조회 API
*
* <p>세션 체크를 우회하기 위해 InterceptorSkipController 인터페이스 구현</p>
* @see BaseRestController 세션 체크 우회 및 JSON 유틸리티 제공
*/
@Controller
public class ObpGwMetricController implements InterceptorSkipController {
public class ObpGwMetricController extends BaseRestController {
private static final Logger log = LoggerFactory.getLogger(ObpGwMetricController.class);
@@ -45,7 +42,7 @@ public class ObpGwMetricController implements InterceptorSkipController {
*/
@RequestMapping(
value = "/kjb/gw-metrics/{datetime}.json",
produces = "application/json;charset=utf-8"
produces = JSON_CONTENT_TYPE
)
@ResponseBody
public String getGwMetrics(
@@ -65,13 +62,9 @@ public class ObpGwMetricController implements InterceptorSkipController {
// JSON 변환 (GSON)
String json = toJson(metrics, pretty);
// 응답 크기 계산 (UTF-8 기준)
int jsonBytes = json.getBytes(StandardCharsets.UTF_8).length;
String jsonSize = formatByteSize(jsonBytes);
long elapsed = System.currentTimeMillis() - startTime;
log.info("GwMetric 조회 완료: datetime={}, count={}, size={}, elapsed={}ms",
datetime, metrics.size(), jsonSize, elapsed);
datetime, metrics.size(), getFormattedJsonSize(json), elapsed);
return json;
@@ -90,7 +83,7 @@ public class ObpGwMetricController implements InterceptorSkipController {
*/
@RequestMapping(
value = "/kjb/gw-metrics/after-timeslice/{datetime}.json",
produces = "application/json;charset=utf-8"
produces = JSON_CONTENT_TYPE
)
@ResponseBody
public String getGwMetricsAfterTimeslice(
@@ -110,13 +103,9 @@ public class ObpGwMetricController implements InterceptorSkipController {
// JSON 변환 (GSON)
String json = toJson(metrics, pretty);
// 응답 크기 계산 (UTF-8 기준)
int jsonBytes = json.getBytes(StandardCharsets.UTF_8).length;
String jsonSize = formatByteSize(jsonBytes);
long elapsed = System.currentTimeMillis() - startTime;
log.info("GwMetric after-timeslice 조회 완료: datetime={}, count={}, size={}, elapsed={}ms",
datetime, metrics.size(), jsonSize, elapsed);
datetime, metrics.size(), getFormattedJsonSize(json), elapsed);
return json;
@@ -125,33 +114,4 @@ public class ObpGwMetricController implements InterceptorSkipController {
throw e;
}
}
/**
* 객체를 JSON 문자열로 변환
*
* @param object 변환할 객체
* @param pretty true면 들여쓰기 적용
* @return JSON 문자열
*/
private String toJson(Object object, Boolean pretty) {
GsonBuilder builder = new GsonBuilder().serializeNulls();
if (Boolean.TRUE.equals(pretty)) {
builder.setPrettyPrinting();
}
return builder.create().toJson(object);
}
/**
* 바이트 크기를 읽기 쉬운 형식으로 변환
* 예: 1024 -> "1.0 KB", 1048576 -> "1.0 MB"
*/
private String formatByteSize(long bytes) {
if (bytes < 1024) {
return bytes + " B";
} else if (bytes < 1024 * 1024) {
return String.format("%.1f KB", bytes / 1024.0);
} else {
return String.format("%.1f MB", bytes / (1024.0 * 1024.0));
}
}
}
@@ -32,6 +32,9 @@ public class ObpGwMetricDTO {
@SerializedName("apiRoutingUri")
private String uri;
@SerializedName("httpMethod")
private String method;
@SerializedName("attemptedCount")
private Long attemptedCount;
@@ -74,6 +77,7 @@ public class ObpGwMetricDTO {
dto.setTimeslice(formatTimeslice(entity.getTimeslice()));
dto.setApiName(entity.getApiName());
dto.setUri(entity.getUri());
dto.setMethod(entity.getMethod());
dto.setAttemptedCount(entity.getAttemptedCount());
dto.setCompletedCount(entity.getCompletedCount());
dto.setApiId(entity.getApiId());
@@ -165,7 +165,9 @@ public class ObpGwMetricManService extends BaseService {
.collect(Collectors.toSet());
Map<String, String> apiNameMap = obpGwMetricDAO.selectApiNames(apigwDataSourceType.getSchema(), apiIds);
Map<String, String> uriMap = obpGwMetricDAO.selectUris(apigwDataSourceType.getSchema(), eaiSvcSernos);
ObpGwMetricDAO.UriMethodResult uriMethodResult = obpGwMetricDAO.selectUrisAndMethods(apigwDataSourceType.getSchema(), eaiSvcSernos);
Map<String, String> uriMap = uriMethodResult.getUriMap();
Map<String, String> methodMap = uriMethodResult.getMethodMap();
log.info("[3단계] 부가정보 조회 완료: apiCount={}, eaiSvcSernoCount={}, elapsed={}ms",
apiIds.size(), eaiSvcSernos.size(), System.currentTimeMillis() - step3Start);
@@ -202,7 +204,8 @@ public class ObpGwMetricManService extends BaseService {
entity.setAttemptedCount(vo.getAttemptedCount());
entity.setCompletedCount(vo.getCompletedCount());
entity.setApiName(apiNameMap.get(vo.getApiId()));
entity.setUri(uriMap.get(vo.getEaiSvcSerno())); // eaiSvcSerno로 URI 조회
entity.setUri(uriMap.get(vo.getEaiSvcSerno())); // eaiSvcSerno로 URI 조회
entity.setMethod(methodMap.get(vo.getEaiSvcSerno())); // eaiSvcSerno로 HTTP Method 조회
entity.setAppId(appIdMap.get(vo.getOrgId()));
entity.setUpdateCount(entity.getUpdateCount() + 1); // 보정 횟수 증가
entity.setUpdatedAt(LocalDateTime.now());
@@ -211,7 +214,7 @@ public class ObpGwMetricManService extends BaseService {
updateCount++;
} else {
// INSERT: 신규 데이터 생성
ObpGwMetric entity = convertToEntity(vo, apiNameMap, uriMap, appIdMap);
ObpGwMetric entity = convertToEntity(vo, apiNameMap, uriMap, methodMap, appIdMap);
obpGwMetricService.save(entity);
insertCount++;
}
@@ -249,7 +252,8 @@ public class ObpGwMetricManService extends BaseService {
* VO → Entity 변환
*/
private ObpGwMetric convertToEntity(ObpGwMetricVO vo, Map<String, String> apiNameMap,
Map<String, String> uriMap, Map<String, String> appIdMap) {
Map<String, String> uriMap, Map<String, String> methodMap,
Map<String, String> appIdMap) {
ObpGwMetric entity = new ObpGwMetric();
entity.setClientId(vo.getClientId());
entity.setHostname(vo.getHostname());
@@ -259,7 +263,8 @@ public class ObpGwMetricManService extends BaseService {
entity.setAttemptedCount(vo.getAttemptedCount());
entity.setCompletedCount(vo.getCompletedCount());
entity.setApiName(apiNameMap.get(vo.getApiId()));
entity.setUri(uriMap.get(vo.getEaiSvcSerno())); // eaiSvcSerno로 URI 조회
entity.setUri(uriMap.get(vo.getEaiSvcSerno())); // eaiSvcSerno로 URI 조회
entity.setMethod(methodMap.get(vo.getEaiSvcSerno())); // eaiSvcSerno로 HTTP Method 조회
entity.setAppId(appIdMap.get(vo.getOrgId()));
entity.setUpdateCount(0);
entity.setCreatedAt(LocalDateTime.now());
@@ -66,15 +66,16 @@
</statement>
<!--
URI 조회 (HTTP_ADAPTER_EXTRA_LOG)
URI 및 HTTP Method 조회 (HTTP_ADAPTER_EXTRA_LOG)
- GUID = TSEAILG1X.EAISVCSERNO
- SERVICE_PROCESS_NUMBER = 100 (인바운드 요청)
- 결과: HTTP_METHOD + ' ' + URL (예: POST /mapi/kjbank/obp/kakaobank/v3/loan/query)
- 결과: HTTP_METHOD (예: POST), URL (예: /mapi/kjbank/obp/kakaobank/v3/loan/query)
-->
<statement id="selectUris" parameterClass="java.util.HashMap" resultClass="java.util.HashMap">
SELECT
GUID AS EAI_SVC_SERNO,
HTTP_METHOD || ' ' || URL AS URI
URL AS URI,
HTTP_METHOD AS METHOD
FROM $schemaId$.HTTP_ADAPTER_EXTRA_LOG
WHERE SERVICE_PROCESS_NUMBER = 100
AND GUID IN