웹훅 신청내역 조회화면

This commit is contained in:
eastargh
2026-08-26 10:58:32 +09:00
parent 929ec1f62d
commit b3f797b726
9 changed files with 649 additions and 2 deletions
@@ -0,0 +1,149 @@
<%@ page language="java" contentType="text/html; charset=utf-8" %>
<%@ page import="java.io.*" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib uri="http://www.springframework.org/tags" prefix="spring" %>
<%@ include file="/jsp/common/include/localemessage.jsp" %>
<%
response.setHeader("Pragma", "No-cache");
response.setHeader("Cache-Control", "no-cache");
response.setHeader("Expires", "0");
%>
<html>
<head>
<title></title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<jsp:include page="/jsp/common/include/css.jsp"/>
<jsp:include page="/jsp/common/include/script.jsp"/>
<style>
.url-cell { display:inline-block; max-width:380px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; vertical-align:middle; }
</style>
<script language="javascript">
var url = '<c:url value="/onl/apim/webhook/webhookReqMan.json" />';
var url_view = '<c:url value="/onl/apim/webhook/webhookReqMan.view" />';
function escapeHtmlJs(s) { return $('<div>').text(s == null ? '' : s).html(); }
function escapeAttr(s) { return escapeHtmlJs(s).replace(/"/g, '&quot;'); }
function formatTargetUrl(cellvalue, options, rowObject) {
var v = cellvalue || '';
return '<span class="url-cell" title="' + escapeAttr(v) + '">' + escapeHtmlJs(v) + '</span>';
}
function init() {
// 뒤로가기 등으로 이전 검색조건 파라미터가 있으면 기본값을 덮어씀
putSelectFromParam();
buildGrid();
}
function buildGrid() {
$('#grid').jqGrid({
datatype: "json",
mtype: 'POST',
url: url,
postData: getSearchForJqgrid("cmd", "LIST"),
colNames: ['id', 'No', '기관명', '리버스 프록시 경로', 'Target URL', '신청일시'],
colModel: [
{name: 'id', align: 'center', key: true, hidden: true},
{name: 'rowNum', align: 'center', width: 45, sortable: false},
{name: 'orgName', align: 'left', width: 160},
{name: 'reverseProxyPath', align: 'left', width: 160},
{name: 'targetUrl', align: 'left', width: 380, formatter: formatTargetUrl},
{name: 'createdDate', align: 'center', width: 140}
],
jsonReader: {repeatitems: false},
pager: $('#pager'),
page: '${param.page}',
rowNum: '${rmsDefaultRowNum}',
autoheight: true,
height: $("#container").height(),
autowidth: true,
viewrecords: true,
rowList: eval('[${rmsDefaultRowList}]'),
ondblClickRow: function (rowId) {
var rowData = $(this).getRowData(rowId);
var id = rowData['id'];
var url2 = url_view + '?cmd=DETAIL';
url2 += '&page=' + $(this).getGridParam("page");
url2 += '&returnUrl=' + getReturnUrl();
url2 += '&menuId=' + '${param.menuId}';
url2 += '&id=' + id;
url2 += '&' + getSearchUrl();
goNav(url2);
},
loadComplete: function () {
var page = $(this).getGridParam('page');
var rowNum = $(this).getGridParam('rowNum');
var rows = $(this).getDataIDs();
var colModel = $(this).getGridParam("colModel");
for (var i = 0; i < rows.length; i++) {
var number = ((page - 1) * rowNum + i) + 1;
$(this).setCell(rows[i], 'rowNum', number);
}
// 서버 고정 정렬(신청일시 역순) — 컬럼 정렬 비활성
for (var i = 0; i < colModel.length; i++) {
$(this).setColProp(colModel[i].name, {sortable: false});
}
},
loadError: function (jqXHR, textStatus, errorThrown) {
var location = '<%=request.getContextPath()%>/';
comloadError(jqXHR, textStatus, errorThrown, location);
}
});
resizeJqGridWidth('grid', 'content_middle', '1000');
}
$(document).ready(function () {
init();
$("#btn_search").click(function () {
var postData = getSearchForJqgrid("cmd", "LIST");
$("#grid").setGridParam({url: url, postData: postData, page: 1}).trigger("reloadGrid");
});
$("select[name^=search]").change(function () { $("#btn_search").click(); });
$("input[name^=search]").keydown(function (key) {
if (key.keyCode == 13) { $("#btn_search").click(); }
});
buttonControl();
});
</script>
</head>
<body>
<div class="right_box">
<div class="content_top">
<ul class="path"><li><a href="#">${rmsMenuPath}</a></li></ul>
</div><!-- end content_top -->
<div class="content_middle" id="content_middle">
<div class="search_wrap">
<button type="button" class="cssbtn" id="btn_search" level="R"><i class="material-icons">search</i> <%= localeMessage.getString("button.search") %></button>
</div>
<div class="title">웹훅 신청내역<span class="tooltip">등록된 웹훅 신청 내역을 조회합니다.</span></div>
<form id="ajaxForm" onsubmit="return false;">
<table class="search_condition" cellspacing=0;>
<colgroup>
<col style="width:120px;"><col style="width:200px;">
<col style="width:120px;"><col style="width:200px;">
</colgroup>
<tbody>
<tr>
<th>기관명</th>
<td><input type="text" name="searchOrgName" autocomplete="off"></td>
<th>리버스 프록시 경로</th>
<td><input type="text" name="searchReverseProxyPath" autocomplete="off"></td>
</tr>
<tr>
<th>Target URL</th>
<td colspan="3"><input type="text" name="searchTargetUrl" autocomplete="off" style="width:95%;"></td>
</tr>
</tbody>
</table>
</form>
<table id="grid"></table>
<div id="pager"></div>
</div><!-- end content_middle -->
</div><!-- end right_box -->
</body>
</html>
@@ -0,0 +1,154 @@
<%@ page language="java" contentType="text/html; charset=utf-8" %>
<%@ page import="java.io.*" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib uri="http://www.springframework.org/tags" prefix="spring" %>
<%@ include file="/jsp/common/include/localemessage.jsp" %>
<%
response.setHeader("Pragma", "No-cache");
response.setHeader("Cache-Control", "no-cache");
response.setHeader("Expires", "0");
%>
<html>
<head>
<title></title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<jsp:include page="/jsp/common/include/css.jsp"/>
<jsp:include page="/jsp/common/include/script.jsp"/>
<style>
.sub_list { width:100%; border-collapse:collapse; }
.sub_list th, .sub_list td { border:1px solid #ebebec; padding:6px 8px; text-align:left; }
.sub_list th { background:#f7f7f8; width:60px; }
.empty-msg { color:#999; padding:6px 2px; }
</style>
<script language="javascript">
var url = '<c:url value="/onl/apim/webhook/webhookReqMan.json" />';
var url_view = '<c:url value="/onl/apim/webhook/webhookReqMan.view" />';
var key = '${param.id}';
var combo;
function escapeHtmlJs(s) { return $('<div>').text(s == null ? '' : s).html(); }
// 이벤트유형 코드 → 모니터링 공통코드(EVENT_TYPE) 코드명
function eventTypeName(code) {
if (!combo || !combo.eventTypeList) { return code; }
for (var i = 0; i < combo.eventTypeList.length; i++) {
if (combo.eventTypeList[i].CODE == code) { return combo.eventTypeList[i].NAME; }
}
return code;
}
function renderApiList(apiIds) {
var $tbody = $('#apiListBody').empty();
if (!apiIds || apiIds.length === 0) {
$tbody.append('<tr><td class="empty-msg">등록된 API가 없습니다.</td></tr>');
return;
}
for (var i = 0; i < apiIds.length; i++) {
$tbody.append('<tr><td>' + escapeHtmlJs(apiIds[i]) + '</td></tr>');
}
}
function renderEventTypes(eventTypes) {
if (!eventTypes || eventTypes.length === 0) {
$('#eventTypes').text('');
return;
}
var names = eventTypes.map(function (code) { return eventTypeName(code); });
$('#eventTypes').text(names.join(', '));
}
function detail() {
if (!key) return;
$.ajax({
type: "POST", url: url, dataType: "json",
data: {cmd: 'DETAIL', id: key},
success: function (data) {
$('#id').text(data.id != null ? data.id : '');
$('#orgName').text(data.orgName || '');
$('#reverseProxyPath').text(data.reverseProxyPath || '');
$('#targetUrl').text(data.targetUrl || '');
$('#secret').text(data.secret || '');
$('#userSecret').text(data.userSecret || '');
$('#createdDate').text(data.createdDate || '');
renderApiList(data.apiIds);
renderEventTypes(data.eventTypes);
},
error: function (e) { alert(e.responseText); }
});
}
$(document).ready(function () {
var returnUrl = getReturnUrlForReturn();
buttonControl();
$.ajax({
type: "POST", url: url, dataType: "json",
data: {cmd: 'LIST_INIT_COMBO'},
success: function (json) {
combo = json;
detail();
},
error: function (e) { alert(e.responseText); }
});
$('#btn_previous').click(function () { goNav(returnUrl); });
});
</script>
</head>
<body>
<div class="right_box">
<div class="content_top">
<ul class="path"><li><a href="#">${rmsMenuPath}</a></li></ul>
</div>
<div class="content_middle">
<div class="search_wrap">
<button type="button" class="cssbtn" id="btn_previous" level="R" status="DETAIL,NEW"><i class="material-icons">arrow_back</i> <%= localeMessage.getString("button.previous") %></button>
</div>
<div class="title" id="title">웹훅 신청내역 상세</div>
<table class="table_row" cellspacing="0">
<colgroup>
<col style="width:12%"/><col style="width:38%"/>
<col style="width:12%"/><col style="width:38%"/>
</colgroup>
<tr>
<th>신청 ID</th>
<td colspan="3"><span id="id"></span></td>
</tr>
<tr>
<th>기관명</th>
<td><span id="orgName"></span></td>
<th>신청일시</th>
<td><span id="createdDate"></span></td>
</tr>
<tr>
<th>리버스 프록시 경로</th>
<td><span id="reverseProxyPath"></span></td>
<th>Target URL</th>
<td><span id="targetUrl"></span></td>
</tr>
<tr>
<th>Secret</th>
<td colspan="3"><span id="secret"></span></td>
</tr>
<tr>
<th>User Secret</th>
<td colspan="3"><span id="userSecret"></span></td>
</tr>
<tr>
<th>신청 이벤트</th>
<td colspan="3"><span id="eventTypes"></span></td>
</tr>
</table>
<div class="title" style="margin-top:20px;">API 목록</div>
<table class="table_row">
<colgroup><col style="width:100%"/></colgroup>
<thead><tr><th style="width:100%;">API ID</th></tr></thead>
<tbody id="apiListBody"></tbody>
</table>
</div>
</div>
</body>
</html>
@@ -34,4 +34,8 @@ public class WebhookReq {
@Column(name = "USER_SECRET", length = 200) @Column(name = "USER_SECRET", length = 200)
@Comment("사용자 Secret Key") @Comment("사용자 Secret Key")
private String userSecret; private String userSecret;
@Column(name = "CREATED_DATE", length = 14)
@Comment("신청일시 (yyyyMMddHHmmss)")
private String createdDate;
} }
@@ -0,0 +1,67 @@
package com.eactive.eai.rms.ext.djb.webhook.controller;
import java.util.HashMap;
import java.util.Map;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.web.SortDefault;
import org.springframework.http.ResponseEntity;
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.base.BaseAnnotationController;
import com.eactive.eai.rms.common.combo.ComboService;
import com.eactive.eai.rms.common.vo.GridResponse;
import com.eactive.eai.rms.ext.djb.webhook.dto.WebhookReqDetailUI;
import com.eactive.eai.rms.ext.djb.webhook.dto.WebhookReqUI;
import com.eactive.eai.rms.ext.djb.webhook.dto.WebhookReqUISearch;
import com.eactive.eai.rms.ext.djb.webhook.service.WebhookReqManService;
import lombok.RequiredArgsConstructor;
/**
* 웹훅 신청내역(PTL_WEBHOOK_REQ) 조회 화면 컨트롤러.
*
* <p>조회 전용 (등록/수정/삭제 없음)</p>
*/
@Controller
@RequiredArgsConstructor
public class WebhookReqManController extends BaseAnnotationController {
private final WebhookReqManService webhookReqManService;
private final ComboService comboService;
@GetMapping(value = "/onl/apim/webhook/webhookReqMan.view")
public void view() {
// 목록 view
}
@GetMapping(value = "/onl/apim/webhook/webhookReqMan.view", params = "cmd=DETAIL")
public String detailView() {
return "/onl/apim/webhook/webhookReqManDetail";
}
@PostMapping(value = "/onl/apim/webhook/webhookReqMan.json", params = "cmd=LIST")
public ResponseEntity<GridResponse<WebhookReqUI>> selectList(
@SortDefault(sort = "createdDate", direction = Sort.Direction.DESC) Pageable pageable,
WebhookReqUISearch search) {
Page<WebhookReqUI> page = webhookReqManService.selectList(pageable, search);
return ResponseEntity.ok(new GridResponse<>(page));
}
@PostMapping(value = "/onl/apim/webhook/webhookReqMan.json", params = "cmd=LIST_INIT_COMBO")
public ResponseEntity<Map<String, Object>> initCombo() {
Map<String, Object> resultMap = new HashMap<>();
resultMap.put("eventTypeList", comboService.getMonitoringCodeSortedBySeq(
WebhookReqManService.CODE_GROUP_EVENT_TYPE));
return ResponseEntity.ok(resultMap);
}
@PostMapping(value = "/onl/apim/webhook/webhookReqMan.json", params = "cmd=DETAIL")
public ResponseEntity<WebhookReqDetailUI> selectDetail(String id) {
return ResponseEntity.ok(webhookReqManService.selectDetail(id));
}
}
@@ -0,0 +1,34 @@
package com.eactive.eai.rms.ext.djb.webhook.dto;
import java.util.List;
import lombok.Data;
/**
* 웹훅 신청내역(PTL_WEBHOOK_REQ) 상세 화면 응답 DTO.
*
* <p>신청 상세 정보에 더해, 연관된 API 목록(PTL_WEBHOOK_REQ_API)과
* 신청 이벤트 목록(PTL_WEBHOOK_REQ_EVENT)을 함께 담는다.</p>
*/
@Data
public class WebhookReqDetailUI {
private String id;
private String orgId;
private String orgName;
private String reverseProxyPath;
private String targetUrl;
private String secret;
private String userSecret;
/* 신청일시 — yyyy-MM-dd HH:mm:ss */
private String createdDate;
/** PTL_WEBHOOK_REQ_API 목록 (API ID) */
private List<String> apiIds;
/** PTL_WEBHOOK_REQ_EVENT 목록 (이벤트유형 코드). 코드명은 화면에서 콤보로 변환한다. */
private List<String> eventTypes;
}
@@ -0,0 +1,21 @@
package com.eactive.eai.rms.ext.djb.webhook.dto;
import lombok.Data;
/**
* 웹훅 신청내역(PTL_WEBHOOK_REQ) 목록 화면 응답 DTO.
*/
@Data
public class WebhookReqUI {
private String id;
private String orgId;
private String orgName;
private String reverseProxyPath;
private String targetUrl;
/* 신청일시 — yyyy-MM-dd HH:mm:ss */
private String createdDate;
}
@@ -0,0 +1,16 @@
package com.eactive.eai.rms.ext.djb.webhook.dto;
import lombok.Data;
@Data
public class WebhookReqUISearch {
/* 기관명 (ptl_org.org_name, LIKE 검색) */
private String searchOrgName;
/* 리버스 프록시 경로 (ptl_org.reverse_proxy_path, LIKE 검색) */
private String searchReverseProxyPath;
/* 수신 URL (LIKE 검색) */
private String searchTargetUrl;
}
@@ -1,11 +1,11 @@
package com.eactive.eai.rms.ext.djb.webhook.repository; package com.eactive.eai.rms.ext.djb.webhook.repository;
import com.eactive.eai.data.jpa.BaseRepository;
import com.eactive.eai.rms.data.EMSDataSource; import com.eactive.eai.rms.data.EMSDataSource;
import com.eactive.eai.rms.data.entity.onl.djb.webhook.WebhookReq; import com.eactive.eai.rms.data.entity.onl.djb.webhook.WebhookReq;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository; import org.springframework.stereotype.Repository;
@Repository @Repository
@EMSDataSource @EMSDataSource
public interface WebhookReqRepository extends JpaRepository<WebhookReq, String> { public interface WebhookReqRepository extends BaseRepository<WebhookReq, String> {
} }
@@ -0,0 +1,202 @@
package com.eactive.eai.rms.ext.djb.webhook.service;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.eactive.apim.portal.portalorg.entity.QPortalOrg;
import com.eactive.eai.rms.common.base.BaseService;
import com.eactive.eai.rms.common.util.StringUtils;
import com.eactive.eai.rms.data.entity.onl.djb.webhook.QWebhookReq;
import com.eactive.eai.rms.data.entity.onl.djb.webhook.QWebhookReqApi;
import com.eactive.eai.rms.data.entity.onl.djb.webhook.QWebhookReqEvent;
import com.eactive.eai.rms.data.entity.onl.djb.webhook.WebhookReq;
import com.eactive.eai.rms.ext.djb.webhook.dto.WebhookReqDetailUI;
import com.eactive.eai.rms.ext.djb.webhook.dto.WebhookReqUI;
import com.eactive.eai.rms.ext.djb.webhook.dto.WebhookReqUISearch;
import com.eactive.eai.rms.ext.djb.webhook.repository.WebhookReqRepository;
import com.eactive.eai.rms.onl.common.exception.BizException;
import com.querydsl.core.BooleanBuilder;
import com.querydsl.jpa.impl.JPAQueryFactory;
import lombok.RequiredArgsConstructor;
/**
* 웹훅 신청내역(PTL_WEBHOOK_REQ) 조회 화면 서비스.
*
* <p>조회 전용. orgId 는 엔티티 연관관계가 아닌 단순 컬럼이므로,
* 기관명은 QueryDSL 로 ptl_org 를 별도 조회하여 매핑한다.</p>
*/
@Service
@RequiredArgsConstructor
@Transactional(transactionManager = "transactionManagerForEMS")
public class WebhookReqManService extends BaseService {
/** 모니터링 공통코드(tseairm28) 상의 웹훅 이벤트유형 코드 그룹. */
public static final String CODE_GROUP_EVENT_TYPE = "EVENT_TYPE";
private static final DateTimeFormatter FMT_DATETIME_OUT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
private static final DateTimeFormatter FMT_DATETIME_IN = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
private final WebhookReqRepository webhookReqRepository;
@PersistenceContext(unitName = "entityManagerFactoryForEMS")
private EntityManager entityManager;
public Page<WebhookReqUI> selectList(Pageable pageable, WebhookReqUISearch search) {
QWebhookReq req = QWebhookReq.webhookReq;
BooleanBuilder predicate = new BooleanBuilder();
if (StringUtils.isNotBlank(search.getSearchOrgName())) {
List<String> orgIds = findOrgIdsByName(search.getSearchOrgName());
if (orgIds.isEmpty()) {
// 일치하는 기관이 없으면 쿼리를 실행할 필요 없이 빈 결과를 바로 반환한다.
return Page.empty(pageable);
}
predicate.and(req.orgId.in(orgIds));
}
if (StringUtils.isNotBlank(search.getSearchReverseProxyPath())) {
List<String> orgIds = findOrgIdsByReverseProxyPath(search.getSearchReverseProxyPath());
if (orgIds.isEmpty()) {
return Page.empty(pageable);
}
predicate.and(req.orgId.in(orgIds));
}
if (StringUtils.isNotBlank(search.getSearchTargetUrl())) {
predicate.and(req.targetUrl.containsIgnoreCase(search.getSearchTargetUrl()));
}
Page<WebhookReq> page = webhookReqRepository.findAll(predicate, pageable);
Map<String, OrgInfo> orgInfoMap = findOrgInfo(page.getContent());
return page.map(e -> convertToListUI(e, orgInfoMap.get(e.getOrgId())));
}
public WebhookReqDetailUI selectDetail(String id) {
WebhookReq entity = webhookReqRepository.findById(id)
.orElseThrow(() -> new BizException("존재하지 않는 웹훅 신청내역입니다."));
Map<String, OrgInfo> orgInfoMap = findOrgInfo(Collections.singletonList(entity));
OrgInfo orgInfo = orgInfoMap.get(entity.getOrgId());
WebhookReqDetailUI ui = new WebhookReqDetailUI();
ui.setId(entity.getId());
ui.setOrgId(entity.getOrgId());
ui.setOrgName(orgInfo != null ? orgInfo.orgName : null);
ui.setReverseProxyPath(orgInfo != null ? orgInfo.reverseProxyPath : null);
ui.setTargetUrl(entity.getTargetUrl());
ui.setSecret(entity.getSecret());
ui.setUserSecret(entity.getUserSecret());
ui.setCreatedDate(fmtCreatedDate(entity.getCreatedDate()));
ui.setApiIds(findApiIds(id));
ui.setEventTypes(findEventTypes(id));
return ui;
}
/** 기관명 LIKE 검색으로 매칭되는 orgId 목록. */
private List<String> findOrgIdsByName(String orgName) {
QPortalOrg org = QPortalOrg.portalOrg;
return new JPAQueryFactory(entityManager)
.select(org.id)
.from(org)
.where(org.orgName.containsIgnoreCase(orgName))
.fetch();
}
/** 리버스 프록시 경로 LIKE 검색으로 매칭되는 orgId 목록. */
private List<String> findOrgIdsByReverseProxyPath(String reverseProxyPath) {
QPortalOrg org = QPortalOrg.portalOrg;
return new JPAQueryFactory(entityManager)
.select(org.id)
.from(org)
.where(org.reverseProxyPath.containsIgnoreCase(reverseProxyPath))
.fetch();
}
/** 목록/상세에 필요한 orgId → (기관명, 리버스 프록시 경로) 매핑 (일괄 조회로 N+1 방지). */
private Map<String, OrgInfo> findOrgInfo(List<WebhookReq> reqs) {
Set<String> orgIds = reqs.stream()
.map(WebhookReq::getOrgId)
.filter(StringUtils::isNotBlank)
.collect(Collectors.toSet());
if (orgIds.isEmpty()) {
return Collections.emptyMap();
}
QPortalOrg org = QPortalOrg.portalOrg;
return new JPAQueryFactory(entityManager)
.select(org.id, org.orgName, org.reverseProxyPath)
.from(org)
.where(org.id.in(orgIds))
.fetch()
.stream()
.collect(Collectors.toMap(t -> t.get(org.id),
t -> new OrgInfo(t.get(org.orgName), t.get(org.reverseProxyPath))));
}
/** 신청 건에 등록된 API ID 목록 (PTL_WEBHOOK_REQ_API). */
private List<String> findApiIds(String webhookReqId) {
QWebhookReqApi api = QWebhookReqApi.webhookReqApi;
return new JPAQueryFactory(entityManager)
.select(api.id.apiId)
.from(api)
.where(api.id.webhookReqId.eq(webhookReqId))
.fetch();
}
/** 신청 건에 등록된 이벤트유형 코드 목록 (PTL_WEBHOOK_REQ_EVENT). */
private List<String> findEventTypes(String webhookReqId) {
QWebhookReqEvent evt = QWebhookReqEvent.webhookReqEvent;
return new JPAQueryFactory(entityManager)
.select(evt.id.eventType)
.from(evt)
.where(evt.id.webhookReqId.eq(webhookReqId))
.fetch();
}
private WebhookReqUI convertToListUI(WebhookReq e, OrgInfo orgInfo) {
WebhookReqUI ui = new WebhookReqUI();
ui.setId(e.getId());
ui.setOrgId(e.getOrgId());
ui.setOrgName(orgInfo != null ? orgInfo.orgName : null);
ui.setReverseProxyPath(orgInfo != null ? orgInfo.reverseProxyPath : null);
ui.setTargetUrl(e.getTargetUrl());
ui.setCreatedDate(fmtCreatedDate(e.getCreatedDate()));
return ui;
}
/** ptl_org 조회 결과(기관명 + 리버스 프록시 경로) 보관용. */
private static final class OrgInfo {
private final String orgName;
private final String reverseProxyPath;
private OrgInfo(String orgName, String reverseProxyPath) {
this.orgName = orgName;
this.reverseProxyPath = reverseProxyPath;
}
}
/** CREATED_DATE(yyyyMMddHHmmss 문자열) → 화면 표시용(yyyy-MM-dd HH:mm:ss). */
private static String fmtCreatedDate(String raw) {
if (StringUtils.isBlank(raw)) {
return "";
}
try {
return LocalDateTime.parse(raw, FMT_DATETIME_IN).format(FMT_DATETIME_OUT);
} catch (Exception e) {
return raw;
}
}
}