Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b7c8e7f41c | |||
| 6c0fca22f3 |
Vendored
+153
-162
@@ -1,189 +1,180 @@
|
||||
pipeline {
|
||||
agent none
|
||||
agent { label 'djb-vm' }
|
||||
|
||||
options {
|
||||
timestamps()
|
||||
disableConcurrentBuilds()
|
||||
skipDefaultCheckout() // stage별 agent의 자동 SCM checkout 방지 (Deploy 노드는 git 접근 불필요, unstash로만 WAR 수신)
|
||||
buildDiscarder(logRotator(numToKeepStr: '20', artifactNumToKeepStr: '20'))
|
||||
}
|
||||
|
||||
environment {
|
||||
JAVA_HOME = '/apps/opts/jdk8'
|
||||
JAVA_HOME_TOMCAT = '/apps/opts/jdk17'
|
||||
GRADLE_HOME = '/apps/opts/gradle-8.7'
|
||||
GRADLE_USER_HOME = '/apps/opts/gradle-home'
|
||||
NODE_HOME = '/apps/opts/node-v24'
|
||||
PATH = "/apps/opts/jdk8/bin:/apps/opts/gradle-8.7/bin:/apps/opts/node-v24/bin:/apps/opts/bin:${env.PATH}"
|
||||
GIT_SSH_COMMAND = 'ssh -o StrictHostKeyChecking=accept-new'
|
||||
WEBHOOK_URL = 'http://172.30.1.50:18000/api/hooks/01ba51b01d3c4c98ae8f3183a23a84ed713197857d024b5da4f303458195eb32'
|
||||
CATALINA_BASE = '/prod/eapim/adminportal'
|
||||
CATALINA_HOME = '/prod/eapim/apache-tomcat-9.0.116'
|
||||
CATALINA_PID = '/prod/eapim/adminportal/temp/catalina.pid'
|
||||
DEPLOY_HTTP_PORT = '39120'
|
||||
}
|
||||
|
||||
stages {
|
||||
stage('Build (djb-vm)') {
|
||||
agent { label 'djb-vm' }
|
||||
environment {
|
||||
JAVA_HOME = '/apps/opts/jdk8'
|
||||
GRADLE_HOME = '/apps/opts/gradle-8.7'
|
||||
GRADLE_USER_HOME = '/apps/opts/gradle-home'
|
||||
NODE_HOME = '/apps/opts/node-v24'
|
||||
PATH = "/apps/opts/jdk8/bin:/apps/opts/gradle-8.7/bin:/apps/opts/node-v24/bin:/apps/opts/bin:${env.PATH}"
|
||||
}
|
||||
stages {
|
||||
stage('Checkout') {
|
||||
steps { checkout scm }
|
||||
}
|
||||
|
||||
stage('Checkout dependencies') {
|
||||
steps {
|
||||
sh '''
|
||||
set -eu
|
||||
cd "$WORKSPACE/.."
|
||||
|
||||
mkdir -p eapim-online
|
||||
|
||||
for REPO in elink-online-core elink-online-core-jpa elink-online-transformer elink-online-common elink-online-emsclient; do
|
||||
TARGET="eapim-online/$REPO"
|
||||
if [ ! -d "$TARGET/.git" ]; then
|
||||
rm -rf "$TARGET"
|
||||
git clone --depth=1 --branch master \
|
||||
"ssh://git@172.30.1.50:2222/djb-eapim/$REPO.git" \
|
||||
"$TARGET"
|
||||
else
|
||||
git -C "$TARGET" fetch --depth=1 origin master
|
||||
git -C "$TARGET" reset --hard origin/master
|
||||
git -C "$TARGET" clean -fdx
|
||||
fi
|
||||
done
|
||||
|
||||
for REPO in elink-portal-common eapim-admin-djb; do
|
||||
if [ ! -d "$REPO/.git" ]; then
|
||||
rm -rf "$REPO"
|
||||
git clone --depth=1 --branch master \
|
||||
"ssh://git@172.30.1.50:2222/djb-eapim/$REPO.git" \
|
||||
"$REPO"
|
||||
else
|
||||
git -C "$REPO" fetch --depth=1 origin master
|
||||
git -C "$REPO" reset --hard origin/master
|
||||
git -C "$REPO" clean -fdx
|
||||
fi
|
||||
done
|
||||
'''
|
||||
}
|
||||
}
|
||||
|
||||
stage('Verify toolchain') {
|
||||
steps {
|
||||
sh '''
|
||||
set -eu
|
||||
java -version
|
||||
gradle --version
|
||||
'''
|
||||
}
|
||||
}
|
||||
|
||||
stage('Build WAR') {
|
||||
steps { sh 'gradle clean build -x test --no-daemon -Pprofile=weblogic' }
|
||||
post {
|
||||
success {
|
||||
sh '''
|
||||
set -eu
|
||||
cd build/libs
|
||||
sha256sum eapim-admin.war > eapim-admin.war.sha256
|
||||
'''
|
||||
archiveArtifacts artifacts: 'build/libs/eapim-admin.war,build/libs/eapim-admin.war.sha256', fingerprint: true
|
||||
stash name: 'war', includes: 'build/libs/eapim-admin.war'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SBOM(CycloneDX) -> xlsx. 산출물 eapim-admin-sbom.xlsx 를 아티팩트로 보관.
|
||||
// 실패해도 배포는 진행하도록 UNSTABLE 로만 표시한다.
|
||||
stage('SBOM') {
|
||||
steps {
|
||||
catchError(buildResult: 'UNSTABLE', stageResult: 'FAILURE') {
|
||||
sh 'gradle sbomXlsx --no-daemon -Pprofile=weblogic'
|
||||
}
|
||||
}
|
||||
post {
|
||||
always {
|
||||
archiveArtifacts allowEmptyArchive: true, artifacts: 'build/reports/sbom/*.xlsx', fingerprint: true
|
||||
}
|
||||
}
|
||||
}
|
||||
stage('Checkout') {
|
||||
steps {
|
||||
checkout scm
|
||||
}
|
||||
}
|
||||
|
||||
stage('Deploy (weblogic)') {
|
||||
agent { label 'weblogic' }
|
||||
environment {
|
||||
JENKINS_NODE_COOKIE = 'dontKillMe' // background weblogic를 빌드 종료 시 죽이지 않게
|
||||
WL_HOME = '/app/eapim/adminportal'
|
||||
WL_DEPLOY_DIR = '/app/eapim/adminportal'
|
||||
WL_WAR_NAME = 'eapim-admin.war'
|
||||
WL_HTTP_PORT = '39120'
|
||||
WL_NOHUP = '/logs/weblogic/domains/eapimDomain/nohup.emsSvr11.out'
|
||||
}
|
||||
stages {
|
||||
stage('Stop WebLogic') {
|
||||
steps {
|
||||
sh '"$WL_HOME/stopEms11.sh"' // 동기: 완전 종료까지 블록, 미기동 시에도 안전
|
||||
}
|
||||
}
|
||||
stage('Deploy WAR') {
|
||||
steps {
|
||||
unstash 'war'
|
||||
sh '''
|
||||
set -eu
|
||||
cp build/libs/eapim-admin.war "$WL_DEPLOY_DIR/$WL_WAR_NAME"
|
||||
ls -la "$WL_DEPLOY_DIR"
|
||||
'''
|
||||
}
|
||||
}
|
||||
stage('Start WebLogic and readiness') {
|
||||
steps {
|
||||
sh '''
|
||||
set -eu
|
||||
"$WL_HOME/startEms11.sh" # nohup & 로 백그라운드 기동, 즉시 리턴
|
||||
|
||||
DEADLINE=$(($(date +%s) + 300))
|
||||
STATUS=000
|
||||
while [ "$(date +%s)" -lt "$DEADLINE" ]; do
|
||||
STATUS=$(curl -sS -o /dev/null -w '%{http_code}' --max-time 3 \
|
||||
"http://localhost:$WL_HTTP_PORT/monitoring/loginForm.do" 2>/dev/null || echo "000")
|
||||
case "$STATUS" in
|
||||
200|302|401) echo "Readiness OK (/monitoring/loginForm.do HTTP $STATUS)"; break ;;
|
||||
esac
|
||||
sleep 3
|
||||
done
|
||||
|
||||
case "$STATUS" in
|
||||
200|302|401) ;;
|
||||
*)
|
||||
echo "Readiness failed within 300s, last HTTP=$STATUS"
|
||||
[ -f "$WL_NOHUP" ] && tail -120 "$WL_NOHUP"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
'''
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
post {
|
||||
success {
|
||||
node('weblogic') {
|
||||
stage('Checkout dependencies') {
|
||||
steps {
|
||||
sh '''
|
||||
set +e
|
||||
payload=$(printf '{"text":"[%s #%s](%s) SUCCESS"}' "$JOB_NAME" "$BUILD_NUMBER" "$BUILD_URL")
|
||||
curl -sS --fail --max-time 5 -H 'Content-Type: application/json' -X POST --data "$payload" "$WEBHOOK_URL" >/dev/null || echo "Webhook failed"
|
||||
set -eu
|
||||
cd "$WORKSPACE/.."
|
||||
|
||||
mkdir -p eapim-online
|
||||
|
||||
for REPO in elink-online-core elink-online-core-jpa elink-online-transformer elink-online-common elink-online-emsclient; do
|
||||
TARGET="eapim-online/$REPO"
|
||||
if [ ! -d "$TARGET/.git" ]; then
|
||||
rm -rf "$TARGET"
|
||||
git clone --depth=1 --branch master \
|
||||
"ssh://git@172.30.1.50:2222/djb-eapim/$REPO.git" \
|
||||
"$TARGET"
|
||||
else
|
||||
git -C "$TARGET" fetch --depth=1 origin master
|
||||
git -C "$TARGET" reset --hard origin/master
|
||||
git -C "$TARGET" clean -fdx
|
||||
fi
|
||||
done
|
||||
|
||||
for REPO in elink-portal-common eapim-admin-djb; do
|
||||
if [ ! -d "$REPO/.git" ]; then
|
||||
rm -rf "$REPO"
|
||||
git clone --depth=1 --branch master \
|
||||
"ssh://git@172.30.1.50:2222/djb-eapim/$REPO.git" \
|
||||
"$REPO"
|
||||
else
|
||||
git -C "$REPO" fetch --depth=1 origin master
|
||||
git -C "$REPO" reset --hard origin/master
|
||||
git -C "$REPO" clean -fdx
|
||||
fi
|
||||
done
|
||||
'''
|
||||
}
|
||||
}
|
||||
failure {
|
||||
node('weblogic') {
|
||||
|
||||
stage('Verify toolchain') {
|
||||
steps {
|
||||
sh '''
|
||||
set -eu
|
||||
java -version
|
||||
gradle --version
|
||||
'''
|
||||
}
|
||||
}
|
||||
|
||||
stage('Build WAR') {
|
||||
steps {
|
||||
sh 'gradle clean build -x test --no-daemon'
|
||||
}
|
||||
post {
|
||||
success {
|
||||
sh '''
|
||||
set -eu
|
||||
cd build/libs
|
||||
sha1sum eapim-admin.war > eapim-admin.war.sha1
|
||||
sha256sum eapim-admin.war > eapim-admin.war.sha256
|
||||
md5sum eapim-admin.war > eapim-admin.war.md5
|
||||
'''
|
||||
archiveArtifacts artifacts: 'build/libs/eapim-admin.war,build/libs/eapim-admin.war.sha1,build/libs/eapim-admin.war.sha256,build/libs/eapim-admin.war.md5', fingerprint: true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stage('Stop Tomcat') {
|
||||
steps {
|
||||
sh '''
|
||||
set +e
|
||||
payload=$(printf '{"text":"[%s #%s](%s) FAILURE"}' "$JOB_NAME" "$BUILD_NUMBER" "$BUILD_URL")
|
||||
curl -sS --fail --max-time 5 -H 'Content-Type: application/json' -X POST --data "$payload" "$WEBHOOK_URL" >/dev/null || echo "Webhook failed"
|
||||
systemctl --user stop eapim-admin 2>/dev/null
|
||||
|
||||
if [ -f "$CATALINA_PID" ] && kill -0 "$(cat "$CATALINA_PID")" 2>/dev/null; then
|
||||
JAVA_HOME="$JAVA_HOME_TOMCAT" "$CATALINA_HOME/bin/shutdown.sh" 30 -force || true
|
||||
for i in $(seq 1 30); do
|
||||
[ -f "$CATALINA_PID" ] && kill -0 "$(cat "$CATALINA_PID")" 2>/dev/null || break
|
||||
sleep 1
|
||||
done
|
||||
fi
|
||||
rm -f "$CATALINA_PID"
|
||||
'''
|
||||
}
|
||||
}
|
||||
|
||||
stage('Deploy monitoring.war') {
|
||||
steps {
|
||||
sh '''
|
||||
set -eu
|
||||
DEPLOY_DIR="$CATALINA_BASE/webapps"
|
||||
rm -rf "$DEPLOY_DIR/monitoring" "$DEPLOY_DIR/monitoring.war"
|
||||
cp build/libs/eapim-admin.war "$DEPLOY_DIR/monitoring.war"
|
||||
ls -la "$DEPLOY_DIR"
|
||||
'''
|
||||
}
|
||||
}
|
||||
|
||||
stage('Start Tomcat and readiness') {
|
||||
steps {
|
||||
sh '''
|
||||
set -eu
|
||||
LOG="$CATALINA_BASE/logs/catalina.$(date +%Y-%m-%d).log"
|
||||
BEFORE=0
|
||||
[ -f "$LOG" ] && BEFORE=$(stat -c %s "$LOG")
|
||||
|
||||
systemctl --user start eapim-admin
|
||||
|
||||
PROBE_URL="http://localhost:$DEPLOY_HTTP_PORT/monitoring/loginForm.do"
|
||||
DEADLINE=$(($(date +%s) + 300))
|
||||
STATUS=000
|
||||
|
||||
while [ "$(date +%s)" -lt "$DEADLINE" ]; do
|
||||
STATUS=$(curl -sS -o /dev/null -w '%{http_code}' --max-time 3 \
|
||||
"$PROBE_URL" 2>/dev/null || echo "000")
|
||||
case "$STATUS" in
|
||||
200|302|401)
|
||||
echo "Readiness OK ($PROBE_URL HTTP $STATUS)"
|
||||
break
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ -f "$LOG" ] && tail -c +$((BEFORE+1)) "$LOG" | grep -qE "Application listener .* Exception|SEVERE.*startup.Catalina"; then
|
||||
echo "Startup error detected in log"
|
||||
tail -c +$((BEFORE+1)) "$LOG" | tail -80
|
||||
exit 1
|
||||
fi
|
||||
sleep 2
|
||||
done
|
||||
|
||||
case "$STATUS" in
|
||||
200|302|401) ;;
|
||||
*)
|
||||
echo "Readiness probe failed within 300s, last HTTP status: $STATUS"
|
||||
echo "--- last 300 lines of catalina log ---"
|
||||
[ -f "$LOG" ] && tail -c +$((BEFORE+1)) "$LOG" | tail -300
|
||||
echo "--- systemd unit status ---"
|
||||
systemctl --user status eapim-admin --no-pager || true
|
||||
echo "--- listening ports ---"
|
||||
ss -tlnp 2>/dev/null | grep -E ":$DEPLOY_HTTP_PORT\\b" || echo "(port $DEPLOY_HTTP_PORT not listening)"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
echo "--- key boot log lines ---"
|
||||
tail -c +$((BEFORE+1)) "$LOG" | grep -E "Server startup|Deployment of web" | head -10 || true
|
||||
'''
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
UserManController_사용자관리_APIGW_INSERT,UPDATE,DELETE
|
||||
AdapterController_어댑터관리_APIGW_INSERT,UPDATE,DELETE
|
||||
ApiInterfaceController_API관리_APIGW_INSERT,UPDATE,DELETE,CLONE
|
||||
ClientController_Client (키정보)관리_APIGW_INSERT,UPDATE,DELETE,SYNC_PORTAL
|
||||
ClientController_Client (키정보)관리_APIGW_INSERT,UPDATE,DELETE
|
||||
RoleController_Role (역할)관리_APIGW_INSERT,UPDATE,DELETE
|
||||
MenuController_Menu관리_APIGW_INSERT,UPDATE,DELETE
|
||||
PortalApprovalLineManController_승인라인관리_APIGW_INSERT,UPDATE,DELETE
|
||||
@@ -16,5 +16,3 @@ PortalPartnershipManController_피드백/개선요청관리_APIGW_UNMASK,DELETE
|
||||
PortalPropertyController_포탈 프로퍼티 관리_APIGW_UPDATE
|
||||
PortalUserTermsManController_약관동의 이력_APIGW_UNMASK
|
||||
MessageRequestManController_메세지발송내역_APIGW_UPDATE_STATUS
|
||||
PortalInquiryManController_Q&A문의관리_APIGW_VISIBILITY
|
||||
PortalMenuManController_포탈메뉴관리_APIGW_INSERT,UPDATE,DELETE,TRANSACTION_PLACEMENT,INITIALIZE,TRANSACTION_RELOAD
|
||||
|
||||
@@ -15,4 +15,4 @@ eai.tableowner=EMSAPP
|
||||
eai.systemmode=L
|
||||
eai.drmode=N
|
||||
# black / blue / green / orange / yellow
|
||||
theme.color=blue
|
||||
theme.color=yellow
|
||||
|
||||
@@ -110,7 +110,7 @@
|
||||
</mvc:interceptor>
|
||||
<mvc:interceptor>
|
||||
<mvc:mapping path="/_onl/**"/>
|
||||
<bean class="com.eactive.eai.rms.common.interceptor.InternalApiTokenInterceptor" />
|
||||
<bean class="com.eactive.eai.rms.common.interceptor.ApiKeyInterceptor" />
|
||||
</mvc:interceptor>
|
||||
<!-- BaseRestController 엔드포인트 IP 화이트리스트 인증 -->
|
||||
<mvc:interceptor>
|
||||
|
||||
@@ -175,6 +175,6 @@
|
||||
</jsp-config>
|
||||
|
||||
<session-config>
|
||||
<session-timeout>60</session-timeout>
|
||||
<session-timeout>60</session-timeout>
|
||||
</session-config>
|
||||
</web-app>
|
||||
@@ -0,0 +1,43 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
||||
targetNamespace="http://xmlns.oracle.com/weblogic/weblogic-web-app"
|
||||
xmlns="http://xmlns.oracle.com/weblogic/weblogic-web-app"
|
||||
elementFormDefault="qualified">
|
||||
|
||||
<xs:element name="weblogic-web-app">
|
||||
<xs:complexType>
|
||||
<xs:all>
|
||||
<xs:element name="context-root" type="xs:string" minOccurs="0"/>
|
||||
<xs:element name="session-descriptor" minOccurs="0">
|
||||
<xs:complexType>
|
||||
<xs:all>
|
||||
<xs:element name="timeout-secs" type="xs:integer" minOccurs="0"/>
|
||||
<xs:element name="persistent-store-type" type="xs:string" minOccurs="0"/>
|
||||
</xs:all>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="container-descriptor" minOccurs="0">
|
||||
<xs:complexType>
|
||||
<xs:all>
|
||||
<xs:element name="prefer-application-packages" minOccurs="0">
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element name="package-name" type="xs:string" minOccurs="0" maxOccurs="unbounded"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="prefer-application-resources" minOccurs="0">
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element name="resource-name" type="xs:string" minOccurs="0" maxOccurs="unbounded"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
</xs:all>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
</xs:all>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
|
||||
</xs:schema>
|
||||
@@ -1,10 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<weblogic-web-app xmlns="http://xmlns.oracle.com/weblogic/weblogic-web-app">
|
||||
<weblogic-web-app
|
||||
xmlns="http://xmlns.oracle.com/weblogic/weblogic-web-app"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://xmlns.oracle.com/weblogic/weblogic-web-app weblogic-web-app.xsd">
|
||||
|
||||
<context-root>monitoring</context-root>
|
||||
<session-descriptor>
|
||||
<timeout-secs>3600</timeout-secs>
|
||||
<cookie-name>JSESSIONID_EMS</cookie-name>
|
||||
<timeout-secs>1800</timeout-secs>
|
||||
<persistent-store-type>replicated_if_clustered</persistent-store-type>
|
||||
</session-descriptor>
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
@charset "utf-8";
|
||||
|
||||
.gnb .depth1 > li:hover > a{color:#008cc5;} /* theme */
|
||||
.gnb .sitemap-link:hover{color:#008cc5;} /* theme */
|
||||
.gnb .depth1 > li > .red_box{background:#008cc5;}/* theme */
|
||||
.gnb .depth2 > li > a:hover{color:#008cc5;}/* theme */
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
@charset "utf-8";
|
||||
|
||||
.gnb .depth1 > li:hover > a{color:#16a085;} /* theme */
|
||||
.gnb .sitemap-link:hover{color:#16a085;} /* theme */
|
||||
.gnb .depth1 > li > .red_box{background:#16a085;}/* theme */
|
||||
.gnb .depth2 > li > a:hover{color:#16a085;}/* theme */
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
@charset "utf-8";
|
||||
|
||||
.gnb .depth1 > li:hover > a{color:#e74c3c;} /* theme */
|
||||
.gnb .sitemap-link:hover{color:#e74c3c;} /* theme */
|
||||
.gnb .depth1 > li > .red_box{background:#e74c3c;}/* theme */
|
||||
.gnb .depth2 > li > a:hover{color:#e74c3c;}/* theme */
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
@charset "utf-8";
|
||||
|
||||
.gnb .depth1 > li:hover > a{color:#ff80c0;} /* theme */
|
||||
.gnb .sitemap-link:hover{color:#ff80c0;} /* theme */
|
||||
.gnb .depth1 > li > .red_box{background:#ff80c0;}/* theme */
|
||||
.gnb .depth2 > li > a:hover{color:#ff80c0;}/* theme */
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
@charset "utf-8";
|
||||
|
||||
.gnb .depth1 > li:hover > a{color:#be8200;} /* theme */
|
||||
.gnb .sitemap-link:hover{color:#be8200;} /* theme */
|
||||
.gnb .depth1 > li > .red_box{background:#be8200;}/* theme */
|
||||
.gnb .depth2 > li > a:hover{color:#be8200;}/* theme */
|
||||
|
||||
|
||||
@@ -62,8 +62,8 @@ header.sub{position:relative; padding-top:80px;}
|
||||
100% { color:rgba(255,96,78,0); }
|
||||
} */
|
||||
|
||||
.gnb_bg{position:absolute; top:80px; left:0; display:none; width:100%; height:282px; box-sizing:border-box; background:white; box-shadow: 0px 5px 5px 0px rgba(0,0,0,0.2);}
|
||||
.gnb{position:absolute; top:0px; left:180px; display:inline-block; height:80px; overflow-y:hidden;}
|
||||
.gnb_bg{position:absolute; top:80px; left:0; display:none; width:100%; height:382px; box-sizing:border-box; background:white; box-shadow: 0px 5px 5px 0px rgba(0,0,0,0.2);}
|
||||
.gnb{position:absolute; top:0px; left:230px; display:inline-block; height:80px; overflow-y:hidden;}
|
||||
.gnb:hover{height:auto; overflow-y:visible;}
|
||||
.gnb:hover + .gnb_bg{display:block;}
|
||||
.gnb h1{float:left; display:block; width:auto; height:100%; line-height:80px;}
|
||||
@@ -71,15 +71,15 @@ header.sub{position:relative; padding-top:80px;}
|
||||
|
||||
.gnb .depth1 > li{position:relative;}
|
||||
.gnb .depth1 > li > a{position:relative; display:block; box-sizing:border-box; color:#000; font-size:14px; width:160px; height:80px; text-align:center; line-height:80px; letter-spacing:-0.5px; opacity:0.7; transition:all 0.3s;}
|
||||
.gnb .depth1 > li > a:after{content:''; position:absolute; top:34px; right:0; display:inline-block; width:1px; height:13px; }
|
||||
.gnb .depth1 > li:first-child > a:before{content:''; position:absolute; top:34px; left:0; display:inline-block; width:1px; height:13px; }
|
||||
.gnb .depth1 > li > a:after{content:''; position:absolute; top:34px; right:0; display:inline-block; width:1px; height:13px; background:#ddd;}
|
||||
.gnb .depth1 > li:first-child > a:before{content:''; position:absolute; top:34px; left:0; display:inline-block; width:1px; height:13px; background:#ddd;}
|
||||
/*
|
||||
.gnb .depth1 > li:hover .red_box{transform:scaleX(1);}
|
||||
.gnb .depth1 > li:hover > a{opacity:1;}
|
||||
.gnb .depth1 > li > .red_box{display:block; position:absolute; width:100%; height:2px; top:78px; box-sizing:border-box; background:#ed1c24; transform-origin:center center; transform:scaleX(0); transition:all 0.3s;}
|
||||
*/
|
||||
.gnb .depth2{position:absolute; left:0; top:80px; width:160px; height:280px; box-sizing:border-box; padding:20px 0; border-right:1px solid #eee; text-align:center; z-index:9;}
|
||||
.gnb .depth1 > li:first-child .depth2{border-left:1px solid #eee;}
|
||||
.gnb .depth2{position:absolute; left:0; top:80px; width:160px; height:410px; box-sizing:border-box; padding:20px 0; border-right:1px solid #eee; text-align:center; z-index:9;}
|
||||
.gnb .depth2.first{border-left:1px solid #eee;}
|
||||
.gnb .depth2 > li > a{display:block; color:#000; font-size:13px; line-height:30px;}
|
||||
.gnb .depth2 > li > a:hover{color:#ff0000;}
|
||||
|
||||
@@ -94,9 +94,9 @@ header.sub{position:relative; padding-top:80px;}
|
||||
.left_box .depth3 > li:hover > a, .left_box .depth3 > li.on > a{color:#ed1c24;}
|
||||
|
||||
.content_top{position:fixed; top:0; left:0; padding:0 30px; width:100%; height:50px; line-height:50px; background:#eee; border-top:1px solid #ddd; border-left:1px solid #ddd; z-index:100;}
|
||||
.content_top .path{margin:0; padding:0;}
|
||||
.content_top .path > li{display:inline-block; margin:0; padding:0;}
|
||||
.content_top .path > li a{display:block; height:50px; line-height:50px; padding-left:10px; font-size:12px; color:#999; text-decoration:none;}
|
||||
.content_top .path{}
|
||||
.content_top .path > li{display:inline-block;}
|
||||
.content_top .path > li a{display:block; height:50px; line-height:50px; padding-left:10px; font-size:12px; color:#999;}
|
||||
.content_top .path > li a:hover{color:#666;}
|
||||
.content_top .path > li a:before{content:'>'; margin-right:10px;}
|
||||
.content_top .path > li:first-child a:before{content:'';}
|
||||
|
||||
@@ -25,18 +25,6 @@
|
||||
components: [], // 재사용 스키마(POC: 빈 상태)
|
||||
};
|
||||
|
||||
// GW authtype(oauth/api_key 등, 서버 DETAIL_SPEC 응답). 인증 badge 라벨 판별용.
|
||||
// (enricher 는 oauth·api_key 를 모두 apiKey-in-header 로 모델링하므로 scheme.type 만으로 구분 불가)
|
||||
var djbAuthType = '';
|
||||
|
||||
// 마지막 저장(또는 최초 로드) 시점의 state.data 스냅샷 — 닫기 전 미저장 변경 여부 판별용.
|
||||
var savedSnapshot = null;
|
||||
function isDirty() { return savedSnapshot !== null && JSON.stringify(state.data) !== savedSnapshot; }
|
||||
function closeEditor() {
|
||||
if (isDirty() && !window.confirm('저장하지 않은 변경사항이 있습니다. 저장하지 않고 닫으시겠습니까?')) return;
|
||||
window.close();
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 2. 유틸
|
||||
// ============================================================
|
||||
@@ -199,11 +187,16 @@
|
||||
}
|
||||
});
|
||||
|
||||
// spec 의 서버는 응답유형과 무관하게 항상 GW 주소로 고정한다 — spec 은 "이 API 를 실제로 어디로
|
||||
// 호출하는가"의 공개 계약이고, 그 기준은 게이트웨이다. sample/mock 은 테스트베드 동작(포탈
|
||||
// ApiTesterFilter 가 응답유형에 따라 sampleResponse 반환 / mockUrl forward)에서만 갈린다.
|
||||
// 경로(op.path.value)에는 어댑터경로가 이미 baking 됨(백엔드 applyAutoRules).
|
||||
spec.servers = [{ url: gatewayAddress() }];
|
||||
// 경로(op.path.value)에는 어댑터경로가 이미 baking 됨(백엔드 applyAutoRules). 서버에는 응답유형별 호스트만 붙인다.
|
||||
// - gw : djb.gateway.base-url (PortalProperty) → [gw호스트 + 어댑터경로 + 경로]
|
||||
// - mock : Mock Server URL → [mock호스트 + 어댑터경로 + 경로]
|
||||
// - 그 외 : 호스트 없음 → [어댑터경로 + 경로]
|
||||
var _rt = (d.portal && d.portal.responseType) || 'sample';
|
||||
var _host;
|
||||
if (_rt === 'gw') _host = (window.DJB_CTX && window.DJB_CTX.gwAddress) || 'http://127.0.0.1:39310';
|
||||
else if (_rt === 'mock') _host = (d.portal && d.portal.mockUrl) || '';
|
||||
else _host = '';
|
||||
if (_host) spec.servers = [{ url: _host }];
|
||||
spec.paths[op.path.value] = { [op.method.value.toLowerCase()]: operation };
|
||||
|
||||
// 보안 스킴
|
||||
@@ -232,8 +225,6 @@
|
||||
const s = { type: row.type.value };
|
||||
if (row.format && row.format.value) s.format = row.format.value;
|
||||
if (row.maxLength && row.maxLength.value) s.maxLength = Number(row.maxLength.value);
|
||||
// 소수 자릿수(GW 레이아웃 유래) 보존 — 예제값 생성 시 '.' 포함 길이 계산에 사용
|
||||
if (row.decimals && row.decimals.value !== '' && row.decimals.value != null) s['x-djb-decimal'] = Number(row.decimals.value);
|
||||
if (row.pattern && row.pattern.value) s.pattern = row.pattern.value;
|
||||
if (row.description && row.description.value) s.description = row.description.value;
|
||||
if (row.example && row.example.value !== '') s.example = coerceExample(row.type.value, row.example.value);
|
||||
@@ -257,11 +248,9 @@
|
||||
if (row.description && row.description.value) obj.properties[name].description = row.description.value;
|
||||
if (row.name && row.name.locked) obj.properties[name]['x-djb-gw'] = true;
|
||||
} else if (row.type.value === 'array') {
|
||||
const itemType = (row.itemsType && row.itemsType.value) || 'string';
|
||||
obj.properties[name] = {
|
||||
type: 'array',
|
||||
// array<object>(GW GRID): 자식 행을 items.properties 로 직렬화. (누락 시 저장 왕복에서 자식 유실)
|
||||
items: itemType === 'object' ? schemaArrayToObject(row.children || []) : { type: itemType }
|
||||
items: { type: (row.itemsType && row.itemsType.value) || 'string' }
|
||||
};
|
||||
if (row.description && row.description.value) obj.properties[name].description = row.description.value;
|
||||
if (row.name && row.name.locked) obj.properties[name]['x-djb-gw'] = true;
|
||||
@@ -312,7 +301,7 @@
|
||||
function goStep(n) {
|
||||
if (n < 1 || n > STEPS.length) return;
|
||||
state.step = n;
|
||||
if (n === 2) state.reqInfoTab = firstReqTabWithData(); // 요청 정보: 값 있는 탭(Header→Parameter→Body 순) 먼저
|
||||
if (n === 2) state.reqInfoTab = 'body'; // 요청/응답 정보: 진입 시 항상 Body 먼저
|
||||
if (n === 3) state.resInfoTab = 'body';
|
||||
renderStepper();
|
||||
renderForm();
|
||||
@@ -346,9 +335,8 @@
|
||||
}
|
||||
function fieldRow(label, controlHtml, opts) {
|
||||
opts = opts || {};
|
||||
const rowSpacing = opts.bordered ? 'px-3 py-3' : 'mb-3';
|
||||
return `
|
||||
<div class="grid grid-cols-12 items-start gap-3 ${rowSpacing}">
|
||||
<div class="grid grid-cols-12 items-start gap-3 mb-3">
|
||||
<label class="col-span-3 pt-1.5 text-sm text-slate-600 ${opts.required ? 'font-medium' : ''}">
|
||||
${escapeHtml(label)}${opts.required ? ' <span class="text-red-500">*</span>' : ''}
|
||||
${opts.locked ? '<span class="ml-1 text-slate-400 text-xs">🔒</span>' : ''}
|
||||
@@ -378,7 +366,7 @@
|
||||
`<div class="flex items-center gap-3 mb-4 p-3 bg-slate-50 border border-slate-200 rounded">
|
||||
<span class="px-2.5 py-1 text-xs font-bold rounded border ${methodColor}">${escapeHtml(op.method.value)}</span>
|
||||
<code class="text-sm font-mono text-slate-800">${escapeHtml(op.path.value)}</code>
|
||||
<span class="text-xs text-slate-400 ml-auto">🔒 게이트웨이 설정</span>
|
||||
<span class="text-xs text-slate-400 ml-auto">🔒 게이트웨이에서 자동 동기화</span>
|
||||
</div>` +
|
||||
fieldRow('Operation ID', lockedInput((window.DJB_CTX && window.DJB_CTX.eaiSvcName) || op.operationId.value), { locked: true }) +
|
||||
fieldRow('API 타이틀', input(i.title.value, 'data-path="info.title.value"', { required: true }), { required: true }) +
|
||||
@@ -451,19 +439,6 @@
|
||||
+ '</div>';
|
||||
}
|
||||
|
||||
// 요청 정보 진입 시 먼저 보일 서브탭 = 값이 든 첫 탭(Header→Parameter→Body 순).
|
||||
// 보통 GET 은 Parameter(query), POST 는 Body 에만 값이 있음. 전부 비면 Body 로.
|
||||
function firstReqTabWithData() {
|
||||
var params = (state.data && state.data.parameters) || [];
|
||||
var hasHeader = params.some(function (p) { return p.in === 'header'; });
|
||||
var hasParam = params.some(function (p) { return p.in === 'query'; });
|
||||
var hasBody = !!(state.data && state.data.requestBody && state.data.requestBody.schema && state.data.requestBody.schema.length);
|
||||
if (hasHeader) return 'header';
|
||||
if (hasParam) return 'param';
|
||||
if (hasBody) return 'body';
|
||||
return 'body';
|
||||
}
|
||||
|
||||
// Step 2 — 요청 정보 (Header / Parameter / Body / API 설명(HTML))
|
||||
function renderReqInfo(root) {
|
||||
const tab = state.reqInfoTab || 'body';
|
||||
@@ -684,7 +659,6 @@
|
||||
const isLocked = row.name.locked;
|
||||
const isObject = row.type.value === 'object';
|
||||
const isArray = row.type.value === 'array';
|
||||
const isArrayOfObject = isArray && (row.itemsType && row.itemsType.value) === 'object';
|
||||
const expandedKey = `${path}.__expanded`;
|
||||
const isExpanded = state[expandedKey] !== false;
|
||||
|
||||
@@ -694,7 +668,7 @@
|
||||
<td class="py-2 px-2 text-center">${isLocked ? '<span class="text-slate-400 text-xs" title="게이트웨이 동기화">🔒</span>' : ''}</td>
|
||||
<td class="py-2 px-2">
|
||||
<div class="flex items-center gap-1" style="padding-left:${depth * 18}px;">
|
||||
${isObject || isArrayOfObject
|
||||
${isObject || isArray
|
||||
? `<button data-toggle-expand="${path}" class="w-4 h-4 text-slate-400 hover:text-slate-700">${isExpanded ? '▾' : '▸'}</button>`
|
||||
: `<span class="w-4 inline-block"></span>`}
|
||||
<span class="text-xs text-slate-400 mr-1">${isObject ? '{}' : isArray ? '[]' : '·'}</span>
|
||||
@@ -725,7 +699,7 @@
|
||||
isArray
|
||||
? `<div class="flex gap-1 items-center">
|
||||
<span class="text-xs text-slate-400">items:</span>
|
||||
${selectInput((row.itemsType && row.itemsType.value) || 'string', ['string', 'integer', 'number', 'boolean', 'object'], `data-path="${path}.itemsType.value" data-rerender="step3"`, { extra: 'text-xs' })}
|
||||
${selectInput((row.itemsType && row.itemsType.value) || 'string', ['string', 'integer', 'number', 'boolean'], `data-path="${path}.itemsType.value"`, { extra: 'text-xs' })}
|
||||
</div>`
|
||||
: input(row.example.value, `data-path="${path}.example.value"`, { placeholder: '예제값' })}
|
||||
</td>
|
||||
@@ -737,8 +711,8 @@
|
||||
</tr>
|
||||
`;
|
||||
|
||||
// children (object + array<object>(GRID) 공통)
|
||||
if ((isObject || isArrayOfObject) && isExpanded) {
|
||||
// children
|
||||
if (isObject && isExpanded) {
|
||||
if (row.children && row.children.length) {
|
||||
html += renderSchemaRows(row.children, `${path}.children`, depth + 1, seq);
|
||||
}
|
||||
@@ -757,44 +731,22 @@
|
||||
// ============================================================
|
||||
// 9. 인증 / 보안 (기본정보 Step 에 병합된 섹션)
|
||||
// ============================================================
|
||||
// 테스트베드 프록시 사용 여부(포탈 djb.gateway.use-proxy / token-use-proxy). DJB_CTX 미지정 시 기본 true.
|
||||
function useProxyOn() { return !(window.DJB_CTX && window.DJB_CTX.useProxy === false); }
|
||||
function tokenProxyOn() { return !(window.DJB_CTX && window.DJB_CTX.tokenUseProxy === false); }
|
||||
|
||||
// 인증 스킴 → 실제 인증방식 라벨/색상. GW enricher 는 oauth·api_key 를 모두 apiKey-in-header 로 모델링하므로
|
||||
// scheme.type("apiKey")만으로는 구분 불가 → authtype(djbAuthType) 및 scheme key(djbOAuth/djbApiKey)로 판별.
|
||||
function schemeAuthLabel(s) {
|
||||
var at = (djbAuthType || '').trim().toLowerCase();
|
||||
if (at === 'oauth' || at === 'oauth2' || at === 'ca' || s.key === 'djbOAuth') {
|
||||
return { label: 'OAUTH', cls: 'bg-purple-100 text-purple-700' };
|
||||
}
|
||||
if (at === 'api_key' || at === 'apikey' || s.key === 'djbApiKey') {
|
||||
return { label: 'API KEY', cls: 'bg-emerald-100 text-emerald-700' };
|
||||
}
|
||||
return { label: (s.type || '').toUpperCase(), cls: 'bg-slate-100 text-slate-700' };
|
||||
}
|
||||
|
||||
function renderAuthSection() {
|
||||
var schemes = state.data.securitySchemes || [];
|
||||
var proxyOn = useProxyOn();
|
||||
var notice =
|
||||
'<div class="mb-3 text-xs bg-slate-50 border border-slate-200 rounded p-2 leading-relaxed text-slate-600">'
|
||||
+ '<div>Swagger 테스트 호출: <b>' + (proxyOn ? '포탈 프록시 경유' : '직접 호출') + '</b>'
|
||||
+ (proxyOn ? ' — <code class="font-mono">/api/call-api</code> 가 인증 토큰·헤더를 대신 주입합니다.'
|
||||
: ' — 브라우저가 대상 주소로 직접 호출(대상 CORS 허용 필요).') + '</div>'
|
||||
+ '<div>토큰 발급: <b>' + (tokenProxyOn() ? '포탈 프록시 경유' : '직접 호출') + '</b></div>'
|
||||
+ '<div class="text-slate-400 mt-1">프록시 여부는 포탈 설정 <code class="font-mono">djb.gateway.use-proxy</code> / <code class="font-mono">djb.gateway.token-use-proxy</code> 로 제어됩니다.</div>'
|
||||
+ '</div>';
|
||||
return card(
|
||||
sectionTitle('인증 / 보안', '게이트웨이(GW)에 설정된 인증만 표시됩니다. (읽기 전용)') +
|
||||
notice +
|
||||
(schemes.length
|
||||
? schemes.map((s, i) => renderSecuritySchemeCard(s, i)).join('')
|
||||
: '<div class="text-sm text-slate-500 py-2">인증 없음</div>')
|
||||
);
|
||||
}
|
||||
function renderSecuritySchemeCard(s, idx) {
|
||||
const auth = schemeAuthLabel(s);
|
||||
const typeBadge = {
|
||||
apiKey: 'bg-emerald-100 text-emerald-700',
|
||||
http: 'bg-blue-100 text-blue-700',
|
||||
oauth2: 'bg-purple-100 text-purple-700',
|
||||
openIdConnect: 'bg-slate-100 text-slate-700'
|
||||
}[s.type] || 'bg-slate-100 text-slate-700';
|
||||
|
||||
let body = '';
|
||||
if (s.type === 'apiKey') {
|
||||
@@ -821,7 +773,7 @@
|
||||
<div class="border border-slate-200 rounded-lg p-3 mb-3 locked-card">
|
||||
<div class="flex items-center gap-2 mb-3">
|
||||
<code class="text-sm font-semibold text-slate-800">${escapeHtml(s.key)}</code>
|
||||
<span class="px-2 py-0.5 text-[10px] uppercase rounded ${auth.cls}">${escapeHtml(auth.label)}</span>
|
||||
<span class="px-2 py-0.5 text-[10px] uppercase rounded ${typeBadge}">${escapeHtml(s.type)}</span>
|
||||
<span class="text-xs text-slate-400 ml-1">🔒 GW</span>
|
||||
</div>
|
||||
${body}
|
||||
@@ -978,32 +930,14 @@
|
||||
}
|
||||
}
|
||||
|
||||
// spec servers 에 넣을 게이트웨이 주소(단일 기준). 응답유형과 무관하게 항상 이 값을 쓴다.
|
||||
function gatewayAddress() {
|
||||
return (window.DJB_CTX && window.DJB_CTX.gwAddress) || 'http://127.0.0.1:39310';
|
||||
}
|
||||
|
||||
// 전체 URL → { origin, path }. Mock Server URL 을 서버+경로로 분해(경로를 별도로 덧붙이지 않기 위함).
|
||||
function splitFullUrl(u) {
|
||||
u = (u || '').trim();
|
||||
if (!u) return { origin: '', path: '/' };
|
||||
try {
|
||||
var parsed = new URL(u);
|
||||
var path = (parsed.pathname || '/') + (parsed.search || '');
|
||||
if (!path || path.charAt(0) !== '/') path = '/' + path;
|
||||
return { origin: parsed.origin, path: path };
|
||||
} catch (e) {
|
||||
// 스킴 누락/파싱 실패 → 통째 origin 취급, path 는 '/'
|
||||
return { origin: u.replace(/\/+$/, ''), path: '/' };
|
||||
}
|
||||
}
|
||||
|
||||
// 응답유형별 실제 호출 주소. gw 는 [호스트 + 경로], mock 은 입력한 전체 URL 그대로(경로 미부착).
|
||||
// 응답유형별 실제 호출 주소. 경로(op.path.value)에 어댑터경로가 이미 포함, 호스트만 응답유형별로 앞에 붙임.
|
||||
function resolvedCallUrl() {
|
||||
var d = state.data;
|
||||
var rt = (d.portal && d.portal.responseType) || 'sample';
|
||||
if (rt === 'mock') return (((d.portal && d.portal.mockUrl) || '').trim()) || '(주소 없음)';
|
||||
var host = (rt === 'gw') ? ((window.DJB_CTX && window.DJB_CTX.gwAddress) || 'http://127.0.0.1:39310') : '';
|
||||
var host;
|
||||
if (rt === 'gw') host = (window.DJB_CTX && window.DJB_CTX.gwAddress) || 'http://127.0.0.1:39310';
|
||||
else if (rt === 'mock') host = (d.portal && d.portal.mockUrl) || '';
|
||||
else host = '';
|
||||
var path = (d.operation && d.operation.path && d.operation.path.value) || '';
|
||||
if (!host) return path || '(주소 없음)';
|
||||
return host.replace(/\/+$/, '') + (path.charAt(0) === '/' ? path : '/' + path);
|
||||
@@ -1026,26 +960,24 @@
|
||||
root.innerHTML =
|
||||
card(
|
||||
sectionTitle('포탈 게시 정보', '개발자포탈 공개 설정. 기존 API 스펙 화면의 값을 불러와 적용합니다.') +
|
||||
'<div class="border border-slate-200 rounded-lg divide-y divide-slate-200">' +
|
||||
fieldRow('API 그룹',
|
||||
`<div class="flex flex-col gap-1">
|
||||
<div class="flex items-center gap-2 flex-wrap">
|
||||
<span id="group-chips">${groupChips || '<span class="text-xs text-slate-400">선택된 그룹 없음</span>'}</span>
|
||||
<button type="button" id="btn-select-group" class="px-2 py-1 text-sm rounded border border-slate-300 hover:bg-slate-50">+ 선택</button>
|
||||
</div>
|
||||
<div class="text-xs text-slate-400">API 그룹을 설정하지 않으면 포탈에 게시되지 않습니다.</div>
|
||||
<div class="text-xs text-slate-400">API 그룹은 apiGroupMan(API 그룹 관리)에서도 관리됩니다.</div>
|
||||
</div>`, { bordered: true }) +
|
||||
fieldRow('포탈 게시 활성화', `<label class="inline-flex items-center gap-2 text-sm">${checkbox(p.displayYn === 'Y', 'data-portal-yn')} 활성화</label>`, { bordered: true }) +
|
||||
fieldRow('역할별 공개 제한',
|
||||
</div>`) +
|
||||
fieldRow('공개 여부', `<label class="inline-flex items-center gap-2 text-sm">${checkbox(p.displayYn === 'Y', 'data-portal-yn')} 공개</label>`) +
|
||||
fieldRow('공개 권한',
|
||||
`<div class="flex flex-col gap-1">
|
||||
<div class="flex gap-4">
|
||||
<label class="inline-flex items-center gap-1 text-sm">${checkbox(!!roles.ROLE_USER, 'data-role="ROLE_USER"')} 개인사용자</label>
|
||||
<label class="inline-flex items-center gap-1 text-sm">${checkbox(!!roles.ROLE_CORP_USER, 'data-role="ROLE_CORP_USER"')} 법인사용자</label>
|
||||
<label class="inline-flex items-center gap-1 text-sm">${checkbox(!!roles.ROLE_CORP_MANAGER, 'data-role="ROLE_CORP_MANAGER"')} 법인관리자</label>
|
||||
</div>
|
||||
<div class="text-xs text-slate-400">선택시 비 로그인 사용자에게 공개 되지 않습니다.</div>
|
||||
</div>`, { bordered: true }) +
|
||||
<div class="text-xs text-slate-400">전부 선택 시 로그인 사용자 전체에게 공개됩니다.</div>
|
||||
</div>`) +
|
||||
(showOrg ? fieldRow('공개 법인',
|
||||
`<div class="flex flex-col gap-1">
|
||||
<div class="flex items-center gap-2 flex-wrap">
|
||||
@@ -1053,35 +985,28 @@
|
||||
<button type="button" id="btn-select-org" class="px-2 py-1 text-sm rounded border border-slate-300 hover:bg-slate-50">+ 선택</button>
|
||||
</div>
|
||||
<div class="text-xs text-slate-400">특정 법인에게만 공개하고 싶을 때 공개 법인을 선택하시면 됩니다.</div>
|
||||
</div>`, { bordered: true }) : '') +
|
||||
</div>`) : '') +
|
||||
fieldRow('응답 유형',
|
||||
'<div>' +
|
||||
['sample', 'mock', 'gw'].map(v =>
|
||||
`<label class="inline-flex items-center gap-1 text-sm mr-3"><input type="radio" name="portal-rt" data-portal-rt="${v}" ${rt === v ? 'checked' : ''}> ${v === 'gw' ? 'GW' : v}</label>`
|
||||
).join('') +
|
||||
(rt === 'sample'
|
||||
? `<div class="mt-2 text-xs text-slate-500">응답 샘플을 바로 리턴합니다.</div>`
|
||||
: `<div class="mt-2 text-xs text-slate-500">실제 호출 주소: <code id="resolved-call-url" class="font-mono text-slate-700 break-all">${escapeHtml(resolvedCallUrl())}</code></div>`
|
||||
+ `<div class="mt-1 text-xs text-slate-500">호출 방식: <b>${useProxyOn() ? '포탈 프록시 경유' : '직접 호출'}</b>`
|
||||
+ (useProxyOn() ? ' (<code class="font-mono">/api/call-api</code> 경유, 인증 헤더 자동 주입)' : ' (브라우저 → 대상 직접 호출, 대상 CORS 허용 필요)') + `</div>`) +
|
||||
`<div class="mt-2 text-xs text-slate-500">실제 호출 주소: <code id="resolved-call-url" class="font-mono text-slate-700 break-all">${escapeHtml(resolvedCallUrl())}</code></div>` +
|
||||
(rt === 'gw' ? `<div class="mt-1 text-xs text-slate-400">GW 주소는 포탈 설정(PTL_PROPERTY, 그룹 Portal)의 <code class="font-mono">djb.gateway.base-url</code> 값을 사용합니다.</div>` : '') +
|
||||
'</div>', { bordered: true }) +
|
||||
(rt === 'mock' ? fieldRow('Mock Server URL', input(p.mockUrl || '', 'data-portal="mockUrl"', { placeholder: 'https://mock.example.com/api/v1/foo' }), { hint: 'Mock 응답을 제공할 전체 호출 URL(경로 포함). 응답 유형이 Mock 일 때 이 주소로 그대로 호출하며, 어댑터/오퍼레이션 경로를 뒤에 덧붙이지 않습니다.', bordered: true }) : '') +
|
||||
'</div>'
|
||||
'</div>') +
|
||||
(rt === 'mock' ? fieldRow('Mock Server URL', input(p.mockUrl || '', 'data-portal="mockUrl"', { placeholder: 'https://mock.example.com' }), { hint: 'Mock 응답을 제공할 서버의 기본 URL. 응답 유형이 Mock 일 때 개발자포탈이 이 주소로 요청을 전달하고, Swagger 스펙의 서버 주소로도 사용됩니다.' }) : '')
|
||||
) +
|
||||
card(
|
||||
sectionTitle('설명 (API 소개 페이지)', 'API 소개 페이지에만 표시됩니다. OpenAPI 스펙 파일에는 포함되지 않습니다.') +
|
||||
'<div id="info-desc-editor">' + (state.data.info.description.value || '') + '</div>'
|
||||
) +
|
||||
// 내보내기 카드는 PTL_PROPERTY(djb.openapi.export.enabled=true) 일 때만 노출. 기본 미노출.
|
||||
((window.DJB_CTX && window.DJB_CTX.exportEnabled) ?
|
||||
card(
|
||||
sectionTitle('내보내기', 'OpenAPI 스펙을 파일로 저장합니다.') +
|
||||
`<div class="flex gap-2">
|
||||
<button data-export="yaml" class="px-4 py-2 text-sm rounded bg-brand-600 text-white hover:bg-brand-700">YAML 다운로드</button>
|
||||
<button data-export="json" class="px-4 py-2 text-sm rounded border border-brand-300 text-brand-700 hover:bg-brand-50">JSON 다운로드</button>
|
||||
</div>`
|
||||
) : '');
|
||||
);
|
||||
initInfoDescEditor(); // 설명 리치 에디터 (API 소개 페이지 전용)
|
||||
}
|
||||
|
||||
@@ -1476,7 +1401,6 @@
|
||||
required: { value: false, locked: false },
|
||||
format: { value: '', locked: false },
|
||||
maxLength: { value: '', locked: false },
|
||||
decimals: { value: '', locked: false },
|
||||
pattern: { value: '', locked: false },
|
||||
description: { value: '', locked: false },
|
||||
example: { value: '', locked: false },
|
||||
@@ -1491,7 +1415,6 @@
|
||||
required: { value: false, locked: false },
|
||||
format: { value: '', locked: false },
|
||||
maxLength: { value: '', locked: false },
|
||||
decimals: { value: '', locked: false },
|
||||
pattern: { value: '', locked: false },
|
||||
description: { value: '', locked: false },
|
||||
example: { value: '', locked: false }
|
||||
@@ -1663,7 +1586,7 @@
|
||||
$('#btn-prev').addEventListener('click', () => goStep(state.step - 1));
|
||||
$('#btn-next').addEventListener('click', () => {
|
||||
if (state.step < STEPS.length) goStep(state.step + 1);
|
||||
else saveSpec(false); // 마지막 단계의 "완료" → 저장 동작
|
||||
else toast('모든 단계를 완료했습니다. 우측 미리보기 또는 내보내기를 사용하세요.');
|
||||
});
|
||||
|
||||
$('#btn-toggle-preview').addEventListener('click', () => setPreviewVisibility(false));
|
||||
@@ -1696,22 +1619,18 @@
|
||||
toast('변경을 취소했습니다');
|
||||
});
|
||||
|
||||
// 내보내기 버튼은 djb.openapi.export.enabled=true 일 때만 DOM 에 존재 → 없으면 바인딩 생략
|
||||
var bExport = $('#btn-export');
|
||||
if (bExport) {
|
||||
bExport.addEventListener('click', () => $('#export-menu').classList.toggle('hidden'));
|
||||
document.addEventListener('click', e => {
|
||||
if (!e.target.closest('#btn-export') && !e.target.closest('#export-menu')) $('#export-menu').classList.add('hidden');
|
||||
});
|
||||
$('#export-menu').addEventListener('click', e => {
|
||||
const b = e.target.closest('[data-export]');
|
||||
if (b) { downloadSpec(b.dataset.export); $('#export-menu').classList.add('hidden'); }
|
||||
});
|
||||
}
|
||||
$('#btn-export').addEventListener('click', () => $('#export-menu').classList.toggle('hidden'));
|
||||
document.addEventListener('click', e => {
|
||||
if (!e.target.closest('#btn-export') && !e.target.closest('#export-menu')) $('#export-menu').classList.add('hidden');
|
||||
});
|
||||
$('#export-menu').addEventListener('click', e => {
|
||||
const b = e.target.closest('[data-export]');
|
||||
if (b) { downloadSpec(b.dataset.export); $('#export-menu').classList.add('hidden'); }
|
||||
});
|
||||
|
||||
var bSave = $('#btn-save'); if (bSave) bSave.addEventListener('click', () => saveSpec(false));
|
||||
var bRegen = $('#btn-regen'); if (bRegen) bRegen.addEventListener('click', regenerate);
|
||||
var bClose = $('#btn-close'); if (bClose) bClose.addEventListener('click', closeEditor);
|
||||
var bClose = $('#btn-close'); if (bClose) bClose.addEventListener('click', () => window.close());
|
||||
|
||||
// Cmd/Ctrl + S → 저장 (브라우저 기본 저장 대화상자 차단)
|
||||
document.addEventListener('keydown', function (e) {
|
||||
@@ -1745,17 +1664,11 @@
|
||||
var row = {
|
||||
name: wrap(name, gw), type: wrap(t, gw), required: wrap(!!required, false),
|
||||
format: wrap(p.format || ''), maxLength: wrap(p.maxLength == null ? '' : p.maxLength),
|
||||
decimals: wrap(p['x-djb-decimal'] == null ? '' : p['x-djb-decimal']),
|
||||
pattern: wrap(p.pattern || ''), description: wrap(p.description || ''),
|
||||
example: wrap(p.example == null ? '' : p.example), children: null
|
||||
};
|
||||
if (t === 'object') row.children = schemaToRows(p);
|
||||
else if (t === 'array') {
|
||||
var itemType = (p.items && p.items.type) || 'string';
|
||||
row.itemsType = wrap(itemType);
|
||||
// array<object>(GW GRID): items.properties 를 자식 행으로 복원. (누락 시 자식 필드 유실)
|
||||
if (itemType === 'object') row.children = schemaToRows(p.items);
|
||||
}
|
||||
else if (t === 'array') row.itemsType = wrap((p.items && p.items.type) || 'string');
|
||||
return row;
|
||||
}
|
||||
function schemaToRows(schema) {
|
||||
@@ -1795,7 +1708,7 @@
|
||||
};
|
||||
d.parameters = (op.parameters || []).map(function (p) {
|
||||
var s = p.schema || {};
|
||||
return { in: p.in || 'query', name: wrap(p.name), type: wrap(s.type || 'string'), required: wrap(!!p.required), format: wrap(s.format || ''), maxLength: wrap(s.maxLength == null ? '' : s.maxLength), decimals: wrap(s['x-djb-decimal'] == null ? '' : s['x-djb-decimal']), pattern: wrap(s.pattern || ''), description: wrap(p.description || ''), example: wrap(p.example == null ? '' : p.example) };
|
||||
return { in: p.in || 'query', name: wrap(p.name), type: wrap(s.type || 'string'), required: wrap(!!p.required), format: wrap(s.format || ''), maxLength: wrap(s.maxLength == null ? '' : s.maxLength), pattern: wrap(s.pattern || ''), description: wrap(p.description || ''), example: wrap(p.example == null ? '' : p.example) };
|
||||
});
|
||||
var rb = op.requestBody || {}; var rbc = rb.content || {}; var mt = Object.keys(rbc)[0] || 'application/json';
|
||||
d.requestBody = { mediaType: mt, required: wrap(!!rb.required), schema: schemaToRows((rbc[mt] || {}).schema) };
|
||||
@@ -1840,34 +1753,19 @@
|
||||
|
||||
// 스키마 기반 샘플 JSON 생성 (자동생성 예제)
|
||||
function _coerce(t, v) { if (t === 'integer') return parseInt(v, 10) || 0; if (t === 'number') return parseFloat(v) || 0; if (t === 'boolean') return v === true || v === 'true'; return v; }
|
||||
// 자릿수 문자열: start 위치부터 1~9 를 순환('123456789123...')
|
||||
function _digits(start, count) { var s = ''; for (var i = 0; i < count; i++) s += String(((start + i) % 9) + 1); return s; }
|
||||
// 숫자 예제값. 길이는 소수점(.)까지 포함한 문자열 전체 길이로 계산한다.
|
||||
// 예) 길이 9 / 소수 5 → 123.45678 (정수부 3 + '.' 1 + 소수부 5 = 9자)
|
||||
// - 길이가 소수부+2 보다 짧으면 정수부 1자리를 확보하고 소수부를 줄인다.
|
||||
// - 길이 미지정이면 정수부 6자리(기존 기본값)를 사용한다.
|
||||
function _numSample(maxLen, dec) {
|
||||
var ml = parseInt(maxLen, 10) || 0, d = parseInt(dec, 10) || 0;
|
||||
if (d <= 0) return _digits(0, ml > 0 ? Math.min(ml, 6) : 6);
|
||||
var il;
|
||||
if (ml > 0) {
|
||||
il = ml - d - 1;
|
||||
if (il < 1) { il = 1; d = Math.max(1, ml - 2); }
|
||||
} else {
|
||||
il = 6;
|
||||
}
|
||||
return _digits(0, il) + '.' + _digits(il, d);
|
||||
}
|
||||
// 기본 예제값: string→sample_명(길이 지정 시 맞춰 자름), integer→123456, number→123.45678, boolean→false
|
||||
// - 숫자는 지정 길이를 넘지 않도록 자릿수를 맞춘다(소수는 '.' 포함 길이 기준).
|
||||
// - number 인데 소수 자릿수 정보가 없으면 3자리로 가정(소수 0자리는 백엔드가 integer 로 매핑).
|
||||
function _defScalar(t, name, maxLen, decimals) {
|
||||
// 기본 예제값: string→sample_명(길이 지정 시 맞춰 자름), integer→123456, number→123456.123, boolean→false
|
||||
// - 숫자는 길이 지정 시 정수부를 그 길이에 맞춰 자름(실수는 소수 .123 유지)
|
||||
function _defScalar(t, name, maxLen) {
|
||||
var ml = parseInt(maxLen, 10);
|
||||
if (t === 'integer') return parseInt(_numSample(ml, 0), 10);
|
||||
if (t === 'integer') {
|
||||
var iv = '123456';
|
||||
if (ml && ml > 0 && iv.length > ml) iv = iv.substring(0, ml);
|
||||
return parseInt(iv, 10);
|
||||
}
|
||||
if (t === 'number') {
|
||||
var d = parseInt(decimals, 10);
|
||||
if (!(d > 0)) d = 3;
|
||||
return parseFloat(_numSample(ml, d));
|
||||
var ip = '123456';
|
||||
if (ml && ml > 0 && ip.length > ml) ip = ip.substring(0, ml);
|
||||
return parseFloat(ip + '.123');
|
||||
}
|
||||
if (t === 'boolean') return false;
|
||||
var v = 'sample_' + (name || '');
|
||||
@@ -1879,7 +1777,7 @@
|
||||
if (t === 'object') return _sampleObj(row.children || []);
|
||||
if (t === 'array') { var it = (row.itemsType && row.itemsType.value) || 'string'; return [it === 'object' ? _sampleObj(row.children || []) : _defScalar(it, (row.name && row.name.value) || 'item', '')]; }
|
||||
if (ex !== undefined && ex !== '') return _coerce(t, ex);
|
||||
return _defScalar(t, (row.name && row.name.value) || '', row.maxLength && row.maxLength.value, row.decimals && row.decimals.value);
|
||||
return _defScalar(t, (row.name && row.name.value) || '', row.maxLength && row.maxLength.value);
|
||||
}
|
||||
function _sampleObj(rows) { var o = {}; (rows || []).forEach(function (r) { if (r && r.name && r.name.value) o[r.name.value] = _sampleVal(r); }); return o; }
|
||||
function ensureExamplesFromSchema(force) {
|
||||
@@ -1925,7 +1823,7 @@
|
||||
if (r.children && r.children.length) fillSchemaExamples(r.children);
|
||||
} else {
|
||||
r.example = r.example || { value: '', locked: false };
|
||||
r.example.value = String(_defScalar(t, r.name.value || '', r.maxLength && r.maxLength.value, r.decimals && r.decimals.value));
|
||||
r.example.value = String(_defScalar(t, r.name.value || '', r.maxLength && r.maxLength.value));
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -2008,7 +1906,6 @@
|
||||
if (res && res.testbedSpec) {
|
||||
try { state.data = specToData(JSON.parse(res.testbedSpec)); } catch (e) { console.error('specToData', e); }
|
||||
}
|
||||
djbAuthType = (res && res.authType) || ''; // 인증 badge 라벨 판별용(oauth/api_key)
|
||||
// 자동생성 규칙(title/tag/summary=API명, 예제, 서버prepend, opId)은 백엔드에서 적용됨 → 그대로 로드
|
||||
state.step = 1; state.responseStatusTab = state.data.responses && state.data.responses['200'] ? '200' : (state.data.responses ? Object.keys(state.data.responses)[0] : '200');
|
||||
var _nm = decodeEntities(ctx.apiName || '') || state.data.info.title.value || '';
|
||||
@@ -2026,16 +1923,12 @@
|
||||
if (res.responseType) pp.responseType = res.responseType;
|
||||
if (res.mockUrl) pp.mockUrl = decodeEntities(res.mockUrl);
|
||||
if (res.apiGroups) { try { pp.apiGroups = JSON.parse(res.apiGroups) || []; } catch (e) { pp.apiGroups = []; } }
|
||||
// 설명(API 소개 페이지) — spec 에 미포함, description 컬럼에 별도 저장 → 별도 필드로 복원
|
||||
if (res.description) state.data.info.description = wrap(decodeEntities(res.description));
|
||||
// 요청/응답 메시지 스펙(HTML) — 저장본은 저장값, 자동생성분은 프론트에서 생성
|
||||
state.data.msgSpec = state.data.msgSpec || { request: '', response: '' };
|
||||
if (res.apiRequestSpec) state.data.msgSpec.request = decodeEntities(res.apiRequestSpec);
|
||||
if (res.apiResponseSpec) state.data.msgSpec.response = decodeEntities(res.apiResponseSpec);
|
||||
// 저장본이 아니면(최초 작성) 자동생성 여부를 confirm 으로 확인 후 진행
|
||||
if ((res.source || '') !== 'saved') {
|
||||
if (window.confirm('저장된 스펙이 없습니다. 게이트웨이 레이아웃 기반으로 예제·설명자료를 자동 생성할까요?')) frontAutoGenerate();
|
||||
}
|
||||
// 저장본이 아니면(자동생성/최초 생성) 추가자료(예제·설명자료)를 프론트에서 스키마 기준 생성
|
||||
if ((res.source || '') !== 'saved') frontAutoGenerate();
|
||||
})
|
||||
.catch(e => console.error('loadInitialData', e));
|
||||
}
|
||||
@@ -2074,7 +1967,6 @@
|
||||
.then(function (res) {
|
||||
if (res && res.status === 'success') {
|
||||
toast(temp ? '임시저장되었습니다' : '저장되었습니다');
|
||||
savedSnapshot = JSON.stringify(state.data); // 저장 성공 시점을 새 저장 기준선으로 갱신
|
||||
var badge = $('#hdr-source'); if (badge) { badge.textContent = '저장본'; badge.className = 'ml-1 px-2 py-0.5 text-[11px] rounded-full bg-emerald-50 text-emerald-700 border border-emerald-200'; badge.classList.remove('hidden'); }
|
||||
} else toast('저장 실패: ' + (res && res.errorMsg || ''), 'error');
|
||||
})
|
||||
@@ -2093,7 +1985,6 @@
|
||||
try { state.data = specToData(JSON.parse(res.testbedSpec)); }
|
||||
catch (e) { toast('자동 생성 실패: 스펙 파싱 오류', 'error'); return; }
|
||||
}
|
||||
djbAuthType = (res && res.authType) || ''; // 인증 badge 라벨 판별용(oauth/api_key)
|
||||
// 구조(title/tag/summary/opId/스키마/보안/헤더)는 백엔드(generateSpec)가 적용한 기초 스펙 그대로 로드.
|
||||
// 추가자료(예제·설명자료)는 프론트에서 스키마 기준 생성 → 재생성 버튼과 동일 결과.
|
||||
frontAutoGenerate();
|
||||
@@ -2111,7 +2002,6 @@
|
||||
// ============================================================
|
||||
function init() {
|
||||
loadInitialData().then(function () {
|
||||
savedSnapshot = JSON.stringify(state.data); // 초기 로드(자동생성 포함) 완료 시점을 저장 기준선으로 설정
|
||||
renderStepper();
|
||||
renderForm();
|
||||
bindGlobalEvents();
|
||||
|
||||
@@ -1,63 +1,298 @@
|
||||
// OpenAPI 에디터 초기 상태 골격(빈 껍데기).
|
||||
// - specToData() 가 deepClone 하여 서버 spec 값으로 덮어쓰는 구조 기준값.
|
||||
// - 실제 데모/샘플 값은 두지 않는다(팝업은 항상 서버 spec 로드 후 렌더).
|
||||
// - docOptions 만 기능 기본값으로 유지(문서 미리보기 옵션).
|
||||
// locked:true = 게이트웨이 자동 주입(회색+자물쇠) / locked:false = 사용자 편집.
|
||||
// 계좌이체 API 모의 데이터
|
||||
// locked: true 인 필드는 게이트웨이가 자동 주입한 값(회색 + 자물쇠)
|
||||
// locked: false / 미지정 인 필드는 사용자가 자유롭게 편집
|
||||
|
||||
window.SAMPLE_DATA = (function () {
|
||||
function f() { return { value: '', locked: false }; }
|
||||
return {
|
||||
info: {
|
||||
title: f(),
|
||||
version: f(),
|
||||
summary: f(),
|
||||
description: f(),
|
||||
termsOfService: f(),
|
||||
contact: { name: f(), email: f(), url: f() },
|
||||
license: { name: f(), url: f() }
|
||||
title: { value: '계좌이체 API', locked: false },
|
||||
version: { value: '1.0.0', locked: false },
|
||||
summary: { value: '', locked: false },
|
||||
description: { value: '', locked: false },
|
||||
termsOfService: { value: '', locked: false },
|
||||
contact: {
|
||||
name: { value: 'DJB API Team', locked: false },
|
||||
email: { value: 'api@djb.co.kr', locked: false },
|
||||
url: { value: '', locked: false }
|
||||
},
|
||||
license: {
|
||||
name: { value: '', locked: false },
|
||||
url: { value: '', locked: false }
|
||||
}
|
||||
},
|
||||
|
||||
tags: [],
|
||||
tags: [
|
||||
{ name: 'transfer', description: '계좌이체 관련 API' }
|
||||
],
|
||||
|
||||
externalDocs: { description: f(), url: f() },
|
||||
externalDocs: {
|
||||
description: { value: '', locked: false },
|
||||
url: { value: '', locked: false }
|
||||
},
|
||||
|
||||
servers: [],
|
||||
servers: [
|
||||
{
|
||||
url: { value: 'https://api-dev.djb.co.kr', locked: true },
|
||||
env: 'development',
|
||||
description: { value: '개발 환경', locked: false }
|
||||
},
|
||||
{
|
||||
url: { value: 'https://api-stg.djb.co.kr', locked: true },
|
||||
env: 'staging',
|
||||
description: { value: '스테이징 환경', locked: false }
|
||||
},
|
||||
{
|
||||
url: { value: 'https://api.djb.co.kr', locked: true },
|
||||
env: 'production',
|
||||
description: { value: '운영 환경', locked: false }
|
||||
}
|
||||
],
|
||||
|
||||
serverVariables: [],
|
||||
serverVariables: [
|
||||
{
|
||||
name: { value: 'basePath', locked: true },
|
||||
default: { value: '/v1', locked: false },
|
||||
enumStr: { value: '/v1,/v2', locked: false },
|
||||
description: { value: 'API 버전 경로', locked: false }
|
||||
}
|
||||
],
|
||||
|
||||
operation: {
|
||||
method: f(),
|
||||
path: f(),
|
||||
operationId: f(),
|
||||
summary: f(),
|
||||
description: f(),
|
||||
tags: { value: [], locked: false },
|
||||
deprecated: { value: false, locked: false }
|
||||
method: { value: 'POST', locked: true },
|
||||
path: { value: '/v1/accounts/transfer', locked: true },
|
||||
operationId: { value: 'transferAccount', locked: false },
|
||||
summary: { value: '', locked: false },
|
||||
description: { value: '', locked: false },
|
||||
tags: { value: ['transfer'], locked: false },
|
||||
deprecated: { value: false, locked: false }
|
||||
},
|
||||
|
||||
// 파라미터 (Path/Query/Header/Cookie)
|
||||
parameters: [],
|
||||
parameters: [
|
||||
{
|
||||
in: 'header',
|
||||
name: { value: 'X-Request-Id', locked: true },
|
||||
type: { value: 'string', locked: true },
|
||||
required: { value: true, locked: true },
|
||||
format: { value: 'uuid', locked: false },
|
||||
maxLength: { value: '', locked: false },
|
||||
pattern: { value: '', locked: false },
|
||||
description: { value: '', locked: false },
|
||||
example: { value: '', locked: false }
|
||||
}
|
||||
],
|
||||
|
||||
// Request Body 스키마
|
||||
// Request Body 스키마 (중첩 3단)
|
||||
requestBody: {
|
||||
mediaType: 'application/json',
|
||||
required: { value: false, locked: false },
|
||||
schema: []
|
||||
required: { value: true, locked: true },
|
||||
schema: [
|
||||
{
|
||||
name: { value: 'transactionId', locked: true },
|
||||
type: { value: 'string', locked: true },
|
||||
required: { value: true, locked: true },
|
||||
format: { value: '', locked: false },
|
||||
maxLength: { value: '40', locked: false },
|
||||
pattern: { value: '^tx-[0-9a-zA-Z-]+$', locked: false },
|
||||
description: { value: '', locked: false },
|
||||
example: { value: 'tx-20260522-001', locked: false },
|
||||
children: null
|
||||
},
|
||||
{
|
||||
name: { value: 'sender', locked: true },
|
||||
type: { value: 'object', locked: true },
|
||||
required: { value: true, locked: true },
|
||||
format: { value: '', locked: false },
|
||||
maxLength: { value: '', locked: false },
|
||||
pattern: { value: '', locked: false },
|
||||
description: { value: '송금인 정보', locked: false },
|
||||
example: { value: '', locked: false },
|
||||
children: [
|
||||
{
|
||||
name: { value: 'accountNumber', locked: true },
|
||||
type: { value: 'string', locked: true },
|
||||
required: { value: true, locked: true },
|
||||
format: { value: '', locked: false },
|
||||
maxLength: { value: '20', locked: false },
|
||||
pattern: { value: '', locked: false },
|
||||
description: { value: '', locked: false },
|
||||
example: { value: '110-1234-567890', locked: false },
|
||||
children: null
|
||||
},
|
||||
{
|
||||
name: { value: 'bankCode', locked: true },
|
||||
type: { value: 'string', locked: true },
|
||||
required: { value: true, locked: true },
|
||||
format: { value: '', locked: false },
|
||||
maxLength: { value: '3', locked: false },
|
||||
pattern: { value: '', locked: false },
|
||||
description: { value: '', locked: false },
|
||||
example: { value: '088', locked: false },
|
||||
children: null
|
||||
},
|
||||
{
|
||||
name: { value: 'holderName', locked: true },
|
||||
type: { value: 'string', locked: true },
|
||||
required: { value: true, locked: true },
|
||||
format: { value: '', locked: false },
|
||||
maxLength: { value: '60', locked: false },
|
||||
pattern: { value: '', locked: false },
|
||||
description: { value: '', locked: false },
|
||||
example: { value: '김철수', locked: false },
|
||||
children: null
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: { value: 'receiver', locked: true },
|
||||
type: { value: 'object', locked: true },
|
||||
required: { value: true, locked: true },
|
||||
format: { value: '', locked: false },
|
||||
maxLength: { value: '', locked: false },
|
||||
pattern: { value: '', locked: false },
|
||||
description: { value: '수취인 정보', locked: false },
|
||||
example: { value: '', locked: false },
|
||||
children: [
|
||||
{
|
||||
name: { value: 'accountNumber', locked: true },
|
||||
type: { value: 'string', locked: true },
|
||||
required: { value: true, locked: true },
|
||||
format: { value: '', locked: false },
|
||||
maxLength: { value: '20', locked: false },
|
||||
pattern: { value: '', locked: false },
|
||||
description: { value: '', locked: false },
|
||||
example: { value: '111-2345-678901', locked: false },
|
||||
children: null
|
||||
},
|
||||
{
|
||||
name: { value: 'bankCode', locked: true },
|
||||
type: { value: 'string', locked: true },
|
||||
required: { value: true, locked: true },
|
||||
format: { value: '', locked: false },
|
||||
maxLength: { value: '3', locked: false },
|
||||
pattern: { value: '', locked: false },
|
||||
description: { value: '', locked: false },
|
||||
example: { value: '020', locked: false },
|
||||
children: null
|
||||
},
|
||||
{
|
||||
name: { value: 'holderName', locked: true },
|
||||
type: { value: 'string', locked: true },
|
||||
required: { value: true, locked: true },
|
||||
format: { value: '', locked: false },
|
||||
maxLength: { value: '60', locked: false },
|
||||
pattern: { value: '', locked: false },
|
||||
description: { value: '', locked: false },
|
||||
example: { value: '이영희', locked: false },
|
||||
children: null
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: { value: 'amount', locked: true },
|
||||
type: { value: 'object', locked: true },
|
||||
required: { value: true, locked: true },
|
||||
format: { value: '', locked: false },
|
||||
maxLength: { value: '', locked: false },
|
||||
pattern: { value: '', locked: false },
|
||||
description: { value: '이체 금액', locked: false },
|
||||
example: { value: '', locked: false },
|
||||
children: [
|
||||
{
|
||||
name: { value: 'value', locked: true },
|
||||
type: { value: 'integer', locked: true },
|
||||
required: { value: true, locked: true },
|
||||
format: { value: 'int64', locked: false },
|
||||
maxLength: { value: '', locked: false },
|
||||
pattern: { value: '', locked: false },
|
||||
description: { value: '', locked: false },
|
||||
example: { value: '50000', locked: false },
|
||||
children: null
|
||||
},
|
||||
{
|
||||
name: { value: 'currency', locked: true },
|
||||
type: { value: 'string', locked: true },
|
||||
required: { value: true, locked: true },
|
||||
format: { value: '', locked: false },
|
||||
maxLength: { value: '3', locked: false },
|
||||
pattern: { value: '^[A-Z]{3}$', locked: false },
|
||||
description: { value: 'ISO 4217 통화코드', locked: false },
|
||||
example: { value: 'KRW', locked: false },
|
||||
children: null
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: { value: 'memo', locked: true },
|
||||
type: { value: 'string', locked: true },
|
||||
required: { value: false, locked: true },
|
||||
format: { value: '', locked: false },
|
||||
maxLength: { value: '100', locked: false },
|
||||
pattern: { value: '', locked: false },
|
||||
description: { value: '', locked: false },
|
||||
example: { value: '5월 회비', locked: false },
|
||||
children: null
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
// 응답 스키마 (상태코드 별)
|
||||
responses: {
|
||||
'200': { description: f(), schema: [] }
|
||||
'200': {
|
||||
description: { value: '이체 성공', locked: false },
|
||||
schema: [
|
||||
{ name: { value: 'transactionId', locked: true }, type: { value: 'string', locked: true }, required: { value: true, locked: true }, format: { value: '', locked: false }, maxLength: { value: '', locked: false }, pattern: { value: '', locked: false }, description: { value: '', locked: false }, example: { value: 'tx-20260522-001', locked: false }, children: null },
|
||||
{ name: { value: 'status', locked: true }, type: { value: 'string', locked: true }, required: { value: true, locked: true }, format: { value: '', locked: false }, maxLength: { value: '', locked: false }, pattern: { value: '', locked: false }, description: { value: '', locked: false }, example: { value: 'COMPLETED', locked: false }, children: null },
|
||||
{ name: { value: 'completedAt', locked: true }, type: { value: 'string', locked: true }, required: { value: true, locked: true }, format: { value: 'date-time', locked: false }, maxLength: { value: '', locked: false }, pattern: { value: '', locked: false }, description: { value: '', locked: false }, example: { value: '2026-05-22T10:00:00Z', locked: false }, children: null },
|
||||
{
|
||||
name: { value: 'balance', locked: true }, type: { value: 'object', locked: true }, required: { value: true, locked: true }, format: { value: '', locked: false }, maxLength: { value: '', locked: false }, pattern: { value: '', locked: false }, description: { value: '잔액 정보', locked: false }, example: { value: '', locked: false },
|
||||
children: [
|
||||
{ name: { value: 'before', locked: true }, type: { value: 'integer', locked: true }, required: { value: true, locked: true }, format: { value: 'int64', locked: false }, maxLength: { value: '', locked: false }, pattern: { value: '', locked: false }, description: { value: '', locked: false }, example: { value: '1000000', locked: false }, children: null },
|
||||
{ name: { value: 'after', locked: true }, type: { value: 'integer', locked: true }, required: { value: true, locked: true }, format: { value: 'int64', locked: false }, maxLength: { value: '', locked: false }, pattern: { value: '', locked: false }, description: { value: '', locked: false }, example: { value: '950000', locked: false }, children: null }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
'400': {
|
||||
description: { value: '잘못된 요청', locked: false },
|
||||
schema: [
|
||||
{
|
||||
name: { value: 'error', locked: true }, type: { value: 'object', locked: true }, required: { value: true, locked: true }, format: { value: '', locked: false }, maxLength: { value: '', locked: false }, pattern: { value: '', locked: false }, description: { value: '', locked: false }, example: { value: '', locked: false },
|
||||
children: [
|
||||
{ name: { value: 'code', locked: true }, type: { value: 'string', locked: true }, required: { value: true, locked: true }, format: { value: '', locked: false }, maxLength: { value: '', locked: false }, pattern: { value: '', locked: false }, description: { value: '', locked: false }, example: { value: 'INVALID_AMOUNT', locked: false }, children: null },
|
||||
{ name: { value: 'message', locked: true }, type: { value: 'string', locked: true }, required: { value: true, locked: true }, format: { value: '', locked: false }, maxLength: { value: '', locked: false }, pattern: { value: '', locked: false }, description: { value: '', locked: false }, example: { value: '금액이 올바르지 않습니다', locked: false }, children: null },
|
||||
{ name: { value: 'details', locked: true }, type: { value: 'array', locked: true }, required: { value: false, locked: true }, format: { value: '', locked: false }, maxLength: { value: '', locked: false }, pattern: { value: '', locked: false }, description: { value: '', locked: false }, example: { value: '', locked: false }, itemsType: { value: 'string', locked: false }, children: null }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
'401': { description: { value: '인증 실패', locked: false }, schema: [] },
|
||||
'500': { description: { value: '서버 오류', locked: false }, schema: [] }
|
||||
},
|
||||
|
||||
// 보안 스킴
|
||||
securitySchemes: [],
|
||||
globalSecurity: [],
|
||||
securitySchemes: [
|
||||
{
|
||||
key: 'ApiKeyAuth',
|
||||
type: 'apiKey',
|
||||
locked: true,
|
||||
name: { value: 'X-API-Key', locked: true },
|
||||
in: { value: 'header', locked: true },
|
||||
description: { value: '', locked: false }
|
||||
}
|
||||
],
|
||||
globalSecurity: ['ApiKeyAuth'],
|
||||
|
||||
// 예제
|
||||
examples: {
|
||||
request: {},
|
||||
response: {}
|
||||
request: {
|
||||
'application/json': {
|
||||
default: '{\n "transactionId": "tx-20260522-001",\n "sender": {\n "accountNumber": "110-1234-567890",\n "bankCode": "088",\n "holderName": "김철수"\n },\n "receiver": {\n "accountNumber": "111-2345-678901",\n "bankCode": "020",\n "holderName": "이영희"\n },\n "amount": {\n "value": 50000,\n "currency": "KRW"\n },\n "memo": "5월 회비"\n}'
|
||||
}
|
||||
},
|
||||
response: {
|
||||
'200': { body: '{\n "transactionId": "tx-20260522-001",\n "status": "COMPLETED"\n}', headers: [ { name: 'X-RateLimit-Remaining', type: 'integer', description: '남은 요청 횟수', example: '99' } ] }
|
||||
}
|
||||
},
|
||||
|
||||
// 문서 옵션
|
||||
|
||||
@@ -44,14 +44,10 @@ $(document).ready(function() {
|
||||
$(this).val(getEndTime());
|
||||
}
|
||||
});
|
||||
|
||||
//검색조건 달력
|
||||
$("#startDatepicker").datepicker();
|
||||
$("#endDatepicker").datepicker();
|
||||
|
||||
var gridPostData = getSearchForJqgrid("cmd","LIST"); //jqgrid에서는 object 로
|
||||
gridPostData['searchStartDateTime'] = $("input[name=searchStartYYYYMMDD]").val().replace(/-/gi, "") + $("input[name=searchStartHHMM]").val();
|
||||
gridPostData['searchEndDateTime'] = $("input[name=searchEndYYYYMMDD]").val().replace(/-/gi, "") + $("input[name=searchEndHHMM]").val();
|
||||
|
||||
var gridPostData = getSearchForJqgrid("cmd","LIST"); //jqgrid에서는 object 로
|
||||
gridPostData['searchStartDateTime'] = $("input[name=searchStartYYYYMMDD]").val() + $("input[name=searchStartHHMM]").val();
|
||||
gridPostData['searchEndDateTime'] = $("input[name=searchEndYYYYMMDD]").val() + $("input[name=searchEndHHMM]").val();
|
||||
$('#grid').jqGrid({
|
||||
datatype:"json",
|
||||
mtype: 'POST',
|
||||
@@ -110,9 +106,9 @@ $(document).ready(function() {
|
||||
resizeJqGridWidth('grid','content_middle','1000');
|
||||
|
||||
$("#btn_search").click(function(){
|
||||
var postData = getSearchForJqgrid("cmd","LIST"); //jqgrid에서는 object 로
|
||||
postData['searchStartDateTime'] = $("input[name=searchStartYYYYMMDD]").val().replace(/-/gi, "") + $("input[name=searchStartHHMM]").val();
|
||||
postData['searchEndDateTime'] = $("input[name=searchEndYYYYMMDD]").val().replace(/-/gi, "") + $("input[name=searchEndHHMM]").val();
|
||||
var postData = getSearchForJqgrid("cmd","LIST"); //jqgrid에서는 object 로
|
||||
postData['searchStartDateTime'] = $("input[name=searchStartYYYYMMDD]").val() + $("input[name=searchStartHHMM]").val();
|
||||
postData['searchEndDateTime'] = $("input[name=searchEndYYYYMMDD]").val() + $("input[name=searchEndHHMM]").val();
|
||||
$("#grid").setGridParam({ postData: postData ,page : "1" }).trigger("reloadGrid");
|
||||
|
||||
});
|
||||
@@ -158,11 +154,11 @@ function getEndTime(){
|
||||
<tr>
|
||||
<th style="width:180px;"><%= localeMessage.getString("auditLogMan.logamndHMS") %></th>
|
||||
<td>
|
||||
<input type="text" name="searchStartYYYYMMDD" id="startDatepicker" value="${param.searchStartYYYYMMDD}" readonly="readonly" size="10" style="width:100px; border:1px solid #ebebec;">
|
||||
<input type="text" name="searchStartHHMM" value="${param.searchStartHHMM}" size="8" style="width:100px; border:1px solid #ebebec;">
|
||||
<input type="text" name="searchStartYYYYMMDD" value="" size="10" style="width:100px; border:1px solid #ebebec;">
|
||||
<input type="text" name="searchStartHHMM" value="" size="8" style="width:100px; border:1px solid #ebebec;">
|
||||
~
|
||||
<input type="text" name="searchEndYYYYMMDD" id="endDatepicker" value="${param.searchEndYYYYMMDD}" readonly="readonly" size="10" style="width:100px; border:1px solid #ebebec;">
|
||||
<input type="text" name="searchEndHHMM" value="${param.searchEndHHMM}" size="8" style="width:100px; border:1px solid #ebebec;">
|
||||
<input type="text" name="searchEndYYYYMMDD" value="" size="10" style="width:100px; border:1px solid #ebebec;">
|
||||
<input type="text" name="searchEndHHMM" value="" size="8" style="width:100px; border:1px solid #ebebec;">
|
||||
<input type="hidden" name="searchStartDateTime" />
|
||||
<input type="hidden" name="searchEndDateTime" />
|
||||
</td>
|
||||
@@ -172,9 +168,9 @@ function getEndTime(){
|
||||
<th style="width:180px;"><%= localeMessage.getString("auditLogMan.systemCode") %></th>
|
||||
<td><div class = "select-style">
|
||||
<select id="searchSystemCode" name="searchSystemCode">
|
||||
<option value="" <c:if test="${empty param.searchSystemCode}">selected</c:if>><%= localeMessage.getString("combo.all")%></option>
|
||||
<option value=""><%= localeMessage.getString("combo.all")%></option>
|
||||
<c:forEach items="${serviceTypeCombos }" var="serviceTypeCombo">
|
||||
<option value="${serviceTypeCombo.code }" <c:if test="${serviceTypeCombo.code == param.searchSystemCode}">selected</c:if>>${serviceTypeCombo.codename }</option>
|
||||
<option value="${serviceTypeCombo.code }">${serviceTypeCombo.codename }</option>
|
||||
</c:forEach>
|
||||
</select>
|
||||
</div>
|
||||
@@ -184,13 +180,12 @@ function getEndTime(){
|
||||
|
||||
<th style="width:180px;"><%= localeMessage.getString("auditLogMan.logTypeText") %></th>
|
||||
<td>
|
||||
<input type="text" value="${param.searchLogTypeText}" name="searchLogTypeText" checked="checked"/>
|
||||
<input type="text" value="" name="searchLogTypeText" checked="checked"/>
|
||||
</td>
|
||||
<th style="width:180px;"><%= localeMessage.getString("auditLogMan.remoteAddress") %></th>
|
||||
<td><input type="text" name="searchRemoteAddress" value="${param.searchRemoteAddress}" style="width:100%"></td>
|
||||
<th style="width:180px;"><%= localeMessage.getString("auditLogMan.parameters") %></th>
|
||||
<th style="width:180px;"></th>
|
||||
<td>
|
||||
<input type="text" name="searchParameters" value="${param.searchParameters}" style="width:100%">
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
||||
@@ -54,18 +54,10 @@ function detail(key){
|
||||
$('#systemCode').val(data.systemCode);
|
||||
$('#logTypeText').val(data.logTypeText);
|
||||
$('#remoteAddress').val(data.remoteAddress);
|
||||
$('#logMsg').val(data.message);
|
||||
$('#logSubMsg').val(data.command);
|
||||
$('#parameters').val(data.parameters);
|
||||
|
||||
// 사유(message)는 값이 있을 때만 노출
|
||||
if (data.message) {
|
||||
$('#logMsg').val(data.message);
|
||||
$('#messageRow').show();
|
||||
} else {
|
||||
$('#logMsg').val('');
|
||||
$('#messageRow').hide();
|
||||
}
|
||||
|
||||
},
|
||||
error:function(e){
|
||||
alert(e.responseText);
|
||||
@@ -129,9 +121,6 @@ $(document).ready(function() {
|
||||
<tr>
|
||||
<th><%= localeMessage.getString("auditLogMan.command") %></th><td><input type="text" id="logSubMsg" name="logSubMsg" readonly="readonly"/></td>
|
||||
</tr>
|
||||
<tr id="messageRow" style="display:none;">
|
||||
<th><%= localeMessage.getString("auditLogMan.message") %></th><td><textarea id="logMsg" name="logMsg" style="width:100%;height:80px" readonly="readonly"></textarea></td>
|
||||
</tr>
|
||||
<tr height="100px">
|
||||
<th><%= localeMessage.getString("auditLogMan.parameters") %></th><td><textarea id="parameters" name="parameters" style="width:100%;height:200px" readonly="readonly"></textarea></td>
|
||||
</tr>
|
||||
|
||||
@@ -1,120 +0,0 @@
|
||||
<%@ page language="java" contentType="text/html; charset=utf-8"%>
|
||||
<%@ page import="java.util.List"%>
|
||||
<%@ page import="com.eactive.eai.rms.common.acl.sitemap.ui.SitemapNode"%>
|
||||
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
|
||||
<%!
|
||||
private void printNodeLabel(javax.servlet.jsp.JspWriter out, SitemapNode node) throws java.io.IOException {
|
||||
String url = node.getMenuUrl();
|
||||
boolean hasLink = url != null && !url.trim().isEmpty() && !"NAN".equalsIgnoreCase(url.trim());
|
||||
if (hasLink) {
|
||||
out.print("<a href=\"javascript:void(0);\" class=\"sitemap-name\" onclick=\"goPage('"
|
||||
+ escapeJs(url) + "','" + escapeJs(node.getMenuId()) + "');return false;\">"
|
||||
+ escapeHtml(node.getMenuName()) + "</a>");
|
||||
} else {
|
||||
out.print("<span class=\"sitemap-name\">" + escapeHtml(node.getMenuName()) + "</span>");
|
||||
}
|
||||
}
|
||||
|
||||
private void printSitemapNode(javax.servlet.jsp.JspWriter out, SitemapNode node) throws java.io.IOException {
|
||||
out.print("<li>");
|
||||
printNodeLabel(out, node);
|
||||
|
||||
List<SitemapNode> children = node.getChildren();
|
||||
if (children != null && !children.isEmpty()) {
|
||||
out.print("<ul>");
|
||||
for (SitemapNode child : children) {
|
||||
printSitemapNode(out, child);
|
||||
}
|
||||
out.print("</ul>");
|
||||
}
|
||||
out.print("</li>");
|
||||
}
|
||||
|
||||
private String escapeHtml(String value) {
|
||||
if (value == null) {
|
||||
return "";
|
||||
}
|
||||
return value.replace("&", "&").replace("<", "<").replace(">", ">").replace("\"", """);
|
||||
}
|
||||
|
||||
private String escapeJs(String value) {
|
||||
if (value == null) {
|
||||
return "";
|
||||
}
|
||||
return value.replace("\\", "\\\\").replace("'", "\\'");
|
||||
}
|
||||
%>
|
||||
<%
|
||||
response.setHeader("Pragma", "No-cache");
|
||||
response.setHeader("Cache-Control", "no-cache");
|
||||
response.setHeader("Expires", "0");
|
||||
|
||||
List<SitemapNode> sitemapTree = (List<SitemapNode>) request.getAttribute("sitemapTree");
|
||||
%>
|
||||
<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>
|
||||
.sitemap-columns { display: flex; flex-wrap: wrap; gap: 40px; }
|
||||
.sitemap-column { min-width: 180px; }
|
||||
.sitemap-category { font-size: 1.1em; font-weight: bold; padding-bottom: 6px; margin-bottom: 8px; border-bottom: 2px solid #ccc; }
|
||||
.sitemap-tree, .sitemap-tree ul { list-style-type: disc; margin: 0; padding-left: 18px; }
|
||||
.sitemap-tree { padding-left: 0; list-style-type: none; }
|
||||
.sitemap-tree li { line-height: 1.8; }
|
||||
a.sitemap-name { text-decoration: none; cursor: pointer; }
|
||||
a.sitemap-name:hover { text-decoration: underline; }
|
||||
</style>
|
||||
<script language="javascript">
|
||||
function goPage(url, menuId) {
|
||||
var serviceType = sessionStorage["serviceType"];
|
||||
|
||||
var pageUrl = url;
|
||||
pageUrl += (pageUrl.indexOf("?") > -1 ? "&" : "?") + "menuId=" + menuId;
|
||||
pageUrl += "&serviceType=" + serviceType;
|
||||
|
||||
parent.leftFrame.location.href = '<c:url value="/leftMenu.do"/>?menuId=' + menuId + '&serviceType=' + serviceType;
|
||||
location.href = pageUrl;
|
||||
}
|
||||
</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">
|
||||
<!-- <div class="title">사이트맵</div> -->
|
||||
<div class="sitemap-columns">
|
||||
<%
|
||||
for (SitemapNode category : sitemapTree) {
|
||||
%>
|
||||
<div class="sitemap-column">
|
||||
<div class="sitemap-category">
|
||||
<%
|
||||
printNodeLabel(out, category);
|
||||
%>
|
||||
</div>
|
||||
<ul class="sitemap-tree">
|
||||
<%
|
||||
List<SitemapNode> children = category.getChildren();
|
||||
if (children != null) {
|
||||
for (SitemapNode child : children) {
|
||||
printSitemapNode(out, child);
|
||||
}
|
||||
}
|
||||
%>
|
||||
</ul>
|
||||
</div>
|
||||
<%
|
||||
}
|
||||
%>
|
||||
</div>
|
||||
</div><!-- end content_middle -->
|
||||
</div><!-- end right_box -->
|
||||
</body>
|
||||
</html>
|
||||
@@ -18,17 +18,7 @@
|
||||
<script language="javascript" >
|
||||
var url = '<c:url value="/common/acl/user/userMan.json" />';
|
||||
var url_view = '<c:url value="/common/acl/user/userMan.view" />';
|
||||
|
||||
function userStatusFormat (cellvalue){
|
||||
if ( cellvalue == '1' ){
|
||||
return '<%= localeMessage.getString("code.normal") %>';
|
||||
}else if ( cellvalue == '2' ) {
|
||||
return '<span style="color:red"><%= localeMessage.getString("code.lock") %></span>';
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
$(document).ready(function() {
|
||||
var gridPostData = getSearchForJqgrid("cmd","LIST"); //jqgrid에서는 object 로
|
||||
$('#grid').jqGrid({
|
||||
datatype:"json",
|
||||
@@ -38,20 +28,16 @@ $(document).ready(function() {
|
||||
colNames:['<%= localeMessage.getString("login.userId") %>',
|
||||
'<%= localeMessage.getString("user.name") %>',
|
||||
'<%= localeMessage.getString("user.roleName") %>',
|
||||
'<%= localeMessage.getString("user.status") %>',
|
||||
'<%= localeMessage.getString("user.createOn") %>',
|
||||
'<%= localeMessage.getString("user.lastLoginYms") %>',
|
||||
'<%= localeMessage.getString("user.depart") %>',
|
||||
'<%= localeMessage.getString("user.teamName") %>',
|
||||
'<%= localeMessage.getString("user.position") %>'
|
||||
],
|
||||
colModel:[
|
||||
{ name : 'USERID' , align:'center', width: 100, sortable:false },
|
||||
{ name : 'USERNAME' , align:'center', width: 100 },
|
||||
{ name : 'ROLEIDNFINAME' , align:'center', width: 300 },
|
||||
{ name : 'STATUS' , align:'center', width: 80, formatter: userStatusFormat },
|
||||
{ name : 'REGDYMS' , align:'center', width: 120, formatter: timeStampFormat },
|
||||
{ name : 'LASTLOGINYMS' , align:'center', width: 120, formatter: timeStampFormat },
|
||||
{ name : 'USERID' , align:'center' ,sortable:false },
|
||||
{ name : 'USERNAME' , align:'center' },
|
||||
{ name : 'ROLEIDNFINAME' , align:'center' },
|
||||
{ name : 'LASTAMNDYMS' , align:'center', formatter: timeStampFormat },
|
||||
{ name : 'DVSNNAME' , align:'center', hidden: true },
|
||||
{ name : 'TEAMNAME' , align:'center', hidden: true },
|
||||
{ name : 'JOBTLNAME' , align:'center', hidden: true }
|
||||
|
||||
@@ -88,8 +88,7 @@ function detail(key){
|
||||
$(tag+"[name="+name+"]").val(data[name.toUpperCase()] ? data[name.toUpperCase()].trim():"");
|
||||
});
|
||||
|
||||
$("input[name=REGDYMS]").inputmask("9999-99-99 99:99:99",{'autoUnmask':true});
|
||||
$("input[name=LASTLOGINYMS]").inputmask("9999-99-99 99:99:99",{'autoUnmask':true});
|
||||
$("input[name=LASTAMNDYMS]").inputmask("9999-99-99 99:99:99",{'autoUnmask':true});
|
||||
/* $("input[name=SECONDMENTSTDT]").inputmask("9999-99-99",{'autoUnmask':true});
|
||||
$("input[name=SECONDMENTENDT]").inputmask("9999-99-99",{'autoUnmask':true}); */
|
||||
|
||||
@@ -217,7 +216,6 @@ $(document).ready(function() {
|
||||
dataType:"json",
|
||||
data:{cmd: 'UPDATE_PASSWORD', userId : $("input[name=userId]").val()},
|
||||
success:function(json){
|
||||
$('select[name="status"]').val("1")
|
||||
alert(json.result);
|
||||
},
|
||||
error:function(e){
|
||||
@@ -279,10 +277,10 @@ $(document).ready(function() {
|
||||
<table class="table_row" cellspacing="0">
|
||||
<input type="hidden" name="jobclcd">
|
||||
<tr>
|
||||
<th style="width:180px;"><%= localeMessage.getString("login.userId") %> <span class=\"required-mark\">*</span></th><td ><input type="text" name="userId"/> </td>
|
||||
<th style="width:180px;"><%= localeMessage.getString("login.userId") %></th><td ><input type="text" name="userId"/> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><%= localeMessage.getString("user.name") %> <span class=\"required-mark\">*</span></th><td><input type="text" name="userName" /></td>
|
||||
<th><%= localeMessage.getString("user.name") %> </th><td><input type="text" name="userName" /></td>
|
||||
</tr>
|
||||
<tr class="datailT">
|
||||
<th><%= localeMessage.getString("user.auth") %> </th><td><input type="text" name="roleidnfiname" /></td>
|
||||
@@ -319,6 +317,7 @@ $(document).ready(function() {
|
||||
<td>
|
||||
<div class="select-style">
|
||||
<select name="status">
|
||||
<option value=""><%= localeMessage.getString("code.normal") %></option>
|
||||
<option value="1"><%= localeMessage.getString("code.normal") %></option>
|
||||
<option value="2"><%= localeMessage.getString("code.lock") %></option>
|
||||
</select>
|
||||
@@ -335,10 +334,7 @@ $(document).ready(function() {
|
||||
<th><%= localeMessage.getString("user.fieldAgentName") %> </th><td><input type="text" name="spotprxyname" /></td>
|
||||
</tr> --%>
|
||||
<tr>
|
||||
<th><%= localeMessage.getString("user.createOn") %> </th><td><input type="text" name="REGDYMS" readonly/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><%= localeMessage.getString("user.lastLoginYms") %> </th><td><input type="text" name="LASTLOGINYMS" readonly/></td>
|
||||
<th><%= localeMessage.getString("user.createOn") %> </th><td><input type="text" name="LASTAMNDYMS" readonly/></td>
|
||||
</tr>
|
||||
<%-- <tr>
|
||||
<th><%= localeMessage.getString("user.secondmentbrncd") %> </th><td><input type="text" name="SECONDMENTBRNCD" readonly/></td>
|
||||
|
||||
@@ -31,12 +31,8 @@
|
||||
<script language="javascript" src="<c:url value="/js/custom.js"/>"></script>
|
||||
<script language="javascript" src="<c:url value="/js/common-${locale}.js"/>"></script>
|
||||
<script type="text/javascript">
|
||||
/* 자동 로그아웃: 서버로 실제 요청이 발생한 경우에만 활동으로 기록 (main.jsp 타이머와 sessionStorage 공유) */
|
||||
(function() {
|
||||
var mark = function() { sessionStorage.setItem('lastActivity', String(Date.now())); };
|
||||
mark(); // 화면(프레임) 로드 자체가 서버 요청이므로 활동으로 기록
|
||||
$(document).ajaxSend(mark); // 이후 이 프레임에서 발생하는 모든 AJAX 요청을 활동으로 기록
|
||||
})();
|
||||
/* 자동 로그아웃: iframe 내 활동을 sessionStorage에 기록 (main.jsp 타이머와 공유) */
|
||||
(function() { var m = function() { sessionStorage.setItem('lastActivity', String(Date.now())); }; ['mousemove','keydown','click','touchstart'].forEach(function(e){ document.addEventListener(e, m, true); }); })();
|
||||
</script>
|
||||
<script language="javascript" src="<c:url value="/js/jquery.inputmask.bundle.min.js"/>"></script>
|
||||
<script language="javascript" src="<c:url value="/js/jquery.searchabledropdown-1.0.8.min.js"/>"></script>
|
||||
@@ -535,88 +531,6 @@
|
||||
}
|
||||
}, 200);
|
||||
}
|
||||
|
||||
/**
|
||||
* 사유 입력 모달 - 확인 시 입력한 사유 문자열을 onConfirm(reason)으로 전달
|
||||
* @param {string} message - 안내 메시지
|
||||
* @param {object} options - (title, confirmText, cancelText, placeholder, required, onConfirm, onCancel)
|
||||
* required 기본 true (빈 사유 차단)
|
||||
*/
|
||||
function showReasonPrompt(message, options) {
|
||||
options = options || {};
|
||||
var title = options.title || '사유 입력';
|
||||
var confirmText = options.confirmText || '확인';
|
||||
var cancelText = options.cancelText || '취소';
|
||||
var placeholder = options.placeholder || '사유를 입력하세요.';
|
||||
var required = options.required !== false;
|
||||
var onConfirm = options.onConfirm || function(){};
|
||||
var onCancel = options.onCancel || function(){};
|
||||
|
||||
// 기존 모달 제거
|
||||
$('#commonReasonModal').remove();
|
||||
|
||||
var iconHtml = getAlertIcon('warning');
|
||||
|
||||
var modalHtml =
|
||||
'<div id="commonReasonModal" class="common-alert-overlay">' +
|
||||
'<div class="common-alert-modal alert-warning">' +
|
||||
'<div class="common-alert-header">' +
|
||||
'<span class="common-alert-icon">' + iconHtml + '</span>' +
|
||||
'<span class="common-alert-title">' + title + '</span>' +
|
||||
'</div>' +
|
||||
'<div class="common-alert-body">' +
|
||||
'<p class="common-alert-message">' + message + '</p>' +
|
||||
'<textarea id="commonReasonInput" maxlength="200" placeholder="' + placeholder + '" style="width:100%;height:80px;margin-top:10px;box-sizing:border-box;font-size:13px;padding:8px;border:1px solid #ddd;border-radius:4px;resize:vertical;"></textarea>' +
|
||||
'<p id="commonReasonError" style="display:none;color:#f44336;font-size:12px;margin:6px 0 0;">사유를 입력해 주세요.</p>' +
|
||||
'</div>' +
|
||||
'<div class="common-alert-footer">' +
|
||||
'<button type="button" class="common-confirm-cancel-btn" id="commonReasonCancelBtn">' + cancelText + '</button>' +
|
||||
'<button type="button" class="common-alert-btn" id="commonReasonOkBtn">' + confirmText + '</button>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'</div>';
|
||||
|
||||
$('body').append(modalHtml);
|
||||
|
||||
setTimeout(function() {
|
||||
$('#commonReasonModal').addClass('show');
|
||||
$('#commonReasonInput').focus();
|
||||
}, 10);
|
||||
|
||||
// 확인 버튼
|
||||
$('#commonReasonOkBtn').on('click', function() {
|
||||
var reason = $.trim($('#commonReasonInput').val());
|
||||
if (required && reason === '') {
|
||||
$('#commonReasonError').show();
|
||||
$('#commonReasonInput').focus();
|
||||
return;
|
||||
}
|
||||
closeCommonReason(function() { onConfirm(reason); });
|
||||
});
|
||||
|
||||
// 취소 버튼
|
||||
$('#commonReasonCancelBtn').on('click', function() {
|
||||
closeCommonReason(onCancel);
|
||||
});
|
||||
|
||||
// ESC 키로 취소
|
||||
$(document).on('keydown.commonReason', function(e) {
|
||||
if (e.keyCode === 27) {
|
||||
closeCommonReason(onCancel);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function closeCommonReason(callback) {
|
||||
$('#commonReasonModal').removeClass('show');
|
||||
setTimeout(function() {
|
||||
$('#commonReasonModal').remove();
|
||||
$(document).off('keydown.commonReason');
|
||||
if (typeof callback === 'function') {
|
||||
callback();
|
||||
}
|
||||
}, 200);
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- 공용 Alert 모달 스타일 -->
|
||||
|
||||
@@ -28,11 +28,9 @@ function init(){
|
||||
success:function(json){
|
||||
new makeOptions("CODE","NAME").setObj($("select[name=searchJobUseYn]")).setNoValueInclude(true).setNoValue("",
|
||||
"<%= localeMessage.getString("combo.all")%>").setData(json.useYnTypeRows).setFormat(codeName3OptionFormat).rendering();
|
||||
new makeOptions("CODE","NAME").setObj($("select[name=searchJobInst]")).setNoValueInclude(true).setNoValue("",
|
||||
new makeOptions("CODE","NAME").setObj($("select[name=searchJobInst]")).setNoValueInclude(true).setNoValue("",
|
||||
"<%= localeMessage.getString("combo.all")%>").setData(json.instanceList).rendering();
|
||||
|
||||
$("select[name=searchJobUseYn]").val("Y");
|
||||
|
||||
|
||||
putSelectFromParam();
|
||||
|
||||
initGrid();
|
||||
|
||||
@@ -7,19 +7,6 @@
|
||||
<%@ 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" %>
|
||||
<%
|
||||
// 비밀번호 규칙 안내문구용 모니터링 프로퍼티 조회
|
||||
com.eactive.eai.rms.common.context.MonitoringContext monitoringContext =
|
||||
(com.eactive.eai.rms.common.context.MonitoringContext) WebApplicationContextUtils
|
||||
.getRequiredWebApplicationContext(pageContext.getServletContext())
|
||||
.getBean("monitoringContext");
|
||||
int pwdLengthCheck = monitoringContext.getIntProperty(
|
||||
com.eactive.eai.rms.common.context.MonitoringContext.RMS_PASSWORD_LENGTH_CHECK, 7);
|
||||
int pwdCombiCheck = monitoringContext.getIntProperty(
|
||||
com.eactive.eai.rms.common.context.MonitoringContext.RMS_PASSWORD_COMBI_CHECK, 2);
|
||||
int pwdRepeatCheck = monitoringContext.getIntProperty(
|
||||
com.eactive.eai.rms.common.context.MonitoringContext.RMS_PASSWORD_REPEAT_CHECK, 3);
|
||||
%>
|
||||
<c:set var="themeColor" value="<%=System.getProperty(\"theme.color\")%>" scope="session" />
|
||||
|
||||
<%
|
||||
@@ -78,7 +65,6 @@
|
||||
// SMS 인증 관련 변수
|
||||
var smsAuthTimer = null;
|
||||
var resendTimer = null;
|
||||
var smsAuthPurpose = 'LOGIN'; // 'LOGIN' | 'PASSWORD_CHANGE' - SMS 인증 모달을 어느 흐름에서 띄웠는지 구분
|
||||
|
||||
function fncPreMain() {
|
||||
if ($("input[name=userId]").val() ==null || $("input[name=userId]").val().length == 0 ){
|
||||
@@ -105,26 +91,15 @@
|
||||
data: $('#loginForm').serialize(),
|
||||
dataType: 'json',
|
||||
success: function(response) {
|
||||
console.log('res', response)
|
||||
if (response.changePassword) {
|
||||
showChangeInitPassword(response);
|
||||
}
|
||||
else if (response.smsAuthRequired) {
|
||||
if (response.smsAuthRequired) {
|
||||
// SMS 인증 필요
|
||||
smsAuthPurpose = 'LOGIN';
|
||||
showSmsAuthModal(response);
|
||||
} else if (response.success) {
|
||||
// 직접 로그인 성공
|
||||
window.location.href = response.redirectUrl;
|
||||
} else {
|
||||
if (response.errorMessage == 'NO_PHONE_NUMBER') {
|
||||
alert("휴대폰번호가 등록되어 있지 않아 로그인할 수 없습니다. 관리자에게 문의하세요")
|
||||
} else if (response.errorMessage == 'FAIL_SEND_AUTH') {
|
||||
alert("인증번호 발송에 실패했습니다. 잠시 후 다시 시도해주세요.")
|
||||
} else {
|
||||
// 로그인 실패
|
||||
alert('로그인에 실패했습니다');
|
||||
}
|
||||
// 로그인 실패
|
||||
alert(response.errorMessage || '로그인에 실패했습니다.');
|
||||
}
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
@@ -133,33 +108,16 @@
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function showChangeInitPassword(data) {
|
||||
$('input[name="resetUserId"]').val($('input[name="userId"]').val());
|
||||
$('input[name="resetPassword"]').val($('input[name="password"]').val());
|
||||
$('#changeInitPassword').hide();
|
||||
$('#pwdChgModal').modal('show');
|
||||
}
|
||||
|
||||
// SMS 인증 모달 표시
|
||||
function showSmsAuthModal(data) {
|
||||
$('#smsAuthMaskedPhone').text(data.maskedPhone);
|
||||
$('#smsAuthCode').val('');
|
||||
updateSmsTestHint(data.smsTestHint);
|
||||
startExpiryTimer(data.expiresAt);
|
||||
startResendTimer(data.resendableAt);
|
||||
$('#smsAuthModal').modal('show');
|
||||
}
|
||||
|
||||
// fixed 모드(djb.sms_auth.mode=fixed)일 때만 서버가 내려주는 테스트용 인증번호 힌트 표시
|
||||
function updateSmsTestHint(hint) {
|
||||
if (hint) {
|
||||
$('#smsAuthTestHint').text(hint).show();
|
||||
} else {
|
||||
$('#smsAuthTestHint').text('').hide();
|
||||
}
|
||||
}
|
||||
|
||||
// 유효시간 타이머
|
||||
function startExpiryTimer(expiresAt) {
|
||||
clearInterval(smsAuthTimer);
|
||||
@@ -213,7 +171,6 @@
|
||||
dataType: 'json',
|
||||
success: function(data) {
|
||||
if (data.sent) {
|
||||
updateSmsTestHint(data.smsTestHint);
|
||||
startExpiryTimer(data.expiresAt);
|
||||
startResendTimer(data.resendableAt);
|
||||
alert('인증번호가 재발송되었습니다.');
|
||||
@@ -227,7 +184,7 @@
|
||||
});
|
||||
}
|
||||
|
||||
// SMS 인증번호 확인 (로그인 / 비밀번호 변경 공용 - smsAuthPurpose로 분기)
|
||||
// SMS 인증번호 확인
|
||||
function verifySmsAuthCode() {
|
||||
var code = $('#smsAuthCode').val();
|
||||
if (!code || code.length !== 6) {
|
||||
@@ -236,25 +193,17 @@
|
||||
return;
|
||||
}
|
||||
|
||||
var isPasswordChange = (smsAuthPurpose === 'PASSWORD_CHANGE');
|
||||
var verifyUrl = isPasswordChange
|
||||
? '<c:url value="/sms-auth/verify-password-change.json"/>'
|
||||
: '<c:url value="/sms-auth/verify.json"/>';
|
||||
|
||||
$.ajax({
|
||||
url: verifyUrl,
|
||||
url: '<c:url value="/sms-auth/verify.json"/>',
|
||||
type: 'POST',
|
||||
data: { authCode: code },
|
||||
dataType: 'json',
|
||||
success: function(data) {
|
||||
if (data.success) {
|
||||
$('#smsAuthModal').modal('hide');
|
||||
if (isPasswordChange) {
|
||||
alert(data.message);
|
||||
}
|
||||
window.location.href = data.redirectUrl;
|
||||
} else {
|
||||
alert(data.errorMessage || data.message || '인증에 실패했습니다.');
|
||||
alert(data.errorMessage || '인증에 실패했습니다.');
|
||||
}
|
||||
},
|
||||
error: function() {
|
||||
@@ -269,81 +218,46 @@
|
||||
clearInterval(resendTimer);
|
||||
$('#smsAuthModal').modal('hide');
|
||||
}
|
||||
|
||||
function checkPwd(){
|
||||
function changePwd(){
|
||||
if ($("input[name=resetUserId]").val() == null || $("input[name=resetUserId]").val().length == 0 ){
|
||||
alert("<%= localeMessage.getString("login.checkid") %>");
|
||||
$("input[name=resetUserId]").trigger("focus");
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
if ($("input[name=resetPassword]").val() == null || $("input[name=resetPassword]").val().trim().length == 0 ){
|
||||
alert("<%= localeMessage.getString("login.checkpwd1") %>");
|
||||
$("input[name=resetPassword]").trigger("focus");
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
if ($("input[name=changePassword]").val() == null || $("input[name=changePassword]").val().trim().length == 0 ){
|
||||
alert("<%= localeMessage.getString("login.checkpwd2") %>");
|
||||
$("input[name=changePassword]").trigger("focus");
|
||||
return false;
|
||||
return ;
|
||||
}
|
||||
if ($("input[name=confirmPassword]").val() == null || $("input[name=confirmPassword]").val().trim().length == 0 ){
|
||||
alert("<%= localeMessage.getString("login.checkpwd3") %>");
|
||||
return false;
|
||||
return ;
|
||||
}
|
||||
if ($("input[name=changePassword]").val() != $("input[name=confirmPassword]").val()){
|
||||
if ($("input[name=confirmPassword]").val() != $("input[name=confirmPassword]").val()){
|
||||
alert("<%= localeMessage.getString("login.checkpwd4") %>");
|
||||
$("input[name=confirmPassword]").trigger("focus");
|
||||
return false;
|
||||
return ;
|
||||
}
|
||||
if ($("input[name=resetUserId]").val() == $("input[name=changePassword]").val()){
|
||||
alert("<%= localeMessage.getString("login.checkpwd5") %>");
|
||||
$("input[name=changePassword]").trigger("focus");
|
||||
return false;
|
||||
return ;
|
||||
}
|
||||
|
||||
return true;
|
||||
//if (idCheck.checked){
|
||||
// setCookie("key",id);
|
||||
//}
|
||||
<%-- $("form").attr("action","<c:url value="/changePassword.do"/>");
|
||||
//$("form").action = "<%=request.getContextPath()%>/changePassword.do";
|
||||
--%>
|
||||
$("form").submit();
|
||||
}
|
||||
|
||||
// 비밀번호 변경 요청 (AJAX) - 서버 룰 검증 실패 시 모달을 닫지 않고 비밀번호 입력값만 초기화
|
||||
function submitChangePassword() {
|
||||
if (!checkPwd()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
url: '<c:url value="/changePassword.do"/>',
|
||||
type: 'POST',
|
||||
data: $('#modalLoginForm').serialize(),
|
||||
dataType: 'json',
|
||||
success: function(response) {
|
||||
if (response.smsAuthRequired) {
|
||||
// 비밀번호는 아직 반영되지 않음 - SMS 인증 완료 시 최종 반영됨
|
||||
smsAuthPurpose = 'PASSWORD_CHANGE';
|
||||
// 두 모달이 겹치면 이전 모달의 enforceFocus/backdrop 이 남아 입력이 막히므로
|
||||
// #pwdChgModal 이 완전히 닫힌 뒤 SMS 인증 모달을 연다
|
||||
$('#pwdChgModal').one('hidden.bs.modal', function() {
|
||||
showSmsAuthModal(response);
|
||||
});
|
||||
$('#pwdChgModal').modal('hide');
|
||||
return;
|
||||
}
|
||||
alert(response.message);
|
||||
if (response.success) {
|
||||
window.location.href = response.redirectUrl || '<c:url value="/emergency.jsp"/>';
|
||||
} else {
|
||||
$("input[name=changePassword]").val('');
|
||||
$("input[name=confirmPassword]").val('');
|
||||
$("input[name=changePassword]").trigger("focus");
|
||||
}
|
||||
},
|
||||
error: function() {
|
||||
alert("<%= localeMessage.getString("login.pwdchangefail") %>");
|
||||
}
|
||||
});
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
$(document).ready(function() {
|
||||
$("input[name=userId]").keydown(function(event){
|
||||
if ( event.which == 13 ) {
|
||||
@@ -357,24 +271,14 @@
|
||||
});
|
||||
$("input[name=changePassword]").keydown(function(event){
|
||||
if ( event.which == 13 ) {
|
||||
event.preventDefault();
|
||||
submitChangePassword();
|
||||
changePwd();
|
||||
}
|
||||
});
|
||||
$("input[name=confirmPassword]").keydown(function(event){
|
||||
if ( event.which == 13 ) {
|
||||
event.preventDefault();
|
||||
submitChangePassword();
|
||||
changePwd();
|
||||
}
|
||||
});
|
||||
// Password Change 링크로 직접 열 때만 초기화 (JS에서 modal('show') 호출 시 relatedTarget 없음)
|
||||
$('#pwdChgModal').on('show.bs.modal', function(event) {
|
||||
if (event.relatedTarget) {
|
||||
$('#modalLoginForm')[0].reset();
|
||||
$('#changeInitPassword').show();
|
||||
}
|
||||
});
|
||||
|
||||
$("select[name=serviceType]").change(function(){
|
||||
fncPreMain();
|
||||
});
|
||||
@@ -382,7 +286,9 @@
|
||||
$("#btn_login").click(function(){
|
||||
fncPreMain();
|
||||
});
|
||||
|
||||
$("#changePwd").click(function(){
|
||||
changePwd();
|
||||
});
|
||||
$("#btn_sso_login").click(function(){
|
||||
fncSsoLogin();
|
||||
});
|
||||
@@ -393,19 +299,6 @@
|
||||
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() {
|
||||
@@ -473,7 +366,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 비밀번호 변경 모달 -->
|
||||
|
||||
<div class="modal fade" id="pwdChgModal" tabindex="-1" role="dialog" aria-labelledby="pwdChgModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
@@ -483,15 +376,13 @@
|
||||
<span aria-hidden="true">X</span>
|
||||
</button>
|
||||
</div>
|
||||
<form id="modalLoginForm" action="<c:url value="/changePassword.do"/>" method="post" onsubmit="return submitChangePassword()" novalidate>
|
||||
<form name="form" id="modalLoginForm" action="<c:url value="/changePassword.do"/>" method="post">
|
||||
<div class="modal-body">
|
||||
<div id="changeInitPassword">
|
||||
<div class="form-group d-flex">
|
||||
<input type="text" name="resetUserId" class="form-control rounded-left" placeholder="User Id" required>
|
||||
</div>
|
||||
<div class="form-group d-flex">
|
||||
<input type="password" name="resetPassword" class="form-control rounded-left" placeholder="<%= localeMessage.getString("login.placeholderCurrentPassword") %>" autocomplete="off" required>
|
||||
</div>
|
||||
<div class="form-group d-flex">
|
||||
<input type="text" name="resetUserId" class="form-control rounded-left" placeholder="User Id" required>
|
||||
</div>
|
||||
<div class="form-group d-flex">
|
||||
<input type="password" name="resetPassword" class="form-control rounded-left" placeholder="<%= localeMessage.getString("login.placeholderCurrentPassword") %>" autocomplete="off" required>
|
||||
</div>
|
||||
<p><%= localeMessage.getString("login.toChangePasswordMessage") %></p>
|
||||
<div class="form-group d-flex">
|
||||
@@ -500,13 +391,9 @@
|
||||
<div class="form-group d-flex">
|
||||
<input type="password" name="confirmPassword" class="form-control rounded-left" placeholder="<%= localeMessage.getString("login.placeholderConfirmationPassword") %>" autocomplete="off" required>
|
||||
</div>
|
||||
<span style="font-size:0.8em; color:red">
|
||||
※ <%= pwdLengthCheck %>글자 이상 & 영문/숫자/특수문자 <%= pwdCombiCheck %>종류 이상<br/>
|
||||
※ 연속된 숫자/문자(예: 123, abc, qwer) 사용 불가<%= pwdRepeatCheck >= 2 ? ", 동일 문자 " + pwdRepeatCheck + "자 이상 반복 불가" : "" %>
|
||||
</span>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="submit" id="changePwd" class="form-control btn btn-primary rounded submit px-3">변경</button>
|
||||
<input type="button" id ="changePwd" class="form-control btn btn-primary rounded submit px-3" value="변경">
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
@@ -524,9 +411,9 @@
|
||||
<p>인증번호가 <strong><span id="smsAuthMaskedPhone"></span></strong>로 발송되었습니다.</p>
|
||||
<div class="form-group">
|
||||
<input type="text" id="smsAuthCode" class="form-control"
|
||||
placeholder="인증번호 6자리" maxlength="6" inputmode="numeric" autocomplete="off">
|
||||
placeholder="인증번호 6자리" maxlength="6"
|
||||
onkeypress="return event.charCode >= 48 && event.charCode <= 57">
|
||||
</div>
|
||||
<p id="smsAuthTestHint" class="text-warning" style="display:none;"></p>
|
||||
<p class="text-muted">남은 시간: <span id="smsAuthRemainingTime" class="text-danger font-weight-bold">60</span>초</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
|
||||
@@ -166,8 +166,10 @@
|
||||
location.href = '<%=request.getContextPath()%>/rms/logout.do';
|
||||
}
|
||||
|
||||
/* 상위 프레임(main.jsp)에서 발생하는 AJAX 요청만 활동으로 기록 (마우스 이동/클릭만으로는 갱신되지 않음) */
|
||||
$(document).ajaxSend(markActivity);
|
||||
/* 상위 프레임(main.jsp) 활동 감지 */
|
||||
['mousemove', 'keydown', 'click', 'touchstart'].forEach(function (e) {
|
||||
document.addEventListener(e, markActivity, true);
|
||||
});
|
||||
|
||||
/* 주기적 유휴 시간 체크 */
|
||||
setInterval(function () {
|
||||
|
||||
@@ -9,8 +9,6 @@
|
||||
<%@page import="com.eactive.eai.rms.common.util.StringUtils" %>
|
||||
<%@page import="com.eactive.eai.rms.common.datasource.DataSourceType" %>
|
||||
<%@page import="com.eactive.eai.rms.common.datasource.DataSourceTypeManager" %>
|
||||
<%@page import="com.eactive.eai.common.util.ApplicationContextProvider" %>
|
||||
<%@page import="com.eactive.eai.rms.data.entity.man.role.RoleMenuAuthService" %>
|
||||
<%@ 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" %>
|
||||
@@ -18,12 +16,6 @@
|
||||
<%
|
||||
String serviceKey = SessionManager.getServiceTypeKey(request);
|
||||
String serviceText = DataSourceTypeManager.getDataSourceType(serviceKey).getText();
|
||||
|
||||
final String SITEMAP_MENU_ID = "0510013";
|
||||
RoleMenuAuthService roleMenuAuthService = ApplicationContextProvider.getContext().getBean(RoleMenuAuthService.class);
|
||||
String sitemapMenuAuth = roleMenuAuthService.getRoleList(SessionManager.getUserId(request), SITEMAP_MENU_ID, serviceKey);
|
||||
boolean showSitemapLink = "R".equals(sitemapMenuAuth) || "W".equals(sitemapMenuAuth);
|
||||
|
||||
String roleScreenId = (String) request.getAttribute("roleScreenId");
|
||||
String mainPage = (String) request.getAttribute("mainPage");
|
||||
String menuId = (String) request.getAttribute("menuId");
|
||||
@@ -248,12 +240,6 @@
|
||||
|
||||
<link rel="stylesheet" type="text/css" media="screen" href="<c:url value="/css/web_ui.css"/>"/>
|
||||
<link rel="stylesheet" type="text/css" media="screen" href="<c:url value="/css/theme_${themeColor}.css"/>"/>
|
||||
<style>
|
||||
.topmenu_box .sitemap-link { display: inline-block; width: 80px; height: 80px; line-height: 80px; box-sizing: border-box; font-size: 14px; color: #666; text-align: center; opacity: 0.7; transition: all 0.3s; margin-top: 0px; }
|
||||
.topmenu_box .sitemap-link:hover { opacity: 1; }
|
||||
.topmenu_box .logout-link { line-height: 80px; margin-top:0; }
|
||||
.topmenu_box > ul li:last-child > a:before { display: none; }
|
||||
</style>
|
||||
<script language="javascript" src="<c:url value="/js/jquery-1.12.1.min.js"/>"></script>
|
||||
<script language="javascript" src="<c:url value="/js/prefixfree.min.js"/>"></script>
|
||||
<script language="javascript" src="<c:url value="/js/jquery.cookie.js"/>"></script>
|
||||
@@ -317,11 +303,11 @@
|
||||
}
|
||||
|
||||
//왼쪽메뉴, 메인 페이지 이동
|
||||
function goPage2(page1, page2, page2_id) {
|
||||
function goPage2(page1, page2, page2_id) {
|
||||
parent.leftFrame.location.href = goNavUrl(page1);
|
||||
parent.mainFrame.location.href = goNavUrl(page2);
|
||||
}
|
||||
|
||||
|
||||
var sessionAjax = createAjaxRequest();
|
||||
|
||||
function getAjaxData() {
|
||||
@@ -543,7 +529,6 @@
|
||||
console.log("selectLocaleValue : " + selectLocaleValue);
|
||||
parent.changeLocale($(this).val());
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
$(function() {
|
||||
@@ -555,35 +540,7 @@
|
||||
<% session.setAttribute("dualLogin", "N"); %>
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
// 세션(자동로그아웃) 타임아웃 카운트다운 - main.jsp의 유휴시간 판단과 동일한 sessionStorage 값을 공유한다.
|
||||
(function () {
|
||||
var IDLE_MS = ${autoLogoutTimeout} * 60 * 1000;
|
||||
|
||||
function getLastActivity() {
|
||||
var t = sessionStorage.getItem('lastActivity');
|
||||
return t ? parseInt(t, 10) : Date.now();
|
||||
}
|
||||
|
||||
function formatRemain(ms) {
|
||||
if (ms < 0) ms = 0;
|
||||
var totalSec = Math.floor(ms / 1000);
|
||||
var mm = Math.floor(totalSec / 60);
|
||||
var ss = totalSec % 60;
|
||||
return (mm < 10 ? '0' + mm : mm) + ':' + (ss < 10 ? '0' + ss : ss);
|
||||
}
|
||||
|
||||
function updateSessionTimeoutTimer() {
|
||||
var remain = IDLE_MS - (Date.now() - getLastActivity());
|
||||
$('#sessionTimeoutTimer').text('세션 만료까지 ' + formatRemain(remain));
|
||||
}
|
||||
|
||||
$(function () {
|
||||
updateSessionTimeoutTimer();
|
||||
setInterval(updateSessionTimeoutTimer, 1000);
|
||||
});
|
||||
})();
|
||||
});
|
||||
|
||||
</script>
|
||||
</head>
|
||||
@@ -594,29 +551,21 @@
|
||||
</h1><!-- /monitoring/images/top_logo.png -->
|
||||
<div class="topmenu_box">
|
||||
<ul>
|
||||
<li style="width:240px;">
|
||||
<a style="width:240px; cursor: default"
|
||||
href="#"><span><%=SessionManager.getUserName(request) %>(<%=SessionManager.getUserId(request) %>)</span>
|
||||
<li style="width:240px;" id="newDashboardShow">
|
||||
<a style="width:240px;"
|
||||
href="#"><span><%=SessionManager.getUserName(request) %>(<%=SessionManager.getUserId(request) %>-<%=System.getProperty("inst.Name") %>)</span>
|
||||
<span onClick="javascript:openColorPopup();"><%=localeMessage.getString("screen.customer") %></span>
|
||||
<% if (!"".equals(LastLoginYms.trim())) { %>
|
||||
<span style="display:block;font-size:10px;"><%=localeMessage.getString("screen.lastLogin") %> : <%=LastLoginYms%></span>
|
||||
<span style="display:block;font-size:10px;"><%=localeMessage.getString("screen.lastLogin") %> : <%=LastLoginYms%> [ <%=LastLoginIp%> ]</span>
|
||||
<% } %>
|
||||
<span id="sessionTimeoutTimer" style="display:block;font-size:10px;"></span>
|
||||
</a></li>
|
||||
<li style="width:100px; line-height:80px; text-align:center;">
|
||||
<select id="selectLocale" name="locale" style="width:60px; padding: 2px; text-align:center;">
|
||||
<li>
|
||||
<select id="selectLocale" name="locale" style="margin-top: 20px; width:80px">
|
||||
<option value="en">English</option>
|
||||
<option value="ko">한글</option>
|
||||
</select>
|
||||
</li>
|
||||
<% if (showSitemapLink) { %>
|
||||
<li>
|
||||
<a href="<c:url value="/common/acl/sitemap/sitemapMan.view"><c:param name="menuId" value="<%=SITEMAP_MENU_ID%>"/><c:param name="serviceType" value="<%=serviceKey%>"/></c:url>" target="mainFrame" class="sitemap-link">
|
||||
<%= localeMessage.getString("screen.sitemap") %>
|
||||
</a>
|
||||
</li>
|
||||
<% } %>
|
||||
<li onclick="logout()"><a href="#" class="logout-link"><img src="<c:url value="/img/icon_logout.png"/>"
|
||||
<li onclick="logout()"><a href="#" class=""><img src="<c:url value="/img/icon_logout.png"/>"
|
||||
alt=""/><%= localeMessage.getString("screen.logout") %>
|
||||
</a></li>
|
||||
</ul>
|
||||
|
||||
@@ -179,8 +179,8 @@
|
||||
autowidth: true,
|
||||
viewrecords : true,
|
||||
gridview : true,
|
||||
ondblClickRow : function(rowId) {
|
||||
/* console.log("rowId", rowId);
|
||||
ondblClickRow : function(rowId) {
|
||||
console.log("rowId", rowId);
|
||||
var rowData = $(this)
|
||||
.getRowData(rowId);
|
||||
var key = rowData['EaiSevrInstncName'];
|
||||
@@ -193,7 +193,7 @@
|
||||
url2 += "&searchAdptrBzwkGroupName="
|
||||
+ key2;
|
||||
url2 += '&menuId='+'${param.menuId}';
|
||||
goNav(url2); */
|
||||
goNav(url2);
|
||||
},
|
||||
loadComplete : function(d) {
|
||||
$("button[name*=img]").unbind("click");
|
||||
|
||||
@@ -82,10 +82,8 @@
|
||||
apiDesc: api.eaiSvcDesc
|
||||
};
|
||||
});
|
||||
// addJSONData는 datatype:"local" 그리드의 p.data를 채우지 않아, 헤더 클릭 정렬 시 p.data(빈 배열) 기준으로 다시 그려지며 행이 사라짐.
|
||||
// setGridParam({data:...}).trigger('reloadGrid')로 채워야 정렬이 정상 동작함.
|
||||
$("#selectedApiGrid").jqGrid("clearGridData");
|
||||
$("#selectedApiGrid").jqGrid('setGridParam', {data: gridData}).trigger('reloadGrid');
|
||||
$("#selectedApiGrid")[0].addJSONData(gridData);
|
||||
|
||||
var data = json;
|
||||
$("#btn_gen_id").hide();
|
||||
@@ -135,14 +133,15 @@
|
||||
var selectedApiGrid = $("#selectedApiGrid");
|
||||
selectedApiGrid.jqGrid({
|
||||
datatype: "local",
|
||||
rownumbers: true, // 가상 인덱스
|
||||
gridview: true, // 성능 향상
|
||||
rownumbers: true, // 가상 인덱스
|
||||
rownumWidth: 40,
|
||||
colNames: ['API ID', 'API 이름'],
|
||||
colModel: [
|
||||
{name: 'apiId', index: 'apiId', width: 100, align: 'center'},
|
||||
{name: 'apiDesc', index: 'apiDesc', width: 100, align: 'center'}
|
||||
],
|
||||
rowNum: 9999,
|
||||
rowNum: -1,
|
||||
// rowList: [10, 20, 30],
|
||||
// pager: '#selectedApiGridPager',
|
||||
// sortname: 'apiId',
|
||||
@@ -157,8 +156,8 @@
|
||||
}
|
||||
});
|
||||
|
||||
selectedApiGrid.jqGrid('setLabel', 'rn', 'No'); // 가상 인덱스 ColNames
|
||||
|
||||
selectedApiGrid.jqGrid('setLabel', 'rn', 'No'); // 가상 인덱스 ColNames
|
||||
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: url,
|
||||
@@ -190,8 +189,6 @@
|
||||
var key = "${param.clientId}";
|
||||
if (key != "" && key != "null") {
|
||||
isDetail = true;
|
||||
} else {
|
||||
$('input[name="grantTypes_"]').prop('checked', true);
|
||||
}
|
||||
init(key, detail);
|
||||
|
||||
@@ -308,8 +305,7 @@
|
||||
dataType: "json",
|
||||
data: {
|
||||
cmd: 'SYNC_PORTAL',
|
||||
clientId: clientId,
|
||||
serviceType: 'APIGW'
|
||||
clientId: clientId
|
||||
},
|
||||
success: function(response) {
|
||||
showSyncResultModal(response);
|
||||
@@ -509,17 +505,18 @@
|
||||
<td><input type="text" name="scope"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><%= localeMessage.getString("clntManDtl.grntTp") %> <small>(*)</small></th>
|
||||
<th><%= localeMessage.getString("clntManDtl.grntTp") %>
|
||||
(<%= localeMessage.getString("clntManDtl.sprt") %> ,) <small>(*)</small></th>
|
||||
<td>
|
||||
<input type="checkbox" name="grantTypes_" value="client_credentials"> client_credentials
|
||||
<%-- <input type="checkbox" name="grantTypes_" value="authorization_code"> authorization_code--%>
|
||||
<%-- <input type="checkbox" name="grantTypes_" value="password"> password--%>
|
||||
<!-- <input type="checkbox" name="grantTypes_" value="refresh_token"> refresh_token -->
|
||||
<input type="checkbox" name="grantTypes_" value="refresh_token"> refresh_token
|
||||
</td>
|
||||
<input type="hidden" name="grantTypes"/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr style="display: none;">
|
||||
<tr>
|
||||
<th>REDIRECT URI</th>
|
||||
<td><input type="text" name="redirectUri"/></td>
|
||||
</tr>
|
||||
@@ -540,7 +537,7 @@
|
||||
</th>
|
||||
<td><input type="text" name="accessTokenValiditySeconds"/></td>
|
||||
</tr>
|
||||
<tr style="display: none;">
|
||||
<tr>
|
||||
<th><%= localeMessage.getString("clntManDtl.rfrshTknExprtnDt") %>
|
||||
</th>
|
||||
<td><input type="text" name="refreshTokenValiditySeconds"/></td>
|
||||
@@ -557,7 +554,7 @@
|
||||
</th>
|
||||
<td><input type="text" name="allowedIps"/></td>
|
||||
</tr>
|
||||
<tr style="display: none;">
|
||||
<tr>
|
||||
<th>Security Key</th>
|
||||
<td><textarea name="securityKey" rows="3" style="width:100%;"></textarea></td>
|
||||
</tr>
|
||||
@@ -742,7 +739,7 @@ function showSyncResultModal(response) {
|
||||
if (response.success) {
|
||||
statusClass = "status-success";
|
||||
var createdCount = response.createdApiSpecCount || 0;
|
||||
var createdMsg = createdCount > 0 ? "<br/><span style='color:#007bff;'>* " + createdCount + "개의 API Spec을 비공개(N)로 자동 생성했습니다. API Spec 관리에서 확인 후 공개하세요.</span>" : "";
|
||||
var createdMsg = createdCount > 0 ? "<br/><span style='color:#007bff;'>* " + createdCount + "개의 API Spec이 자동 생성되었습니다.</span>" : "";
|
||||
|
||||
if (response.action === 'INSERTED') {
|
||||
statusMsg = "<strong>반영 완료</strong> - 포탈에 새로운 인증정보가 생성되었습니다." + createdMsg;
|
||||
@@ -772,7 +769,6 @@ function showSyncResultModal(response) {
|
||||
var syncedApis = response.syncedApis || [];
|
||||
var invalidApis = response.invalidApis || [];
|
||||
var createdApiSpecs = response.createdApiSpecs || [];
|
||||
var specGenFailed = response.specGenFailed || [];
|
||||
|
||||
$targetCount.text(targetApis.length);
|
||||
|
||||
@@ -786,11 +782,8 @@ function showSyncResultModal(response) {
|
||||
if (invalidApis.includes(apiId)) {
|
||||
status = "미존재 (APIGW에 없음)";
|
||||
statusClass = "api-invalid";
|
||||
} else if (specGenFailed.includes(apiId)) {
|
||||
status = "반영됨 (Spec 자동생성 실패·빈 항목)";
|
||||
statusClass = "api-created";
|
||||
} else if (createdApiSpecs.includes(apiId)) {
|
||||
status = "반영됨 (Spec 자동생성·비공개)";
|
||||
status = "반영됨 (Spec 신규생성)";
|
||||
statusClass = "api-created";
|
||||
} else if (syncedApis.includes(apiId)) {
|
||||
status = "반영됨";
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
var url = '<c:url value="/onl/admin/authserver/scopeMan.json" />';
|
||||
var url_view = '<c:url value="/onl/admin/authserver/scopeMan.view" />';
|
||||
var api_url_view = '<c:url value="/onl/apim/apigroup/apiGroupMan.view" />';
|
||||
var mapping_url = '<c:url value="/onl/admin/authserver/apiScopeMan.json" />';
|
||||
|
||||
var isDetail = false;
|
||||
function isValid(){
|
||||
@@ -64,9 +65,9 @@ function mappingInfo(key){
|
||||
|
||||
$.ajax({
|
||||
type : "POST",
|
||||
url: url,
|
||||
url: mapping_url,
|
||||
dataType: "json",
|
||||
data: {cmd: 'API_LIST', searchScopeId : key},
|
||||
data: {cmd: 'LIST', searchScopeId : key},
|
||||
success: function(json){
|
||||
var mappingData = json.rows;
|
||||
|
||||
@@ -130,6 +131,45 @@ function init(key, callback){
|
||||
}
|
||||
}
|
||||
|
||||
function saveApiGridData(scopeId, returnUrl) {
|
||||
var postData = [];
|
||||
var rows = $("#selectedApiGrid").jqGrid('getRowData');
|
||||
|
||||
$.each(rows, function(index, item) {
|
||||
item.scopeId = scopeId;
|
||||
});
|
||||
|
||||
postData.push({
|
||||
name: "apiList",
|
||||
value: JSON.stringify(rows)
|
||||
});
|
||||
|
||||
postData.push({ name: "cmd" , value:"INSERT_APILIST" });
|
||||
postData.push({ name: "scopeId" , value:scopeId });
|
||||
|
||||
$.ajax({
|
||||
type : "POST",
|
||||
url:mapping_url,
|
||||
data: postData,
|
||||
beforeSend: function() {
|
||||
$("[id^='btn_']").prop("disabled", true);
|
||||
$("#btn_modify").text("처리중 . . . . .");
|
||||
},
|
||||
success:function(args){
|
||||
alert("저장 되었습니다.");
|
||||
|
||||
goNav(returnUrl);//LIST로 이동
|
||||
},
|
||||
error:function(e){
|
||||
alert(e.responseText);
|
||||
},
|
||||
complete: function() {
|
||||
$("[id^='btn_']").prop("disabled", false);
|
||||
$("#btn_modify").text("수정");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
var returnUrl = getReturnUrlForReturn();
|
||||
var key ="${param.scopeId}";
|
||||
@@ -142,39 +182,28 @@ $(document).ready(function() {
|
||||
if (!isValid()){
|
||||
return;
|
||||
}
|
||||
var scopeId = $('input[name=scopeId]').val();
|
||||
var postData = $('#ajaxForm').serializeArray();
|
||||
|
||||
var rows = $("#selectedApiGrid").jqGrid('getRowData');
|
||||
$.each(rows, function(index, item) {
|
||||
item.scopeId = scopeId;
|
||||
});
|
||||
postData.push({ name: "apiList" , value: JSON.stringify(rows) });
|
||||
|
||||
if (isDetail){
|
||||
postData.push({ name: "cmd" , value:"UPDATE"});
|
||||
}else{
|
||||
postData.push({ name: "cmd" , value:"INSERT"});
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
type : "POST",
|
||||
url:url,
|
||||
data:postData,
|
||||
beforeSend: function() {
|
||||
$("[id^='btn_']").prop("disabled", true);
|
||||
$("#btn_modify").text("처리중 . . . . .");
|
||||
},
|
||||
success:function(args){
|
||||
alert("저장 되었습니다.");
|
||||
goNav(returnUrl);//LIST로 이동
|
||||
alert("SCOPE 정보가 저장 되었습니다. 이어서 API 리스트를 저장합니다.");
|
||||
|
||||
if(!isDetail) { // INSERT 일 경우 ScopeId를 업데이트
|
||||
key = $('input[name=scopeId]').val();
|
||||
}
|
||||
saveApiGridData(key, returnUrl); // API LIST 저장.
|
||||
|
||||
|
||||
},
|
||||
error:function(e){
|
||||
alert(e.responseText);
|
||||
},
|
||||
complete: function() {
|
||||
$("[id^='btn_']").prop("disabled", false);
|
||||
$("#btn_modify").text("수정");
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -25,7 +25,7 @@ $(document).ready(function() {
|
||||
datatype:"json",
|
||||
mtype: 'POST',
|
||||
url: url,
|
||||
postData : { cmd : 'LIST', searchName: $('input[name=searchName]').val(), searchUseYn: $('select[name=searchUseYn]').val()},
|
||||
postData : { cmd : 'LIST', searchName: $('input[name=searchName]').val()},
|
||||
colNames:['<%= localeMessage.getString("infAdpConMan.adaNm") %>',
|
||||
'<%= localeMessage.getString("infAdpConMan.adaDes") %>',
|
||||
'<%= localeMessage.getString("infAdpConMan.thrPerSecond") %>',
|
||||
@@ -62,7 +62,6 @@ $(document).ready(function() {
|
||||
url2 += '&menuId='+'${param.menuId}';
|
||||
//검색값
|
||||
url2 += '&searchName='+$("input[name=searchName]").val();
|
||||
url2 += '&searchUseYn='+$("select[name=searchUseYn]").val();
|
||||
//key값
|
||||
url2 += '&name='+name;
|
||||
goNav(url2);
|
||||
@@ -81,7 +80,7 @@ $(document).ready(function() {
|
||||
resizeJqGridWidth('grid','content_middle','1000');
|
||||
|
||||
$("#btn_search").click(function(){
|
||||
$("#grid").setGridParam({ postData: { searchName: $('input[name=searchName]').val(), searchUseYn: $('select[name=searchUseYn]').val()}, page:1 }).trigger("reloadGrid");
|
||||
$("#grid").setGridParam({ postData: { searchName: $('input[name=searchName]').val()}, page:1 }).trigger("reloadGrid");
|
||||
});
|
||||
$("#btn_new").click(function(){
|
||||
var url2 = url_view;
|
||||
@@ -127,16 +126,6 @@ $(document).ready(function() {
|
||||
<tr>
|
||||
<th style="width:180px;"><%= localeMessage.getString("infAdpConMan.adaNm") %></th>
|
||||
<td><input type="text" name="searchName" value="${param.searchName}"></td>
|
||||
<th style="width:180px;"><%= localeMessage.getString("infAdpConMan.useYn") %></th>
|
||||
<td>
|
||||
<div class="select-style">
|
||||
<select name="searchUseYn">
|
||||
<option value="">전체</option>
|
||||
<option value="1">사용함</option>
|
||||
<option value="0">사용안함</option>
|
||||
</select>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@@ -25,7 +25,7 @@ $(document).ready(function() {
|
||||
datatype:"json",
|
||||
mtype: 'POST',
|
||||
url: url,
|
||||
postData : { cmd : 'LIST', searchName: $('input[name=searchName]').val(), searchUseYn: $('select[name=searchUseYn]').val()},
|
||||
postData : { cmd : 'LIST', searchName: $('input[name=searchName]').val()},
|
||||
colNames:['클라이언트 ID',
|
||||
'클라이언트명',
|
||||
'<%= localeMessage.getString("infAdpConMan.thrPerSecond") %>',
|
||||
@@ -62,7 +62,6 @@ $(document).ready(function() {
|
||||
url2 += '&menuId='+'${param.menuId}';
|
||||
//검색값
|
||||
url2 += '&searchName='+$("input[name=searchName]").val();
|
||||
url2 += '&searchUseYn='+$("select[name=searchUseYn]").val();
|
||||
//key값
|
||||
url2 += '&name='+name;
|
||||
goNav(url2);
|
||||
@@ -81,7 +80,7 @@ $(document).ready(function() {
|
||||
resizeJqGridWidth('grid','content_middle','1000');
|
||||
|
||||
$("#btn_search").click(function(){
|
||||
$("#grid").setGridParam({ postData: { searchName: $('input[name=searchName]').val(), searchUseYn: $('select[name=searchUseYn]').val()}, page:1 }).trigger("reloadGrid");
|
||||
$("#grid").setGridParam({ postData: { searchName: $('input[name=searchName]').val()}, page:1 }).trigger("reloadGrid");
|
||||
});
|
||||
$("#btn_new").click(function(){
|
||||
var url2 = url_view;
|
||||
@@ -91,8 +90,7 @@ $(document).ready(function() {
|
||||
url2 += '&menuId='+'${param.menuId}';
|
||||
//검색값
|
||||
url2 += '&searchName=';
|
||||
url2 += '&searchUseYn='+$('select[name=searchUseYn]').val();
|
||||
|
||||
|
||||
goNav(url2);
|
||||
});
|
||||
|
||||
@@ -126,16 +124,6 @@ $(document).ready(function() {
|
||||
<tr>
|
||||
<th style="width:180px;">클라이언트명</th>
|
||||
<td><input type="text" name="searchName" value="${param.searchName}"></td>
|
||||
<th style="width:180px;">사용여부</th>
|
||||
<td>
|
||||
<div class="select-style">
|
||||
<select name="searchUseYn">
|
||||
<option value="">전체</option>
|
||||
<option value="1">사용함</option>
|
||||
<option value="0">사용안함</option>
|
||||
</select>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@@ -298,7 +298,6 @@ $(document).ready(function() {
|
||||
url: url,
|
||||
data: postData,
|
||||
success: function(args) {
|
||||
console.log('mod', args);
|
||||
showAlert("<%= localeMessage.getString("common.saveMsg") %>", {
|
||||
type: 'success',
|
||||
title: '저장 완료',
|
||||
@@ -494,6 +493,11 @@ $(document).ready(function() {
|
||||
<span class="bucket-summary-value" id="summaryThreshold">0<span class="unit">req</span></span>
|
||||
<span class="bucket-summary-sub" id="summaryThresholdCalc"></span>
|
||||
</div>
|
||||
<div class="bucket-summary-item">
|
||||
<span class="bucket-summary-label">적용 API</span>
|
||||
<span class="bucket-summary-value" id="summaryActiveApi">0<span class="unit">개</span></span>
|
||||
<span class="bucket-summary-sub" id="summaryActiveApiList"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -47,40 +47,27 @@ function groupNameFormat(cellvalue, options, rowObject) {
|
||||
return strVal;
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
$(document).ready(function() {
|
||||
$("input[name=searchStartYYYYMMDD],input[name=searchEndYYYYMMDD]").inputmask("yyyy-mm-dd",{'autoUnmask':true});
|
||||
$("input[name=searchStartTime],input[name=searchEndTime]").inputmask("hh:mm:ss",{'autoUnmask':true});
|
||||
|
||||
|
||||
$("input[name=searchStartYYYYMMDD]").each(function(){
|
||||
if ($(this).val() == undefined || $(this).val() == null || $(this).val() == ""){
|
||||
$(this).val(getToday());
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
$("input[name=searchEndYYYYMMDD]").each(function(){
|
||||
if ($(this).val() == undefined || $(this).val() == null || $(this).val() == ""){
|
||||
$(this).val(getToday());
|
||||
}
|
||||
});
|
||||
|
||||
$("input[name=searchStartTime]").each(function(){
|
||||
if ($(this).val() == undefined || $(this).val() == null || $(this).val() == ""){
|
||||
$(this).val("000000");
|
||||
}
|
||||
});
|
||||
|
||||
$("input[name=searchEndTime]").each(function(){
|
||||
if ($(this).val() == undefined || $(this).val() == null || $(this).val() == ""){
|
||||
$(this).val("235959");
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
var gridPostData = getSearchForJqgrid("cmd","LIST"); //jqgrid에서는 object 로
|
||||
|
||||
|
||||
|
||||
var gridPostData = getSearchForJqgrid("cmd","LIST"); //jqgrid에서는 object 로
|
||||
|
||||
var start = $("input[name=searchStartYYYYMMDD]").val().replace(/-/gi,"");
|
||||
var end = $("input[name=searchEndYYYYMMDD]").val().replace(/-/gi,"");
|
||||
|
||||
|
||||
gridPostData["searchStartDate"] = start;
|
||||
gridPostData["searchEndDate"] = end;
|
||||
|
||||
@@ -145,20 +132,18 @@ $(document).ready(function() {
|
||||
resizeJqGridWidth('grid','content_middle','1000');
|
||||
|
||||
$("#btn_search").click(function(){
|
||||
var postData = getSearchForJqgrid("cmd","LIST"); //jqgrid에서는 object 로
|
||||
|
||||
var postData = getSearchForJqgrid("cmd","LIST"); //jqgrid에서는 object 로
|
||||
|
||||
var start = $("input[name=searchStartYYYYMMDD]").val().replace(/-/gi,"");
|
||||
var end = $("input[name=searchEndYYYYMMDD]").val().replace(/-/gi,"");
|
||||
var startTime = $("input[name=searchStartTime]").val();
|
||||
var endTime = $("input[name=searchEndTime]").val();
|
||||
|
||||
if((start + startTime) > (end + endTime)){
|
||||
|
||||
if(start > end){
|
||||
alert("조회기간을 확인해주세요.");
|
||||
return false;
|
||||
}
|
||||
$("input[name=searchStartDate]").val(start);
|
||||
$("input[name=searchEndDate]").val(end);
|
||||
|
||||
|
||||
postData["searchStartDate"] = start;
|
||||
postData["searchEndDate"] = end;
|
||||
|
||||
@@ -212,24 +197,14 @@ $(document).ready(function() {
|
||||
<tbody>
|
||||
<tr>
|
||||
<th style="width:180px;"><%= localeMessage.getString("infConHstMan.date") %></th>
|
||||
<td colspan="5">
|
||||
<td>
|
||||
<input type="text" name="searchStartYYYYMMDD" id="startDatepicker" readonly="readonly" value="" size="10" style="width:80px; border:1px solid #ebebec;">
|
||||
<input type="text" name="searchStartTime" value="" size="8" style="width:70px; border:1px solid #ebebec;">
|
||||
~
|
||||
~
|
||||
<input type="text" name="searchEndYYYYMMDD" value="" id="endDatepicker" size="10" readonly="readonly" style="width:80px; border:1px solid #ebebec;">
|
||||
<input type="text" name="searchEndTime" value="" size="8" style="width:70px; border:1px solid #ebebec;">
|
||||
<input type="hidden" name="searchStartDate" value="" style="width:0px;">
|
||||
<input type="hidden" name="searchEndDate" value="" style="width:0px;">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th style="width:180px;"><%= localeMessage.getString("infConHstMan.instNm") %></th>
|
||||
<td><input type="text" name="searchInstanceName" value="${param.searchInstanceName}"></td>
|
||||
<th style="width:180px;"><%= localeMessage.getString("infConHstMan.eaiSvcCd") %></th>
|
||||
<td><input type="text" name="searchApiName" value="${param.searchApiName}"></td>
|
||||
<th style="width:180px;"><%= localeMessage.getString("infConHstMan.adpGrpNm") %></th>
|
||||
<td><input type="text" name="searchAdapterGroupName" value="${param.searchAdapterGroupName}"></td>
|
||||
</tr>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</form>
|
||||
|
||||
@@ -88,7 +88,7 @@ $(document).ready(function() {
|
||||
datatype:"json",
|
||||
mtype: 'POST',
|
||||
url: url,
|
||||
postData : { cmd : 'LIST', searchGroupName: $('input[name=searchGroupName]').val(), searchUseYn: $('select[name=searchUseYn]').val()},
|
||||
postData : { cmd : 'LIST', searchGroupName: $('input[name=searchGroupName]').val()},
|
||||
colNames:['그룹 ID',
|
||||
'그룹명',
|
||||
'초당 임계치',
|
||||
@@ -125,7 +125,6 @@ $(document).ready(function() {
|
||||
url2 += '&menuId='+'${param.menuId}';
|
||||
//검색값
|
||||
url2 += '&searchGroupName='+$("input[name=searchGroupName]").val();
|
||||
url2 += '&searchUseYn='+$("select[name=searchUseYn]").val();
|
||||
//key값
|
||||
url2 += '&groupId='+groupId;
|
||||
goNav(url2);
|
||||
@@ -144,7 +143,7 @@ $(document).ready(function() {
|
||||
resizeJqGridWidth('grid','content_middle','1000');
|
||||
|
||||
$("#btn_search").click(function(){
|
||||
$("#grid").setGridParam({ postData: { searchGroupName: $('input[name=searchGroupName]').val(), searchUseYn: $('select[name=searchUseYn]').val()}, page:1 }).trigger("reloadGrid");
|
||||
$("#grid").setGridParam({ postData: { searchGroupName: $('input[name=searchGroupName]').val()}, page:1 }).trigger("reloadGrid");
|
||||
});
|
||||
$("#btn_new").click(function(){
|
||||
var url2 = url_view;
|
||||
@@ -189,16 +188,6 @@ $(document).ready(function() {
|
||||
<tr>
|
||||
<th style="width:180px;">그룹명</th>
|
||||
<td><input type="text" name="searchGroupName" value="${param.searchGroupName}"></td>
|
||||
<th style="width:180px;">사용여부</th>
|
||||
<td>
|
||||
<div class="select-style">
|
||||
<select name="searchUseYn">
|
||||
<option value="">전체</option>
|
||||
<option value="1">사용함</option>
|
||||
<option value="0">사용안함</option>
|
||||
</select>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@@ -25,7 +25,7 @@ $(document).ready(function() {
|
||||
datatype:"json",
|
||||
mtype: 'POST',
|
||||
url: url,
|
||||
postData : { cmd : 'LIST', searchName: $('input[name=searchName]').val(), searchUseYn: $('select[name=searchUseYn]').val()},
|
||||
postData : { cmd : 'LIST', searchName: $('input[name=searchName]').val()},
|
||||
colNames:['<%= localeMessage.getString("infIfConMan.ifNm") %>',
|
||||
'<%= localeMessage.getString("infIfConMan.ifDesc") %>',
|
||||
'<%= localeMessage.getString("infAdpConMan.thrPerSecond") %>',
|
||||
@@ -62,7 +62,6 @@ $(document).ready(function() {
|
||||
url2 += '&menuId='+'${param.menuId}';
|
||||
//검색값
|
||||
url2 += '&searchName='+$("input[name=searchName]").val();
|
||||
url2 += '&searchUseYn='+$("select[name=searchUseYn]").val();
|
||||
//key값
|
||||
url2 += '&name='+name;
|
||||
goNav(url2);
|
||||
@@ -81,7 +80,7 @@ $(document).ready(function() {
|
||||
resizeJqGridWidth('grid','content_middle','1000');
|
||||
|
||||
$("#btn_search").click(function(){
|
||||
$("#grid").setGridParam({ postData: { searchName: $('input[name=searchName]').val(), searchUseYn: $('select[name=searchUseYn]').val()}, page:1 }).trigger("reloadGrid");
|
||||
$("#grid").setGridParam({ postData: { searchName: $('input[name=searchName]').val()}, page:1 }).trigger("reloadGrid");
|
||||
});
|
||||
$("#btn_new").click(function(){
|
||||
var url2 = url_view;
|
||||
@@ -127,16 +126,6 @@ $(document).ready(function() {
|
||||
<tr>
|
||||
<th style="width:180px;"><%= localeMessage.getString("infIfConMan.ifNm") %></th>
|
||||
<td><input type="text" name="searchName" value="${param.searchName}"></td>
|
||||
<th style="width:180px;"><%= localeMessage.getString("infAdpConMan.useYn") %></th>
|
||||
<td>
|
||||
<div class="select-style">
|
||||
<select name="searchUseYn">
|
||||
<option value="">전체</option>
|
||||
<option value="1">사용함</option>
|
||||
<option value="0">사용안함</option>
|
||||
</select>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@@ -71,18 +71,18 @@
|
||||
|
||||
$('#grid').children().remove();
|
||||
|
||||
const itemTypes = ['Field', 'Grid', 'Group', 'Attr', 'Array'];
|
||||
const itemTypes = ['Field', 'Grid', 'Group', 'Attr'];
|
||||
|
||||
columns = [ // for columnData prop
|
||||
{ title: '<%= localeMessage.getString("layoutMan.itemDesc") %>', name: 'loutItemDesc', type: 'text', width:280, resize:true, minWidth:120 },
|
||||
{ title: '<%= localeMessage.getString("layoutMan.itmeEn") %>', name: 'loutItemName', type: 'text', width:280, resize:true, minWidth:120 },
|
||||
{ title: '<%=localeMessage.getString("standardLayout.itemLevel")%>', name: 'loutItemDepth', type: 'numeric', width:50 },
|
||||
{ title: '<%= localeMessage.getString("layoutMan.itemDesc") %>', name: 'loutItemDesc', type: 'text', width:280, resize:true, minWidth:120 },
|
||||
{ title: '<%=localeMessage.getString("standardLayout.itemLevel")%>', name: 'loutItemDepth', type: 'text', width:50 },
|
||||
{ title: '<%= localeMessage.getString("standardLayout.itemType") %>', name: 'loutItemType', type: 'autocomplete', source: itemTypes, width:70 },
|
||||
{ title: '<%= localeMessage.getString("standardLayout.arraySize") %>', name: 'loutItemOccCnt', type: 'text', width:90 },
|
||||
{ title: '<%= localeMessage.getString("standardLayout.refField") %>', name: 'loutItemOccRef', type: 'text', width:90 },
|
||||
{ title: '<%=localeMessage.getString("layoutMan.dataType")%>', name: 'loutItemDataType', type: 'autocomplete', source: basicDataTypes, width:160 },
|
||||
{ title: '<%=localeMessage.getString("layoutMan.dataLen")%>', name: 'loutItemLength', type: 'numeric', width: 70, align: 'center' },
|
||||
{ title: '<%=localeMessage.getString("layoutMan.decpLen")%>', name: 'loutItemDecimal', type: 'numeric', width: 70 },
|
||||
{ title: '<%=localeMessage.getString("layoutMan.decpLen")%>', name: 'loutItemDecimal', type: 'text', width: 70 },
|
||||
{ title: '<%=localeMessage.getString("layoutMan.defaultVal")%>', name: 'loutItemDefault', type: 'text', width: 70 },
|
||||
{ title: '<%=localeMessage.getString("layoutMan.mask")%>', name: 'loutItemMaskYn', type: 'checkbox', width: 70 },
|
||||
{ title: '<%=localeMessage.getString("layoutMan.moffset")%>', name: 'loutItemMaskOffset', type: 'text', width: 50 },
|
||||
@@ -182,41 +182,6 @@
|
||||
$('#dataTotalLen').text(totalLen);
|
||||
}
|
||||
|
||||
function focusGridCell(colIndex, rowIndex) {
|
||||
var cellName = jexcel.getColumnNameFromId([colIndex, rowIndex]);
|
||||
var cell = grid.getCell(cellName);
|
||||
grid.updateSelection(cell);
|
||||
cell.scrollIntoView({block: 'center', inline: 'center'});
|
||||
grid.openEditor(cell, false);
|
||||
}
|
||||
|
||||
var INTEGER_REGEXP = /^(0|[1-9][0-9]*)$/;
|
||||
|
||||
function validateNumberColumns() {
|
||||
var data = grid.getData();
|
||||
var gridColumns = grid.options.columns;
|
||||
|
||||
for (var col = 0; col < gridColumns.length; col++) {
|
||||
if (gridColumns[col].type !== 'numeric') {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (var row = 0; row < data.length; row++) {
|
||||
var value = data[row][col];
|
||||
if (value == null) {
|
||||
continue;
|
||||
}
|
||||
value = $.trim(String(value));
|
||||
if (value !== '' && !INTEGER_REGEXP.test(value)) {
|
||||
alert("숫자만 입력할 수 있습니다.");
|
||||
focusGridCell(col, row);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function detail(key) {
|
||||
if (!isDetail) {
|
||||
return
|
||||
@@ -356,10 +321,6 @@
|
||||
});
|
||||
|
||||
$("#btn_modify").click(function(){
|
||||
if (!validateNumberColumns()) {
|
||||
return;
|
||||
}
|
||||
|
||||
var postData = $('#ajaxForm').serializeArray();
|
||||
if (isDetail){
|
||||
postData.push({ name: "cmd" , value:"UPDATE"});
|
||||
|
||||
@@ -1,173 +0,0 @@
|
||||
<%@ 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"/>
|
||||
<script language="javascript">
|
||||
var url = '<c:url value="/onl/admin/rule/layoutSyncHistoryMan.json" />';
|
||||
var url_view = '<c:url value="/onl/admin/rule/layoutSyncHistoryMan.view" />';
|
||||
|
||||
function formatPrcssRslt(cellvalue, options, rowObject) {
|
||||
if (cellvalue === 'S') return '<span style="color:#1a73e8;font-weight:bold;">성공</span>';
|
||||
if (cellvalue === 'F') return '<span style="color:#d93025;font-weight:bold;">실패</span>';
|
||||
return cellvalue || '';
|
||||
}
|
||||
|
||||
function init() {
|
||||
$("#startDatepicker").datepicker().inputmask("yyyy-mm-dd", {'autoUnmask': true});
|
||||
$("#endDatepicker").datepicker().inputmask("yyyy-mm-dd", {'autoUnmask': true});
|
||||
|
||||
var today = getToday();
|
||||
|
||||
$("input[name=searchStartYYYYMMDD]").val(today.substring(0, 6) + "01");
|
||||
$("input[name=searchEndYYYYMMDD]").val(today);
|
||||
$("input[name=searchStartDate]").val(today.substring(0, 6) + "01");
|
||||
$("input[name=searchEndDate]").val(today);
|
||||
|
||||
// 뒤로가기 등으로 이전 검색조건 파라미터가 있으면 기본값을 덮어씀
|
||||
putSelectFromParam();
|
||||
|
||||
buildGrid();
|
||||
}
|
||||
|
||||
function buildGrid() {
|
||||
$('#grid').jqGrid({
|
||||
datatype: "json",
|
||||
mtype: 'POST',
|
||||
url: url,
|
||||
postData: getSearchForJqgrid("cmd", "LIST"),
|
||||
colNames: ['No', '서비스명', 'Command', '연동구분명', '연동시간', '결과', '결과메세지'],
|
||||
colModel: [
|
||||
{name: 'seqno', align: 'center', width: 50, sortable: false},
|
||||
{name: 'serviceName', align: 'center', width: 100, sortable: false},
|
||||
{name: 'command', align: 'center', width: 80, sortable: false},
|
||||
{name: 'loutName', align: 'left', width: 180, sortable: false},
|
||||
{name: 'recvAmndHms', align: 'center', width: 140, sortable: false},
|
||||
{name: 'prcssRslt', align: 'center', width: 60, sortable: false, formatter: formatPrcssRslt},
|
||||
{name: 'prcssRsltCmnt', align: 'left', width: 300, sortable: false}
|
||||
],
|
||||
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 seqno = rowData['seqno'];
|
||||
var url2 = url_view + '?cmd=DETAIL';
|
||||
url2 += '&page=' + $(this).getGridParam("page");
|
||||
url2 += '&returnUrl=' + getReturnUrl();
|
||||
url2 += '&menuId=' + '${param.menuId}';
|
||||
url2 += '&seqno=' + seqno;
|
||||
url2 += '&' + getSearchUrl();
|
||||
goNav(url2);
|
||||
},
|
||||
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 start = $("input[name=searchStartYYYYMMDD]").val().replace(/-/gi, "");
|
||||
var end = $("input[name=searchEndYYYYMMDD]").val().replace(/-/gi, "");
|
||||
|
||||
if (start && end && start > end) {
|
||||
alert("조회기간 종료일은 시작일보다 커야합니다.");
|
||||
$("input[name=searchEndYYYYMMDD]").focus();
|
||||
return false;
|
||||
}
|
||||
$("input[name=searchStartDate]").val(start);
|
||||
$("input[name=searchEndDate]").val(end);
|
||||
|
||||
var postData = getSearchForJqgrid("cmd", "LIST");
|
||||
$("#grid").setGridParam({url: url, postData: postData, page: 1}).trigger("reloadGrid");
|
||||
});
|
||||
|
||||
$("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">IIM 레이아웃 연동 이력과 eCams 인터페이스 배포 이력을 조회합니다.</span></div>
|
||||
<form id="ajaxForm" onsubmit="return false;">
|
||||
<table class="search_condition" cellspacing=0;>
|
||||
<colgroup>
|
||||
<col style="width:120px;"><col style="width:260px;">
|
||||
<col style="width:120px;"><col style="width:260px;">
|
||||
<col style="width:120px;"><col style="width:260px;">
|
||||
</colgroup>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>연동시간</th>
|
||||
<td>
|
||||
<input type="text" name="searchStartYYYYMMDD" id="startDatepicker" readonly="readonly" value="" size="10" style="width:100px; border:1px solid #ebebec;">
|
||||
~
|
||||
<input type="text" name="searchEndYYYYMMDD" value="" id="endDatepicker" size="10" readonly="readonly" style="width:100px; border:1px solid #ebebec;">
|
||||
<input type="hidden" name="searchStartDate" value="">
|
||||
<input type="hidden" name="searchEndDate" value="">
|
||||
</th>
|
||||
<th>서비스명</th>
|
||||
<td><input type="text" name="searchServiceName" autocomplete="off" style="width:95%;"></td>
|
||||
<th>Command</th>
|
||||
<td><input type="text" name="searchCommand" autocomplete="off" style="width:95%;"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>연동구분명</th>
|
||||
<td><input type="text" name="searchLoutName" autocomplete="off" style="width:95%;"></td>
|
||||
<th>결과</th>
|
||||
<td>
|
||||
<div class="select-style" style="display:inline-block; margin-left:6px;">
|
||||
<select name="searchPrcssRslt">
|
||||
<option value="">전체</option>
|
||||
<option value="S">성공</option>
|
||||
<option value="F">실패</option>
|
||||
</select>
|
||||
</div>
|
||||
</td>
|
||||
<th></th>
|
||||
<td></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</form>
|
||||
<table id="grid"></table>
|
||||
<div id="pager"></div>
|
||||
</div><!-- end content_middle -->
|
||||
</div><!-- end right_box -->
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,141 +0,0 @@
|
||||
<%@ 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>
|
||||
.succ-y { color:#1a73e8; font-weight:bold; }
|
||||
.succ-n { color:#d93025; font-weight:bold; }
|
||||
.pre-wrap { white-space:pre-wrap; word-break:break-all; height:500px; overflow: auto; line-height:1.5; }
|
||||
.recvdata-toolbar { text-align:right; margin-top: 5px; margin-bottom:6px; }
|
||||
</style>
|
||||
<script language="javascript">
|
||||
var url = '<c:url value="/onl/admin/rule/layoutSyncHistoryMan.json" />';
|
||||
var key = '${param.seqno}';
|
||||
var rawRecvData = '';
|
||||
var isRecvDataPretty = false;
|
||||
|
||||
function detail() {
|
||||
if (!key) return;
|
||||
$.ajax({
|
||||
type: "POST", url: url, dataType: "json",
|
||||
data: {cmd: 'DETAIL', seqno: key},
|
||||
success: function (data) {
|
||||
$('#seqno').text(data.seqno != null ? data.seqno : '');
|
||||
$('#serviceName').text(data.serviceName || '');
|
||||
$('#command').text(data.command || '');
|
||||
$('#loutName').text(data.loutName || '');
|
||||
$('#recvAmndHms').text(data.recvAmndHms || '');
|
||||
$('#prcssRsltCmnt').text(data.prcssRsltCmnt || '');
|
||||
|
||||
rawRecvData = data.recvData || '';
|
||||
isRecvDataPretty = false;
|
||||
$('#recvData').text(rawRecvData);
|
||||
|
||||
if (data.prcssRslt === 'S') {
|
||||
$('#prcssRslt').html('<span class="succ-y">성공</span>');
|
||||
} else if (data.prcssRslt === 'F') {
|
||||
$('#prcssRslt').html('<span class="succ-n">실패</span>');
|
||||
} else {
|
||||
$('#prcssRslt').text(data.prcssRslt || '');
|
||||
}
|
||||
},
|
||||
error: function (e) { alert(e.responseText); }
|
||||
});
|
||||
}
|
||||
|
||||
// JSON 지수표기(예: 1e+21) 값이 vkbeautify 처리 중 깨지지 않도록 문자열로 감쌌다가 되돌린다.
|
||||
function preserveExponential(jsonString) {
|
||||
var protectedJson = jsonString.replace(/:\s*([+-]?\d+\.?\d*e[+-]?\d+)/gi, ': "$1"');
|
||||
var prettyJson = vkbeautify.json(protectedJson);
|
||||
prettyJson = prettyJson.replace(/:\s*"([+-]?\d+\.?\d*e[+-]?\d+)"/gi, ': $1');
|
||||
return prettyJson;
|
||||
}
|
||||
|
||||
$(document).ready(function () {
|
||||
var returnUrl = getReturnUrlForReturn();
|
||||
|
||||
buttonControl();
|
||||
detail();
|
||||
|
||||
$('#btn_previous').click(function () { goNav(returnUrl); });
|
||||
|
||||
$('#btn_pretty_recvdata').click(function () {
|
||||
if (!rawRecvData) return;
|
||||
if (isRecvDataPretty) {
|
||||
$('#recvData').text(rawRecvData);
|
||||
isRecvDataPretty = false;
|
||||
return;
|
||||
}
|
||||
try {
|
||||
$('#recvData').text(preserveExponential(rawRecvData));
|
||||
isRecvDataPretty = true;
|
||||
} catch (e) {
|
||||
alert('JSON 형식이 아니거나 정렬할 수 없습니다.');
|
||||
}
|
||||
});
|
||||
});
|
||||
</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>No</th>
|
||||
<td><span id="seqno"></span></td>
|
||||
<th>Service Name</th>
|
||||
<td><span id="serviceName"></span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>레이아웃명</th>
|
||||
<td><span id="loutName"></span></td>
|
||||
<th>Command</th>
|
||||
<td><span id="command"></span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>연동시간</th>
|
||||
<td><span id="recvAmndHms"></span></td>
|
||||
<th>결과</th>
|
||||
<td><span id="prcssRslt"></span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>결과메세지</th>
|
||||
<td colspan="3"><span id="prcssRsltCmnt"></span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>수신데이타</th>
|
||||
<td colspan="3">
|
||||
<div class="recvdata-toolbar">
|
||||
<button type="button" class="cssbtn" id="btn_pretty_recvdata"><i class="material-icons">format_align_left</i> 정렬</button>
|
||||
</div>
|
||||
<div id="recvData" class="pre-wrap"></div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,258 +0,0 @@
|
||||
<%@ page language="java" contentType="text/html; charset=utf-8"%>
|
||||
<%@ page import="java.io.*"%>
|
||||
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
|
||||
<%@ 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=euc-kr">
|
||||
<jsp:include page="/jsp/common/include/css.jsp"/>
|
||||
<jsp:include page="/jsp/common/include/script.jsp"/>
|
||||
<script language="javascript" >
|
||||
var url = '<c:url value="/onl/transaction/apim/apiInterfaceMan.json"/>';
|
||||
var url_view = '<c:url value="/onl/transaction/apim/apiInterfaceMan.view"/>';
|
||||
|
||||
var selectName = "searchEaiBzwkDstcd"; // selectBox Name
|
||||
function processSelectedData() {
|
||||
var grid = $("#grid");
|
||||
var selectedRowIds = grid.jqGrid('getGridParam', 'selarrrow');
|
||||
var selectedData = [];
|
||||
|
||||
for(var i = 0; i < selectedRowIds.length; i++) {
|
||||
var rowData = grid.jqGrid('getRowData', selectedRowIds[i]);
|
||||
selectedData.push({
|
||||
bizCode: rowData.eaiBzwkDstcd, // CLINET
|
||||
apiId: rowData.eaiSvcName,
|
||||
apiDesc: rowData.eaiSvcDesc, // SCOPE-API
|
||||
APIFULLPATH: rowData.apiFullPath,
|
||||
BZWKSVCKEYNAME: rowData.bzwksvckeyname
|
||||
});
|
||||
}
|
||||
|
||||
if(selectedData.length === 0) {
|
||||
alert("선택된 항목이 없습니다.");
|
||||
return;
|
||||
}
|
||||
|
||||
alert("선택한 " + selectedData.length + "건의 API를 추가합니다.");
|
||||
window.returnValue = selectedData.length === 1 ? selectedData[0] : selectedData;
|
||||
window.close();
|
||||
}
|
||||
|
||||
function updateSelectionInfo(gridId) {
|
||||
var grid = $(gridId || "#grid");
|
||||
var selectedRows = grid.jqGrid('getGridParam', 'selarrrow');
|
||||
$("#selected-count").text("총: " + selectedRows.length + " 건");
|
||||
}
|
||||
|
||||
function init( callback) {
|
||||
$.ajax({
|
||||
type : "POST",
|
||||
url:url,
|
||||
dataType:"json",
|
||||
data:{cmd: 'LIST_COMBO'},
|
||||
success:function(json){
|
||||
new makeOptions("BIZCODE","BIZNAME").setObj($("select[name=searchEaiBzwkDstcd]")).setNoValueInclude(true).setNoValue("","전체").setData(json.bizList).setFormat(codeName3OptionFormat).rendering();
|
||||
|
||||
setSearchable(selectName); // 콤보에 searchable 설정
|
||||
|
||||
if (typeof callback === 'function') {
|
||||
callback();
|
||||
}
|
||||
},
|
||||
error:function(e){
|
||||
alert(e.responseText);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
function detail(){
|
||||
var postData = getSearchForJqgrid("cmd","LIST"); //jqgrid에서는 object 로
|
||||
$("#grid").setGridParam({ url:url,postData: postData }).trigger("reloadGrid");
|
||||
}
|
||||
|
||||
function search(){
|
||||
var postData = getSearchForJqgrid("cmd","LIST"); //jqgrid에서는 object 로
|
||||
$("#grid").setGridParam({ url:url,postData: postData ,page:1 }).trigger("reloadGrid");
|
||||
}
|
||||
function ioFormatter(cellvalue,options,rowObject){
|
||||
var serviceType = sessionStorage["serviceType"];
|
||||
if(cellvalue == "I"){
|
||||
return "타발";
|
||||
}else if(cellvalue =="O"){
|
||||
return "당발";
|
||||
}
|
||||
}
|
||||
function adapterNameShortFormatter(cellvalue,options,rowObject){
|
||||
if(cellvalue == null || cellvalue == '')
|
||||
return ''
|
||||
return cellvalue.substring(1, 4);
|
||||
}
|
||||
|
||||
function list(){
|
||||
detail()
|
||||
var gridPostData = getSearchForJqgrid("cmd","LIST"); //jqgrid에서는 object 로
|
||||
// gridPostData.searchApiEnableYn = 'Y';
|
||||
var urlParams = new URLSearchParams(window.location.search);
|
||||
var selectedApiIds = urlParams.get("selectedApiIds");
|
||||
$('#grid').jqGrid({
|
||||
datatype : "json",
|
||||
mtype : 'POST',
|
||||
url : url,
|
||||
postData : gridPostData,
|
||||
colNames : [ '업무구분',
|
||||
'<%= localeMessage.getString("eaiMessage.eaiSvcName")%>',
|
||||
'<%= localeMessage.getString("eaiMessage.eaiSvcDesc")%>',
|
||||
'API FULL PATH',
|
||||
'업무서비스명',
|
||||
'요청',
|
||||
'응답',
|
||||
'작성자',
|
||||
'가상응답여부',
|
||||
'SyncAsyncType',
|
||||
],
|
||||
colModel : [ { name : 'eaiBzwkDstcd' , align : 'center' , width:'40', sortable:false},
|
||||
{ name : 'eaiSvcName' , align : 'left' , width:'100'},
|
||||
{ name : 'eaiSvcDesc' , align : 'left' },
|
||||
{ name : 'apiFullPath' , align : 'left' , width:'100'},
|
||||
{ name : 'bzwksvckeyname' , align : 'left' , width:'100', hidden: true},
|
||||
{ name : 'fromAdapter' , align : 'center' , width:'40', formatter:adapterNameShortFormatter },
|
||||
{ name : 'toAdapter' , align : 'center' , width:'40', formatter:adapterNameShortFormatter },
|
||||
{ name : 'author' , align : 'center' , width:'60' },
|
||||
{ name : 'simYn' , align : 'center' , width:'40' },
|
||||
{ name : 'syncAsyncType' , align : 'center' , width:'40', hidden: true},
|
||||
],
|
||||
jsonReader : {
|
||||
repeatitems : false
|
||||
},
|
||||
pager : $('#pager'),
|
||||
page : '${param.page}',
|
||||
rowNum : '${rmsDefaultRowNum}',
|
||||
autoheight : true,
|
||||
height : $("#container").height(),
|
||||
autowidth : true,
|
||||
viewrecords : true,
|
||||
multiselect: false,
|
||||
multiboxonly: false,
|
||||
rowList : eval('[${rmsDefaultRowList}]'),
|
||||
loadComplete:function (d){
|
||||
var $grid = $(this);
|
||||
if(selectedApiIds) {
|
||||
const selectedIds = selectedApiIds.split(',');
|
||||
$.each($grid.getDataIDs(), function(_, id) {
|
||||
var rowData = $grid.getRowData(id);
|
||||
if(selectedIds.includes(rowData.eaiSvcName)) {
|
||||
$grid.jqGrid('setSelection', id, true);
|
||||
}
|
||||
});
|
||||
}
|
||||
var colModel = $(this).getGridParam("colModel");
|
||||
for(var i = 0 ; i< colModel.length; i++){
|
||||
$(this).setColProp(colModel[i].name, {sortable : false});
|
||||
}
|
||||
updateSelectionInfo("#grid");
|
||||
},
|
||||
onSelectRow: function(rowid, status, e) {
|
||||
updateSelectionInfo("#grid");
|
||||
},
|
||||
onSelectAll: function(rowids, status) {
|
||||
updateSelectionInfo("#grid");
|
||||
},
|
||||
ondblClickRow : function(rowId) {
|
||||
const rowData = $(this).getRowData(rowId);
|
||||
const returnValue = {
|
||||
bizCode: rowData.eaiBzwkDstcd,
|
||||
apiId: rowData.eaiSvcName,
|
||||
apiDesc: rowData.eaiSvcDesc
|
||||
};
|
||||
//alert("선택한 API를 추가합니다.\nAPI ID: " + rowData.eaiSvcName);
|
||||
window.returnValue = returnValue;
|
||||
window.close();
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
|
||||
init(list);
|
||||
|
||||
resizeJqGridWidth('grid','content_middle','1000');
|
||||
|
||||
// 업무구분명 선택 시 자동 검색
|
||||
$("select[name=searchEaiBzwkDstcd]").change(function() {
|
||||
search();
|
||||
});
|
||||
$("#btn_search").click(function(){
|
||||
search();
|
||||
});
|
||||
$("input[name^=search]").keydown(function(key){
|
||||
if (key.keyCode == 13){
|
||||
$("#btn_search").click();
|
||||
}
|
||||
});
|
||||
|
||||
buttonControl();
|
||||
|
||||
$("#btn_select").click(function(){
|
||||
processSelectedData();
|
||||
});
|
||||
|
||||
$("#btn_close").click(function () {
|
||||
window.close();
|
||||
});
|
||||
});
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div>
|
||||
<div class="content_middle" id="content_middle" style="margin-top: 0px">
|
||||
<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>
|
||||
<button type="button" class="cssbtn" id="btn_select" level="R" ><i class="material-icons">check</i> <%= localeMessage.getString("button.check") %></button>
|
||||
<button type="button" class="cssbtn" id="btn_close" level="R" status="DETAIL"><i class="material-icons">close</i> <%= localeMessage.getString("button.close") %></button>
|
||||
</div>
|
||||
<div class="title" id="title" >${rmsMenuName}</div>
|
||||
<!-- 선택 정보 표시 영역 추가 -->
|
||||
<div style="margin-bottom: 5px;">
|
||||
<span id="selected-count" style="color: #666; font-size: 12px;"></span>
|
||||
</div>
|
||||
<table class="search_condition" cellspacing=0;>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th style="width:180px;">업무구분명</th>
|
||||
<td>
|
||||
<select name="searchEaiBzwkDstcd" value="${param.searchEaiBzwkDstcd}">
|
||||
</select>
|
||||
</td>
|
||||
<th style="width:180px;"><%= localeMessage.getString("eaiMessage.eaiSvcName")%></th>
|
||||
<td>
|
||||
<input type="text" name="searchEaiSvcName" value="${param.searchEaiSvcName}">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th style="width:180px;"><%= localeMessage.getString("eaiMessage.eaiSvcDesc")%></th>
|
||||
<td>
|
||||
<input type="text" name="searchEaiSvcDesc" value="${param.searchEaiSvcDesc}">
|
||||
</td>
|
||||
<th style="width:180px;">API FULL PATH</th>
|
||||
<td>
|
||||
<input type="text" name="searchApiFullPath" value="${param.searchApiFullPath}">
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<table id="grid" ></table>
|
||||
<div id="pager"></div>
|
||||
</div><!-- end content_middle -->
|
||||
</div><!-- end right_box -->
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -44,9 +44,6 @@
|
||||
.div-hover {
|
||||
background-color: #EDD5DD
|
||||
}
|
||||
|
||||
.btn_move { width:32px; min-width: 32px; height: 32px; background-color: #fff; border:1px solid #999; border-radius:4px; margin-bottom: 4px; }
|
||||
|
||||
|
||||
</style>
|
||||
<jsp:include page="/jsp/common/include/script.jsp"/>
|
||||
@@ -57,7 +54,6 @@
|
||||
const LAYOUT_ITEM_TYPE_GROUP = "Group";
|
||||
const LAYOUT_ITEM_TYPE_GRID = "Grid";
|
||||
const LAYOUT_ITEM_TYPE_FIELD = "Field";
|
||||
const LAYOUT_ITEM_TYPE_ARRAY = "Array";
|
||||
|
||||
var isDetail = false;
|
||||
|
||||
@@ -554,14 +550,6 @@
|
||||
return data;
|
||||
}
|
||||
|
||||
// 항목 자신이 Array인 경우, 경로/변환명령 끝에 반복 표시 [*]를 추가
|
||||
function addArrayTag(loutItemType, data) {
|
||||
if (loutItemType == LAYOUT_ITEM_TYPE_ARRAY && !data.endsWith("[*]")) {
|
||||
data = data + "[*]";
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
//변환명에 있는 서비스코드와 if서비스 코드 매칭
|
||||
function validation(){
|
||||
if ($('input[name=cnvsnName]').val() == '') {
|
||||
@@ -891,7 +879,7 @@
|
||||
}
|
||||
});
|
||||
|
||||
$("#auto").click(function(){
|
||||
$("input[name=auto]").click(function(){
|
||||
//debugger;
|
||||
jsPlumbInstance.deleteEveryConnection();
|
||||
|
||||
@@ -901,10 +889,10 @@
|
||||
let selectValue = "";
|
||||
let targetValue = "";
|
||||
for ( var i = 0; i < sourceData.length; i++) {
|
||||
if (sourceData[i]['loutItemType'] != "Field" && sourceData[i]['loutItemType'] != "Attr" && sourceData[i]['loutItemType'] != "Array") continue;
|
||||
if (sourceData[i]['loutItemType'] != "Field" && sourceData[i]['loutItemType'] != "Attr") continue;
|
||||
selectValue = (sourceData[i]['LOUTITEMPATH']).substr(sourceData[i]['loutName'].length+1);
|
||||
for (var j = 0; j <targetData.length; j++) {
|
||||
if (targetData[j]['loutItemType'] != "Field" && targetData[j]['loutItemType'] != "Attr" && targetData[j]['loutItemType'] != "Array") continue;
|
||||
if (targetData[j]['loutItemType'] != "Field" && targetData[j]['loutItemType'] != "Attr") continue;
|
||||
targetValue = (targetData[j]['LOUTITEMPATH']).substr(targetData[j]['loutName'].length+1);
|
||||
if (targetValue == selectValue ) {
|
||||
var data = sourceData[i]['LOUTITEMPATH'];
|
||||
@@ -918,7 +906,7 @@
|
||||
}
|
||||
});
|
||||
|
||||
$("#addAll").click(function() {
|
||||
$("input[name=addAll]").click(function() {
|
||||
console.debug('addAll');
|
||||
var srcRowId = sourceGrid.getGridParam( "selrow" );
|
||||
var tgtRowId = targetGrid.getGridParam( "selrow" );
|
||||
@@ -928,8 +916,8 @@
|
||||
//field, ATTR 여부 판단
|
||||
var sourceRow = sourceGrid.getRowData( srcRowId );
|
||||
var targetRow = targetGrid.getRowData( tgtRowId );
|
||||
if ( (sourceRow["loutItemType"] == 'Field' || sourceRow["loutItemType"] == 'Attr' || sourceRow["loutItemType"] == 'Array') &&
|
||||
(targetRow["loutItemType"] == 'Field' || targetRow["loutItemType"] == 'Attr' || targetRow["loutItemType"] == 'Array') ){
|
||||
if ( (sourceRow["loutItemType"] == 'Field' || sourceRow["loutItemType"] == 'Attr') &&
|
||||
(targetRow["loutItemType"] == 'Field' || targetRow["loutItemType"] == 'Attr') ){
|
||||
;
|
||||
}else{
|
||||
alert('<%=localeMessage.getString("transformManDetail.alert3")%>');
|
||||
@@ -948,15 +936,15 @@
|
||||
var targetValue = "";
|
||||
var j=tgtIndex-1-1;
|
||||
for ( var i = srcIndex-1; i < sourceData.length; i++) {
|
||||
if (sourceData[i]['loutItemType'] != LAYOUT_ITEM_TYPE_FIELD && sourceData[i]['loutItemType'] != LAYOUT_ITEM_TYPE_ARRAY) continue;
|
||||
if (sourceData[i]['loutItemType'] != LAYOUT_ITEM_TYPE_FIELD) continue;
|
||||
j++;
|
||||
while (targetData.length -1 >= j && targetData[j]["loutItemType"] != LAYOUT_ITEM_TYPE_FIELD && targetData[j]["loutItemType"] != LAYOUT_ITEM_TYPE_ARRAY){
|
||||
while (targetData.length -1 >= j &&targetData[j]["loutItemType"] != LAYOUT_ITEM_TYPE_FIELD){
|
||||
j++;
|
||||
}
|
||||
if (targetData.length -1 < j) break;
|
||||
var targetRowId = targetData[j]['id'];
|
||||
targetGrid.jqGrid('setCell',targetRowId,'CNVSNCMDNAME',addArrayTag(sourceData[i]['loutItemType'], addGroupTag(srcGroup,sourceData[i]['LOUTITEMPATH'])));
|
||||
targetGrid.jqGrid('setCell',targetRowId,'CNVSNRSULTITEMPATHNAME',addArrayTag(targetData[j]['loutItemType'], addGroupTag(tgtGroup,targetData[j]['LOUTITEMPATH'])));
|
||||
targetGrid.jqGrid('setCell',targetRowId,'CNVSNCMDNAME',addGroupTag(srcGroup,sourceData[i]['LOUTITEMPATH']));
|
||||
targetGrid.jqGrid('setCell',targetRowId,'CNVSNRSULTITEMPATHNAME',addGroupTag(tgtGroup,targetData[j]['LOUTITEMPATH']));
|
||||
targetGrid.jqGrid('setCell',targetRowId,'CNVSNITEMSERNO',targetData[j]['loutItemSerno']);
|
||||
|
||||
const sourceEndpointElementId = sourceGrid.getEndpointElementId(sourceData[i]['id']);
|
||||
@@ -969,7 +957,7 @@
|
||||
}
|
||||
});
|
||||
|
||||
$("#removeAll").click(function(){
|
||||
$("input[name=removeAll]").click(function(){
|
||||
var tgtRowId = targetGrid.getGridParam( "selrow" );
|
||||
if (tgtRowId == null) {alert("타겟레이아웃을 선택하여 주십시요.");return;}
|
||||
//grid data
|
||||
@@ -988,49 +976,6 @@
|
||||
jsPlumbInstance.deleteConnectionsForElement(document.getElementById(targetEndpointElementId));
|
||||
}
|
||||
});
|
||||
|
||||
$("#add").click(function() {
|
||||
|
||||
var srcRowId = sourceGrid.getGridParam( "selrow" );
|
||||
var tgtRowId = targetGrid.getGridParam( "selrow" );
|
||||
if (srcRowId == null) {alert('<%=localeMessage.getString("transformManDetail.alert1")%>');return;}
|
||||
if (tgtRowId == null) {alert('<%=localeMessage.getString("transformManDetail.alert2")%>');return;}
|
||||
|
||||
//field, ATTR 여부 판단
|
||||
var sourceRow = sourceGrid.getRowData( srcRowId );
|
||||
var targetRow = targetGrid.getRowData( tgtRowId );
|
||||
if ( (sourceRow["loutItemType"] == 'Field' || sourceRow["loutItemType"] == 'Attr' || sourceRow["loutItemType"] == 'Array') &&
|
||||
(targetRow["loutItemType"] == 'Field' || targetRow["loutItemType"] == 'Attr' || targetRow["loutItemType"] == 'Array') ){
|
||||
;
|
||||
}else{
|
||||
alert('<%=localeMessage.getString("transformManDetail.alert3")%>');
|
||||
return ;
|
||||
}
|
||||
|
||||
targetGrid.jqGrid('setCell',tgtRowId,'CNVSNCMDNAME',addArrayTag(sourceRow['loutItemType'], addGroupTag(srcGroup,sourceRow['LOUTITEMPATH'])));
|
||||
targetGrid.jqGrid('setCell',tgtRowId,'CNVSNRSULTITEMPATHNAME',addArrayTag(targetRow['loutItemType'], addGroupTag(tgtGroup,targetRow['LOUTITEMPATH'])));
|
||||
targetGrid.jqGrid('setCell',tgtRowId,'CNVSNITEMSERNO',targetRow['loutItemSerno']);
|
||||
|
||||
try {
|
||||
jsPlumbConnect(srcRowId, tgtRowId);
|
||||
} catch (e) {console.error(e);}
|
||||
});
|
||||
|
||||
$("#remove").click(function(){
|
||||
var tgtRowId = targetGrid.getGridParam( "selrow" );
|
||||
if (tgtRowId == null) {alert("타겟레이아웃을 선택하여 주십시요.");return;}
|
||||
//grid data
|
||||
var targetData = targetGrid.getRowData();
|
||||
|
||||
targetGrid.jqGrid('setCell', tgtRowId, 'CNVSNCMDNAME', null);
|
||||
targetGrid.jqGrid('setCell', tgtRowId, 'CNVSNRSULTITEMPATHNAME', null);
|
||||
targetGrid.jqGrid('setCell', tgtRowId, 'CNVSNITEMSERNO', null);
|
||||
targetGrid.jqGrid('setCell', tgtRowId, 'CNVSNITEMBASCVAL', null);
|
||||
|
||||
const targetEndpointElementId = targetGrid.getEndpointElementId(tgtRowId);
|
||||
jsPlumbInstance.deleteConnectionsForElement(document.getElementById(targetEndpointElementId));
|
||||
|
||||
});
|
||||
|
||||
$("textarea[name=CNVSNRSULTITEMPATHNAME],textarea[name=CNVSNCMDNAME],input[name=CNVSNITEMBASCVAL],input[name=CNVSNITEMSERNO]").blur(function(){
|
||||
var rowId = targetGrid.getGridParam( "selrow" );
|
||||
@@ -1070,7 +1015,7 @@
|
||||
var targetData = targetGrid.getRowData();
|
||||
var gridData = new Array();
|
||||
for (var i = 0; i <targetData.length; i++) {
|
||||
if ((targetData[i]['loutItemType'] != "Field") && (targetData[i]['loutItemType'] != "Attr") && (targetData[i]['loutItemType'] != "Array")) {
|
||||
if ((targetData[i]['loutItemType'] != "Field") && (targetData[i]['loutItemType'] != "Attr")) {
|
||||
continue;
|
||||
}
|
||||
if (targetData[i]['CNVSNCMDNAME']==null
|
||||
@@ -1213,7 +1158,7 @@
|
||||
function srcformatterFunction(cellvalue,options,rowObject){
|
||||
var rowId = options["rowId"];
|
||||
var loutItemType = rowObject["loutItemType"];
|
||||
if (loutItemType == 'Field' || loutItemType == 'Attr' || loutItemType == 'Array'){
|
||||
if (loutItemType == 'Field' || loutItemType == 'Attr'){
|
||||
return "<div id='div_src_"+rowId+"' name='src_"+rowId+"' style='width:28px;height:17px;background-color: #D12F35; border-color: #ab1717; border-style: solid; border-width: 1px; border-radius: 3px;' />";
|
||||
}else{
|
||||
return "<div id='div_srcgroup_"+rowId+"' name='srcgroup_"+rowId+"' style='width:28px;height:17px;' />";
|
||||
@@ -1225,7 +1170,7 @@
|
||||
function tgtformatterFunction(cellvalue,options,rowObject){
|
||||
var rowId = options["rowId"];
|
||||
var loutItemType = rowObject["loutItemType"];
|
||||
if (loutItemType == 'Field' || loutItemType == 'Attr' || loutItemType == 'Array'){
|
||||
if (loutItemType == 'Field' || loutItemType == 'Attr'){
|
||||
return "<div id='div_tgt_"+rowId+"_base' name='div_tgt_"+rowId+"_base' style='width:30px;height:20px; background-color:gray;' >"
|
||||
+ "<div id='div_tgt_"+rowId+"' name='tgt_"+rowId+"' style='width:28px;height:17px; background-color: #3E7E9C; border-color: #217ca7; border-style: solid; border-width: 1px; border-radius: 3px;' onclick='jqGridOnTargetGridEndpointClick(this)'>"
|
||||
+ "</div></div>";
|
||||
@@ -1724,8 +1669,8 @@
|
||||
return;
|
||||
}
|
||||
|
||||
if ((sourceRow["loutItemType"] != 'Field' && sourceRow["loutItemType"] != 'Attr' && sourceRow["loutItemType"] != 'Array')
|
||||
|| (targetRow["loutItemType"] != 'Field' && targetRow["loutItemType"] != 'Attr' && targetRow["loutItemType"] != 'Array')) {
|
||||
if ((sourceRow["loutItemType"] != 'Field' && sourceRow["loutItemType"] != 'Attr')
|
||||
|| (targetRow["loutItemType"] != 'Field' && targetRow["loutItemType"] != 'Attr')) {
|
||||
alert('<%=localeMessage.getString("transformManDetail.alert3")%>');
|
||||
return ;
|
||||
}
|
||||
@@ -1739,7 +1684,7 @@
|
||||
|
||||
if (targetRow['CNVSNCMDNAME'] == '') {
|
||||
// 변환 명령 값이 한번 설정 되면 변경 되지 않게 함.
|
||||
var cnvsnCmdName = addArrayTag(sourceRow["loutItemType"], addGroupTag(srcGroup,sourceRow["LOUTITEMPATH"]));
|
||||
var cnvsnCmdName = addGroupTag(srcGroup,sourceRow["LOUTITEMPATH"]);
|
||||
// function까지 처리되도록 수정했으나, parameter가 하나인 경우에만 정상처리됨
|
||||
if($('#functionCombo').val() != "") {
|
||||
cnvsnCmdName = $('#functionCombo').val() + "(" + cnvsnCmdName + ")";
|
||||
@@ -1748,7 +1693,7 @@
|
||||
targetGrid.jqGrid('setCell',tgtRowId,'CNVSNCMDNAME', cnvsnCmdName);
|
||||
}
|
||||
|
||||
targetGrid.jqGrid('setCell',tgtRowId,'CNVSNRSULTITEMPATHNAME',addArrayTag(targetRow["loutItemType"], addGroupTag(tgtGroup,targetRow["LOUTITEMPATH"])));
|
||||
targetGrid.jqGrid('setCell',tgtRowId,'CNVSNRSULTITEMPATHNAME',addGroupTag(tgtGroup,targetRow["LOUTITEMPATH"]));
|
||||
targetGrid.jqGrid('setCell',tgtRowId,'CNVSNITEMSERNO',targetRow['loutItemSerno']);
|
||||
targetGrid.jqGrid('setCell',tgtRowId,'CNVSNSRCITEMS', cnvsnSrcItems);
|
||||
targetGrid.jqGrid('setCell',tgtRowId,'ISVALIDMAPPING', true);
|
||||
@@ -1940,8 +1885,8 @@
|
||||
if (loutItemType == LAYOUT_ITEM_TYPE_GRID || loutItemType == LAYOUT_ITEM_TYPE_GROUP) {
|
||||
// group 인 경우
|
||||
items = createGridGroupContextMenuItems(grid, rowId);
|
||||
} else if(loutItemType == LAYOUT_ITEM_TYPE_FIELD || loutItemType == LAYOUT_ITEM_TYPE_ARRAY) {
|
||||
// field, array 인 경우
|
||||
} else if(loutItemType == LAYOUT_ITEM_TYPE_FIELD) {
|
||||
// field 인 경우
|
||||
items = createFieldContextMenuItems(grid, positionElement.attr('id'));
|
||||
} else {
|
||||
return false;
|
||||
@@ -1993,8 +1938,8 @@
|
||||
var key = "";
|
||||
var args = new Object();
|
||||
args['eaiSvcName'] = $('input[name=eaiSvcName]').val();
|
||||
var url=url_view; //'<c:url value="/onl/transaction/extnl/interfaceMan.view"/>';
|
||||
url = url + "?cmd=API_POPUP";
|
||||
var url='<c:url value="/onl/transaction/extnl/interfaceMan.view"/>';
|
||||
url = url + "?cmd=POPUP";
|
||||
var ret = showModal(url,args,1020,630, function(arg){
|
||||
var args = null;
|
||||
if(arg == null || arg == undefined ) {//chrome
|
||||
@@ -2008,11 +1953,11 @@
|
||||
|
||||
var ret = args.returnValue;
|
||||
console.log("ret",ret);
|
||||
key = ret['apiId'];
|
||||
key = ret['key'];
|
||||
|
||||
$("input[name=eaiSvcName]").val(key);
|
||||
$("input[name=eaiSvcDesc]").val(ret['apiDesc']);
|
||||
bzwkDstCd = ret['bizCode'];
|
||||
$("input[name=eaiSvcDesc]").val(ret['eaiSvcDesc']);
|
||||
bzwkDstCd = ret['eaiBzwkDstCd'];
|
||||
|
||||
$("input[name=eaiSvcName]").change();
|
||||
});
|
||||
@@ -2044,7 +1989,7 @@
|
||||
break;
|
||||
}
|
||||
|
||||
if (loutItemType == LAYOUT_ITEM_TYPE_FIELD || loutItemType == LAYOUT_ITEM_TYPE_ARRAY) {
|
||||
if (loutItemType == LAYOUT_ITEM_TYPE_FIELD) {
|
||||
loutItemPath = loutItemPath.replace(new RegExp("^" + prefix), "");
|
||||
console.debug(grid.getId() + " : loutItemPath : " + loutItemPath);
|
||||
rowData['GROUP_LOUTITEMPATH'] = loutItemPath;
|
||||
@@ -2207,12 +2152,12 @@
|
||||
<div id="sourcePage"></div>
|
||||
</div>
|
||||
</td>
|
||||
<td style="width:30%; min-width:70px; text-align:center; vertical-align: top; ">
|
||||
<button type=button id="remove" name="remove" class="cssbtn btn_move" title="remove"><</button>
|
||||
<button type=button id="add" name="add" class="cssbtn btn_move" title="add"> > </button><br />
|
||||
<button type=button id="removeAll" name="removeAll" class="cssbtn btn_move" title="remove all"> << </button>
|
||||
<button type=button id="addAll" name="addAll" class="cssbtn btn_move" title="add all"> >> </button><br />
|
||||
<button type=button id="auto" name="auto" class="cssbtn btn_move" style="width: 70px; " title="auto"> Auto </button>
|
||||
<td style="width:30%; text-align:center; vertical-align: top;">
|
||||
|
||||
<br /><!-- 이 br 이 없을 때 버튼이 제대로 동작 하지 않는다. -->
|
||||
<input type=button id="removeAll" name="removeAll" class="btn_img btn_removeAll"/><br />
|
||||
<input type=button id="addAll" name="addAll" class="btn_img btn_addAll"/><br />
|
||||
<input type=button id="auto" name="auto" class="btn_img btn_auto" />
|
||||
</td>
|
||||
<td style="vertical-align:top;">
|
||||
<table class="table_row" cellspacing="0">
|
||||
|
||||
@@ -19,8 +19,7 @@
|
||||
<jsp:include page="/jsp/common/include/css.jsp"/>
|
||||
<jsp:include page="/jsp/common/include/script.jsp"/>
|
||||
|
||||
<script language="javascript" >
|
||||
var url_view = '<c:url value="/onl/admin/rule/transform2/transform2Man.view" />';
|
||||
<script language="javascript" >
|
||||
var $ = jQuery.noConflict();
|
||||
var bzwkDstCd = "";
|
||||
|
||||
@@ -82,9 +81,8 @@ $(document).ready(function() {
|
||||
var key = "";
|
||||
var args = new Object();
|
||||
args['eaiSvcName'] = $('input[name=eaiSvcName]').val();
|
||||
var url=url_view; //'<c:url value="/onl/transaction/extnl/interfaceMan.view"/>';
|
||||
url = url + "?cmd=API_POPUP";
|
||||
console.log('[new]', url);
|
||||
var url='<c:url value="/onl/transaction/extnl/interfaceMan.view"/>';
|
||||
url = url + "?cmd=POPUP";
|
||||
var ret = showModal(url,args,1020,630, function(arg){
|
||||
var args = null;
|
||||
if(arg == null || arg == undefined ) {//chrome
|
||||
@@ -98,11 +96,11 @@ $(document).ready(function() {
|
||||
|
||||
var ret = args.returnValue;
|
||||
console.log("ret",ret);
|
||||
key = ret['apiId'];
|
||||
key = ret['key'];
|
||||
|
||||
$("input[name=eaiSvcName]").val(key);
|
||||
$("input[name=eaiSvcDesc]").val(ret['apiDesc']);
|
||||
bzwkDstCd = ret['bizCode'];
|
||||
$("input[name=eaiSvcDesc]").val(ret['eaiSvcDesc']);
|
||||
bzwkDstCd = ret['eaiBzwkDstCd'];
|
||||
|
||||
$("input[name=eaiSvcName]").change();
|
||||
//$("select[name=eaiSevrDstcd]").val(ret['eaiSevrDstcd']);
|
||||
|
||||
@@ -4,18 +4,11 @@
|
||||
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
|
||||
<%@ include file="/jsp/common/include/localemessage.jsp" %>
|
||||
<%@ page import="com.eactive.eai.common.util.SystemUtil"%>
|
||||
<%@ page import="com.eactive.eai.rms.common.context.MonitoringContext"%>
|
||||
<%@ page import="com.eactive.eai.rms.common.util.CommonUtil"%>
|
||||
<%
|
||||
response.setHeader("Pragma", "No-cache");
|
||||
response.setHeader("Cache-Control", "no-cache");
|
||||
response.setHeader("Expires", "0");
|
||||
|
||||
MonitoringContext monitoringContext = (MonitoringContext)CommonUtil.getBean(request, "monitoringContext");
|
||||
String deployYnStr = monitoringContext.getStringProperty(MonitoringContext.IOMAP_DEPLOY_YN_BY_RMS, "N");
|
||||
boolean deployYn = "Y".equalsIgnoreCase(deployYnStr);
|
||||
String adminOnlyYnStr = monitoringContext.getStringProperty(MonitoringContext.IOMAP_DEPLOY_YN_BY_ADMIN_ONLY, "Y");
|
||||
boolean adminOnly = "Y".equalsIgnoreCase(adminOnlyYnStr);
|
||||
%>
|
||||
<html>
|
||||
<head>
|
||||
@@ -24,48 +17,62 @@
|
||||
|
||||
<jsp:include page="/jsp/common/include/css.jsp"/>
|
||||
<jsp:include page="/jsp/common/include/script.jsp"/>
|
||||
<script>
|
||||
<script language="javascript" >
|
||||
var url = '<c:url value="/onl/admin/rule/transformMan.json" />';
|
||||
var url_view = '<c:url value="/onl/admin/rule/transformMan.view" />';
|
||||
var url_excel= '<c:url value="/onl/admin/rule/transformMan.excel"/>';
|
||||
var roleString = "<%=SessionManager.getRoleIdString(request)%>";
|
||||
var devSvr = <%=SystemUtil.isDevServer()%>;
|
||||
var stageSvr = <%=SystemUtil.isStagingServer()%>;
|
||||
var adminOnly = <%=adminOnly%>;
|
||||
|
||||
function dateFormater(cellvalue, options, rowObject){
|
||||
if (cellvalue==null|| cellvalue==undefined) return "";
|
||||
if (cellvalue.trim().length < 14) return "";
|
||||
var display = "";
|
||||
display=display+cellvalue.substr(0,4)+"-";
|
||||
display=display+cellvalue.substr(4,2)+"-";
|
||||
display=display+cellvalue.substr(6,2)+" ";
|
||||
|
||||
display=display+cellvalue.substr(8,2)+":";
|
||||
display=display+cellvalue.substr(10,2)+":";
|
||||
display=display+cellvalue.substr(12,2);
|
||||
|
||||
return display;
|
||||
}
|
||||
|
||||
|
||||
$(document).ready(function() {
|
||||
|
||||
var postData = getSearchForJqgrid("cmd","LIST"); //jqgrid에서는 object 로
|
||||
|
||||
$('#grid').jqGrid({
|
||||
datatype:"json",
|
||||
mtype: 'POST',
|
||||
url: url,
|
||||
postData : postData,
|
||||
postData : { cmd : 'LIST'
|
||||
, searchBizCode : $('input[name=searchBizCode]').val()
|
||||
, searchCnvsnName : $('input[name=searchCnvsnName]').val()
|
||||
, searchCnvsnDesc : $('input[name=searchCnvsnDesc]').val()
|
||||
|
||||
},
|
||||
colNames:[
|
||||
'<%=localeMessage.getString("transformMan.eaibzwkdstcd")%>',
|
||||
'<%=localeMessage.getString("transformMan.telCngMappCd")%>',
|
||||
'<%=localeMessage.getString("transformMan.telCngMappDesc")%>',
|
||||
'<%=localeMessage.getString("transformMan.ifSvcCd")%>',
|
||||
'<%=localeMessage.getString("transformMan.ifSvcDesc")%>',
|
||||
'<%=localeMessage.getString("transformMan.eaiSvrDiv")%>',
|
||||
'<%= localeMessage.getString("combo.useYn")%> ',
|
||||
'<%=localeMessage.getString("transformMan.modifyDate")%>',
|
||||
'<%=localeMessage.getString("transformMan.verInfo")%>',
|
||||
'cnvsnType',
|
||||
'타입',
|
||||
'flag'
|
||||
],
|
||||
colModel:[
|
||||
{ name : 'EAIBZWKDSTCD' , align:'center' ,sortable:false , width:"30"},
|
||||
{ name : 'CNVSNNAME' , align:'left' , width:"100" },
|
||||
{ name : 'CNVSNDESC' , align:'left' , width:"100" },
|
||||
{ name : 'EAISVCNAME' , align:'left' , width:"70" },
|
||||
{ name : 'EAISVCDESC' , align:'left' , width:"100"},
|
||||
{ name : 'EAIBZWKDSTCD' , align:'center' ,sortable:false , width : "40"},
|
||||
{ name : 'CNVSNNAME' , align:'left' },
|
||||
{ name : 'EAISVCNAME' , align:'left' },
|
||||
{ name : 'EAISVCDESC' , align:'left' },
|
||||
{ name : 'EAISEVRDSTCD' , hidden : true },
|
||||
{ name : 'USEYN', align:'center', width:30 ,editoptions:{value:"0:<%= localeMessage.getString("combo.usen")%>;1:<%= localeMessage.getString("combo.usey")%>"}, formatter:"select"},
|
||||
{ name : 'LASTAMNDHMS' , align:'center' , width : "60" },
|
||||
{ name : 'VERINFO' , align:'center' , width : "30" },
|
||||
{ name : 'CNVSNTYPE' , hidden : true },
|
||||
{ name : 'USEYN', align:'center', width:30,editoptions:{value:"0:<%= localeMessage.getString("combo.usen")%>;1:<%= localeMessage.getString("combo.usey")%>"}, formatter:"select"},
|
||||
{ name : 'LASTAMNDHMS' , align:'center' , formatter: dateFormater, width : "70" },
|
||||
{ name : 'VERINFO' , align:'center' , width : "70" },
|
||||
{ name : 'CNVSNTYPE' , hidden : true },
|
||||
{ name : 'FLAG' , hidden : true }
|
||||
],
|
||||
jsonReader: {
|
||||
@@ -129,8 +136,7 @@ $(document).ready(function() {
|
||||
//key값
|
||||
url2 += '&cnvsnName='+cnvsnName;
|
||||
url2 += '&eaiSvcName='+eaiSvcName;
|
||||
//url2 += '&eaiSvcDesc='+eaiSvcDesc;
|
||||
url2 += '&eaiSvcDesc='+encodeURIComponent(eaiSvcDesc);
|
||||
url2 += '&eaiSvcDesc='+eaiSvcDesc;
|
||||
url2 += '&cnvsnDesc='+cnvsnDesc;
|
||||
url2 += '&eaiSevrDStcd='+eaiSevrDStcd;
|
||||
url2 += '&useYn='+useYn;
|
||||
@@ -155,6 +161,7 @@ $(document).ready(function() {
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
resizeJqGridWidth('grid','content_middle','1000');
|
||||
|
||||
$("#btn_search").click(function(){
|
||||
@@ -210,7 +217,7 @@ $(document).ready(function() {
|
||||
}
|
||||
return false;
|
||||
}else{
|
||||
if(confirm("<%=localeMessage.getString("eaiMessage.confirm2")%>")){
|
||||
if(confirm("초기화 하시겠습니까?")){
|
||||
var postData = [];
|
||||
postData.push({ name: "cmd" , value:"LIST_INITIALIZE"});
|
||||
postData.push({ name: "chkItems" , value : chkArr});
|
||||
@@ -235,13 +242,8 @@ $(document).ready(function() {
|
||||
});
|
||||
|
||||
$('#btn_cmsave').click(onclickBtnCmSave);
|
||||
$('#btn_clone').click(onclickBtnClone);
|
||||
|
||||
$("#btn_delete").click(function(){
|
||||
if(!confirm("삭제하시겠습니까?")){
|
||||
return;
|
||||
}
|
||||
|
||||
var id = "grid";
|
||||
var cnvsnNameArr = new Array();
|
||||
var eaiSvcNameArr = new Array();
|
||||
@@ -261,7 +263,7 @@ $(document).ready(function() {
|
||||
}
|
||||
|
||||
if(chkcnt == 0){
|
||||
alert("<%= localeMessage.getString("standardLayout.message4") %>");
|
||||
alert("변환매핑을 선택해 주세요");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -301,18 +303,10 @@ $(document).ready(function() {
|
||||
});
|
||||
buttonControl();
|
||||
setBtnHide(roleString, "admin", "btn_initialize");
|
||||
|
||||
if(devSvr || stageSvr){
|
||||
if (adminOnly) {
|
||||
setBtnHide(roleString, "admin", "btn_clone");
|
||||
}
|
||||
}else{
|
||||
$("#btn_clone").hide();
|
||||
}
|
||||
//setBtnControl(roleString, "admin", "btn_cmsave");
|
||||
//if(!devSvr){
|
||||
// $("#btn_cmsave").hide();
|
||||
//}
|
||||
if(!devSvr){
|
||||
$("#btn_cmsave").hide();
|
||||
}
|
||||
});
|
||||
|
||||
function setBtnControl(roleString, condiVal, compoId)
|
||||
@@ -435,62 +429,6 @@ function addCMResource(eaiBzwkDsdCd, obj, cnvsnNames) {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function onclickBtnClone() {
|
||||
var sel = $('#grid').jqGrid('getGridParam', 'selarrrow');
|
||||
if (sel.length == 0) {
|
||||
alert('<%= localeMessage.getString("eaiMessage.alert17")%>');
|
||||
return;
|
||||
}
|
||||
var selectedTransformNames = [];
|
||||
|
||||
$(sel).each(function(index, i) {
|
||||
var cnvsnName = $('#grid').getCell(i, 'CNVSNNAME');
|
||||
selectedTransformNames.push(cnvsnName);
|
||||
});
|
||||
|
||||
var url2 = url_view;
|
||||
url2 += "?cmd=POPUP CALENDAR";
|
||||
var args = new Object();
|
||||
args['selectedCnvsnName'] = selectedTransformNames.join(',');
|
||||
showModal(url2,args,540,270, function(arg){
|
||||
var args = null;
|
||||
if(arg == null || arg == undefined ) {
|
||||
//chrome
|
||||
args = this.dialogArguments;
|
||||
args.returnValue = this.returnValue;
|
||||
} else {
|
||||
//ie
|
||||
args = arg;
|
||||
}
|
||||
if( !args || !args.returnValue ) return;
|
||||
deployTransforms(args.returnValue);
|
||||
});
|
||||
}
|
||||
|
||||
function deployTransforms(ret) {
|
||||
var params = ret;
|
||||
params['cmd'] = 'CLONETOPROD';
|
||||
$.ajax({
|
||||
type : "POST",
|
||||
url:url,
|
||||
dataType:"json",
|
||||
data:params,
|
||||
success:function(json){
|
||||
alert(json.message);
|
||||
list($("#grid").getGridParam("page"));
|
||||
},
|
||||
error:function(xhr,status,error){
|
||||
if (xhr.responseText != null) {
|
||||
try {
|
||||
alert(JSON.parse(xhr.responseText).errorMsg);
|
||||
} catch (e) {
|
||||
alert(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
</script>
|
||||
</head>
|
||||
@@ -503,11 +441,6 @@ function deployTransforms(ret) {
|
||||
</div><!-- end content_top -->
|
||||
<div class="content_middle" id="content_middle">
|
||||
<div class="search_wrap">
|
||||
<% if (SystemUtil.isDevServer() && deployYn) { %>
|
||||
<button type="button" class="cssbtn" id="btn_clone" level="W"><i class="material-icons">move_up</i> <%= localeMessage.getString("button.copyStage") %></button>
|
||||
<% } else if (SystemUtil.isStagingServer() && deployYn) { %>
|
||||
<button type="button" class="cssbtn" id="btn_clone" level="W"><i class="material-icons">move_up</i> <%= localeMessage.getString("button.copyProd") %></button>
|
||||
<% } %>
|
||||
<button type="button" class="cssbtn" id="btn_excel" level="W" status="DETAIL"><i class="material-icons">table_view</i> <%= localeMessage.getString("button.excel") %></button>
|
||||
<button type="button" class="cssbtn" id="btn_initialize" level="W" ><i class="material-icons">restart_alt</i> <%= localeMessage.getString("button.reset") %></button>
|
||||
<button type="button" class="cssbtn" id="btn_delete" level="W" status="DETAIL,NEW"><i class="material-icons">delete</i> <%= localeMessage.getString("button.delete") %></button>
|
||||
@@ -536,13 +469,9 @@ function deployTransforms(ret) {
|
||||
</tr>
|
||||
<tr>
|
||||
<th style="width:180px;"><%=localeMessage.getString("transformMan.telCngMappDesc")%> </th>
|
||||
<td>
|
||||
<td colspan="3">
|
||||
<input type="text" name="searchCnvsnDesc" value="${param.searchCnvsnDesc}">
|
||||
</td>
|
||||
<th style="width:180px;"><%=localeMessage.getString("transformMan.ifSvcCd")%> </th>
|
||||
<td>
|
||||
<input type="text" name="searchIfSvcCd" value="${param.searchIfSvcCd}">
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@@ -1,258 +0,0 @@
|
||||
<%@ page language="java" contentType="text/html; charset=utf-8"%>
|
||||
<%@ page import="java.io.*"%>
|
||||
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
|
||||
<%@ 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=euc-kr">
|
||||
<jsp:include page="/jsp/common/include/css.jsp"/>
|
||||
<jsp:include page="/jsp/common/include/script.jsp"/>
|
||||
<script language="javascript" >
|
||||
var url = '<c:url value="/onl/transaction/apim/apiInterfaceMan.json"/>';
|
||||
var url_view = '<c:url value="/onl/transaction/apim/apiInterfaceMan.view"/>';
|
||||
|
||||
var selectName = "searchEaiBzwkDstcd"; // selectBox Name
|
||||
function processSelectedData() {
|
||||
var grid = $("#grid");
|
||||
var selectedRowIds = grid.jqGrid('getGridParam', 'selarrrow');
|
||||
var selectedData = [];
|
||||
|
||||
for(var i = 0; i < selectedRowIds.length; i++) {
|
||||
var rowData = grid.jqGrid('getRowData', selectedRowIds[i]);
|
||||
selectedData.push({
|
||||
bizCode: rowData.eaiBzwkDstcd, // CLINET
|
||||
apiId: rowData.eaiSvcName,
|
||||
apiDesc: rowData.eaiSvcDesc, // SCOPE-API
|
||||
APIFULLPATH: rowData.apiFullPath,
|
||||
BZWKSVCKEYNAME: rowData.bzwksvckeyname
|
||||
});
|
||||
}
|
||||
|
||||
if(selectedData.length === 0) {
|
||||
alert("선택된 항목이 없습니다.");
|
||||
return;
|
||||
}
|
||||
|
||||
alert("선택한 " + selectedData.length + "건의 API를 추가합니다.");
|
||||
window.returnValue = selectedData.length === 1 ? selectedData[0] : selectedData;
|
||||
window.close();
|
||||
}
|
||||
|
||||
function updateSelectionInfo(gridId) {
|
||||
var grid = $(gridId || "#grid");
|
||||
var selectedRows = grid.jqGrid('getGridParam', 'selarrrow');
|
||||
$("#selected-count").text("총: " + selectedRows.length + " 건");
|
||||
}
|
||||
|
||||
function init( callback) {
|
||||
$.ajax({
|
||||
type : "POST",
|
||||
url:url,
|
||||
dataType:"json",
|
||||
data:{cmd: 'LIST_COMBO'},
|
||||
success:function(json){
|
||||
new makeOptions("BIZCODE","BIZNAME").setObj($("select[name=searchEaiBzwkDstcd]")).setNoValueInclude(true).setNoValue("","전체").setData(json.bizList).setFormat(codeName3OptionFormat).rendering();
|
||||
|
||||
setSearchable(selectName); // 콤보에 searchable 설정
|
||||
|
||||
if (typeof callback === 'function') {
|
||||
callback();
|
||||
}
|
||||
},
|
||||
error:function(e){
|
||||
alert(e.responseText);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
function detail(){
|
||||
var postData = getSearchForJqgrid("cmd","LIST"); //jqgrid에서는 object 로
|
||||
$("#grid").setGridParam({ url:url,postData: postData }).trigger("reloadGrid");
|
||||
}
|
||||
|
||||
function search(){
|
||||
var postData = getSearchForJqgrid("cmd","LIST"); //jqgrid에서는 object 로
|
||||
$("#grid").setGridParam({ url:url,postData: postData ,page:1 }).trigger("reloadGrid");
|
||||
}
|
||||
function ioFormatter(cellvalue,options,rowObject){
|
||||
var serviceType = sessionStorage["serviceType"];
|
||||
if(cellvalue == "I"){
|
||||
return "타발";
|
||||
}else if(cellvalue =="O"){
|
||||
return "당발";
|
||||
}
|
||||
}
|
||||
function adapterNameShortFormatter(cellvalue,options,rowObject){
|
||||
if(cellvalue == null || cellvalue == '')
|
||||
return ''
|
||||
return cellvalue.substring(1, 4);
|
||||
}
|
||||
|
||||
function list(){
|
||||
detail()
|
||||
var gridPostData = getSearchForJqgrid("cmd","LIST"); //jqgrid에서는 object 로
|
||||
// gridPostData.searchApiEnableYn = 'Y';
|
||||
var urlParams = new URLSearchParams(window.location.search);
|
||||
var selectedApiIds = urlParams.get("selectedApiIds");
|
||||
$('#grid').jqGrid({
|
||||
datatype : "json",
|
||||
mtype : 'POST',
|
||||
url : url,
|
||||
postData : gridPostData,
|
||||
colNames : [ '업무구분',
|
||||
'<%= localeMessage.getString("eaiMessage.eaiSvcName")%>',
|
||||
'<%= localeMessage.getString("eaiMessage.eaiSvcDesc")%>',
|
||||
'API FULL PATH',
|
||||
'업무서비스명',
|
||||
'요청',
|
||||
'응답',
|
||||
'작성자',
|
||||
'가상응답여부',
|
||||
'SyncAsyncType',
|
||||
],
|
||||
colModel : [ { name : 'eaiBzwkDstcd' , align : 'center' , width:'40', sortable:false},
|
||||
{ name : 'eaiSvcName' , align : 'left' , width:'100'},
|
||||
{ name : 'eaiSvcDesc' , align : 'left' },
|
||||
{ name : 'apiFullPath' , align : 'left' , width:'100'},
|
||||
{ name : 'bzwksvckeyname' , align : 'left' , width:'100', hidden: true},
|
||||
{ name : 'fromAdapter' , align : 'center' , width:'40', formatter:adapterNameShortFormatter },
|
||||
{ name : 'toAdapter' , align : 'center' , width:'40', formatter:adapterNameShortFormatter },
|
||||
{ name : 'author' , align : 'center' , width:'60' },
|
||||
{ name : 'simYn' , align : 'center' , width:'40' },
|
||||
{ name : 'syncAsyncType' , align : 'center' , width:'40', hidden: true},
|
||||
],
|
||||
jsonReader : {
|
||||
repeatitems : false
|
||||
},
|
||||
pager : $('#pager'),
|
||||
page : '${param.page}',
|
||||
rowNum : '${rmsDefaultRowNum}',
|
||||
autoheight : true,
|
||||
height : $("#container").height(),
|
||||
autowidth : true,
|
||||
viewrecords : true,
|
||||
multiselect: false,
|
||||
multiboxonly: false,
|
||||
rowList : eval('[${rmsDefaultRowList}]'),
|
||||
loadComplete:function (d){
|
||||
var $grid = $(this);
|
||||
if(selectedApiIds) {
|
||||
const selectedIds = selectedApiIds.split(',');
|
||||
$.each($grid.getDataIDs(), function(_, id) {
|
||||
var rowData = $grid.getRowData(id);
|
||||
if(selectedIds.includes(rowData.eaiSvcName)) {
|
||||
$grid.jqGrid('setSelection', id, true);
|
||||
}
|
||||
});
|
||||
}
|
||||
var colModel = $(this).getGridParam("colModel");
|
||||
for(var i = 0 ; i< colModel.length; i++){
|
||||
$(this).setColProp(colModel[i].name, {sortable : false});
|
||||
}
|
||||
updateSelectionInfo("#grid");
|
||||
},
|
||||
onSelectRow: function(rowid, status, e) {
|
||||
updateSelectionInfo("#grid");
|
||||
},
|
||||
onSelectAll: function(rowids, status) {
|
||||
updateSelectionInfo("#grid");
|
||||
},
|
||||
ondblClickRow : function(rowId) {
|
||||
const rowData = $(this).getRowData(rowId);
|
||||
const returnValue = {
|
||||
bizCode: rowData.eaiBzwkDstcd,
|
||||
apiId: rowData.eaiSvcName,
|
||||
apiDesc: rowData.eaiSvcDesc
|
||||
};
|
||||
//alert("선택한 API를 추가합니다.\nAPI ID: " + rowData.eaiSvcName);
|
||||
window.returnValue = returnValue;
|
||||
window.close();
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
|
||||
init(list);
|
||||
|
||||
resizeJqGridWidth('grid','content_middle','1000');
|
||||
|
||||
// 업무구분명 선택 시 자동 검색
|
||||
$("select[name=searchEaiBzwkDstcd]").change(function() {
|
||||
search();
|
||||
});
|
||||
$("#btn_search").click(function(){
|
||||
search();
|
||||
});
|
||||
$("input[name^=search]").keydown(function(key){
|
||||
if (key.keyCode == 13){
|
||||
$("#btn_search").click();
|
||||
}
|
||||
});
|
||||
|
||||
buttonControl();
|
||||
|
||||
$("#btn_select").click(function(){
|
||||
processSelectedData();
|
||||
});
|
||||
|
||||
$("#btn_close").click(function () {
|
||||
window.close();
|
||||
});
|
||||
});
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div>
|
||||
<div class="content_middle" id="content_middle" style="margin-top: 0px">
|
||||
<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>
|
||||
<button type="button" class="cssbtn" id="btn_select" level="R" ><i class="material-icons">check</i> <%= localeMessage.getString("button.check") %></button>
|
||||
<button type="button" class="cssbtn" id="btn_close" level="R" status="DETAIL"><i class="material-icons">close</i> <%= localeMessage.getString("button.close") %></button>
|
||||
</div>
|
||||
<div class="title" id="title" >${rmsMenuName}</div>
|
||||
<!-- 선택 정보 표시 영역 추가 -->
|
||||
<div style="margin-bottom: 5px;">
|
||||
<span id="selected-count" style="color: #666; font-size: 12px;"></span>
|
||||
</div>
|
||||
<table class="search_condition" cellspacing=0;>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th style="width:180px;">업무구분명</th>
|
||||
<td>
|
||||
<select name="searchEaiBzwkDstcd" value="${param.searchEaiBzwkDstcd}">
|
||||
</select>
|
||||
</td>
|
||||
<th style="width:180px;"><%= localeMessage.getString("eaiMessage.eaiSvcName")%></th>
|
||||
<td>
|
||||
<input type="text" name="searchEaiSvcName" value="${param.searchEaiSvcName}">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th style="width:180px;"><%= localeMessage.getString("eaiMessage.eaiSvcDesc")%></th>
|
||||
<td>
|
||||
<input type="text" name="searchEaiSvcDesc" value="${param.searchEaiSvcDesc}">
|
||||
</td>
|
||||
<th style="width:180px;">API FULL PATH</th>
|
||||
<td>
|
||||
<input type="text" name="searchApiFullPath" value="${param.searchApiFullPath}">
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<table id="grid" ></table>
|
||||
<div id="pager"></div>
|
||||
</div><!-- end content_middle -->
|
||||
</div><!-- end right_box -->
|
||||
</body>
|
||||
</html>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -46,10 +46,10 @@ $(document).ready(function() {
|
||||
, src_layout : src_layout
|
||||
},
|
||||
colNames:[
|
||||
'변환결과 항목경로명',
|
||||
'변환명령',
|
||||
'항목 기본값',
|
||||
'항목 일련번호',
|
||||
'<%=localeMessage.getString("tranManDtPop.conRsItPtNm")%>',
|
||||
'<%=localeMessage.getString("tranManDtPop.conOrd")%>',
|
||||
'<%=localeMessage.getString("tranManDtPop.ItDft")%>',
|
||||
'<%=localeMessage.getString("tranManDtPop.itSrlNmb")%>',
|
||||
'타겟Flag',
|
||||
'소스Flag',
|
||||
'<%=localeMessage.getString("tranManDtPop.errMsg")%>'
|
||||
@@ -104,6 +104,7 @@ $(document).ready(function() {
|
||||
resizeJqGridWidth('grid','title','1000');
|
||||
|
||||
$("#btn_close").click(function(){
|
||||
//debugger;
|
||||
window.close();
|
||||
});
|
||||
$("body").css("overflow-x","hidden");
|
||||
@@ -114,12 +115,13 @@ $(document).ready(function() {
|
||||
<body>
|
||||
<div class="popup_box" id="title">
|
||||
<div class="search_wrap">
|
||||
<img id="btn_close" src="<c:url value="/img/btn_close.png"/>" level="R"/>
|
||||
<%-- <img id="btn_close" src="<c:url value="/img/btn_close.png"/>" level="R"/> --%>
|
||||
<button type="button" class="cssbtn" id="btn_close" level="R"><i class="material-icons">cancel</i> <%= localeMessage.getString("button.close") %></button>
|
||||
</div>
|
||||
<div class="title">변환매핑항목 상세</div>
|
||||
<div class="title"><%=localeMessage.getString("tranManDtPop.title")%></div>
|
||||
<table class="search_condition" cellspacing="0">
|
||||
<tr>
|
||||
<th style="width:180px;">변환매핑 코드</th>
|
||||
<th style="width:180px;"><%=localeMessage.getString("tranManDtPop.conMapCd")%></th>
|
||||
<td>
|
||||
<input type="text" name="cnvsnName" readonly>
|
||||
</td>
|
||||
|
||||
@@ -19,8 +19,7 @@
|
||||
<jsp:include page="/jsp/common/include/css.jsp"/>
|
||||
<jsp:include page="/jsp/common/include/script.jsp"/>
|
||||
|
||||
<script language="javascript" >
|
||||
var url_view = '<c:url value="/onl/admin/rule/transformMan.view" />';
|
||||
<script language="javascript" >
|
||||
var $ = jQuery.noConflict();
|
||||
var bzwkDstCd = "";
|
||||
|
||||
@@ -82,9 +81,8 @@ $(document).ready(function() {
|
||||
var key = "";
|
||||
var args = new Object();
|
||||
args['eaiSvcName'] = $('input[name=eaiSvcName]').val();
|
||||
var url=url_view; //'<c:url value="/onl/transaction/extnl/interfaceMan.view"/>';
|
||||
url = url + "?cmd=API_POPUP";
|
||||
console.log('[new]', url);
|
||||
var url='<c:url value="/onl/transaction/extnl/interfaceMan.view"/>';
|
||||
url = url + "?cmd=POPUP";
|
||||
var ret = showModal(url,args,1020,630, function(arg){
|
||||
var args = null;
|
||||
if(arg == null || arg == undefined ) {//chrome
|
||||
@@ -98,15 +96,15 @@ $(document).ready(function() {
|
||||
|
||||
var ret = args.returnValue;
|
||||
console.log("ret",ret);
|
||||
key = ret['apiId'];
|
||||
key = ret['key'];
|
||||
|
||||
$("input[name=eaiSvcName]").val(key);
|
||||
$("input[name=eaiSvcDesc]").val(ret['apiDesc']);
|
||||
bzwkDstCd = ret['bizCode'];
|
||||
$("input[name=eaiSvcDesc]").val(ret['eaiSvcDesc']);
|
||||
bzwkDstCd = ret['eaiBzwkDstCd'];
|
||||
|
||||
$("input[name=eaiSvcName]").change();
|
||||
//$("select[name=eaiSevrDstcd]").val(ret['eaiSevrDstcd']);
|
||||
//$("select[name=eaiSevrDstcd]").attr("disabled",true);
|
||||
$("select[name=eaiSevrDstcd]").val(ret['eaiSevrDstcd']);
|
||||
$("select[name=eaiSevrDstcd]").attr("disabled",true);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -146,7 +144,7 @@ $(document).ready(function() {
|
||||
</ul>
|
||||
</div><!-- end content_top -->
|
||||
<div class="content_middle">
|
||||
<div class="search_wrap">
|
||||
<div class="search_wrap">
|
||||
<button type="button" class="cssbtn" id="btn_regist" level="W" status="DETAIL,NEW"><i class="material-icons">save</i> <%= localeMessage.getString("button.register") %></button>
|
||||
<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>
|
||||
<%-- <img src="<c:url value="/img/btn_regist.png"/>" alt="" id="btn_regist" level="W" status="DETAIL,NEW" /> --%>
|
||||
@@ -161,29 +159,25 @@ $(document).ready(function() {
|
||||
<form id="ajaxForm">
|
||||
<table class="table_row" cellspacing="0">
|
||||
<tr>
|
||||
<th style="width:14%; min-width:130px;"><%=localeMessage.getString("transformMan.ifSvcCd")%> </th>
|
||||
<td style="width:31%;">
|
||||
<input type="text" name="eaiSvcName" style="width:60%;" readonly> <!-- <img id="btn_search" src="<c:url value="/img/btn_pop_search.png" />" level="R" class="btn_img" /> -->
|
||||
<th style="width:25%;"><%=localeMessage.getString("transformMan.ifSvcCd")%> </th>
|
||||
<td colspan="3">
|
||||
<input type="text" name="eaiSvcName" style="width:calc(100% - 60px);" readonly> <%-- <img id="btn_search" src="<c:url value="/img/btn_pop_search.png" />" level="R" class="btn_img" /> --%>
|
||||
<button type="button" class="cssbtn smallBtn2" id="btn_search" style="vertical-align:middle; font-weight:bold;" level="R"><i class="material-icons">search</i><%= localeMessage.getString("button.search") %></button>
|
||||
</td>
|
||||
<th style="width:14%; min-width:130px;"><%=localeMessage.getString("transformMan.ifSvcDesc")%></th>
|
||||
<td style="width:41%;">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><%=localeMessage.getString("transformMan.ifSvcDesc")%></th>
|
||||
<td colspan="3">
|
||||
<input type="text" name="eaiSvcDesc" readOnly>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><%=localeMessage.getString("transformMan.eaiSvrDiv")%></th>
|
||||
<td>
|
||||
<td colspan="3">
|
||||
<div class="select-style">
|
||||
<select name="eaiSevrDstcd" />
|
||||
</div>
|
||||
</td>
|
||||
<th style="width:13%; min-width:130px;"><%=localeMessage.getString("combo.useYn")%></th>
|
||||
<td>
|
||||
<div class="select-style">
|
||||
<select name="useYn" />
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><%=localeMessage.getString("transformMan.cnvsnType")%></th>
|
||||
@@ -192,13 +186,20 @@ $(document).ready(function() {
|
||||
<select name="cnvsnType" />
|
||||
</div>
|
||||
</td>
|
||||
|
||||
<th><%=localeMessage.getString("combo.useYn")%></th>
|
||||
<td>
|
||||
<div class="select-style">
|
||||
<select name="useYn" />
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><%=localeMessage.getString("transformMan.telCngMappCd")%></th>
|
||||
<td><input type="text" name="cnvsnName" readonly="readonly"/></td>
|
||||
<td colspan="3"><input type="text" name="cnvsnName" readonly="readonly"/></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<th><%=localeMessage.getString("transformMan.cnvsnDesc")%></th><td colspan="3"><textarea name="cnvsnDesc" style="width:100%;height:50px"></textarea></td>
|
||||
<tr height="100px">
|
||||
<th><%=localeMessage.getString("transformMan.cnvsnDesc")%></th><td colspan="3"><textarea name="cnvsnDesc" style="width:100%;height:100px"></textarea></td>
|
||||
</tr>
|
||||
</table>
|
||||
</form>
|
||||
|
||||
@@ -47,6 +47,9 @@ function init(callback){
|
||||
dataType:"json",
|
||||
data:{cmd: 'LIST_INIT', codeGroupArr : 'SVC_SAME_TIME_TYPE,<%= tranTypeCode %>'},
|
||||
success:function(json){
|
||||
new makeOptions("CODE","NAME").setObj($("select[name=searchSvcMotivUsedStcd]")).setNoValueInclude(true).setNoValue("", "<%=localeMessage.getString("combo.all")%>").setData(json.list[0].SVC_SAME_TIME_TYPE).rendering();
|
||||
new makeOptions("CODE","NAME").setObj($("select[name=searchPsvintfacdsticName]")).setNoValueInclude(true).setNoValue("", "<%=localeMessage.getString("combo.all")%>").setData(json.list[0].SVC_SAME_TIME_TYPE).rendering();
|
||||
new makeOptions("CODE","NAME").setObj($("select[name=searchFlowControl]")).setNoValueInclude(true).setNoValue("", "<%=localeMessage.getString("combo.all")%>").setData(json.listFlowControl).rendering();
|
||||
new makeOptions("CODE","NAME").setObj($("select[name=searchTranType]")).setNoValueInclude(true).setNoValue("", "<%=localeMessage.getString("combo.all")%>").setData(json.list[0].<%= tranTypeCode %>).rendering();
|
||||
svrInst = json.listInoutType;
|
||||
putSelectFromParam();
|
||||
@@ -86,6 +89,8 @@ $(document).ready(function() {
|
||||
gridPostData["rmsMenuAuth"]= auth;
|
||||
|
||||
init(detail);
|
||||
gridPostData["searchSvcMotivUsedStcd"] = '${param.searchSvcMotivUsedStcd}';
|
||||
gridPostData["searchPsvintfacdsticName"] = '${param.searchPsvintfacdsticName}';
|
||||
|
||||
$('#grid').jqGrid({
|
||||
datatype:"json",
|
||||
@@ -95,8 +100,10 @@ $(document).ready(function() {
|
||||
colNames:['<%= localeMessage.getString("eaiMsg.bizCode")%> ',
|
||||
'<%= localeMessage.getString("eaiMessage.eaiSvcName")%> ',
|
||||
'<%= localeMessage.getString("eaiMessage.eaiSvcDesc")%> ',
|
||||
'<%= localeMessage.getString("eaiMessage.gstatSysAdptr")%>',
|
||||
'<%= localeMessage.getString("eaiMessage.psvSysAdptr")%>',
|
||||
'<%= localeMessage.getString("eaiMessage.svcMotivUsedStcd")%>',
|
||||
'<%= localeMessage.getString("eaiMessage.psvintfacdsticName")%>',
|
||||
'<%= localeMessage.getString("eaiMessage.queueName")%> ',
|
||||
'<%= localeMessage.getString("eaiMessage.flowCtrlRoutName")%> ',
|
||||
'<%= localeMessage.getString("eaiMessage.tranType")%> ',
|
||||
'<%= localeMessage.getString("combo.useYn")%> ',
|
||||
'<%= localeMessage.getString("eaiMessage.sevrDstCd")%>',
|
||||
@@ -106,13 +113,15 @@ $(document).ready(function() {
|
||||
{ name : 'EAIBZWKDSTCD' , align:'center' ,sortable:false, width:20 },
|
||||
{ name : 'EAISVCNAME' , align:'left', width:50 },
|
||||
{ name : 'EAISVCDESC' , align:'left' },
|
||||
{ name : 'GSTATSYSADPTRBZWKGROUPNAME', align:'left', width:80 },
|
||||
{ name : 'PSVSYSADPTRBZWKGROUPNAME', align:'left', width:80 },
|
||||
{ name : 'SVCMOTIVUSEDSTCD', align:'center', width:50 },
|
||||
{ name : 'PSVINTFACDSTICNAME', align:'center', width:50 },
|
||||
{ name : 'NONMOTIVROUTURINAME' , align:'left', width:90 },
|
||||
{ name : 'FLOWCTRLROUTNAME' , align:'center', width:40 },
|
||||
{ name : 'TRANTYPE', align:'center', width:30 },
|
||||
{ name : 'USEYN', align:'center', width:30,editoptions:{value:"0:<%= localeMessage.getString("combo.usen")%>;1:<%= localeMessage.getString("combo.usey")%>"}, formatter:"select"},
|
||||
{ name : 'EAISEVRDSTCD', align:'center', width:30 , formatter:formatter, hidden:true},
|
||||
{ name : 'VERINFO', align:'center', width:30 }
|
||||
|
||||
|
||||
],
|
||||
jsonReader: {
|
||||
repeatitems:false
|
||||
@@ -417,25 +426,28 @@ function deployInterfaces(ret) {
|
||||
<td>
|
||||
<input type="text" name="searchEaiSvcName" value="${param.searchEaiSvcName}">
|
||||
</td>
|
||||
<th style="width:10%; min-width:120px;"><%= localeMessage.getString("eaiMessage.svcMotivUsedStcd")%></th>
|
||||
<td><div class="select-style"><select name="searchSvcMotivUsedStcd" value="${param.searchSvcMotivUsedStcd}"></select></div></td>
|
||||
<th style="width:10%; min-width:90px;"><%= localeMessage.getString("eaiMessage.tranType")%></th>
|
||||
<td><div class="select-style"><select name="searchTranType" value="${param.searchTranType}"></select></div></td>
|
||||
|
||||
<th style="width:10%; min-width:90px;"><%= localeMessage.getString("eaiMessage.eaiSvcDesc")%></th>
|
||||
<td>
|
||||
<input type="text" name="searchEaiSvcDesc" value="${param.searchEaiSvcDesc}">
|
||||
</td>
|
||||
<th style="width:10%; min-width:90px;"><%= localeMessage.getString("eaiMessage.tranType")%></th>
|
||||
<td><div class="select-style"><select name="searchTranType" value="${param.searchTranType}"></select></div></td>
|
||||
</tr>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><%= localeMessage.getString("eaiMessage.searchBzwkDstCd")%></th>
|
||||
<td>
|
||||
<input type="text" name="searchEaiBzwkDstcd" value="${param.searchEaiBzwkDstcd}">
|
||||
</td>
|
||||
<th><%= localeMessage.getString("eaiMessage.gstatSysAdptr")%></th>
|
||||
<th><%= localeMessage.getString("eaiMessage.psvintfacdsticName")%></th>
|
||||
<td><div class="select-style"><select name="searchPsvintfacdsticName" value="${param.searchPsvintfacdsticName}"></select></div></td>
|
||||
<th><%= localeMessage.getString("eaiMessage.searchFlowControl")%></th>
|
||||
<td><div class="select-style"><select name="searchFlowControl" value="${param.searchFlowControl}"></select></div></td>
|
||||
<th><%= localeMessage.getString("eaiMessage.queueName")%></th>
|
||||
<td>
|
||||
<input type="text" name="searchGstatSysAdptrBzwkGroupName" value="${param.searchGstatSysAdptrBzwkGroupName}">
|
||||
</td>
|
||||
<th><%= localeMessage.getString("eaiMessage.psvSysAdptr")%></th>
|
||||
<td>
|
||||
<input type="text" name="searchPsvSysAdptrBzwkGroupName" value="${param.searchPsvSysAdptrBzwkGroupName}">
|
||||
<input type="text" name="searchQueueName" value="${param.searchQueueName}">
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
||||
@@ -415,12 +415,6 @@
|
||||
<input type="text" name="groupName" data-required data-warning="API 그룹 명을 입력하여 주십시오.">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th style="width:150px;">그룹 설명</th>
|
||||
<td colspan="7">
|
||||
<input type="text" name="groupDesc">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th style="width:150px;">공개 여부</th>
|
||||
<td style="width:100px;">
|
||||
@@ -467,6 +461,14 @@
|
||||
<img id="mainIconPreview" src="#" alt="Main Icon Preview" style="display:none;height:20px">
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
|
||||
<%--<tr>
|
||||
<th>컨텐츠 내용</th>
|
||||
<td colspan="5">
|
||||
<textarea name="contentDetail" id="contentDetail" rows="10" cols="50"></textarea>
|
||||
</td>
|
||||
</tr>--%>
|
||||
</table>
|
||||
<div style="margin-top:15px;">
|
||||
<div style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 5px ">
|
||||
|
||||
@@ -72,22 +72,13 @@
|
||||
});
|
||||
|
||||
}
|
||||
// 체크박스는 name 을 search* 로 두면 getSearchForJqgrid 가 해제 상태에서도 value 를 담아버린다
|
||||
// (input[type!=radio][name^=search] 를 checked 여부와 무관하게 수집) → 여기서 직접 붙인다
|
||||
function getPopupSearchData() {
|
||||
var postData = getSearchForJqgrid("cmd","LIST"); //jqgrid에서는 object 로
|
||||
postData.searchApiSpecYn = $("#chkApiSpec").is(":checked") ? "Y" : "";
|
||||
postData.searchPortalDisplayYn = $("#chkPortalDisplay").is(":checked") ? "Y" : "";
|
||||
return postData;
|
||||
}
|
||||
|
||||
function detail(){
|
||||
var postData = getPopupSearchData();
|
||||
var postData = getSearchForJqgrid("cmd","LIST"); //jqgrid에서는 object 로
|
||||
$("#grid").setGridParam({ url:url,postData: postData }).trigger("reloadGrid");
|
||||
}
|
||||
|
||||
function search(){
|
||||
var postData = getPopupSearchData();
|
||||
var postData = getSearchForJqgrid("cmd","LIST"); //jqgrid에서는 object 로
|
||||
$("#grid").setGridParam({ url:url,postData: postData ,page:1 }).trigger("reloadGrid");
|
||||
}
|
||||
function ioFormatter(cellvalue,options,rowObject){
|
||||
@@ -106,7 +97,7 @@
|
||||
|
||||
function list(){
|
||||
detail()
|
||||
var gridPostData = getPopupSearchData();
|
||||
var gridPostData = getSearchForJqgrid("cmd","LIST"); //jqgrid에서는 object 로
|
||||
// gridPostData.searchApiEnableYn = 'Y';
|
||||
var urlParams = new URLSearchParams(window.location.search);
|
||||
var selectedApiIds = urlParams.get("selectedApiIds");
|
||||
@@ -125,27 +116,21 @@
|
||||
'작성자',
|
||||
'가상응답여부',
|
||||
'SyncAsyncType',
|
||||
'변경일시',
|
||||
],
|
||||
colModel : [ { name : 'eaiBzwkDstcd' , align : 'center' , width:'40', sortable:false},
|
||||
{ name : 'eaiSvcName' , align : 'left' , width:'100', sortable:true},
|
||||
{ name : 'eaiSvcDesc' , align : 'left' , sortable:false},
|
||||
{ name : 'apiFullPath' , align : 'left' , width:'100', sortable:false},
|
||||
{ name : 'eaiSvcName' , align : 'left' , width:'100'},
|
||||
{ name : 'eaiSvcDesc' , align : 'left' },
|
||||
{ name : 'apiFullPath' , align : 'left' , width:'100'},
|
||||
{ name : 'bzwksvckeyname' , align : 'left' , width:'100', hidden: true},
|
||||
{ name : 'fromAdapter' , align : 'center' , width:'40', formatter:adapterNameShortFormatter, sortable:false },
|
||||
{ name : 'toAdapter' , align : 'center' , width:'40', formatter:adapterNameShortFormatter, sortable:false },
|
||||
{ name : 'author' , align : 'center' , width:'60', sortable:false },
|
||||
{ name : 'simYn' , align : 'center' , width:'40', sortable:false },
|
||||
{ name : 'fromAdapter' , align : 'center' , width:'40', formatter:adapterNameShortFormatter },
|
||||
{ name : 'toAdapter' , align : 'center' , width:'40', formatter:adapterNameShortFormatter },
|
||||
{ name : 'author' , align : 'center' , width:'60' },
|
||||
{ name : 'simYn' , align : 'center' , width:'40' },
|
||||
{ name : 'syncAsyncType' , align : 'center' , width:'40', hidden: true},
|
||||
{ name : 'lastModifiedDate' , align : 'center' , width:'130', sortable:true },
|
||||
],
|
||||
jsonReader : {
|
||||
repeatitems : false
|
||||
},
|
||||
// 서버(ApiInterfaceController#selectList)가 sidx/sord 대신 sortName/sortOrder 파라미터로 정렬조건을 받는다
|
||||
prmNames : { sort : 'sortName', order : 'sortOrder' },
|
||||
sortname : 'lastModifiedDate',
|
||||
sortorder : 'desc',
|
||||
pager : $('#pager'),
|
||||
page : '${param.page}',
|
||||
rowNum : '${rmsDefaultRowNum}',
|
||||
@@ -167,6 +152,10 @@
|
||||
}
|
||||
});
|
||||
}
|
||||
var colModel = $(this).getGridParam("colModel");
|
||||
for(var i = 0 ; i< colModel.length; i++){
|
||||
$(this).setColProp(colModel[i].name, {sortable : false});
|
||||
}
|
||||
updateSelectionInfo("#grid");
|
||||
},
|
||||
onSelectRow: function(rowid, status, e) {
|
||||
@@ -200,9 +189,6 @@
|
||||
$("select[name=searchEaiBzwkDstcd]").change(function() {
|
||||
search();
|
||||
});
|
||||
$("#chkApiSpec, #chkPortalDisplay").change(function() {
|
||||
search();
|
||||
});
|
||||
$("#btn_search").click(function(){
|
||||
search();
|
||||
});
|
||||
@@ -261,18 +247,6 @@
|
||||
<input type="text" name="searchApiFullPath" value="${param.searchApiFullPath}">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th style="width:180px;">포탈 노출</th>
|
||||
<td colspan="3">
|
||||
<label class="checkbox-inline">
|
||||
<input type="checkbox" id="chkApiSpec" <c:if test="${param.searchApiSpecYn eq 'Y'}">checked</c:if>> OpenAPI Spec 등록
|
||||
</label>
|
||||
|
||||
<label class="checkbox-inline">
|
||||
<input type="checkbox" id="chkPortalDisplay" <c:if test="${param.searchPortalDisplayYn eq 'Y'}">checked</c:if>> 포탈 게시
|
||||
</label>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<table id="grid" ></table>
|
||||
|
||||
@@ -56,16 +56,11 @@
|
||||
$("#expectEndDate").datepicker().inputmask("yyyy-mm-dd", {'autoUnmask': true});
|
||||
|
||||
$("#btn_app_approve").click(function () {
|
||||
var isDeleteReq = (currentRequestType === 'DELETE');
|
||||
var gwAction = (isDeleteReq && $('#chkGwDelete').is(':checked')) ? 'DELETE' : 'BLOCK';
|
||||
var confirmMsg = isDeleteReq
|
||||
? "해지 승인 시 포털 인증키(Credential)는 삭제되며,\n게이트웨이 클라이언트는 [" + (gwAction === 'DELETE' ? "완전 삭제" : "차단(비활성화)") + "] 처리됩니다.\n승인하시겠습니까?"
|
||||
: "승인하시겠습니까?";
|
||||
if (confirm(confirmMsg)) {
|
||||
if (confirm("승인하시겠습니까?")) {
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: url,
|
||||
data: {cmd: "APPROVE", id: key, gwAction: gwAction},
|
||||
data: {cmd: "APPROVE", id: key},
|
||||
success: function (args) {
|
||||
<%--alert("<%= localeMessage.getString("approval.approveSuccess") %>");--%>
|
||||
alert("승인되었습니다.");
|
||||
@@ -106,32 +101,6 @@
|
||||
}
|
||||
});
|
||||
|
||||
$("#btn_app_force_reject").click(function () {
|
||||
if (!confirm("승인자 목록에 삭제된 사용자가 있어 승인 진행이 불가능한 건입니다.\n강제 반려하시겠습니까?")) {
|
||||
return;
|
||||
}
|
||||
var rejectReason = prompt("강제 반려 사유를 입력해 주세요");
|
||||
if (rejectReason != null) {
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: url,
|
||||
data: {cmd: "FORCE_REJECT", id: key, rejectReason: rejectReason},
|
||||
success: function (args) {
|
||||
alert("강제 반려되었습니다.");
|
||||
if (window.dialogArguments && window.dialogArguments.self) {
|
||||
window.dialogArguments.self.location.reload();
|
||||
} else if (window.opener) {
|
||||
window.opener.location.reload();
|
||||
}
|
||||
window.close();
|
||||
},
|
||||
error: function (e) {
|
||||
alert(e.responseText);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
$('#btn_app_redeploy').click(function () {
|
||||
if (confirm("재배포 하시겠습니까?")) {
|
||||
$.ajax({
|
||||
@@ -176,65 +145,11 @@
|
||||
}
|
||||
});
|
||||
|
||||
$("#btn_app_delete").click(function () {
|
||||
if (!confirm("이 승인 요청을 삭제하시겠습니까?\n삭제 후에는 복구할 수 없습니다.")) {
|
||||
return;
|
||||
}
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: url,
|
||||
data: {cmd: "DELETE", id: key},
|
||||
success: function (args) {
|
||||
alert("삭제되었습니다.");
|
||||
if (window.dialogArguments && window.dialogArguments.self) {
|
||||
window.dialogArguments.self.location.reload();
|
||||
} else if (window.opener) {
|
||||
window.opener.location.reload();
|
||||
}
|
||||
window.close();
|
||||
},
|
||||
error: function (e) {
|
||||
alert(approvalErrorMessage(e));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$("#btn_close").click(function () {
|
||||
window.close();
|
||||
});
|
||||
});
|
||||
|
||||
function approvalErrorMessage(e) {
|
||||
try {
|
||||
var json = JSON.parse(e.responseText);
|
||||
return json.errorMsg || e.responseText;
|
||||
} catch (ex) {
|
||||
return e.responseText;
|
||||
}
|
||||
}
|
||||
|
||||
// 요청자(PTL_USER) row 가 삭제되면 상세 조회 자체가 불가능하다. 이 경우 삭제만 가능하도록 노출한다.
|
||||
function showLoadFailure(message) {
|
||||
$("#approvalSubject").text("상세 정보를 조회할 수 없는 승인 요청");
|
||||
$("#approvalDetail").text(message);
|
||||
$("#btn_modify, #btn_app_approve, #btn_app_reject, #btn_app_force_reject, #btn_app_redeploy").hide();
|
||||
$("#gwActionSection, #gwActionResult").hide();
|
||||
document.getElementById("btn_app_delete").style.display = "";
|
||||
}
|
||||
|
||||
var currentRequestType = null; // 현재 신청 유형 (DELETE 승인 시 GW 처리방식 선택 노출용)
|
||||
|
||||
// 값이 있을 때만 행을 노출한다. (해지 신청처럼 해당 항목이 없는 유형에서 빈 행이 남지 않도록)
|
||||
function setOptionalRow(rowId, spanId, value) {
|
||||
if (value) {
|
||||
$("#" + spanId).text(value);
|
||||
$("#" + rowId).show();
|
||||
} else {
|
||||
$("#" + spanId).text("");
|
||||
$("#" + rowId).hide();
|
||||
}
|
||||
}
|
||||
|
||||
function detail(key) {
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
@@ -263,11 +178,10 @@
|
||||
$("#createdDate").text(data.createdDate).inputmask("9999-99-99 99:99:99.999", {'autoUnmask': true});
|
||||
$("#expectEndDate").val(data.expectEndDate);
|
||||
|
||||
var isCurrentApprover = data.approvers.some(approver =>
|
||||
if (data.approvers.some(approver =>
|
||||
approver.approverStatus === 'CURRENT' &&
|
||||
approver.user && approver.user.USERID === loginUser
|
||||
);
|
||||
if (isCurrentApprover) {
|
||||
approver.user.USERID === loginUser
|
||||
)) {
|
||||
document.getElementById("btn_app_approve").style.display = "";
|
||||
document.getElementById("btn_app_reject").style.display = "";
|
||||
} else {
|
||||
@@ -275,29 +189,6 @@
|
||||
document.getElementById("btn_app_reject").style.display = "none";
|
||||
}
|
||||
|
||||
// API 이용해지(DELETE) 건: 승인 가능 상태의 현재 승인자에게만 GW 처리방식 선택 노출
|
||||
currentRequestType = data.requestType;
|
||||
var inProgress = data.approvalStatus.code === 'REQUESTED' || data.approvalStatus.code === 'PROCESSING';
|
||||
if (data.requestType === 'DELETE' && isCurrentApprover && inProgress) {
|
||||
$("#gwActionSection").show();
|
||||
} else {
|
||||
$("#gwActionSection").hide();
|
||||
}
|
||||
// 처리 완료된 해지 건은 어떤 방식으로 실행됐는지 표시
|
||||
if (data.requestType === 'DELETE' && data.gwAction) {
|
||||
$("#gwActionResult").text("GW 처리방식: " + (data.gwAction === 'DELETE' ? "완전 삭제" : "차단(비활성화)")).show();
|
||||
} else {
|
||||
$("#gwActionResult").hide();
|
||||
}
|
||||
|
||||
// 삭제된 승인자(USERID 없음)로 진행 불가한 건: 요청됨/진행중 상태면 승인자가 아니어도 강제 반려 허용
|
||||
var hasDeletedApprover = data.approvers.some(approver =>
|
||||
!approver.user || !approver.user.USERID
|
||||
);
|
||||
var isProgressStatus = data.approvalStatus.code === 'REQUESTED' || data.approvalStatus.code === 'PROCESSING';
|
||||
document.getElementById("btn_app_force_reject").style.display =
|
||||
(hasDeletedApprover && isProgressStatus && !isCurrentApprover) ? "" : "none";
|
||||
|
||||
if (data.approvalStatus.code === 'FAILED') {
|
||||
document.getElementById("btn_app_redeploy").style.display = "";
|
||||
}
|
||||
@@ -316,21 +207,6 @@
|
||||
|
||||
$("#clientName").text(data.clientName);
|
||||
|
||||
// 신청서에 담긴 Client 등록 정보(승인 시 GW 클라이언트로 반영되는 값). 값이 있을 때만 노출
|
||||
setOptionalRow("appDescriptionRow", "appDescription", data.appDescription);
|
||||
setOptionalRow("ipWhitelistRow", "ipWhitelist", data.ipWhitelist);
|
||||
setOptionalRow("callbackUrlRow", "callbackUrl", data.callbackUrl);
|
||||
|
||||
// 신청 사유. 해지 신청은 사유 입력이 필수이므로 "해지 사유"로 표기하고, 그 외 유형은 "신청 사유"
|
||||
if (data.requestReason) {
|
||||
$("#requestReasonLabel").text(data.requestType === 'DELETE' ? "해지 사유" : "신청 사유");
|
||||
$("#requestReason").text(data.requestReason);
|
||||
$("#requestReasonRow").show();
|
||||
} else {
|
||||
$("#requestReason").text("");
|
||||
$("#requestReasonRow").hide();
|
||||
}
|
||||
|
||||
var apiServiceHtml = "";
|
||||
for (var i = 0; i < data.apiServices.length; i++) {
|
||||
var apiService = data.apiServices[i];
|
||||
@@ -385,9 +261,7 @@
|
||||
$("#apisTable tbody").html(apisHtml);
|
||||
},
|
||||
error: function (e) {
|
||||
var message = approvalErrorMessage(e);
|
||||
alert(message);
|
||||
showLoadFailure(message);
|
||||
alert(e.responseText);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -434,22 +308,6 @@
|
||||
<th style="width:20%;">이름</th>
|
||||
<td><span id="clientName"></span></td>
|
||||
</tr>
|
||||
<tr id="appDescriptionRow" style="display:none;">
|
||||
<th style="width:20%;">앱 설명</th>
|
||||
<td><span id="appDescription" style="white-space: pre-wrap;"></span></td>
|
||||
</tr>
|
||||
<tr id="ipWhitelistRow" style="display:none;">
|
||||
<th style="width:20%;">IP 화이트리스트</th>
|
||||
<td><span id="ipWhitelist" style="white-space: pre-wrap;"></span></td>
|
||||
</tr>
|
||||
<tr id="callbackUrlRow" style="display:none;">
|
||||
<th style="width:20%;">Callback URL</th>
|
||||
<td><span id="callbackUrl" style="white-space: pre-wrap;"></span></td>
|
||||
</tr>
|
||||
<tr id="requestReasonRow" style="display:none;">
|
||||
<th style="width:20%;"><span id="requestReasonLabel">신청 사유</span></th>
|
||||
<td><span id="requestReason" style="white-space: pre-wrap;"></span></td>
|
||||
</tr>
|
||||
</table>
|
||||
<div class="title">API 목록</div>
|
||||
<table id="apisTable" class="table_row" cellspacing="0">
|
||||
@@ -477,13 +335,6 @@
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div id="gwActionSection" style="display:none; margin: 12px 0 0 4px; color: #c0392b;">
|
||||
<label style="cursor:pointer;">
|
||||
<input type="checkbox" id="chkGwDelete"/>
|
||||
GW 인증정보 완전 삭제 (미체크 시 차단 처리 — 인증정보 보존, 복구 가능)
|
||||
</label>
|
||||
</div>
|
||||
<div id="gwActionResult" style="display:none; margin: 12px 0 0 4px; font-weight: bold;"></div>
|
||||
<div class="popup_button">
|
||||
<button type="button" class="cssbtn" id="btn_modify" level="W"><i
|
||||
class="material-icons">save</i>수정
|
||||
@@ -494,12 +345,6 @@
|
||||
<button type="button" class="cssbtn" style="display: none;" id="btn_app_reject" level="W"><i
|
||||
class="material-icons">close</i>반려
|
||||
</button>
|
||||
<button type="button" class="cssbtn" style="display: none;" id="btn_app_force_reject" level="W"><i
|
||||
class="material-icons">block</i>강제 반려
|
||||
</button>
|
||||
<button type="button" class="cssbtn" style="display: none;" id="btn_app_delete" level="W"><i
|
||||
class="material-icons">delete</i>삭제
|
||||
</button>
|
||||
<button type="button" class="cssbtn" style="display: none;" id="btn_app_redeploy" level="W"><i
|
||||
class="material-icons">close</i>재처리
|
||||
</button>
|
||||
|
||||
@@ -131,24 +131,6 @@
|
||||
// var reverseNumber = records - ((page - 1) * rowNum + i);
|
||||
$(this).setCell(rows[i], 'rowNum', number);
|
||||
}
|
||||
|
||||
// 승인 상태별 행 배경색: 요청됨(#fff3cd) / 진행중(#d1ecf1) 강조
|
||||
if (d && d.rows) {
|
||||
for (var j = 0; j < d.rows.length; j++) {
|
||||
var st = d.rows[j].approvalStatus || {};
|
||||
var code = st.code || '';
|
||||
var desc = st.description || '';
|
||||
var bg = '';
|
||||
if (code === 'REQUESTED' || desc === '요청됨') {
|
||||
bg = '#fff3cd';
|
||||
} else if (code === 'PROCESSING' || desc === '진행중') {
|
||||
bg = '#d1ecf1';
|
||||
}
|
||||
if (bg) {
|
||||
$('#grid tr#' + $.jgrid.jqID(d.rows[j].id)).css('background-color', bg);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
loadError: function(jqXHR, textStatus, errorThrown) {
|
||||
var location = '<%=request.getContextPath()%>/';
|
||||
@@ -194,7 +176,7 @@
|
||||
<div class="title">승인 요청 목록<span class="tooltip">승인 요청을 관리합니다.</span></div>
|
||||
<c:if test="${hardDeleteEnabled}">
|
||||
<div style="background:#fff3cd; border:1px solid #ffc107; padding:8px 15px; margin:5px 0; border-radius:4px; color:#856404;">
|
||||
<strong>[주의]</strong> 삭제 기능은 <strong>개발(dev) 환경에서만</strong> 동작합니다. 선택 삭제 시 승인 요청과 승인자 이력이 DB에서 영구 삭제됩니다.
|
||||
<strong>[주의]</strong> 선택 삭제 시 승인 요청과 승인자 이력이 DB에서 영구 삭제됩니다.
|
||||
</div>
|
||||
</c:if>
|
||||
<form id="ajaxForm" onsubmit="return false;">
|
||||
|
||||
@@ -45,10 +45,6 @@
|
||||
padding: 2px 5px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
.cssbtn:disabled {
|
||||
opacity: 0.45;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
</style>
|
||||
<script language="javascript">
|
||||
<%
|
||||
@@ -154,55 +150,6 @@
|
||||
}
|
||||
});
|
||||
|
||||
$("#btn_user_force_reject").click(function() {
|
||||
if (!confirm("승인자 목록에 삭제된 사용자가 있어 승인 진행이 불가능한 건입니다.\n강제 반려하시겠습니까?")) {
|
||||
return;
|
||||
}
|
||||
var rejectReason = prompt("강제 반려 사유를 입력해 주세요");
|
||||
if (rejectReason != null) {
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: url,
|
||||
data: {cmd: "FORCE_REJECT", id: key, rejectReason: rejectReason},
|
||||
success: function(args) {
|
||||
alert("강제 반려되었습니다.");
|
||||
if (window.dialogArguments && window.dialogArguments.self) {
|
||||
window.dialogArguments.self.location.reload();
|
||||
} else if (window.opener) {
|
||||
window.opener.location.reload();
|
||||
}
|
||||
window.close();
|
||||
},
|
||||
error: function(e) {
|
||||
alert(e.responseText);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
$("#btn_user_delete").click(function() {
|
||||
if (!confirm("삭제된 요청자·사용자 또는 법인이 포함되어 승인·반려할 수 없는 요청입니다.\n이 승인 요청을 삭제하시겠습니까?\n삭제 후에는 복구할 수 없습니다.")) {
|
||||
return;
|
||||
}
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: url,
|
||||
data: {cmd: "DELETE", id: key},
|
||||
success: function(args) {
|
||||
alert("삭제되었습니다.");
|
||||
if (window.dialogArguments && window.dialogArguments.self) {
|
||||
window.dialogArguments.self.location.reload();
|
||||
} else if (window.opener) {
|
||||
window.opener.location.reload();
|
||||
}
|
||||
window.close();
|
||||
},
|
||||
error: function(e) {
|
||||
alert(approvalErrorMessage(e));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$('#btn_user_redeploy').click(function() {
|
||||
if (confirm("재배포 하시겠습니까?")) {
|
||||
$.ajax({
|
||||
@@ -270,55 +217,20 @@
|
||||
|
||||
// Display Portal User details
|
||||
var portalUser = data.portalUser;
|
||||
var portalOrg = portalUser ? portalUser.portalOrgUIs : null;
|
||||
|
||||
// 삭제된 대상 사용자/법인 판별
|
||||
var userDeleted = !portalUser
|
||||
|| portalUser.userName === '반려된 사용자'
|
||||
|| portalUser.userName === '삭제된 사용자';
|
||||
var orgDeleted = !portalOrg
|
||||
|| portalOrg.orgStatus === 'REMOVED'
|
||||
|| portalOrg.orgName === '삭제된법인';
|
||||
var deletedTarget = userDeleted || orgDeleted;
|
||||
|
||||
if (portalUser) {
|
||||
$("#loginId").text(portalUser.loginId);
|
||||
$("#userName").text(portalUser.userName);
|
||||
$("#mobileNumber").text(portalUser.mobileNumber);
|
||||
$("#userStatusDescription").text(portalUser.userStatusDescription);
|
||||
$("#roleCodeDescription").text(portalUser.roleCodeDescription);
|
||||
} else {
|
||||
$("#loginId, #userName, #mobileNumber, #userStatusDescription, #roleCodeDescription").text("삭제된 사용자");
|
||||
}
|
||||
|
||||
var isCurrentApprover = data.approvers.some(approver =>
|
||||
$("#loginId").text(portalUser.loginId);
|
||||
$("#userName").text(portalUser.userName);
|
||||
$("#mobileNumber").text(portalUser.mobileNumber);
|
||||
$("#userStatusDescription").text(portalUser.userStatusDescription);
|
||||
$("#roleCodeDescription").text(portalUser.roleCodeDescription);
|
||||
if (data.approvers.some(approver =>
|
||||
approver.approverStatus === 'CURRENT' &&
|
||||
approver.user && approver.user.USERID === loginUser
|
||||
);
|
||||
|
||||
if (deletedTarget) {
|
||||
// 삭제된 사용자/법인 포함 → 승인·반려 불가. 버튼은 비활성(disable) 표기, 삭제 버튼 노출
|
||||
$("#btn_user_approve, #btn_user_reject").show().prop("disabled", true);
|
||||
document.getElementById("btn_user_force_reject").style.display = "none";
|
||||
document.getElementById("btn_user_delete").style.display = "";
|
||||
approver.user.USERID === loginUser
|
||||
)) {
|
||||
document.getElementById("btn_user_approve").style.display = "";
|
||||
document.getElementById("btn_user_reject").style.display = "";
|
||||
} else {
|
||||
$("#btn_user_approve, #btn_user_reject").prop("disabled", false);
|
||||
document.getElementById("btn_user_delete").style.display = "none";
|
||||
if (isCurrentApprover) {
|
||||
document.getElementById("btn_user_approve").style.display = "";
|
||||
document.getElementById("btn_user_reject").style.display = "";
|
||||
} else {
|
||||
document.getElementById("btn_user_approve").style.display = "none";
|
||||
document.getElementById("btn_user_reject").style.display = "none";
|
||||
}
|
||||
|
||||
// 삭제된 승인자(USERID 없음)로 진행 불가한 건: 요청됨/진행중 상태면 승인자가 아니어도 강제 반려 허용
|
||||
var hasDeletedApprover = data.approvers.some(approver =>
|
||||
!approver.user || !approver.user.USERID
|
||||
);
|
||||
var isProgressStatus = data.approvalStatus.code === 'REQUESTED' || data.approvalStatus.code === 'PROCESSING';
|
||||
document.getElementById("btn_user_force_reject").style.display =
|
||||
(hasDeletedApprover && isProgressStatus && !isCurrentApprover) ? "" : "none";
|
||||
document.getElementById("btn_user_approve").style.display = "none";
|
||||
document.getElementById("btn_user_reject").style.display = "none";
|
||||
}
|
||||
|
||||
if (data.approvalStatus.description === '배포실패'){
|
||||
@@ -326,27 +238,23 @@
|
||||
}
|
||||
|
||||
// Display Portal Org details
|
||||
if (portalOrg) {
|
||||
$("#orgName").text(portalOrg.orgName);
|
||||
$("#orgStatusDescription").text(portalOrg.orgStatusDescription);
|
||||
$("#serviceName").text(portalOrg.serviceName);
|
||||
$("#corpRegNo").text(formatCorpRegNo(portalOrg.corpRegNo));
|
||||
$("#compRegNo").text(formatCompanyRegNo(portalOrg.compRegNo));
|
||||
var portalOrg = portalUser.portalOrgUIs;
|
||||
$("#orgName").text(portalOrg.orgName);
|
||||
$("#orgStatusDescription").text(portalOrg.orgStatusDescription);
|
||||
$("#serviceName").text(portalOrg.serviceName);
|
||||
$("#corpRegNo").text(formatCorpRegNo(portalOrg.corpRegNo));
|
||||
$("#compRegNo").text(formatCompanyRegNo(portalOrg.compRegNo));
|
||||
|
||||
$("#scPhoneNumber").text(portalOrg.scPhoneNumber); // 고객센터 전화번호
|
||||
$("#orgPhoneNumber").text(portalOrg.orgPhoneNumber); // 회사 전화번호
|
||||
$("#ceoName").text(portalOrg.ceoName); // 대표자 성명
|
||||
$("#orgIndustryType").text(portalOrg.orgIndustryType || ""); // 업종
|
||||
$("#orgAddr").text(portalOrg.orgAddr); // 소재지
|
||||
$("#orgSectors").text(portalOrg.orgSectors || ""); // 업태
|
||||
$("#orgCode").text(portalOrg.orgCode);
|
||||
$("#ipWhitelist").text(portalOrg.ipWhitelist || "");
|
||||
} else {
|
||||
$("#orgName").text("삭제된법인");
|
||||
$("#orgStatusDescription, #serviceName, #corpRegNo, #compRegNo, #scPhoneNumber, #orgPhoneNumber, #ceoName, #orgIndustryType, #orgAddr, #orgSectors, #orgCode, #ipWhitelist").text("");
|
||||
}
|
||||
$("#scPhoneNumber").text(portalOrg.scPhoneNumber); // 고객센터 전화번호
|
||||
$("#orgPhoneNumber").text(portalOrg.orgPhoneNumber); // 회사 전화번호
|
||||
$("#ceoName").text(portalOrg.ceoName); // 대표자 성명
|
||||
$("#orgIndustryType").text(portalOrg.orgIndustryType || ""); // 업종
|
||||
$("#orgAddr").text(portalOrg.orgAddr); // 소재지
|
||||
$("#orgSectors").text(portalOrg.orgSectors || ""); // 업태
|
||||
$("#orgCode").text(portalOrg.orgCode);
|
||||
$("#ipWhitelist").text(portalOrg.ipWhitelist || "");
|
||||
|
||||
if (portalOrg && portalOrg.compRegFile) {
|
||||
if (portalOrg.compRegFile) {
|
||||
$("#fileName").text(portalOrg.fileName);
|
||||
if (portalOrg.fileName) {
|
||||
$("#fileWrapper").show().off('click').on('click', function() {
|
||||
@@ -362,31 +270,10 @@
|
||||
}
|
||||
},
|
||||
error: function(e) {
|
||||
var message = approvalErrorMessage(e);
|
||||
alert(message);
|
||||
showLoadFailure(message);
|
||||
alert(e.responseText);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function approvalErrorMessage(e) {
|
||||
try {
|
||||
var json = JSON.parse(e.responseText);
|
||||
return json.errorMsg || e.responseText;
|
||||
} catch (ex) {
|
||||
return e.responseText;
|
||||
}
|
||||
}
|
||||
|
||||
// 요청자(PTL_USER) row 가 삭제되면 상세 조회 자체가 불가능하다. 이 경우 삭제만 가능하도록 노출한다.
|
||||
function showLoadFailure(message) {
|
||||
$("#approvalSubject").text("상세 정보를 조회할 수 없는 승인 요청");
|
||||
$("#approvalDetail").text(message);
|
||||
$("#approvalDetailRow").show();
|
||||
$("#btn_unmask").hide();
|
||||
$("#btn_user_approve, #btn_user_reject, #btn_user_force_reject, #btn_user_redeploy").hide();
|
||||
document.getElementById("btn_user_delete").style.display = "";
|
||||
}
|
||||
function updateDetailView(data) {
|
||||
$("#approvalType").text(data.approvalType === 'USER' ? "법인사용자" : "API 사용");
|
||||
$("#approvalStatus").text(data.approvalStatus.description);
|
||||
@@ -407,18 +294,15 @@
|
||||
|
||||
// 사용자 정보 업데이트
|
||||
var portalUser = data.portalUser;
|
||||
if (portalUser) {
|
||||
$("#loginId").text(portalUser.loginId);
|
||||
$("#userName").text(portalUser.userName);
|
||||
$("#mobileNumber").text(portalUser.mobileNumber);
|
||||
$("#userStatusDescription").text(portalUser.userStatusDescription);
|
||||
$("#roleCodeDescription").text(portalUser.roleCodeDescription);
|
||||
} else {
|
||||
$("#loginId, #userName, #mobileNumber, #userStatusDescription, #roleCodeDescription").text("삭제된 사용자");
|
||||
}
|
||||
$("#loginId").text(portalUser.loginId);
|
||||
$("#userName").text(portalUser.userName);
|
||||
$("#mobileNumber").text(portalUser.mobileNumber);
|
||||
$("#userStatusDescription").text(portalUser.userStatusDescription);
|
||||
$("#roleCodeDescription").text(portalUser.roleCodeDescription);
|
||||
|
||||
// 법인 정보 업데이트
|
||||
updateOrgInfo(portalUser ? portalUser.portalOrgUIs : null);
|
||||
var portalOrg = portalUser.portalOrgUIs;
|
||||
updateOrgInfo(portalOrg);
|
||||
|
||||
// 마스킹 상태에 따라 데이터를 다르게 표시
|
||||
var isMasked = $("#btn_unmask").attr("status") === "DETAIL";
|
||||
@@ -460,11 +344,6 @@
|
||||
|
||||
// 법인 정보 업데이트 함수
|
||||
function updateOrgInfo(portalOrg) {
|
||||
if (!portalOrg) {
|
||||
$("#orgName").text("삭제된법인");
|
||||
$("#orgStatusDescription, #serviceName, #corpRegNo, #compRegNo, #scPhoneNumber, #orgPhoneNumber, #ceoName, #orgIndustryType, #orgAddr, #orgSectors, #orgCode, #ipWhitelist").text("");
|
||||
return;
|
||||
}
|
||||
$("#orgName").text(portalOrg.orgName);
|
||||
$("#orgStatusDescription").text(portalOrg.orgStatusDescription);
|
||||
$("#serviceName").text(portalOrg.serviceName);
|
||||
@@ -607,8 +486,6 @@
|
||||
<div class="popup_button">
|
||||
<button type="button" class="cssbtn" style="display: none;" id="btn_user_approve" level="W"><i class="material-icons">check</i>승인</button>
|
||||
<button type="button" class="cssbtn" style="display: none;" id="btn_user_reject" level="W"><i class="material-icons">close</i>반려</button>
|
||||
<button type="button" class="cssbtn" style="display: none;" id="btn_user_force_reject" level="W"><i class="material-icons">block</i>강제 반려</button>
|
||||
<button type="button" class="cssbtn" style="display: none;" id="btn_user_delete" level="W"><i class="material-icons">delete</i>삭제</button>
|
||||
<button type="button" class="cssbtn" style="display: none;" id="btn_user_redeploy" level="W"><i class="material-icons">close</i>재처리</button>
|
||||
<button type="button" class="cssbtn" id="btn_close" level="R"><i class="material-icons">cancel</i> <%= localeMessage.getString("button.close") %>
|
||||
</button>
|
||||
|
||||
@@ -62,19 +62,7 @@
|
||||
return '<span title="' + escapeAttr(rowObject.sentDateFull || '') + '">' + escapeHtmlJs(rowObject.sentDateShort || '') + '</span>';
|
||||
}
|
||||
|
||||
function toDashDate(yyyymmdd) {
|
||||
return yyyymmdd.substring(0, 4) + "-" + yyyymmdd.substring(4, 6) + "-" + yyyymmdd.substring(6, 8);
|
||||
}
|
||||
|
||||
function init() {
|
||||
$("#startDatepicker").datepicker().inputmask("yyyy-mm-dd",{'autoUnmask':true});
|
||||
$("#endDatepicker").datepicker().inputmask("yyyy-mm-dd",{'autoUnmask':true});
|
||||
|
||||
var today = getToday();
|
||||
|
||||
$("input[name=searchFromDate]").val(toDashDate(today.substring(0,6)+"01"));
|
||||
$("input[name=searchToDate]").val(toDashDate(today));
|
||||
|
||||
$.ajax({
|
||||
type: "POST", url: url, dataType: "json",
|
||||
data: {cmd: 'LIST_INIT_COMBO'},
|
||||
@@ -157,20 +145,6 @@
|
||||
init();
|
||||
|
||||
$("#btn_search").click(function () {
|
||||
var start = $("input[name=searchFromDate]").val().replace(/-/gi, "");
|
||||
var end = $("input[name=searchToDate]").val().replace(/-/gi, "");
|
||||
|
||||
if (start && start > getToday()) {
|
||||
alert("시작일이 오늘 이후입니다. 시작일을 확인해주세요.");
|
||||
$("input[name=searchFromDate]").focus();
|
||||
return false;
|
||||
}
|
||||
if (start && end && start > end) {
|
||||
alert("조회기간 종료일은 시작일보다 커야합니다.");
|
||||
$("input[name=searchToDate]").focus();
|
||||
return false;
|
||||
}
|
||||
|
||||
var postData = getSearchForJqgrid("cmd", "LIST");
|
||||
$("#grid").setGridParam({url: url, postData: postData, page: 1}).trigger("reloadGrid");
|
||||
});
|
||||
@@ -218,15 +192,14 @@
|
||||
<option value="">전체</option>
|
||||
<option value="EMAIL">EMAIL</option>
|
||||
<option value="SMS">SMS</option>
|
||||
<option value="KAKAO">KAKAO</option>
|
||||
<option value="MESSENGER">MESSENGER</option>
|
||||
</select>
|
||||
</div>
|
||||
</td>
|
||||
<th>요청기간</th>
|
||||
<td colspan="3">
|
||||
<input type="text" name="searchFromDate" id="startDatepicker" readonly="readonly" value="" size="10" style="width:100px; border:1px solid #ebebec;">
|
||||
~
|
||||
<input type="text" name="searchToDate" value="" id="endDatepicker" size="10" readonly="readonly" style="width:100px; border:1px solid #ebebec;">
|
||||
<input type="date" name="searchFromDate"> ~ <input type="date" name="searchToDate">
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
||||
@@ -47,8 +47,6 @@
|
||||
$('#sentDate').text(data.sentDateFull || '');
|
||||
// 서버에서 마스킹+escape 된 안전 HTML
|
||||
$('#messageHtml').html(data.messageHtml || '');
|
||||
// 응답내용(RESPONSE_DATA) — 원문이므로 text 로만 출력
|
||||
$('#responseData').text(data.responseData || '');
|
||||
|
||||
// PENDING 건만 '실패 처리' 노출 (권한 제어는 buttonControl 이 담당)
|
||||
if (data.requestStatus !== 'PENDING') {
|
||||
@@ -144,10 +142,6 @@
|
||||
<th>메세지 내용</th>
|
||||
<td colspan="3"><div id="messageHtml" class="msg-detail"></div></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>응답내용</th>
|
||||
<td colspan="3"><div id="responseData" class="msg-detail"></div></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -22,8 +22,6 @@
|
||||
function formatInquiryStatus(cellvalue, options, rowObject) {
|
||||
if (cellvalue == 'PENDING') {
|
||||
return '<span style="color: red;">문의중</span>';
|
||||
} else if (cellvalue == 'REVIEWING') {
|
||||
return '<span >문의검토중</span>'
|
||||
} else if (cellvalue == 'RESPONDED') {
|
||||
return '<span >답변완료</span>'
|
||||
} else if (cellvalue == 'CLOSED') {
|
||||
@@ -33,17 +31,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
function formatVisibility(cellvalue, options, rowObject) {
|
||||
if (cellvalue == 'PRIVATE') {
|
||||
return '<span style="color:#c0392b; font-weight:bold;">비공개</span>';
|
||||
} else if (cellvalue == 'ALL') {
|
||||
return '전체공개';
|
||||
} else if (cellvalue == 'ORG') {
|
||||
return '법인공개';
|
||||
}
|
||||
return cellvalue;
|
||||
}
|
||||
|
||||
function formatFile(cellvalue, options, rowObject) {
|
||||
var iconPath = '${pageContext.request.contextPath}/images/icon_file.png';
|
||||
var icon = rowObject.hasAttachment ? '<img src="' + iconPath + '" alt="File Attached" style="vertical-align: middle; margin-left: 5px; width: 16px; height: 16px;">' : '';
|
||||
@@ -112,14 +99,13 @@
|
||||
mtype: 'POST',
|
||||
url: url,
|
||||
postData : { cmd : 'LIST' },
|
||||
colNames:['No.', 'id', '제목', '법인명', '상태', '공개범위', '작성자', '등록일', '답변자','답변일'],
|
||||
colNames:['No.', 'id', '제목', '법인명', '상태', '작성자', '등록일', '답변자','답변일'],
|
||||
colModel:[
|
||||
{ name : 'rowNum' , align:'center', width:50, sortable:false },
|
||||
{ name : 'id' , align:'center', key:true, hidden:true},
|
||||
{ name : 'inquirySubject' , align:'left' , width:200 , formatter: formatFile},
|
||||
{ name : 'orgName' , align:'center' , width:100 , },
|
||||
{ name : 'inquiryStatus' , align:'center', width:70, formatter: formatInquiryStatus },
|
||||
{ name : 'visibility' , align:'center', width:70, formatter: formatVisibility },
|
||||
{ name : 'inquirer.userName', align:'center', width:70 },
|
||||
{ name : 'createdDate' , align:'center', width:100, formatter: timeStampFormat },
|
||||
{ name : 'responderName' , align:'center', width:70 },
|
||||
@@ -221,7 +207,6 @@
|
||||
<select name="searchInquiryStatus">
|
||||
<option value="">전체</option>
|
||||
<option value="PENDING">문의중</option>
|
||||
<option value="REVIEWING">문의검토중</option>
|
||||
<option value="RESPONDED">답변완료</option>
|
||||
<option value="CLOSED">답변종료</option>
|
||||
</select>
|
||||
|
||||
@@ -79,46 +79,12 @@
|
||||
margin-left: 2px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.reason-modal-overlay {
|
||||
display: none;
|
||||
position: fixed;
|
||||
top: 0; left: 0;
|
||||
width: 100%; height: 100%;
|
||||
background: rgba(0, 0, 0, 0.4);
|
||||
z-index: 1000;
|
||||
}
|
||||
.reason-modal-box {
|
||||
position: absolute;
|
||||
top: 50%; left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
background: #fff;
|
||||
border-radius: 6px;
|
||||
padding: 20px;
|
||||
width: 400px;
|
||||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
.reason-modal-box h3 {
|
||||
margin: 0 0 12px;
|
||||
font-size: 16px;
|
||||
}
|
||||
.reason-modal-box textarea {
|
||||
width: 100%;
|
||||
min-height: 80px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.reason-modal-actions {
|
||||
margin-top: 12px;
|
||||
text-align: right;
|
||||
}
|
||||
</style>
|
||||
<script language="javascript">
|
||||
var url = '<c:url value="/onl/apim/portalinquiry/portalInquiryMan.json" />';
|
||||
var url_view = '<c:url value="/onl/apim/portalinquiry/portalInquiryMan.view" />';
|
||||
var file_download = '<c:url value="/file/download.do" />';
|
||||
var inquiryStatus = 'PENDING';
|
||||
var originalVisibility = 'ORG';
|
||||
var reasonModalCallback = null;
|
||||
|
||||
function decodeHTMLEntities(text) {
|
||||
var textArea = document.createElement('textarea');
|
||||
@@ -142,9 +108,7 @@
|
||||
|
||||
inquiryStatus = data.inquiryStatus;
|
||||
if (data.inquiryStatus == 'PENDING') {
|
||||
$("#inquiryStatus").text('문의중');
|
||||
} else if (data.inquiryStatus == 'REVIEWING') {
|
||||
$("#inquiryStatus").text('문의검토중');
|
||||
$("#inquiryStatus").text('문의중');
|
||||
} else if (data.inquiryStatus == 'RESPONDED') {
|
||||
$("#inquiryStatus").text('답변완료');
|
||||
} else if (data.inquiryStatus == 'CLOSED') {
|
||||
@@ -158,24 +122,6 @@
|
||||
var decodedContent = decodeHTMLEntities(data.responseDetail);
|
||||
$("#responseDetail").summernote('code', decodedContent || '');
|
||||
|
||||
// 공개범위 / 조회수
|
||||
$('#visibilitySelect').val(data.visibility || 'ORG');
|
||||
originalVisibility = data.visibility || 'ORG';
|
||||
$('#viewCount').text(data.viewCount || 0);
|
||||
|
||||
// 조회수 증가 (sessionStorage dedup — 새로고침 시 중복 증가 억제)
|
||||
var viewedKey = 'inquiry_viewed_' + key;
|
||||
if (!sessionStorage.getItem(viewedKey)) {
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: url,
|
||||
data: {cmd: 'INCREMENT_VIEW', id: key},
|
||||
success: function () {
|
||||
sessionStorage.setItem(viewedKey, 'true');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 댓글 표시
|
||||
listComment(key);
|
||||
|
||||
@@ -201,17 +147,8 @@
|
||||
if (!data || data.length === 0) return;
|
||||
$.each(data, function (i, comment) {
|
||||
var date = comment.createdDate ? timeStampFormat(comment.createdDate) : '';
|
||||
var isPrivate = comment.visibility === 'PRIVATE';
|
||||
var row = $('<div>').addClass('info-row');
|
||||
if (isPrivate) {
|
||||
// 비공개 댓글은 색상으로 구별
|
||||
row.css({'background-color': '#FFF5F5', 'border-radius': '4px', 'padding': '4px'});
|
||||
}
|
||||
var labelDiv = $('<div>').addClass('info-label').css('color', '#555').text(comment.writerName || '');
|
||||
if (isPrivate) {
|
||||
$('<span>').text(' 비공개').css({'color': '#c0392b', 'font-size': '0.85em', 'font-weight': 'bold'}).appendTo(labelDiv);
|
||||
}
|
||||
labelDiv.appendTo(row);
|
||||
$('<div>').addClass('info-label').css('color', '#555').text(comment.writerName || '').appendTo(row);
|
||||
var content = $('<div>').addClass('info-content');
|
||||
$('<div>').css('white-space', 'pre-wrap').text(comment.commentDetail).appendTo(content);
|
||||
var meta = $('<div>').css({'font-size': '0.9em', 'color': '#999', 'margin-top': '2px'});
|
||||
@@ -222,19 +159,6 @@
|
||||
})(comment.id, comment.inquiryId))
|
||||
.appendTo(meta);
|
||||
meta.appendTo(content);
|
||||
|
||||
// 댓글 공개범위 변경 컨트롤 (사유 필수)
|
||||
var visCtrl = $('<div>').css({'margin-top': '4px', 'font-size': '0.85em'});
|
||||
var sel = $('<select>').append('<option value="ALL">공개</option>').append('<option value="PRIVATE">비공개</option>');
|
||||
sel.val(comment.visibility || 'ALL');
|
||||
var reasonInput = $('<input>').attr({'type': 'text', 'placeholder': '변경 사유'}).css({'margin-left': '4px'});
|
||||
var applyBtn = $('<button>').attr('type', 'button').addClass('cssbtn smallBtn2').css({'margin-left': '4px', 'font-size': '0.85em', 'padding': '1px 6px'})
|
||||
.text('공개범위변경').on('click', (function(id, inquiryId, selEl, reasonEl) {
|
||||
return function() { changeCommentVisibility(id, inquiryId, selEl.val(), reasonEl.val()); };
|
||||
})(comment.id, comment.inquiryId, sel, reasonInput));
|
||||
visCtrl.append(sel).append(reasonInput).append(applyBtn);
|
||||
visCtrl.appendTo(content);
|
||||
|
||||
content.appendTo(row);
|
||||
row.appendTo($commentList);
|
||||
});
|
||||
@@ -245,84 +169,6 @@
|
||||
});
|
||||
}
|
||||
|
||||
function openReasonModal(callback) {
|
||||
reasonModalCallback = callback;
|
||||
$('#reasonModalInput').val('');
|
||||
$('#reasonModal').show();
|
||||
$('#reasonModalInput').focus();
|
||||
}
|
||||
|
||||
function closeReasonModal() {
|
||||
$('#reasonModal').hide();
|
||||
reasonModalCallback = null;
|
||||
}
|
||||
|
||||
// 답변(summernote) 내용이 실제로 입력됐는지 확인
|
||||
function hasAnswerContent() {
|
||||
var v = $('#responseDetail').summernote('code');
|
||||
var text = $('<div>').html(v || '').text().replace(/ /g, ' ').trim();
|
||||
return text.length > 0;
|
||||
}
|
||||
|
||||
function saveInquiryAnswer(returnUrl) {
|
||||
var postData = $('#ajaxForm').serializeArray();
|
||||
postData.push({name: "cmd", value: "INSERT"});
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: url,
|
||||
data: postData,
|
||||
success: function (json) {
|
||||
alert("<%= localeMessage.getString("common.saveMsg") %>");
|
||||
goNav(returnUrl);
|
||||
},
|
||||
error: function (e) {
|
||||
alert(e.responseText);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 게시물 공개범위 변경(사유 필수). 성공 후 답변 내용이 있으면 이어서 저장.
|
||||
function changeInquiryVisibility(newVis, reason, returnUrl, thenSaveAnswer) {
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: url,
|
||||
data: {cmd: 'VISIBILITY', serviceType: 'APIGW', targetType: 'INQUIRY', id: $('#id').val(),
|
||||
visibility: newVis, auditReason: reason},
|
||||
success: function () {
|
||||
originalVisibility = newVis;
|
||||
if (thenSaveAnswer) {
|
||||
saveInquiryAnswer(returnUrl);
|
||||
} else {
|
||||
alert('변경되었습니다.');
|
||||
goNav(returnUrl);
|
||||
}
|
||||
},
|
||||
error: function (e) {
|
||||
alert(e.responseText);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function changeCommentVisibility(commentId, inquiryId, visibility, reason) {
|
||||
if (!reason || !reason.trim()) {
|
||||
alert('공개범위 변경 사유를 입력하세요.');
|
||||
return;
|
||||
}
|
||||
if (!confirm('댓글 공개범위를 변경하시겠습니까?')) return;
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: url,
|
||||
data: {cmd: 'VISIBILITY', serviceType: 'APIGW', targetType: 'COMMENT', id: commentId, visibility: visibility, auditReason: reason},
|
||||
success: function () {
|
||||
alert('변경되었습니다.');
|
||||
listComment(inquiryId);
|
||||
},
|
||||
error: function (e) {
|
||||
alert(e.responseText);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function deleteComment(commentId, inquiryId) {
|
||||
if (inquiryStatus == 'CLOSED') {
|
||||
alert('답변이 종료되어 삭제할 수 없습니다');
|
||||
@@ -362,38 +208,24 @@
|
||||
alert('답변이 종료되어 수정할 수 없습니다');
|
||||
return;
|
||||
}
|
||||
|
||||
var newVis = $('#visibilitySelect').val();
|
||||
var visChanged = (newVis !== originalVisibility);
|
||||
|
||||
if (visChanged) {
|
||||
// 공개범위 변경 감지 → 사유 입력 모달 (답변 내용이 있으면 함께 저장)
|
||||
var saveAnswerToo = hasAnswerContent();
|
||||
openReasonModal(function (reason) {
|
||||
changeInquiryVisibility(newVis, reason, returnUrl, saveAnswerToo);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// 공개범위 변경 없음 → 기존 답변 저장
|
||||
if (!checkRequired("ajaxForm")) return;
|
||||
if (!confirm("<%= localeMessage.getString("common.checkSave")%>")) return;
|
||||
saveInquiryAnswer(returnUrl);
|
||||
});
|
||||
|
||||
$("#reasonModalOk").click(function () {
|
||||
var reason = ($('#reasonModalInput').val() || '').trim();
|
||||
if (!reason) {
|
||||
alert('변경 사유를 입력하세요.');
|
||||
return;
|
||||
}
|
||||
var cb = reasonModalCallback;
|
||||
closeReasonModal();
|
||||
if (cb) cb(reason);
|
||||
});
|
||||
var postData = $('#ajaxForm').serializeArray();
|
||||
postData.push({name: "cmd", value: "INSERT"});
|
||||
|
||||
$("#reasonModalCancel").click(function () {
|
||||
closeReasonModal();
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: url,
|
||||
data: postData,
|
||||
success: function (json) {
|
||||
alert("<%= localeMessage.getString("common.saveMsg") %>");
|
||||
goNav(returnUrl);
|
||||
},
|
||||
error: function (e) {
|
||||
alert(e.responseText);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$("#btn_comment").click(function () {
|
||||
@@ -476,22 +308,7 @@
|
||||
<div class="info-label">작성일</div>
|
||||
<div id="createdDate" class="info-content"></div>
|
||||
</div>
|
||||
<div class="info-row">
|
||||
<div class="info-label">조회수</div>
|
||||
<div id="viewCount" class="info-content">0</div>
|
||||
</div>
|
||||
<div class="info-row">
|
||||
<div class="info-label">공개범위</div>
|
||||
<div class="info-content" style="display:flex; gap:8px; align-items:center;">
|
||||
<select id="visibilitySelect">
|
||||
<option value="ALL">전체공개</option>
|
||||
<option value="ORG">법인공개</option>
|
||||
<option value="PRIVATE">비공개</option>
|
||||
</select>
|
||||
<span style="font-size:0.85em; color:#999;">변경 후 상단 [수정] 버튼을 누르면 사유 입력창이 표시됩니다.</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="info-row" id="attachFileRow" style="display: none;">
|
||||
<div class="info-row" id="attachFileRow" style="display: none;">
|
||||
<div class="info-label">첨부파일</div>
|
||||
<div id="attachFiles" class="info-content"></div>
|
||||
</div>
|
||||
@@ -539,17 +356,5 @@
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 공개범위 변경 사유 입력 모달 -->
|
||||
<div id="reasonModal" class="reason-modal-overlay">
|
||||
<div class="reason-modal-box">
|
||||
<h3>공개범위 변경 사유</h3>
|
||||
<textarea id="reasonModalInput" placeholder="변경 사유를 입력하세요."></textarea>
|
||||
<div class="reason-modal-actions">
|
||||
<button type="button" class="cssbtn" id="reasonModalOk">확인</button>
|
||||
<button type="button" class="cssbtn" id="reasonModalCancel">취소</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -189,7 +189,7 @@
|
||||
<td>
|
||||
<input type="text" id="keywordList" style="width:100%" placeholder="쉼표(,)로 구분하여 입력">
|
||||
<div class="help-text" style="color:#666; font-size:12px; margin-top:5px;">
|
||||
예시: 인증, 대출, 예금
|
||||
예시: #인증, #대출, #예금
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@@ -1,636 +0,0 @@
|
||||
<%@ 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" %>
|
||||
<%
|
||||
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"/>
|
||||
<jsp:include page="/jsp/common/include/portal.jsp"/>
|
||||
|
||||
<style>
|
||||
/* ── 스윔레인 보드 ── */
|
||||
.menu-board { display: flex; gap: 12px; align-items: flex-start; }
|
||||
.menu-lane-strip { display: flex; gap: 12px; overflow-x: auto; padding-bottom: 10px; flex: 1 1 auto; }
|
||||
.menu-lane { background: #f1f3f5; border-radius: 10px; min-width: 230px; max-width: 230px; flex: 0 0 230px; }
|
||||
.menu-lane.unplaced { background: #fff4e6; border: 1px dashed #fd7e14; }
|
||||
.lane-header { display: flex; align-items: center; gap: 6px; padding: 10px 12px;
|
||||
border-bottom: 1px solid #dee2e6; cursor: grab; }
|
||||
.menu-lane.unplaced .lane-header { cursor: default; }
|
||||
.lane-title { font-weight: 700; font-size: 14px; flex: 1 1 auto; overflow: hidden;
|
||||
text-overflow: ellipsis; white-space: nowrap; }
|
||||
.lane-body { list-style: none; margin: 0; padding: 8px; min-height: 60px; }
|
||||
.menu-card { background: #fff; border: 1px solid #dee2e6; border-radius: 8px;
|
||||
padding: 8px 10px; margin-bottom: 8px; cursor: grab;
|
||||
box-shadow: 0 1px 2px rgba(0,0,0,0.06); }
|
||||
.menu-card.hidden-item, .menu-lane.hidden-item > .lane-header { opacity: 0.45; }
|
||||
.menu-card .card-name { font-size: 13px; font-weight: 600; }
|
||||
.menu-card .card-id { font-size: 11px; color: #868e96; }
|
||||
.menu-card .card-path { font-size: 11px; color: #495057; word-break: break-all; }
|
||||
.card-actions, .lane-actions { display: flex; gap: 4px; }
|
||||
.icon-btn { border: none; background: transparent; padding: 1px 3px; cursor: pointer;
|
||||
font-size: 14px; color: #495057; }
|
||||
.icon-btn:hover { color: #0d6efd; }
|
||||
.icon-btn.danger:hover { color: #dc3545; }
|
||||
.badge-src { font-size: 10px; }
|
||||
.role-check-group { display: flex; flex-wrap: wrap; gap: 4px 12px; }
|
||||
.role-check-group .form-check { min-width: 45%; }
|
||||
.dflt-hint { font-size: 11px; color: #868e96; }
|
||||
.board-toolbar { display: flex; gap: 6px; margin-bottom: 10px; align-items: center; }
|
||||
.board-toolbar .spacer { flex: 1 1 auto; }
|
||||
.sortable-placeholder { border: 2px dashed #adb5bd; border-radius: 8px; height: 48px;
|
||||
margin-bottom: 8px; background: #e9ecef; }
|
||||
.lane-placeholder { border: 2px dashed #adb5bd; border-radius: 10px; min-width: 230px; }
|
||||
</style>
|
||||
|
||||
<script language="javascript">
|
||||
// AuthorizeInterceptor 는 serviceType 을 "요청 파라미터"에서 읽으므로
|
||||
// 모든 .json 호출 URL 에 serviceType 을 부착한다 (누락 시 권한 조회 실패 → Access denied)
|
||||
var url = urlAddServiceType('<c:url value="/onl/apim/portalmenu/portalMenuMan.json" />');
|
||||
|
||||
// 화면 상태 (저장 버튼 클릭 시에만 서버 반영 — 드래그/토글은 클라이언트 상태만 변경)
|
||||
var state = { itemsById: {}, lanes: [], unplaced: [], roles: [] };
|
||||
var dirty = false;
|
||||
|
||||
function escapeHtml(s) {
|
||||
return String(s == null ? '' : s)
|
||||
.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>')
|
||||
.replace(/"/g, '"').replace(/'/g, ''');
|
||||
}
|
||||
|
||||
function loadAll(afterFn) {
|
||||
$.post(url, { cmd: 'LIST_ALL' }, function (data) {
|
||||
buildState(data);
|
||||
render();
|
||||
dirty = false;
|
||||
if (afterFn) afterFn();
|
||||
}, 'json').fail(ajaxFail);
|
||||
}
|
||||
|
||||
function buildState(data) {
|
||||
state.itemsById = {};
|
||||
state.roles = data.roles || [];
|
||||
(data.items || []).forEach(function (it) { state.itemsById[it.menuId] = it; });
|
||||
|
||||
var placedTop = [], childrenByParent = {}, unplaced = [];
|
||||
(data.items || []).forEach(function (it) {
|
||||
if (!it.placed) { unplaced.push(it.menuId); return; }
|
||||
if (!it.parentId) {
|
||||
placedTop.push(it);
|
||||
} else {
|
||||
(childrenByParent[it.parentId] = childrenByParent[it.parentId] || []).push(it);
|
||||
}
|
||||
});
|
||||
placedTop.sort(function (a, b) { return (a.sortOrder || 0) - (b.sortOrder || 0); });
|
||||
|
||||
state.lanes = placedTop.map(function (top) {
|
||||
var kids = (childrenByParent[top.menuId] || [])
|
||||
.sort(function (a, b) { return (a.sortOrder || 0) - (b.sortOrder || 0); })
|
||||
.map(function (c) { return { menuId: c.menuId, visibleYn: c.visibleYn || 'Y' }; });
|
||||
delete childrenByParent[top.menuId];
|
||||
return { menuId: top.menuId, visibleYn: top.visibleYn || 'Y', children: kids };
|
||||
});
|
||||
// 부모가 미배치/부재인 고아 배치는 미배치로 강등
|
||||
Object.keys(childrenByParent).forEach(function (pid) {
|
||||
childrenByParent[pid].forEach(function (c) { unplaced.push(c.menuId); });
|
||||
});
|
||||
state.unplaced = unplaced;
|
||||
}
|
||||
|
||||
// ── 렌더 ──
|
||||
function cardHtml(menuId, visibleYn, inLane) {
|
||||
var it = state.itemsById[menuId];
|
||||
if (!it) return '';
|
||||
var hidden = visibleYn === 'N';
|
||||
var srcBadge = it.sourceType === 'PORTAL'
|
||||
? '<span class="badge text-bg-secondary badge-src">기본</span>'
|
||||
: '<span class="badge text-bg-warning badge-src">커스텀</span>';
|
||||
var grpBadge = it.groupYn === 'Y' ? ' <span class="badge text-bg-info badge-src">그룹</span>' : '';
|
||||
var placeBtn = (!inLane && it.groupYn === 'Y')
|
||||
? '<button type="button" class="icon-btn" title="최상위 레인으로 배치" onclick="placeAsLane(\'' + menuId + '\')"><i class="bi bi-box-arrow-in-up"></i></button>'
|
||||
: '';
|
||||
var eyeBtn = inLane
|
||||
? '<button type="button" class="icon-btn" title="노출/숨김" onclick="toggleVisible(\'' + menuId + '\')"><i class="bi ' + (hidden ? 'bi-eye-slash' : 'bi-eye') + '"></i></button>'
|
||||
: '';
|
||||
return '<li class="menu-card' + (hidden ? ' hidden-item' : '') + '" data-menu-id="' + escapeHtml(menuId) + '">'
|
||||
+ '<div class="d-flex align-items-start">'
|
||||
+ '<div class="flex-grow-1">'
|
||||
+ '<div class="card-name">' + escapeHtml(it.menuName) + ' ' + srcBadge + grpBadge + '</div>'
|
||||
+ '<div class="card-id">' + escapeHtml(it.menuId) + '</div>'
|
||||
+ (it.menuPath ? '<div class="card-path">' + escapeHtml(it.menuPath) + '</div>' : '')
|
||||
+ '</div>'
|
||||
+ '<div class="card-actions">'
|
||||
+ placeBtn + eyeBtn
|
||||
+ '<button type="button" class="icon-btn" title="수정" onclick="openModal(\'' + menuId + '\')"><i class="bi bi-pencil"></i></button>'
|
||||
+ '<button type="button" class="icon-btn danger" level="W" title="' + (it.sourceType === 'PORTAL' ? '미배치로 이동' : '삭제') + '" onclick="minusItem(\'' + menuId + '\')"><i class="bi bi-dash-circle"></i></button>'
|
||||
+ '</div></div></li>';
|
||||
}
|
||||
|
||||
function laneHtml(lane) {
|
||||
var it = state.itemsById[lane.menuId];
|
||||
if (!it) return '';
|
||||
var hidden = lane.visibleYn === 'N';
|
||||
var sectionBadge = it.menuSection === 'MYPAGE'
|
||||
? '<span class="badge text-bg-primary badge-src">마이페이지</span>'
|
||||
: '<span class="badge text-bg-success badge-src">GNB</span>';
|
||||
var srcBadge = it.sourceType === 'PORTAL' ? '' : ' <span class="badge text-bg-warning badge-src">커스텀</span>';
|
||||
var cards = lane.children.map(function (c) { return cardHtml(c.menuId, c.visibleYn, true); }).join('');
|
||||
return '<div class="menu-lane' + (hidden ? ' hidden-item' : '') + '" data-menu-id="' + escapeHtml(lane.menuId) + '">'
|
||||
+ '<div class="lane-header lane-drag-handle">'
|
||||
+ '<span class="lane-title">' + escapeHtml(it.menuName) + '</span>'
|
||||
+ sectionBadge + srcBadge
|
||||
+ '<div class="lane-actions">'
|
||||
+ '<button type="button" class="icon-btn" title="노출/숨김" onclick="toggleVisible(\'' + lane.menuId + '\')"><i class="bi ' + (hidden ? 'bi-eye-slash' : 'bi-eye') + '"></i></button>'
|
||||
+ '<button type="button" class="icon-btn" title="수정" onclick="openModal(\'' + lane.menuId + '\')"><i class="bi bi-pencil"></i></button>'
|
||||
+ '<button type="button" class="icon-btn danger" level="W" title="레인 미배치" onclick="unplaceLane(\'' + lane.menuId + '\')"><i class="bi bi-dash-circle"></i></button>'
|
||||
+ '</div></div>'
|
||||
+ '<ul class="lane-body" data-lane-id="' + escapeHtml(lane.menuId) + '">' + cards + '</ul>'
|
||||
+ '</div>';
|
||||
}
|
||||
|
||||
function render() {
|
||||
var strip = state.lanes.map(laneHtml).join('');
|
||||
$('#laneStrip').html(strip);
|
||||
$('#unplacedBody').html(state.unplaced.map(function (id) { return cardHtml(id, 'Y', false); }).join(''));
|
||||
initSortables();
|
||||
buttonControl();
|
||||
}
|
||||
|
||||
function initSortables() {
|
||||
$('#laneStrip').sortable({
|
||||
items: '.menu-lane',
|
||||
handle: '.lane-drag-handle',
|
||||
placeholder: 'lane-placeholder',
|
||||
tolerance: 'pointer',
|
||||
update: function () { syncFromDom(); }
|
||||
});
|
||||
$('.lane-body, #unplacedBody').sortable({
|
||||
connectWith: '.lane-body, #unplacedBody',
|
||||
items: '.menu-card',
|
||||
placeholder: 'sortable-placeholder',
|
||||
tolerance: 'pointer',
|
||||
stop: function () { syncFromDom(); }
|
||||
});
|
||||
}
|
||||
|
||||
// 드래그 종료 후 DOM 순서 → 상태 동기화 (visible 값은 기존 상태 유지)
|
||||
function syncFromDom() {
|
||||
var visibleMap = {};
|
||||
state.lanes.forEach(function (l) {
|
||||
visibleMap[l.menuId] = l.visibleYn;
|
||||
l.children.forEach(function (c) { visibleMap[c.menuId] = c.visibleYn; });
|
||||
});
|
||||
|
||||
var lanes = [];
|
||||
$('#laneStrip .menu-lane').each(function () {
|
||||
var laneId = $(this).data('menu-id');
|
||||
var children = [];
|
||||
$(this).find('.lane-body .menu-card').each(function () {
|
||||
var id = $(this).data('menu-id');
|
||||
children.push({ menuId: id, visibleYn: visibleMap[id] || 'Y' });
|
||||
});
|
||||
lanes.push({ menuId: laneId, visibleYn: visibleMap[laneId] || 'Y', children: children });
|
||||
});
|
||||
var unplaced = [];
|
||||
$('#unplacedBody .menu-card').each(function () { unplaced.push($(this).data('menu-id')); });
|
||||
|
||||
state.lanes = lanes;
|
||||
state.unplaced = unplaced;
|
||||
dirty = true;
|
||||
render();
|
||||
}
|
||||
|
||||
// ── 카드/레인 조작 ──
|
||||
function toggleVisible(menuId) {
|
||||
state.lanes.forEach(function (l) {
|
||||
if (l.menuId === menuId) l.visibleYn = (l.visibleYn === 'N' ? 'Y' : 'N');
|
||||
l.children.forEach(function (c) {
|
||||
if (c.menuId === menuId) c.visibleYn = (c.visibleYn === 'N' ? 'Y' : 'N');
|
||||
});
|
||||
});
|
||||
dirty = true;
|
||||
render();
|
||||
}
|
||||
|
||||
function placeAsLane(menuId) {
|
||||
state.unplaced = state.unplaced.filter(function (id) { return id !== menuId; });
|
||||
state.lanes.push({ menuId: menuId, visibleYn: 'Y', children: [] });
|
||||
dirty = true;
|
||||
render();
|
||||
}
|
||||
|
||||
function minusItem(menuId) {
|
||||
var it = state.itemsById[menuId];
|
||||
if (!it) return;
|
||||
if (it.sourceType === 'PORTAL') {
|
||||
removeFromBoard(menuId);
|
||||
if (state.unplaced.indexOf(menuId) < 0) state.unplaced.push(menuId);
|
||||
dirty = true;
|
||||
render();
|
||||
} else {
|
||||
if (!confirm('커스텀 항목은 미배치 시 삭제됩니다.\n[' + it.menuName + '] 을(를) 삭제하시겠습니까?')) return;
|
||||
$.post(url, { cmd: 'DELETE', menuId: menuId }, function () {
|
||||
removeFromBoard(menuId);
|
||||
state.unplaced = state.unplaced.filter(function (id) { return id !== menuId; });
|
||||
delete state.itemsById[menuId];
|
||||
dirty = true;
|
||||
render();
|
||||
}, 'json').fail(ajaxFail);
|
||||
}
|
||||
}
|
||||
|
||||
function unplaceLane(laneId) {
|
||||
var lane = null;
|
||||
state.lanes = state.lanes.filter(function (l) {
|
||||
if (l.menuId === laneId) { lane = l; return false; }
|
||||
return true;
|
||||
});
|
||||
if (!lane) return;
|
||||
lane.children.forEach(function (c) {
|
||||
if (state.unplaced.indexOf(c.menuId) < 0) state.unplaced.push(c.menuId);
|
||||
});
|
||||
var it = state.itemsById[laneId];
|
||||
if (it && it.sourceType !== 'PORTAL') {
|
||||
if (confirm('커스텀 항목은 미배치 시 삭제됩니다.\n[' + it.menuName + '] 항목을 삭제하시겠습니까?')) {
|
||||
$.post(url, { cmd: 'DELETE', menuId: laneId }, function () {
|
||||
delete state.itemsById[laneId];
|
||||
render();
|
||||
}, 'json').fail(ajaxFail);
|
||||
} else {
|
||||
state.unplaced.push(laneId);
|
||||
}
|
||||
} else {
|
||||
if (state.unplaced.indexOf(laneId) < 0) state.unplaced.push(laneId);
|
||||
}
|
||||
dirty = true;
|
||||
render();
|
||||
}
|
||||
|
||||
function removeFromBoard(menuId) {
|
||||
state.lanes = state.lanes.filter(function (l) { return l.menuId !== menuId; });
|
||||
state.lanes.forEach(function (l) {
|
||||
l.children = l.children.filter(function (c) { return c.menuId !== menuId; });
|
||||
});
|
||||
}
|
||||
|
||||
// ── 저장 / 초기화 / 캐시 Reload ──
|
||||
function savePlacements() {
|
||||
// 미배치 상태의 커스텀 항목은 저장 시 삭제 처리 (스펙: 커스텀은 미배치=삭제)
|
||||
var unplacedAdmin = state.unplaced.filter(function (id) {
|
||||
var it = state.itemsById[id];
|
||||
return it && it.sourceType !== 'PORTAL';
|
||||
});
|
||||
if (unplacedAdmin.length > 0) {
|
||||
var names = unplacedAdmin.map(function (id) { return state.itemsById[id].menuName; }).join(', ');
|
||||
if (!confirm('미배치된 커스텀 항목은 저장 시 삭제됩니다: ' + names + '\n계속하시겠습니까?')) return;
|
||||
}
|
||||
|
||||
var rows = [];
|
||||
state.lanes.forEach(function (l, i) {
|
||||
rows.push({ menuId: l.menuId, parentId: null, sortOrder: (i + 1) * 10, visibleYn: l.visibleYn });
|
||||
l.children.forEach(function (c, j) {
|
||||
rows.push({ menuId: c.menuId, parentId: l.menuId, sortOrder: (j + 1) * 10, visibleYn: c.visibleYn });
|
||||
});
|
||||
});
|
||||
|
||||
var deleteCalls = unplacedAdmin.map(function (id) {
|
||||
return $.post(url, { cmd: 'DELETE', menuId: id });
|
||||
});
|
||||
$.when.apply($, deleteCalls).always(function () {
|
||||
$.ajax({
|
||||
url: url + '&cmd=TRANSACTION_PLACEMENT',
|
||||
type: 'POST',
|
||||
contentType: 'application/json; charset=utf-8',
|
||||
data: JSON.stringify(rows)
|
||||
}).done(function () {
|
||||
dirty = false;
|
||||
loadAll(function () {
|
||||
if (confirm('배치가 저장되었습니다.\n포탈 메뉴 캐시를 즉시 반영(Reload)하시겠습니까?\n(미반영 시 캐시 TTL 후 자동 반영)')) {
|
||||
reloadPortalCache();
|
||||
}
|
||||
});
|
||||
}).fail(ajaxFail);
|
||||
});
|
||||
}
|
||||
|
||||
function initializeMenus() {
|
||||
if (!confirm('메뉴 초기화 시 포탈 기본(menu.yml) 값으로 복원되고\n커스텀 항목은 모두 삭제됩니다. 계속하시겠습니까?')) return;
|
||||
$.post(url, { cmd: 'INITIALIZE' }, function () {
|
||||
loadAll(function () { alert('메뉴가 초기화되었습니다.'); });
|
||||
}, 'json').fail(ajaxFail);
|
||||
}
|
||||
|
||||
// ── 캐시 Reload ──
|
||||
var reloadModal;
|
||||
var reloadBtnHtml;
|
||||
|
||||
function reloadPortalCache() {
|
||||
var $btn = $('#btnReloadCache');
|
||||
if ($btn.prop('disabled')) return;
|
||||
if (reloadBtnHtml === undefined) reloadBtnHtml = $btn.html();
|
||||
$btn.prop('disabled', true)
|
||||
.html('<span class="spinner-border spinner-border-sm"></span> 리로드 중...');
|
||||
|
||||
$.post(url, { cmd: 'TRANSACTION_RELOAD' }, function (result) {
|
||||
showReloadResult(result);
|
||||
}, 'json').fail(ajaxFail).always(function () {
|
||||
$btn.prop('disabled', false).html(reloadBtnHtml);
|
||||
});
|
||||
}
|
||||
|
||||
function showReloadResult(result) {
|
||||
if (!result) {
|
||||
alert('캐시 리로드 실패: 알 수 없는 오류');
|
||||
return;
|
||||
}
|
||||
|
||||
var servers = result.servers || [];
|
||||
var $summary = $('#reloadSummary');
|
||||
$summary.removeClass('alert-success alert-danger alert-warning')
|
||||
.addClass(result.success ? 'alert-success'
|
||||
: (result.successCount > 0 ? 'alert-warning' : 'alert-danger'))
|
||||
.text(result.message || '');
|
||||
|
||||
var rows = '';
|
||||
servers.forEach(function (s) {
|
||||
var badge = s.success
|
||||
? '<span class="badge text-bg-success">성공</span>'
|
||||
: '<span class="badge text-bg-danger">실패</span>';
|
||||
var detail = s.success
|
||||
? ('항목 ' + (s.itemCount == null ? '-' : s.itemCount) + '건'
|
||||
+ (s.reloadedAt ? ' / ' + escapeHtml(String(s.reloadedAt)) : ''))
|
||||
: escapeHtml(String(s.message || '-'));
|
||||
rows += '<tr>'
|
||||
+ '<td class="text-center">' + badge + '</td>'
|
||||
+ '<td style="word-break:break-all;">' + escapeHtml(String(s.url || '')) + '</td>'
|
||||
+ '<td>' + detail + '</td>'
|
||||
+ '<td class="text-end">' + (s.elapsedMs == null ? '-' : s.elapsedMs + 'ms') + '</td>'
|
||||
+ '</tr>';
|
||||
});
|
||||
if (rows === '') {
|
||||
rows = '<tr><td colspan="4" class="text-center text-muted">'
|
||||
+ '호출 대상 서버가 없습니다. 아래 설정 안내를 확인하세요.</td></tr>';
|
||||
}
|
||||
$('#reloadResultBody').html(rows);
|
||||
|
||||
if (!reloadModal) reloadModal = new bootstrap.Modal(document.getElementById('reloadResultModal'));
|
||||
reloadModal.show();
|
||||
}
|
||||
|
||||
function ajaxFail(xhr) {
|
||||
var msg = '처리 중 오류가 발생했습니다.';
|
||||
try {
|
||||
var body = JSON.parse(xhr.responseText);
|
||||
if (body.errorMsg) msg = body.errorMsg;
|
||||
else if (body.message) msg = body.message;
|
||||
} catch (e) { /* ignore */ }
|
||||
alert(msg + ' (HTTP ' + xhr.status + ')');
|
||||
}
|
||||
|
||||
// ── 추가/수정 모달 ──
|
||||
var menuModal;
|
||||
|
||||
function roleChecksHtml(namePrefix, checkedCsv) {
|
||||
var checked = (checkedCsv || '').split(',').map(function (s) { return s.trim(); }).filter(Boolean);
|
||||
var html = '';
|
||||
var all = [{ roleCode: 'AUTHENTICATED', roleName: '로그인 사용자', roleType: '-' }].concat(state.roles);
|
||||
all.forEach(function (r) {
|
||||
var id = namePrefix + '_' + r.roleCode;
|
||||
html += '<div class="form-check form-check-sm">'
|
||||
+ '<input class="form-check-input" type="checkbox" name="' + namePrefix + '" id="' + id + '" value="' + escapeHtml(r.roleCode) + '"'
|
||||
+ (checked.indexOf(r.roleCode) >= 0 ? ' checked' : '') + '>'
|
||||
+ '<label class="form-check-label" for="' + id + '" style="font-size:12px;">'
|
||||
+ escapeHtml(r.roleName) + ' <span class="text-muted">(' + escapeHtml(r.roleCode) + ')</span></label>'
|
||||
+ '</div>';
|
||||
});
|
||||
return html;
|
||||
}
|
||||
|
||||
function openModal(menuId) {
|
||||
var isNew = !menuId;
|
||||
var it = isNew ? { sourceType: 'ADMIN', groupYn: 'N', menuSection: 'GNB', newWindowYn: 'N' }
|
||||
: state.itemsById[menuId];
|
||||
if (!it) return;
|
||||
var isPortal = it.sourceType === 'PORTAL';
|
||||
|
||||
$('#modalTitle').text(isNew ? '메뉴 추가' : '메뉴 수정' + (isPortal ? ' (기본 항목)' : ''));
|
||||
$('#fMenuId').val(it.menuId || '').prop('readonly', !isNew);
|
||||
$('#fMenuName').val(it.menuName || '');
|
||||
$('#fMenuPath').val(it.menuPath || '');
|
||||
$('#fIconClass').val(it.iconClass || '');
|
||||
$('#fMenuSection').val(it.menuSection || 'GNB').prop('disabled', isPortal);
|
||||
$('#fGroupYn').prop('checked', it.groupYn === 'Y').prop('disabled', isPortal);
|
||||
$('#fNewWindowYn').prop('checked', it.newWindowYn === 'Y');
|
||||
$('#exposeRoleChecks').html(roleChecksHtml('exposeRole', it.exposeRoles));
|
||||
$('#accessRoleChecks').html(roleChecksHtml('accessRole', it.accessRoles));
|
||||
|
||||
// 기본 항목: 기본값(DFLT_*) 병기
|
||||
if (isPortal) {
|
||||
$('#hintMenuName').text('기본값: ' + (it.dfltMenuName || '-'));
|
||||
$('#hintMenuPath').text('기본값: ' + (it.dfltMenuPath || '-'));
|
||||
$('#hintExposeRoles').text('기본값: ' + (it.dfltExposeRoles || '전체 노출'));
|
||||
$('#hintAccessRoles').text('기본값: ' + (it.dfltAccessRoles || '제한 없음'));
|
||||
$('.dflt-hint').show();
|
||||
} else {
|
||||
$('.dflt-hint').hide();
|
||||
}
|
||||
|
||||
$('#modalMode').val(isNew ? 'INSERT' : 'UPDATE');
|
||||
menuModal.show();
|
||||
}
|
||||
|
||||
function submitModal() {
|
||||
var cmd = $('#modalMode').val();
|
||||
var data = {
|
||||
cmd: cmd,
|
||||
menuId: $('#fMenuId').val().trim(),
|
||||
menuName: $('#fMenuName').val().trim(),
|
||||
menuPath: $('#fMenuPath').val().trim(),
|
||||
iconClass: $('#fIconClass').val().trim(),
|
||||
menuSection: $('#fMenuSection').val(),
|
||||
groupYn: $('#fGroupYn').is(':checked') ? 'Y' : 'N',
|
||||
newWindowYn: $('#fNewWindowYn').is(':checked') ? 'Y' : 'N',
|
||||
exposeRoles: $('input[name=exposeRole]:checked').map(function () { return this.value; }).get().join(','),
|
||||
accessRoles: $('input[name=accessRole]:checked').map(function () { return this.value; }).get().join(',')
|
||||
};
|
||||
if (!data.menuId) { alert('메뉴 ID 를 입력하세요.'); return; }
|
||||
if (cmd === 'INSERT' && !/^[a-z0-9-]+$/.test(data.menuId)) {
|
||||
alert('메뉴 ID 는 kebab-case(소문자/숫자/하이픈)만 허용합니다.');
|
||||
return;
|
||||
}
|
||||
if (!data.menuName) { alert('노출명을 입력하세요.'); return; }
|
||||
|
||||
$.post(url, data, function () {
|
||||
menuModal.hide();
|
||||
loadAll();
|
||||
}, 'json').fail(ajaxFail);
|
||||
}
|
||||
|
||||
$(document).ready(function () {
|
||||
menuModal = new bootstrap.Modal(document.getElementById('menuModal'));
|
||||
loadAll();
|
||||
|
||||
window.addEventListener('beforeunload', function (e) {
|
||||
if (dirty) { e.preventDefault(); e.returnValue = ''; }
|
||||
});
|
||||
});
|
||||
</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="title">포탈 메뉴 관리</div>
|
||||
|
||||
<div class="board-toolbar">
|
||||
<button type="button" class="btn btn-sm btn-primary" level="W" onclick="openModal()">
|
||||
<i class="bi bi-plus-lg"></i> 메뉴 추가</button>
|
||||
<button type="button" class="btn btn-sm btn-outline-danger" level="W" onclick="initializeMenus()">
|
||||
<i class="bi bi-arrow-counterclockwise"></i> 메뉴 초기화</button>
|
||||
<span class="spacer"></span>
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary" id="btnReloadCache" level="W" onclick="reloadPortalCache()">
|
||||
<i class="bi bi-arrow-repeat"></i> 캐시 Reload</button>
|
||||
<button type="button" class="btn btn-sm btn-success" level="W" onclick="savePlacements()">
|
||||
<i class="bi bi-save"></i> 저장</button>
|
||||
</div>
|
||||
|
||||
<div style="background:#e7f1ff; border:1px solid #0d6efd33; padding:8px 15px; margin-bottom:10px; border-radius:6px; font-size:12px; color:#31507a;">
|
||||
드래그로 위치를 조절한 뒤 <strong>저장</strong> 버튼으로 일괄 반영합니다.
|
||||
기본 항목([기본] 배지)은 [-] 시 <strong>미배치</strong>로만 이동하고, 커스텀 항목은 미배치 시 <strong>삭제</strong>됩니다.
|
||||
저장 후 <strong>캐시 Reload</strong> 를 실행해야 포탈에 즉시 반영됩니다(미실행 시 TTL 경과 후 반영).
|
||||
<br>
|
||||
캐시 Reload 대상 서버는 포탈 프로퍼티 <code>Portal / portal.internal.menu-reload-url</code> 로 설정합니다.
|
||||
포탈이 <strong>다중 서버(이중화)</strong> 로 기동된 경우 모든 서버의 리로드 URL 을
|
||||
<strong>콤마(,) 또는 줄바꿈</strong> 으로 나열해야 전체 서버에 반영됩니다
|
||||
(예: <code>http://10.0.0.11:39130/internal/menu/reload, http://10.0.0.12:39130/internal/menu/reload</code>).
|
||||
서버별 요청 타임아웃은 <strong>3초</strong> 이며, 실행 결과는 서버별로 표시됩니다.
|
||||
</div>
|
||||
|
||||
<div class="menu-board">
|
||||
<div class="menu-lane-strip" id="laneStrip"><!-- 렌더링 --></div>
|
||||
<div class="menu-lane unplaced">
|
||||
<div class="lane-header">
|
||||
<span class="lane-title">미배치</span>
|
||||
<span class="badge text-bg-warning badge-src">보관함</span>
|
||||
</div>
|
||||
<ul class="lane-body" id="unplacedBody"><!-- 렌더링 --></ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 추가/수정 모달 -->
|
||||
<div class="modal fade" id="menuModal" tabindex="-1" aria-hidden="true">
|
||||
<div class="modal-dialog modal-lg">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="modalTitle">메뉴 추가</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="닫기"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<input type="hidden" id="modalMode" value="INSERT">
|
||||
<div class="row g-3">
|
||||
<div class="col-md-6">
|
||||
<label class="form-label" for="fMenuId">메뉴 ID (kebab-case)</label>
|
||||
<input type="text" class="form-control form-control-sm" id="fMenuId" placeholder="ex) custom-link">
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label" for="fMenuName">노출명</label>
|
||||
<input type="text" class="form-control form-control-sm" id="fMenuName">
|
||||
<div class="dflt-hint" id="hintMenuName"></div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label" for="fMenuPath">Path (그룹은 생략 가능)</label>
|
||||
<input type="text" class="form-control form-control-sm" id="fMenuPath" placeholder="/example 또는 https://...">
|
||||
<div class="dflt-hint" id="hintMenuPath"></div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label" for="fIconClass">아이콘 클래스 (마이페이지용, ex: fa-users)</label>
|
||||
<input type="text" class="form-control form-control-sm" id="fIconClass">
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label class="form-label" for="fMenuSection">섹션</label>
|
||||
<select class="form-select form-select-sm" id="fMenuSection">
|
||||
<option value="GNB">GNB (상단 메뉴)</option>
|
||||
<option value="MYPAGE">마이페이지</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-4 d-flex align-items-end">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="fGroupYn">
|
||||
<label class="form-check-label" for="fGroupYn">그룹(상위 메뉴)</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4 d-flex align-items-end">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="fNewWindowYn">
|
||||
<label class="form-check-label" for="fNewWindowYn">새 창 열기</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">노출 권한 (미선택 = 전체 노출)</label>
|
||||
<div class="role-check-group" id="exposeRoleChecks"></div>
|
||||
<div class="dflt-hint" id="hintExposeRoles"></div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">접근 권한 (미선택 = 제한 없음)</label>
|
||||
<div class="role-check-group" id="accessRoleChecks"></div>
|
||||
<div class="dflt-hint" id="hintAccessRoles"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-sm btn-secondary" data-bs-dismiss="modal">취소</button>
|
||||
<button type="button" class="btn btn-sm btn-primary" level="W" onclick="submitModal()">저장</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 캐시 Reload 결과 -->
|
||||
<div class="modal fade" id="reloadResultModal" tabindex="-1" aria-hidden="true">
|
||||
<div class="modal-dialog modal-lg">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">포탈 메뉴 캐시 Reload 결과</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="닫기"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="alert py-2 px-3" id="reloadSummary" style="font-size:13px;"></div>
|
||||
<table class="table table-sm table-bordered align-middle" style="font-size:12px;">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th style="width:70px;" class="text-center">결과</th>
|
||||
<th>서버 URL</th>
|
||||
<th style="width:38%;">상세</th>
|
||||
<th style="width:80px;" class="text-end">소요</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="reloadResultBody"></tbody>
|
||||
</table>
|
||||
<div class="text-muted" style="font-size:12px;">
|
||||
대상 서버는 포탈 프로퍼티 <code>Portal / portal.internal.menu-reload-url</code> 에
|
||||
콤마(,) 또는 줄바꿈으로 나열합니다. 서버별 타임아웃 3초, 전 서버 병렬 호출입니다.
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-sm btn-secondary" data-bs-dismiss="modal">닫기</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div><!-- end content_middle -->
|
||||
</div><!-- end right_box -->
|
||||
</body>
|
||||
</html>
|
||||
@@ -18,43 +18,17 @@
|
||||
var url = '<c:url value="/onl/apim/portalnotice/portalNoticeMan.json" />';
|
||||
var url_view = '<c:url value="/onl/apim/portalnotice/portalNoticeMan.view" />';
|
||||
var combo;
|
||||
|
||||
// 코드 상수 — PortalNoticeUI 와 동기화
|
||||
var NOTICE_TYPE_INCIDENT = '3';
|
||||
|
||||
// IncidentState 라벨 — 상세 화면과 동일하게 유지
|
||||
var STATE_LABELS = {
|
||||
'INVESTIGATING': '조사중',
|
||||
'IDENTIFIED': '원인 파악',
|
||||
'MONITORING': '모니터링',
|
||||
'RESOLVED': '해소',
|
||||
'CANCELED': '취소'
|
||||
};
|
||||
|
||||
function formatIncidentState(cellvalue, options, rowObject) {
|
||||
if (!cellvalue) return '';
|
||||
var label = STATE_LABELS[cellvalue] || cellvalue;
|
||||
var closed = (cellvalue === 'RESOLVED' || cellvalue === 'CANCELED');
|
||||
return closed
|
||||
? '<span title="' + cellvalue + '">' + label + '</span>'
|
||||
: '<span style="color: red;" title="' + cellvalue + '">' + label + '</span>';
|
||||
}
|
||||
|
||||
|
||||
function formatNoticeType(cellvalue, options, rowObject) {
|
||||
var name = cellvalue;
|
||||
if (combo && combo.noticeTypeList) {
|
||||
for (var i = 0; i < combo.noticeTypeList.length; i++) {
|
||||
if (combo.noticeTypeList[i].CODE == cellvalue) {
|
||||
name = combo.noticeTypeList[i].NAME;
|
||||
break;
|
||||
}
|
||||
if (!combo || !combo.noticeTypeList) {
|
||||
return cellvalue;
|
||||
}
|
||||
for (var i = 0; i < combo.noticeTypeList.length; i++) {
|
||||
if (combo.noticeTypeList[i].CODE == cellvalue) {
|
||||
return combo.noticeTypeList[i].NAME;
|
||||
}
|
||||
}
|
||||
// 장애인데 종료 시각이 없으면 아직 진행 중이다
|
||||
if (cellvalue == NOTICE_TYPE_INCIDENT && rowObject && !rowObject.endAt) {
|
||||
return '<span style="color: red;">' + name + '-진행중</span>';
|
||||
}
|
||||
return name;
|
||||
return cellvalue;
|
||||
}
|
||||
|
||||
function formatuseYn(cellvalue, options, rowObject) {
|
||||
@@ -141,15 +115,14 @@
|
||||
searchNoticeSubject: $('input[name=searchNoticeSubject]').val(),
|
||||
searchNoticeDetail: $('input[name=searchNoticeDetail]').val()
|
||||
},
|
||||
colNames:['id', 'No.', '게시유형', '제목', '고정여부', '사용', '장애상태', '마지막 수정일', '등록일', '등록자', '조회수'],
|
||||
colNames:['id', 'No.', '게시유형', '제목', '고정여부', '사용', '마지막 수정일', '등록일', '등록자', '조회수'],
|
||||
colModel:[
|
||||
{ name : 'id' , align:'center', key:true, hidden:true},
|
||||
{ name : 'rowNum' , align:'center', width:60 },
|
||||
{ name : 'noticeType' , align:'center', width:100, formatter: formatNoticeType },
|
||||
{ name : 'noticeType' , align:'center', width:60, formatter: formatNoticeType },
|
||||
{ name : 'noticeSubject' , align:'left' , width:300, formatter: formatFile },
|
||||
{ name : 'fixYn' , align:'center', width:60, formatter: formatFixYn },
|
||||
{ name : 'useYn' , align:'center', width:60, formatter: formatuseYn },
|
||||
{ name : 'state' , align:'center', width:90, sortable:false, formatter: formatIncidentState },
|
||||
{ name : 'lastModifiedDate', align:'center', width:120 , formatter: timeStampFormat},
|
||||
{ name : 'createdDate' , align:'center', width:120, formatter: timeStampFormat },
|
||||
{ name : 'inquirerName' , align:'center', width:100 },
|
||||
|
||||
@@ -32,35 +32,10 @@
|
||||
// 영향 API 목록 (UI 상태)
|
||||
var affectedApis = [];
|
||||
|
||||
// 장애 타임라인 (UI 상태)
|
||||
var currentIncidentId = null;
|
||||
var currentIncidentState = null;
|
||||
|
||||
// 종결 상태 — 재전이 불가 (재발은 새 장애로 등록)
|
||||
var TERMINAL_STATES = ['RESOLVED', 'CANCELED'];
|
||||
|
||||
// IncidentState 라벨 — 상태 콤보 문구와 동일하게 유지
|
||||
var STATE_LABELS = {
|
||||
'INVESTIGATING': '조사중',
|
||||
'IDENTIFIED': '원인 파악',
|
||||
'MONITORING': '모니터링',
|
||||
'RESOLVED': '해소',
|
||||
'CANCELED': '취소'
|
||||
};
|
||||
|
||||
function isIncidentType(t) {
|
||||
return t === NOTICE_TYPE_INCIDENT || t === NOTICE_TYPE_MAINTENANCE;
|
||||
}
|
||||
|
||||
function stateLabel(code) {
|
||||
if (!code) return '';
|
||||
return (STATE_LABELS[code] || code) + ' (' + code + ')';
|
||||
}
|
||||
|
||||
function escapeHtml(text) {
|
||||
return $('<div>').text(text == null ? '' : text).html();
|
||||
}
|
||||
|
||||
function init() {
|
||||
var key = "${param.id}";
|
||||
isDetail = key != "" && key != "null";
|
||||
@@ -101,57 +76,13 @@
|
||||
// INCIDENT 만 상태 영역 표시
|
||||
if (t === NOTICE_TYPE_INCIDENT) {
|
||||
$('.incident-state-row').show();
|
||||
$('.incident-endat-hint').show();
|
||||
} else {
|
||||
$('.incident-state-row').hide();
|
||||
$('.incident-endat-hint').hide();
|
||||
}
|
||||
// 장애는 종결(해소·취소) 후에만 종료 시각을 손볼 수 있다. 진행 중에는 상태를 따른다.
|
||||
var closed = $.inArray(currentIncidentState, TERMINAL_STATES) >= 0;
|
||||
$('#endAt').prop('readonly', t === NOTICE_TYPE_INCIDENT && !closed);
|
||||
$('.incident-endat-hint').text(closed
|
||||
? '※ 종결된 장애입니다. 실제 복구 시각으로 수정할 수 있습니다.'
|
||||
: '※ 타임라인에서 해소·취소로 전이하면 자동 입력됩니다.');
|
||||
} else {
|
||||
$('.incident-row').hide();
|
||||
$('.incident-state-row').hide();
|
||||
$('.incident-endat-hint').hide();
|
||||
$('#endAt').prop('readonly', false);
|
||||
}
|
||||
// 양식(템플릿)은 장애 공지에만 쓴다 - 점검은 문구 체계가 다르다
|
||||
$('.template-row').toggle(t === NOTICE_TYPE_INCIDENT);
|
||||
|
||||
// 타임라인은 등록된 장애(수정 모드)에서만 다룬다
|
||||
if (t === NOTICE_TYPE_INCIDENT && isDetail && currentIncidentId) {
|
||||
$('#timelineSection').show();
|
||||
} else {
|
||||
$('#timelineSection').hide();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 장애 공지 양식(제목·본문)을 서버에서 받아 채운다.
|
||||
* 문구는 PTL_PROPERTY 'djb.apistatus.draft.*' 이며 자동 탐지 초안과 같은 양식이다.
|
||||
*
|
||||
* @param force true 면 이미 입력한 제목·본문도 (확인 후) 덮어쓴다
|
||||
*/
|
||||
function applyTemplate(force) {
|
||||
if (force) {
|
||||
var hasInput = $.trim($('#noticeSubject').val()) !== '' || !$('#contents').summernote('isEmpty');
|
||||
if (hasInput && !confirm('현재 입력한 제목·본문을 양식으로 덮어씁니다. 계속하시겠습니까?')) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
$.ajax({
|
||||
type: "POST", url: url, dataType: "json",
|
||||
data: {cmd: 'DRAFT_TEMPLATE', apisJson: JSON.stringify(affectedApis)},
|
||||
success: function (data) {
|
||||
if (!data) return;
|
||||
$('#noticeSubject').val(data.subject || '');
|
||||
$('#contents').summernote('code', data.detail || '');
|
||||
},
|
||||
error: function (e) { alert(e.responseText); }
|
||||
});
|
||||
}
|
||||
|
||||
function decodeHTMLEntities(text) {
|
||||
@@ -217,11 +148,8 @@
|
||||
// AuthorizeInterceptor 가 정상적으로 권한 인식. window.open 직접 호출 시 권한 부족 발생.
|
||||
// 본 페이지는 summernote 본문 에디터 등 다른 iframe 이 함께 존재할 수 있어
|
||||
// frames[0] 가정은 위험 → DOM 에서 .ui-dialog 내 iframe 중 src=apiGroupMan 인 것을 식별.
|
||||
// 공지 영향 API 는 포탈에 실제 노출되는 것만 고르면 되므로 두 조건을 켠 채로 연다.
|
||||
// (타 화면에서 여는 팝업은 파라미터가 없어 해제 상태 — 팝업 기본값은 건드리지 않는다)
|
||||
var selectedIds = affectedApis.map(function(a) { return a.apiId; }).join(',');
|
||||
var url2 = apiPopupUrl + '?cmd=POPUP&searchApiSpecYn=Y&searchPortalDisplayYn=Y'
|
||||
+ '&selectedApiIds=' + encodeURIComponent(selectedIds);
|
||||
var url2 = apiPopupUrl + '?cmd=POPUP&selectedApiIds=' + encodeURIComponent(selectedIds);
|
||||
var args = {};
|
||||
showModal(url2, args, 1280, 860, function () {
|
||||
var doc = args.self && args.self.document;
|
||||
@@ -264,150 +192,6 @@
|
||||
renderAffectedApis();
|
||||
}
|
||||
|
||||
// ───────────────────── 장애 처리 타임라인 ─────────────────────
|
||||
|
||||
/** 현재 상태 기준으로 전이 가능한 선택지만 남긴다. 실제 차단은 서버가 한다. */
|
||||
function applyStateChangeGuard() {
|
||||
var terminal = $.inArray(currentIncidentState, TERMINAL_STATES) >= 0;
|
||||
|
||||
$('#timelineStateChange').prop('disabled', terminal);
|
||||
if (terminal) {
|
||||
$('#timelineStateChange').prop('checked', false);
|
||||
$('#timelineNextState').prop('disabled', true).val('');
|
||||
}
|
||||
$('#timelineClosedHint').toggle(terminal);
|
||||
$('#timelineResolveHint').toggle(!terminal);
|
||||
|
||||
// 현재와 동일한 상태로는 전이할 수 없다
|
||||
$('#timelineNextState option').each(function() {
|
||||
var v = $(this).val();
|
||||
$(this).prop('disabled', v !== '' && v === currentIncidentState);
|
||||
});
|
||||
if ($('#timelineNextState').val() === currentIncidentState) {
|
||||
$('#timelineNextState').val('');
|
||||
}
|
||||
}
|
||||
|
||||
function loadTimeline() {
|
||||
if (!currentIncidentId) {
|
||||
renderTimeline([]);
|
||||
return;
|
||||
}
|
||||
$.ajax({
|
||||
type: "POST", url: url, dataType: "json",
|
||||
data: {cmd: 'TIMELINE_LIST', incidentId: currentIncidentId},
|
||||
success: function (rows) { renderTimeline(rows || []); },
|
||||
error: function (e) { alert(e.responseText); }
|
||||
});
|
||||
}
|
||||
|
||||
function renderTimeline(rows) {
|
||||
var $tb = $('#timelineTbody');
|
||||
$tb.empty();
|
||||
if (!rows.length) {
|
||||
$tb.append('<tr><td colspan="6" style="text-align:center;color:#999;">등록된 타임라인이 없습니다.</td></tr>');
|
||||
return;
|
||||
}
|
||||
$.each(rows, function(_, row) {
|
||||
var author = row.authorType === 'SYSTEM' ? 'System' : 'Admin';
|
||||
var visible = row.visibleYn === 'Y';
|
||||
var $tr = $('<tr>');
|
||||
$tr.append('<td style="text-align:center;white-space:nowrap;">' + escapeHtml(row.eventAt) + '</td>');
|
||||
$tr.append('<td style="text-align:center;">' + author + '</td>');
|
||||
$tr.append('<td style="text-align:center;white-space:nowrap;">' + (row.stateAfter ? escapeHtml(stateLabel(row.stateAfter)) : '-') + '</td>');
|
||||
$tr.append('<td>' + escapeHtml(row.body).replace(/\n/g, '<br>') + '</td>');
|
||||
|
||||
// 현재 상태(라벨) + 눌렀을 때 바뀔 상태(버튼) 를 나눠 표기 — 버튼 하나면 의미가 모호하다
|
||||
var $visibleTd = $('<td style="text-align:center;white-space:nowrap;">');
|
||||
$('<span>')
|
||||
.text(visible ? '공개중' : '비공개')
|
||||
.css({color: visible ? '#1B8C4A' : '#999', 'margin-right': '5px'})
|
||||
.appendTo($visibleTd);
|
||||
$('<button type="button" class="cssbtn" level="W" status="DETAIL" style="min-width:0;padding:3px 8px;">')
|
||||
.text(visible ? '비공개로' : '공개로')
|
||||
.click(function() { toggleTimelineVisible(row.timelineId, visible ? 'N' : 'Y'); })
|
||||
.appendTo($visibleTd);
|
||||
$tr.append($visibleTd);
|
||||
|
||||
var $deleteTd = $('<td style="text-align:center;">');
|
||||
$('<button type="button" class="cssbtn smallBtn" level="W" status="DETAIL" style="min-width:0;width:90%;">')
|
||||
.text('삭제')
|
||||
.click(function() { deleteTimeline(row.timelineId); })
|
||||
.appendTo($deleteTd);
|
||||
$tr.append($deleteTd);
|
||||
|
||||
$tb.append($tr);
|
||||
});
|
||||
buttonControl(true);
|
||||
}
|
||||
|
||||
function addTimeline() {
|
||||
if (!currentIncidentId) {
|
||||
alert('장애 정보를 먼저 저장하여 주십시오.');
|
||||
return;
|
||||
}
|
||||
var body = $('#timelineBody').val();
|
||||
if (!body || !$.trim(body)) {
|
||||
alert('타임라인 메세지를 입력하여 주십시오.');
|
||||
$('#timelineBody').focus();
|
||||
return;
|
||||
}
|
||||
var stateChange = $('#timelineStateChange').is(':checked');
|
||||
var nextState = $('#timelineNextState').val();
|
||||
if (stateChange && !nextState) {
|
||||
alert('변경할 상태를 선택하여 주십시오.');
|
||||
return;
|
||||
}
|
||||
$.ajax({
|
||||
type: "POST", url: url, dataType: "json",
|
||||
data: {cmd: 'TIMELINE_ADD', incidentId: currentIncidentId, body: body,
|
||||
stateChange: stateChange, nextState: nextState},
|
||||
success: function (data) {
|
||||
$('#timelineBody').val('');
|
||||
$('#timelineStateChange').prop('checked', false).trigger('change');
|
||||
// 상태 전이 시 서버가 상태/이전 상태/종료 시각을 함께 갱신한다
|
||||
$('#stateText').text(data.state ? stateLabel(data.state) : '');
|
||||
$('#previousState').text(data.previousState ? stateLabel(data.previousState) : '없음');
|
||||
$('#endAt').val(data.endAt || '');
|
||||
currentIncidentState = data.state || null;
|
||||
applyStateChangeGuard();
|
||||
// 종결 전이 직후부터 종료 시각을 손볼 수 있어야 한다
|
||||
toggleIncidentFields();
|
||||
loadTimeline();
|
||||
},
|
||||
error: function (e) { alert(e.responseText); }
|
||||
});
|
||||
}
|
||||
|
||||
function toggleTimelineVisible(timelineId, visibleYn) {
|
||||
$.ajax({
|
||||
type: "POST", url: url,
|
||||
data: {cmd: 'TIMELINE_VISIBLE', timelineId: timelineId, visibleYn: visibleYn},
|
||||
success: loadTimeline,
|
||||
error: function (e) { alert(e.responseText); }
|
||||
});
|
||||
}
|
||||
|
||||
function deleteTimeline(timelineId) {
|
||||
if (confirm("<%= localeMessage.getString("common.checkDelete")%>") != true) return;
|
||||
$.ajax({
|
||||
type: "POST", url: url,
|
||||
data: {cmd: 'TIMELINE_DELETE', timelineId: timelineId},
|
||||
success: loadTimeline,
|
||||
error: function (e) { alert(e.responseText); }
|
||||
});
|
||||
}
|
||||
|
||||
/** 장애/점검 공지는 최초 게시 후 게시유형을 바꿀 수 없다. */
|
||||
function lockNoticeType(noticeType) {
|
||||
if (!isIncidentType(noticeType)) return;
|
||||
$('#noticeType').prop('disabled', true);
|
||||
if (!$('#noticeTypeHidden').length) {
|
||||
$('#ajaxForm').append('<input type="hidden" id="noticeTypeHidden" name="noticeType">');
|
||||
}
|
||||
$('#noticeTypeHidden').val(noticeType);
|
||||
}
|
||||
|
||||
function detail(key) {
|
||||
if (!isDetail) return;
|
||||
$.ajax({
|
||||
@@ -423,28 +207,17 @@
|
||||
var decodedContent = decodeHTMLEntities(data.noticeDetail);
|
||||
$('#contents').summernote('code', decodedContent);
|
||||
|
||||
// 게시유형은 최초 저장 후 변경 불가 (disabled 는 전송되지 않아 hidden 으로 값을 유지)
|
||||
lockNoticeType(data.noticeType);
|
||||
|
||||
// 장애/점검 필드
|
||||
$('#summary').val(data.summary || '');
|
||||
$('#startedAt').val(data.startedAt || '');
|
||||
$('#endAt').val(data.endAt || '');
|
||||
// 상태/이전 상태는 타임라인 게시로만 바뀐다 (직접 수정 불가)
|
||||
$('#stateText').text(data.state ? stateLabel(data.state) : '');
|
||||
$('#previousState').text(data.previousState ? stateLabel(data.previousState) : '없음');
|
||||
$('#state').val(data.state || 'INVESTIGATING');
|
||||
$('#previousState').text(data.previousState || '없음');
|
||||
affectedApis = (data.affectedApis || []).map(function(a){
|
||||
return {apiId: a.apiId, apiName: a.apiName || ''};
|
||||
});
|
||||
renderAffectedApis();
|
||||
|
||||
currentIncidentId = data.incidentId || null;
|
||||
currentIncidentState = data.state || null;
|
||||
applyStateChangeGuard();
|
||||
toggleIncidentFields();
|
||||
if (data.noticeType === NOTICE_TYPE_INCIDENT && currentIncidentId) {
|
||||
loadTimeline();
|
||||
}
|
||||
|
||||
if (data.fileInfo) {
|
||||
fileInfo = {
|
||||
@@ -469,25 +242,9 @@
|
||||
|
||||
$('#noticeType').on('change', toggleIncidentFields);
|
||||
|
||||
// 신규 등록에서 장애를 고르면 빈 본문에 한해 양식을 자동으로 채운다.
|
||||
// (수정 모드나 이미 쓴 본문은 건드리지 않는다 - 덮어쓰려면 '양식 적용' 버튼)
|
||||
$('#noticeType').on('change', function () {
|
||||
if (!isDetail && $(this).val() === NOTICE_TYPE_INCIDENT && $('#contents').summernote('isEmpty')) {
|
||||
applyTemplate(false);
|
||||
}
|
||||
});
|
||||
$('#btn_applyTemplate').click(function () { applyTemplate(true); });
|
||||
|
||||
$('#btn_addAffectedApi').click(openApiPopup);
|
||||
$('#btn_removeAffectedApi').click(removeSelectedAffectedApis);
|
||||
|
||||
$('#timelineStateChange').on('change', function() {
|
||||
var checked = $(this).is(':checked');
|
||||
$('#timelineNextState').prop('disabled', !checked);
|
||||
if (!checked) $('#timelineNextState').val('');
|
||||
});
|
||||
$('#btn_addTimeline').click(addTimeline);
|
||||
|
||||
$('#fileInput').change(function(e) {
|
||||
var file = e.target.files[0];
|
||||
if (file) {
|
||||
@@ -516,11 +273,6 @@
|
||||
alert('점검 종료 시각을 입력해 주십시오.');
|
||||
return;
|
||||
}
|
||||
// datetime-local 값은 'YYYY-MM-DDTHH:mm' 이라 문자열 비교로 선후가 가려진다
|
||||
if ($('#endAt').val() && $('#endAt').val() < $('#startedAt').val()) {
|
||||
alert('종료 시각은 시작 시각보다 빠를 수 없습니다.');
|
||||
return;
|
||||
}
|
||||
if (affectedApis.length === 0) {
|
||||
alert('영향 API를 1건 이상 선택해 주십시오.');
|
||||
return;
|
||||
@@ -530,10 +282,6 @@
|
||||
if (confirm("<%= localeMessage.getString("common.checkSave")%>") != true) return;
|
||||
|
||||
var formData = new FormData($("#ajaxForm")[0]);
|
||||
// Summernote 그림 다이얼로그의 내부 파일 input(name="files")이 #ajaxForm 안에 있어
|
||||
// 선택한 이미지가 지워지지 않고 남아 있으면 여기서 함께 딸려 온다.
|
||||
// 본 화면은 첨부파일 기능을 쓰지 않으므로 항상 제거한다.
|
||||
formData.delete("files");
|
||||
formData.set("useYn", $("#useYn").is(":checked") ? "Y" : "N");
|
||||
formData.set("fixYn", $("#fixYn").is(":checked") ? "Y" : "N");
|
||||
formData.set("noticeDetail", $('#contents').summernote('code'));
|
||||
@@ -631,12 +379,7 @@
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>본문 <font color="red">*</font>
|
||||
<div class="template-row" style="display:none;margin-top:5px;">
|
||||
<button type="button" class="cssbtn smallBtn" id="btn_applyTemplate" level="W" status="DETAIL,NEW"
|
||||
style="min-width:0;width:90%;" title="장애 공지 양식으로 제목과 본문을 채웁니다.">양식 적용</button>
|
||||
</div>
|
||||
</th>
|
||||
<th>본문 <font color="red">*</font></th>
|
||||
<td colspan="3">
|
||||
<textarea id="contents" name="noticeDetail" style="width:100%;height:300px" data-required data-warning="본문을 입력하여 주십시오."></textarea>
|
||||
</td>
|
||||
@@ -651,15 +394,21 @@
|
||||
<th>종료</th>
|
||||
<td>
|
||||
<input type="datetime-local" id="endAt" name="endAt" style="width:90%" placeholder="해소시 자동 채움">
|
||||
<div class="incident-endat-hint" style="display:none;color:#888;font-size:12px;margin-top:3px;">
|
||||
※ 타임라인에서 해소·취소로 전이하면 자동 입력됩니다.
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<!-- 상태/이전 상태는 타임라인 게시로만 전이한다 (직접 수정 불가) -->
|
||||
<tr class="incident-row incident-state-row" style="display:none;">
|
||||
<th>상태</th>
|
||||
<td><span id="stateText"></span></td>
|
||||
<td>
|
||||
<div class="select-style">
|
||||
<select id="state" name="state">
|
||||
<option value="INVESTIGATING">INVESTIGATING - 조사중</option>
|
||||
<option value="IDENTIFIED">IDENTIFIED - 원인 파악</option>
|
||||
<option value="MONITORING">MONITORING - 모니터링</option>
|
||||
<option value="RESOLVED">RESOLVED - 해소</option>
|
||||
<option value="CANCELED">CANCELED - 취소</option>
|
||||
</select>
|
||||
</div>
|
||||
</td>
|
||||
<th>이전 상태</th>
|
||||
<td><span id="previousState">없음</span></td>
|
||||
</tr>
|
||||
@@ -686,61 +435,6 @@
|
||||
</tr>
|
||||
</table>
|
||||
</form>
|
||||
|
||||
<!-- 타임라인은 [수정] 저장과 별개로 게시 즉시 반영되므로 폼 밖에 둔다 -->
|
||||
<div id="timelineSection" style="display:none;margin-top:20px;">
|
||||
<div class="title">타임라인</div>
|
||||
<table class="table_row" cellspacing="0" style="width:100%;">
|
||||
<colgroup>
|
||||
<col style="width: 130px"/>
|
||||
<col style="width: 80px"/>
|
||||
<col style="width: 140px"/>
|
||||
<col/>
|
||||
<col style="width: 160px"/>
|
||||
<col style="width: 80px"/>
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>시간</th>
|
||||
<th>작성자</th>
|
||||
<th>상태</th>
|
||||
<th>메세지</th>
|
||||
<th>개발자포탈 공개</th>
|
||||
<th>삭제</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="timelineTbody">
|
||||
<tr><td colspan="6" style="text-align:center;color:#999;">등록된 타임라인이 없습니다.</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div style="margin-top:8px;display:flex;align-items:center;gap:8px;">
|
||||
<label>
|
||||
<input type="checkbox" id="timelineStateChange"> 상태 변경
|
||||
</label>
|
||||
<div class="select-style" style="width:220px;">
|
||||
<select id="timelineNextState" disabled>
|
||||
<option value="">(현재 상태 유지)</option>
|
||||
<option value="INVESTIGATING">INVESTIGATING - 조사중</option>
|
||||
<option value="IDENTIFIED">IDENTIFIED - 원인 파악</option>
|
||||
<option value="MONITORING">MONITORING - 모니터링</option>
|
||||
<option value="RESOLVED">RESOLVED - 해소</option>
|
||||
<option value="CANCELED">CANCELED - 취소</option>
|
||||
</select>
|
||||
</div>
|
||||
<span id="timelineResolveHint" style="color:#888;font-size:12px;">※ 해소(RESOLVED)·취소(CANCELED)로 전이하면 종료 시각이 자동 입력됩니다.</span>
|
||||
<span id="timelineClosedHint" style="display:none;color:#C0392B;font-size:12px;">
|
||||
※ 종결된 장애입니다. 상태 변경 불가 — 재발 시 새 장애로 등록하십시오.
|
||||
</span>
|
||||
</div>
|
||||
<div style="margin-top:5px;display:flex;gap:5px;">
|
||||
<textarea id="timelineBody" style="flex:1;height:60px;"
|
||||
placeholder="타임라인 메세지 입력 예 : 모니터링 5분간 정상 응답 확인 후 완료 상태 전이 예정"></textarea>
|
||||
<button type="button" class="cssbtn" id="btn_addTimeline" level="W" status="DETAIL">
|
||||
<i class="material-icons">send</i> 게시
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
@@ -312,9 +312,7 @@ $(document).ready(function() {
|
||||
<div class="content_middle" id="content_middle">
|
||||
<div class="search_wrap">
|
||||
<button type="button" class="cssbtn" id="btn_new" level="W"><i class="material-icons">add</i> <%= localeMessage.getString("button.new") %></button>
|
||||
<c:if test="${hardDeleteEnabled}">
|
||||
<button type="button" class="cssbtn" id="btn_delete_selected" level="W" style="background-color: #dc3545; border-color: #dc3545; color: white;"><i class="material-icons">delete_forever</i> 선택 삭제</button>
|
||||
</c:if>
|
||||
<button type="button" class="cssbtn" id="btn_search" level="R"><i class="material-icons">search</i> <%= localeMessage.getString("button.search") %></button>
|
||||
</div>
|
||||
<%--법인정보관리--%>
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
response.setHeader("Expires", "0");
|
||||
|
||||
MonitoringContext monitoringContext = (MonitoringContext)CommonUtil.getBean(request, "monitoringContext");
|
||||
String reverseProxyUrl = monitoringContext.getStringProperty(MonitoringContext.RMS_WEBHOOK_REVERSE_PROXY_URL, "");
|
||||
String reverseProxyUrl = monitoringContext.getStringProperty("djb.reverse_proxy.url", "");
|
||||
%>
|
||||
<%!
|
||||
public String getRequiredLabel(String label) {
|
||||
@@ -138,11 +138,7 @@
|
||||
$("#btn_delete").hide();
|
||||
}
|
||||
|
||||
$("select[name=approvalStatus]").val(portalOrgData.approvalStatus);
|
||||
// 승인상태는 읽기전용(수정 불가) - 값은 그대로 제출되도록 disabled 대신 잠금 처리
|
||||
$("select[name=approvalStatus]")
|
||||
.css({'pointer-events':'none', 'background-color':'#eee'})
|
||||
.attr('tabindex', '-1');
|
||||
$("#approvalStatus").val(portalOrgData.approvalStatus);
|
||||
$("#compRegNo").val(formatCompanyRegNo(portalOrgData.compRegNo));
|
||||
$("#corpRegNo").val(formatCorpRegNo(portalOrgData.corpRegNo));
|
||||
$("#createdDate").inputmask("9999-99-99 99:99:99", {'autoUnmask': true});
|
||||
@@ -484,7 +480,8 @@
|
||||
}
|
||||
}
|
||||
|
||||
// 저장 확인/사유 입력은 유효성 검사 및 FormData 구성 후 처리 (하단 submit 분기)
|
||||
if (confirm("<%= localeMessage.getString("common.checkSave")%>") !== true)
|
||||
return;
|
||||
|
||||
// FormData 생성 전에 분리된 전화번호 필드 제거
|
||||
$("#scPhonePrefix, #scPhoneMiddle, #scPhoneLast, #orgPhonePrefix, #orgPhoneMiddle, #orgPhoneLast").removeAttr('name');
|
||||
@@ -519,47 +516,23 @@
|
||||
}
|
||||
|
||||
|
||||
// cmd 및 사유(auditReason) 첨부 후 전송
|
||||
function submitOrg(cmd, auditReason) {
|
||||
formData.append("cmd", cmd);
|
||||
// 감사로그(AUDIT_LOG) 발화를 위한 systemCode. 법인/가입자 리포지토리는 고정 EMS 스키마라 스키마 전환 영향 없음
|
||||
formData.append("serviceType", "APIGW");
|
||||
if (auditReason) {
|
||||
formData.append("auditReason", auditReason);
|
||||
// cmd 파라미터 추가
|
||||
formData.append("cmd", isDetail ? "UPDATE" : "INSERT");
|
||||
|
||||
$.ajax({
|
||||
type : "POST",
|
||||
url:url,
|
||||
data: formData,
|
||||
processData: false,
|
||||
contentType: false,
|
||||
success:function(json){
|
||||
alert("<%= localeMessage.getString("common.saveMsg") %>");
|
||||
goNav(returnUrl);
|
||||
},
|
||||
error:function(e){
|
||||
alert(e.responseText);
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
type : "POST",
|
||||
url:url,
|
||||
data: formData,
|
||||
processData: false,
|
||||
contentType: false,
|
||||
success:function(json){
|
||||
alert("<%= localeMessage.getString("common.saveMsg") %>");
|
||||
goNav(returnUrl);
|
||||
},
|
||||
error:function(e){
|
||||
alert(e.responseText);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (isDetail) {
|
||||
// 수정: 상태를 REMOVED로 바꾼 경우 삭제(cmd=DELETE)로 감사 기록 + 익명화
|
||||
var targetCmd = ($("#orgStatus").val() === 'REMOVED') ? "DELETE" : "UPDATE";
|
||||
var isRemove = (targetCmd === "DELETE");
|
||||
showReasonPrompt(
|
||||
isRemove ? "해당 법인을 삭제(탈퇴) 처리합니다. 사유를 입력해 주세요." : "법인 정보를 수정합니다. 사유를 입력해 주세요.",
|
||||
{
|
||||
title: isRemove ? "삭제 사유" : "수정 사유",
|
||||
onConfirm: function(reason) { submitOrg(targetCmd, reason); }
|
||||
}
|
||||
);
|
||||
} else {
|
||||
// 신규 등록: 사유 불필요
|
||||
if (confirm("<%= localeMessage.getString("common.checkSave")%>") !== true) return;
|
||||
submitOrg("INSERT", null);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$("#btn_previous").click(function() {
|
||||
@@ -568,28 +541,25 @@
|
||||
|
||||
$("#btn_delete").click(function(){
|
||||
|
||||
showReasonPrompt("해당 법인을 삭제 처리합니다. 사유를 입력해 주세요.", {
|
||||
title: "삭제 사유",
|
||||
onConfirm: function(reason) {
|
||||
var postData = $('#ajaxForm').serializeArray();
|
||||
postData.push({name: "cmd" , value:"DELETE"});
|
||||
postData.push({name: "serviceType", value: "APIGW"});
|
||||
postData.push({name: "auditReason", value: reason});
|
||||
if(confirm("<%= localeMessage.getString("common.confirmMsg")%>")){
|
||||
var postData = $('#ajaxForm').serializeArray();
|
||||
postData.push({name: "cmd" , value:"DELETE"});
|
||||
|
||||
$.ajax({
|
||||
type : "POST",
|
||||
url:url,
|
||||
data:postData,
|
||||
success:function(args){
|
||||
alert("<%= localeMessage.getString("common.deleteMsg") %>");
|
||||
goNav(returnUrl);
|
||||
},
|
||||
error:function(e){
|
||||
alert(e.responseText);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
$.ajax({
|
||||
type : "POST",
|
||||
url:url,
|
||||
data:postData,
|
||||
success:function(args){
|
||||
alert("<%= localeMessage.getString("common.deleteMsg") %>");
|
||||
goNav(returnUrl);
|
||||
},
|
||||
error:function(e){
|
||||
alert(e.responseText);
|
||||
}
|
||||
});
|
||||
}else{
|
||||
return false;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
var url = '<c:url value="/onl/apim/portalproperty/propertyMan.json" />';
|
||||
var url_view = '<c:url value="/onl/apim/portalproperty/propertyMan.view" />';
|
||||
var lastsel2;
|
||||
var clickedCol; //클릭한 셀의 컬럼 인덱스(포커스 이동용)
|
||||
|
||||
function isValid() {
|
||||
if ($('input[name=prptyGroupName]').val() == "") {
|
||||
@@ -40,7 +39,7 @@
|
||||
if ($('input[name=prptyName]').val() == "") {
|
||||
alert("<%= localeMessage.getString("propertyDetail.checkRequired2") %>");
|
||||
return false;
|
||||
} else if ($('[name=prpty2Val]').val() == "") {
|
||||
} else if ($('input[name=prpty2Val]').val() == "") {
|
||||
alert("<%= localeMessage.getString("propertyDetail.checkRequired3") %>");
|
||||
return false;
|
||||
}
|
||||
@@ -90,19 +89,7 @@
|
||||
'설명',
|
||||
'<%= localeMessage.getString("propertyDetail.delYn") %>'],
|
||||
colModel: [{name: 'PRPTYNAME', width: 50, align: 'left', editable: true},
|
||||
{
|
||||
name: 'PRPTY2VAL',
|
||||
width: 150,
|
||||
align: 'left',
|
||||
editable: true,
|
||||
//다행(멀티라인) 값 편집 — 관련 사이트 목록처럼 줄 단위 표기하는 값 지원
|
||||
edittype: 'textarea',
|
||||
editoptions: {rows: 3, cols: 60},
|
||||
//줄바꿈이 그리드에서도 그대로 보이도록
|
||||
cellattr: function () {
|
||||
return ' style="white-space:pre-line; word-break:break-all;"';
|
||||
}
|
||||
},
|
||||
{name: 'PRPTY2VAL', width: 150, align: 'left', editable: true},
|
||||
{name: 'PRPTYDESC', width: 200, align: 'left', editable: true, edittype: 'textarea', editoptions: {rows: 2}},
|
||||
{
|
||||
name: 'DELETEYN',
|
||||
@@ -116,10 +103,6 @@
|
||||
},
|
||||
loadComplete: function () {
|
||||
},
|
||||
onCellSelect: function (rowid, iCol) {
|
||||
//onSelectRow보다 먼저 발화 → 클릭한 컬럼 인덱스 저장
|
||||
clickedCol = iCol;
|
||||
},
|
||||
onSelectRow: function (rowid, status) {
|
||||
if (lastsel2 != undefined) {
|
||||
if ($("#grid tr#" + lastsel2).attr("editable") == 1) { //editable=1 means row in edit mode
|
||||
@@ -127,8 +110,7 @@
|
||||
}
|
||||
}
|
||||
$('#grid').restoreRow(lastsel2);
|
||||
//focusField로 클릭한 셀에 포커스(미지정 시 첫 편집 컬럼으로 감)
|
||||
$('#grid').editRow(rowid, {keys: true, focusField: clickedCol});
|
||||
$('#grid').editRow(rowid, true);
|
||||
lastsel2 = rowid;
|
||||
},
|
||||
onSortCol: function () {
|
||||
@@ -277,7 +259,7 @@
|
||||
}
|
||||
var data = new Object();
|
||||
data["PRPTYNAME"] = $("input[name=prptyName]").val();
|
||||
data["PRPTY2VAL"] = $("[name=prpty2Val]").val();
|
||||
data["PRPTY2VAL"] = $("input[name=prpty2Val]").val();
|
||||
data["PRPTYDESC"] = $("input[name=prptyDesc]").val();
|
||||
|
||||
var rows = $("#grid")[0].rows;
|
||||
@@ -358,8 +340,7 @@
|
||||
<tr>
|
||||
<th><%= localeMessage.getString("propertyDetail.propertyValue") %>
|
||||
</th>
|
||||
<td colspan="3"><textarea name="prpty2Val" rows="3"
|
||||
style="width:100%; box-sizing:border-box; resize:vertical;"></textarea>
|
||||
<td colspan="3"><input type="text" name="prpty2Val"/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
||||
@@ -347,11 +347,9 @@
|
||||
<button type="button" class="cssbtn" id="btn_new" level="W"><i
|
||||
class="material-icons">add</i> <%= localeMessage.getString("button.new") %>
|
||||
</button>
|
||||
<c:if test="${hardDeleteEnabled}">
|
||||
<button type="button" class="cssbtn" id="btn_delete_selected" level="W" style="background-color: #dc3545; border-color: #dc3545; color: white;"><i
|
||||
class="material-icons">delete_forever</i> 선택 삭제
|
||||
</button>
|
||||
</c:if>
|
||||
<button type="button" class="cssbtn" id="btn_search" level="R"><i
|
||||
class="material-icons">search</i> <%= localeMessage.getString("button.search") %>
|
||||
</button>
|
||||
|
||||
@@ -165,10 +165,6 @@
|
||||
$("#userStatus").val(data.userStatus);
|
||||
$("#roleCode").val(data.roleCode).trigger('change');
|
||||
$("#approvalStatus").val(data.approvalStatus);
|
||||
// 승인상태는 읽기전용(수정 불가) - 값은 그대로 제출되도록 disabled 대신 잠금 처리
|
||||
$("#approvalStatus")
|
||||
.css({'pointer-events':'none', 'background-color':'#eee'})
|
||||
.attr('tabindex', '-1');
|
||||
|
||||
// 탈퇴 상태 처리
|
||||
if (originalUserStatus === STATUS.REMOVED) {
|
||||
@@ -285,7 +281,7 @@
|
||||
}
|
||||
|
||||
const fullLoginId = id + "@" + domain;
|
||||
const emailPattern = /^[A-Za-z0-9._%+-]*@[A-Za-z0-9][-A-Za-z0-9.]+\.[A-Za-z]{2,}$/;
|
||||
const emailPattern = /^[A-Za-z0-9._-]*@[A-Za-z0-9][-A-Za-z0-9.]+\.[A-Za-z]{2,}$/;
|
||||
if (!emailPattern.test(fullLoginId)) {
|
||||
alert(fullLoginId + " 은 올바른 이메일 형식이 아닙니다.");
|
||||
return;
|
||||
@@ -317,7 +313,7 @@
|
||||
}
|
||||
|
||||
function validateEmailId(input) {
|
||||
input.value = input.value.replace(/[^A-Za-z0-9._%+-]/g, '');
|
||||
input.value = input.value.replace(/[^A-Za-z0-9._-]/g, '');
|
||||
}
|
||||
|
||||
function validateDomainInput(input) {
|
||||
@@ -492,49 +488,29 @@
|
||||
formData.push({name: 'mobileNumber', value: mobileNumber});
|
||||
}
|
||||
|
||||
// cmd 및 사유(auditReason) 첨부 후 전송
|
||||
function submitUser(cmd, auditReason) {
|
||||
formData.push({ name: 'cmd', value: cmd });
|
||||
// 감사로그(AUDIT_LOG) 발화를 위한 systemCode. 가입자 리포지토리는 고정 EMS 스키마라 스키마 전환 영향 없음
|
||||
formData.push({ name: 'serviceType', value: 'APIGW' });
|
||||
if (auditReason) {
|
||||
formData.push({ name: 'auditReason', value: auditReason });
|
||||
formData.push({
|
||||
name: 'cmd',
|
||||
value: isDetail ? "UPDATE" : "INSERT"
|
||||
});
|
||||
|
||||
if (!confirm("<%= localeMessage.getString("common.checkSave")%>")) return;
|
||||
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: url,
|
||||
data: formData,
|
||||
success: function (json) {
|
||||
if (json.status === "fail") {
|
||||
alert(json.errorMsg || "저장에 실패했습니다.");
|
||||
return;
|
||||
}
|
||||
alert("<%= localeMessage.getString("common.saveMsg") %>");
|
||||
goNav(returnUrl);
|
||||
},
|
||||
error: function (e) {
|
||||
alert(e.responseText);
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: url,
|
||||
data: formData,
|
||||
success: function (json) {
|
||||
if (json.status === "fail") {
|
||||
alert(json.errorMsg || "저장에 실패했습니다.");
|
||||
return;
|
||||
}
|
||||
alert("<%= localeMessage.getString("common.saveMsg") %>");
|
||||
goNav(returnUrl);
|
||||
},
|
||||
error: function (e) {
|
||||
alert(e.responseText);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (isDetail) {
|
||||
// 수정: 상태를 REMOVED로 바꾼 경우 삭제(cmd=DELETE)로 감사 기록 + 익명화
|
||||
var targetCmd = ($("#userStatus").val() === 'REMOVED') ? "DELETE" : "UPDATE";
|
||||
var isRemove = (targetCmd === "DELETE");
|
||||
showReasonPrompt(
|
||||
isRemove ? "해당 가입자를 삭제(탈퇴) 처리합니다. 사유를 입력해 주세요." : "가입자 정보를 수정합니다. 사유를 입력해 주세요.",
|
||||
{
|
||||
title: isRemove ? "삭제 사유" : "수정 사유",
|
||||
onConfirm: function(reason) { submitUser(targetCmd, reason); }
|
||||
}
|
||||
);
|
||||
} else {
|
||||
// 신규 등록: 사유 불필요. 초기 패스워드는 비밀번호 초기화와 동일(이메일ID + subfix)
|
||||
if (!confirm("가입자를 등록하시겠습니까?\n초기 패스워드는 '이메일ID${passwordInitSubfix}' 입니다.")) return;
|
||||
submitUser("INSERT", null);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$("#btn_previous").click(function () {
|
||||
@@ -542,32 +518,27 @@
|
||||
});
|
||||
|
||||
$("#btn_delete").click(function () {
|
||||
showReasonPrompt("해당 가입자를 삭제 처리합니다. 사유를 입력해 주세요.", {
|
||||
title: "삭제 사유",
|
||||
onConfirm: function(reason) {
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: url,
|
||||
data: {
|
||||
cmd: "DELETE",
|
||||
id: $('input[name=id]').val(),
|
||||
serviceType: "APIGW",
|
||||
auditReason: reason
|
||||
},
|
||||
success: function () {
|
||||
alert("<%= localeMessage.getString("common.deleteMsg") %>");
|
||||
goNav(returnUrl);
|
||||
},
|
||||
error: function (e) {
|
||||
alert(e.responseText);
|
||||
}
|
||||
});
|
||||
if (!confirm("<%= localeMessage.getString("common.confirmMsg")%>")) return;
|
||||
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: url,
|
||||
data: {
|
||||
cmd: "DELETE",
|
||||
id: $('input[name=id]').val()
|
||||
},
|
||||
success: function () {
|
||||
alert("<%= localeMessage.getString("common.deleteMsg") %>");
|
||||
goNav(returnUrl);
|
||||
},
|
||||
error: function (e) {
|
||||
alert(e.responseText);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$("#btn_password_reset").click(function () {
|
||||
if (!confirm("패스워드를 초기화 하시겠습니까?\n초기 패스워드는 '이메일ID${passwordInitSubfix}' 입니다.")) return;
|
||||
if (!confirm("패스워드를 초기화 하시겠습니까?\n초기 패스워드는 '!' + 이메일ID 입니다.")) return;
|
||||
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
|
||||
@@ -172,9 +172,6 @@ $(document).ready(function() {
|
||||
<form id="ajaxForm">
|
||||
<input type="hidden" name="id">
|
||||
<table class="table_row" cellspacing="0">
|
||||
<colgroup>
|
||||
<col width="200px">
|
||||
</colgroup>
|
||||
<tr>
|
||||
<th><%= localeMessage.getString("messageTemplate.messageCode") %> <font color="red"> *</font></th>
|
||||
<td><input type="text" name="messageCode" data-required data-warning="메세지 코드는 필수입니다"/></td>
|
||||
|
||||
@@ -1,149 +0,0 @@
|
||||
<%@ 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, '"'); }
|
||||
|
||||
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>
|
||||
@@ -1,154 +0,0 @@
|
||||
<%@ 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>
|
||||
@@ -1,243 +0,0 @@
|
||||
<%@ 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; }
|
||||
.succ-y { color:#1a73e8; font-weight:bold; }
|
||||
.succ-n { color:#d93025; font-weight:bold; }
|
||||
</style>
|
||||
<script language="javascript">
|
||||
var url = '<c:url value="/onl/apim/webhook/webhookSendLogMan.json" />';
|
||||
var url_view = '<c:url value="/onl/apim/webhook/webhookSendLogMan.view" />';
|
||||
var combo;
|
||||
|
||||
function escapeHtmlJs(s) { return $('<div>').text(s == null ? '' : s).html(); }
|
||||
function escapeAttr(s) { return escapeHtmlJs(s).replace(/"/g, '"'); }
|
||||
|
||||
function formatTargetUrl(cellvalue, options, rowObject) {
|
||||
var v = cellvalue || '';
|
||||
return '<span class="url-cell" title="' + escapeAttr(v) + '">' + escapeHtmlJs(v) + '</span>';
|
||||
}
|
||||
|
||||
// 이벤트유형 코드 → 모니터링 공통코드(EVENT_TYPE) 코드명
|
||||
function formatEventType(cellvalue, options, rowObject) {
|
||||
if (!combo || !combo.eventTypeList) {
|
||||
return escapeHtmlJs(cellvalue);
|
||||
}
|
||||
for (var i = 0; i < combo.eventTypeList.length; i++) {
|
||||
if (combo.eventTypeList[i].CODE == cellvalue) {
|
||||
return escapeHtmlJs(combo.eventTypeList[i].NAME);
|
||||
}
|
||||
}
|
||||
return escapeHtmlJs(cellvalue);
|
||||
}
|
||||
|
||||
function formatSuccess(cellvalue, options, rowObject) {
|
||||
if (cellvalue === true) return '<span class="succ-y">성공</span>';
|
||||
if (cellvalue === false) return '<span class="succ-n">실패</span>';
|
||||
return '';
|
||||
}
|
||||
|
||||
|
||||
function init() {
|
||||
|
||||
$("#startDatepicker").datepicker().inputmask("yyyy-mm-dd",{'autoUnmask':true});
|
||||
$("#endDatepicker").datepicker().inputmask("yyyy-mm-dd",{'autoUnmask':true});
|
||||
|
||||
var today = getToday();
|
||||
|
||||
$("input[name=searchStartYYYYMMDD]").val(today.substring(0,6)+"01");
|
||||
$("input[name=searchEndYYYYMMDD]").val(today);
|
||||
$("input[name=searchStartDate]").val(today.substring(0,6)+"01");
|
||||
$("input[name=searchEndDate]").val(today);
|
||||
|
||||
$.ajax({
|
||||
type: "POST", url: url, dataType: "json",
|
||||
data: {cmd: 'LIST_INIT_COMBO'},
|
||||
success: function (json) {
|
||||
combo = json;
|
||||
new makeOptions("CODE", "NAME").setObj($("select[name=searchEventType]"))
|
||||
.setNoValueInclude(true).setNoValue("", "<%=localeMessage.getString("combo.all")%>")
|
||||
.setData(json.eventTypeList).rendering();
|
||||
|
||||
// 뒤로가기 등으로 이전 검색조건 파라미터가 있으면 기본값을 덮어씀
|
||||
putSelectFromParam();
|
||||
|
||||
// 콤보(이벤트유형) 로드 후 그리드 생성 — formatEventType 경합 방지
|
||||
buildGrid();
|
||||
},
|
||||
error: function (e) { alert(e.responseText); }
|
||||
});
|
||||
}
|
||||
|
||||
function buildGrid() {
|
||||
$('#grid').jqGrid({
|
||||
datatype: "json",
|
||||
mtype: 'POST',
|
||||
url: url,
|
||||
postData: getSearchForJqgrid("cmd", "LIST"),
|
||||
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},
|
||||
{name: 'orgName', align: 'left', width: 140},
|
||||
{name: 'eventType', align: 'center', width: 120, formatter: formatEventType},
|
||||
{name: 'targetUrl', align: 'left', width: 300, formatter: formatTargetUrl},
|
||||
{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'),
|
||||
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 start = $("input[name=searchStartYYYYMMDD]").val().replace(/-/gi, "");
|
||||
var end = $("input[name=searchEndYYYYMMDD]").val().replace(/-/gi, "");
|
||||
|
||||
if (start && start > getToday()) {
|
||||
alert("시작일이 오늘 이후입니다. 시작일을 확인해주세요.");
|
||||
$("input[name=searchStartYYYYMMDD]").focus();
|
||||
return false;
|
||||
}
|
||||
if (start && end && start > end) {
|
||||
alert("조회기간 종료일은 시작일보다 커야합니다.");
|
||||
$("input[name=searchEndYYYYMMDD]").focus();
|
||||
return false;
|
||||
}
|
||||
$("input[name=searchStartDate]").val(start);
|
||||
$("input[name=searchEndDate]").val(end);
|
||||
|
||||
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;">
|
||||
<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>
|
||||
<div class="select-style">
|
||||
<select name="searchEventType"></select>
|
||||
</div>
|
||||
</td>
|
||||
<th>성공여부</th>
|
||||
<td>
|
||||
<div class="select-style">
|
||||
<select name="searchSuccess">
|
||||
<option value="">전체</option>
|
||||
<option value="Y">성공</option>
|
||||
<option value="N">실패</option>
|
||||
</select>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Target URL</th>
|
||||
<td><input type="text" name="searchTargetUrl" autocomplete="off" style="width:95%;"></td>
|
||||
<th>발송기간</th>
|
||||
<td>
|
||||
<input type="text" name="searchStartYYYYMMDD" id="startDatepicker" readonly="readonly" value="" size="10" style="width:100px; border:1px solid #ebebec;">
|
||||
~
|
||||
<input type="text" name="searchEndYYYYMMDD" value="" id="endDatepicker" size="10" readonly="readonly" style="width:100px; border:1px solid #ebebec;">
|
||||
<input type="hidden" name="searchStartDate" value="">
|
||||
<input type="hidden" name="searchEndDate" value="">
|
||||
</td>
|
||||
<th></th>
|
||||
<td></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</form>
|
||||
<table id="grid"></table>
|
||||
<div id="pager"></div>
|
||||
</div><!-- end content_middle -->
|
||||
</div><!-- end right_box -->
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,140 +0,0 @@
|
||||
<%@ 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>
|
||||
.succ-y { color:#1a73e8; font-weight:bold; }
|
||||
.succ-n { color:#d93025; font-weight:bold; }
|
||||
.pre-wrap { white-space:pre-wrap; word-break:break-all; min-height:60px; line-height:1.5; }
|
||||
</style>
|
||||
<script language="javascript">
|
||||
var url = '<c:url value="/onl/apim/webhook/webhookSendLogMan.json" />';
|
||||
var url_view = '<c:url value="/onl/apim/webhook/webhookSendLogMan.view" />';
|
||||
var key = '${param.id}';
|
||||
|
||||
function escapeHtmlJs(s) { return $('<div>').text(s == null ? '' : s).html(); }
|
||||
|
||||
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 || '');
|
||||
$('#eventType').text(data.eventTypeName || data.eventType || '')
|
||||
.attr('title', data.eventType || '');
|
||||
$('#targetUrl').text(data.targetUrl || '');
|
||||
$('#proxyUrl').text(data.proxyUrl || '');
|
||||
$('#statusCode').text(data.statusCode != null ? data.statusCode : '');
|
||||
$('#retryCount').text(data.retryCount != null ? data.retryCount : '');
|
||||
$('#sendBy').text(data.sendBy || '');
|
||||
$('#sentAt').text(data.sentAt || '');
|
||||
$('#signature').text(data.signature || '');
|
||||
$('#userSecret').text(data.userSecret || '');
|
||||
$('#errorMessage').text(data.errorMessage || '');
|
||||
$('#payload').text(data.payload || '');
|
||||
$('#responseBody').text(data.responseBody || '');
|
||||
|
||||
if (data.success === true) {
|
||||
$('#success').html('<span class="succ-y">성공</span>');
|
||||
} else if (data.success === false) {
|
||||
$('#success').html('<span class="succ-n">실패</span>');
|
||||
} else {
|
||||
$('#success').text('');
|
||||
}
|
||||
},
|
||||
error: function (e) { alert(e.responseText); }
|
||||
});
|
||||
}
|
||||
|
||||
$(document).ready(function () {
|
||||
var returnUrl = getReturnUrlForReturn();
|
||||
|
||||
buttonControl();
|
||||
detail();
|
||||
|
||||
$('#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><span id="id"></span></td>
|
||||
<th>기관명</th>
|
||||
<td><span id="orgName"></span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>이벤트유형</th>
|
||||
<td><span id="eventType"></span></td>
|
||||
<th>Status Code</th>
|
||||
<td><span id="statusCode"></span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Target URL</th>
|
||||
<td><span id="targetUrl"></span></td>
|
||||
<th>Proxy URL</th>
|
||||
<td><span id="proxyUrl"></span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>성공여부</th>
|
||||
<td><span id="success"></span></td>
|
||||
<th>재시도횟수</th>
|
||||
<td><span id="retryCount"></span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>발송서버</th>
|
||||
<td><span id="sendBy"></span></td>
|
||||
<th>발송일시</th>
|
||||
<td><span id="sentAt"></span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Signature</th>
|
||||
<td><span id="signature"></span></td>
|
||||
<th>User Secret</th>
|
||||
<td><span id="userSecret"></span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Payload</th>
|
||||
<td colspan="3"><div id="payload" class="pre-wrap"></div></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Response Body</th>
|
||||
<td colspan="3"><div id="responseBody" class="pre-wrap"></div></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>에러 메세지</th>
|
||||
<td colspan="3"><div id="errorMessage" class="pre-wrap"></div></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -33,18 +33,6 @@
|
||||
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;
|
||||
}
|
||||
</style>
|
||||
<jsp:include page="/jsp/common/include/script.jsp"/>
|
||||
<script src="<c:url value="/addon/echarts/echarts.min.js"/>"></script>
|
||||
@@ -70,7 +58,6 @@
|
||||
|
||||
// 총건수 도넛 차트
|
||||
var totalDonutOption = {
|
||||
color: ['#5470C6', '#FAC858', '#EE6666'],
|
||||
title: {
|
||||
text: '총 건수 분포',
|
||||
left: 'center',
|
||||
@@ -79,27 +66,22 @@
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
formatter: function (params) {
|
||||
return params.name + ': ' + params.value.toLocaleString() + ' (' + params.percent + '%)';
|
||||
}
|
||||
formatter: '{b}: {c} ({d}%)'
|
||||
},
|
||||
legend: {
|
||||
orient: 'vertical',
|
||||
right: 30,
|
||||
top: 'middle',
|
||||
orient: 'horizontal',
|
||||
bottom: 10,
|
||||
data: ['성공', 'Timeout', '시스템오류']
|
||||
},
|
||||
series: [{
|
||||
name: '총건수',
|
||||
type: 'pie',
|
||||
radius: ['38%', '65%'],
|
||||
center: ['44%', '55%'],
|
||||
radius: ['40%', '70%'],
|
||||
center: ['50%', '50%'],
|
||||
avoidLabelOverlap: true,
|
||||
label: {
|
||||
show: true,
|
||||
formatter: function (params) {
|
||||
return params.name + ': ' + params.value.toLocaleString();
|
||||
}
|
||||
formatter: '{b}: {c}'
|
||||
},
|
||||
emphasis: {
|
||||
label: {
|
||||
@@ -112,10 +94,22 @@
|
||||
show: true
|
||||
},
|
||||
data: [
|
||||
{ value: 0, name: '성공', itemStyle: { color: '#5470C6' } },
|
||||
{ value: 0, name: '성공', itemStyle: { color: '#91CC75' } },
|
||||
{ value: 0, name: 'Timeout', itemStyle: { color: '#FAC858' } },
|
||||
{ value: 0, name: '시스템오류', itemStyle: { color: '#EE6666' } }
|
||||
]
|
||||
}],
|
||||
graphic: [{
|
||||
type: 'text',
|
||||
left: 'center',
|
||||
top: 'center',
|
||||
style: {
|
||||
text: '0',
|
||||
textAlign: 'center',
|
||||
fill: '#333',
|
||||
fontSize: 24,
|
||||
fontWeight: 'bold'
|
||||
}
|
||||
}]
|
||||
};
|
||||
|
||||
@@ -125,7 +119,7 @@
|
||||
|
||||
// 호출량 차트
|
||||
var callOption = {
|
||||
title: { text: '호출량 추이', left: 'center', top: 10, textStyle: { fontSize: 14 } },
|
||||
title: { text: '호출량 추이', left: 'center', textStyle: { fontSize: 14 } },
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: { type: 'cross', label: { backgroundColor: '#6a7985' } }
|
||||
@@ -197,9 +191,13 @@
|
||||
{ value: totalTimeout, name: 'Timeout' },
|
||||
{ value: totalSystemErr, name: '시스템오류' }
|
||||
]
|
||||
}],
|
||||
graphic: [{
|
||||
style: {
|
||||
text: grandTotal.toLocaleString()
|
||||
}
|
||||
}]
|
||||
});
|
||||
$("#totalDonutCenterText").text(grandTotal.toLocaleString());
|
||||
|
||||
|
||||
// 호출량 차트 업데이트
|
||||
@@ -299,28 +297,6 @@
|
||||
fetchChartData();
|
||||
}
|
||||
|
||||
// 조회기간을 시작일 기준 이전/다음 달의 1일~말일로 변경 후 조회 (direction: -1 이전달, 1 다음달)
|
||||
function shiftMonthRange(direction) {
|
||||
var v = $("input[name=searchStartDateTime]").val().replace(/-/g, "");
|
||||
if (!v || v.length < 6) return;
|
||||
|
||||
var year = parseInt(v.substring(0, 4));
|
||||
var month = parseInt(v.substring(4, 6)) - 1; // 0-based
|
||||
|
||||
var first = new Date(year, month + direction, 1);
|
||||
var last = new Date(year, month + direction + 1, 0); // 해당 달의 말일
|
||||
|
||||
function pad(n) { return String(n).padStart(2, '0'); }
|
||||
function fmt(d) {
|
||||
return d.getFullYear() + '-' + pad(d.getMonth() + 1) + '-' + pad(d.getDate());
|
||||
}
|
||||
|
||||
$("input[name=searchStartDateTime]").val(fmt(first));
|
||||
$("input[name=searchEndDateTime]").val(fmt(last));
|
||||
|
||||
search();
|
||||
}
|
||||
|
||||
function exportToExcel(isSummary) {
|
||||
var cmdType = isSummary ? 'EXCEL_EXPORT_SUMMARY' : 'EXCEL_EXPORT';
|
||||
console.log('[Excel Export] Starting... (type: ' + cmdType + ')');
|
||||
@@ -451,13 +427,12 @@
|
||||
mtype: 'POST',
|
||||
postData: gridPostData,
|
||||
colNames: [
|
||||
'API ID', 'API명',
|
||||
'API명',
|
||||
'총건수', '성공', 'Timeout', '시스템오류',
|
||||
'평균응답(ms)', '최소응답(ms)', '최대응답(ms)'
|
||||
],
|
||||
colModel: [
|
||||
{ name: 'apiName', align: 'left', width: '150', sortable: false },
|
||||
{ name: 'apiDesc', align: 'left', width: '250', sortable: false },
|
||||
{ name: 'apiName', 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 },
|
||||
@@ -480,9 +455,6 @@
|
||||
for (var i = 0; i < colModel.length; i++) {
|
||||
$(this).setColProp(colModel[i].name, { sortable: false });
|
||||
}
|
||||
// 조회(reloadGrid) 시 컬럼 폭이 고정폭(shrinkToFit:false)으로 남아
|
||||
// 실제 컨테이너 폭과 어긋나 가로 스크롤이 생기는 것을 방지
|
||||
$(this).jqGrid('setGridWidth', $('#content_middle').width(), true);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -494,7 +466,7 @@
|
||||
mtype: 'POST',
|
||||
postData: gridPostData,
|
||||
colNames: [
|
||||
'통계시간', 'API ID', '인스턴스', '업무구분', '클라이언트ID',
|
||||
'통계시간', 'API명', '인스턴스', '업무구분', '클라이언트ID',
|
||||
'Inbound Adapter', 'Outbound Adapter',
|
||||
'총건수', '성공', 'Timeout', '시스템오류',
|
||||
'평균응답(ms)', '최소응답(ms)', '최대응답(ms)'
|
||||
@@ -529,9 +501,6 @@
|
||||
for (var i = 0; i < colModel.length; i++) {
|
||||
$(this).setColProp(colModel[i].name, { sortable: false });
|
||||
}
|
||||
// 조회(reloadGrid) 시 컬럼 폭이 고정폭(shrinkToFit:false)으로 남아
|
||||
// 실제 컨테이너 폭과 어긋나 가로 스크롤이 생기는 것을 방지
|
||||
$(this).jqGrid('setGridWidth', $('#content_middle').width(), true);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -541,8 +510,6 @@
|
||||
$("input[name=searchStartDateTime]").inputmask("9999-99-99", { 'autoUnmask': true });
|
||||
$("input[name=searchEndDateTime]").inputmask("9999-99-99", { 'autoUnmask': true });
|
||||
|
||||
$("input[name=searchStartDateTime], input[name=searchEndDateTime]").datepicker();
|
||||
|
||||
// 기본값 설정 (어제 기준 해당 월 1일 ~ 어제)
|
||||
var today = getToday();
|
||||
var todayStr = today.replace(/-/g, '');
|
||||
@@ -582,14 +549,6 @@
|
||||
search();
|
||||
});
|
||||
|
||||
$("#btnPrevMonth").click(function() {
|
||||
shiftMonthRange(-1);
|
||||
});
|
||||
|
||||
$("#btnNextMonth").click(function() {
|
||||
shiftMonthRange(1);
|
||||
});
|
||||
|
||||
$("#btn_excel_export_summary").click(function() {
|
||||
exportToExcel(true);
|
||||
});
|
||||
@@ -628,51 +587,41 @@
|
||||
</button>
|
||||
</div>
|
||||
<div class="title" id="title">API 일별 통계</div>
|
||||
<table class="search_condition" cellspacing="0" style="table-layout:fixed;">
|
||||
<colgroup>
|
||||
<col style="width:150px;">
|
||||
<col>
|
||||
<col style="width:150px;">
|
||||
<col>
|
||||
<col style="width:150px;">
|
||||
<col>
|
||||
</colgroup>
|
||||
<table class="search_condition" cellspacing="0">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>조회기간</th>
|
||||
<th style="width:100px;">조회기간</th>
|
||||
<td colspan="5">
|
||||
<input type="text" name="searchStartDateTime" value="${param.searchStartDateTime}" style="width:100px;">
|
||||
~
|
||||
<input type="text" name="searchEndDateTime" value="${param.searchEndDateTime}" style="width:100px;">
|
||||
<button type="button" id="btnPrevMonth" class="cssbtn small"><</button>
|
||||
<button type="button" id="btnNextMonth" class="cssbtn small">></button>
|
||||
<span style="color:#888; font-size:12px; margin-left:10px;">(최대 31일)</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>API ID</th>
|
||||
<th style="width:100px;">API명</th>
|
||||
<td>
|
||||
<input type="text" name="searchApiName" value="${param.searchApiName}">
|
||||
</td>
|
||||
<th>업무구분</th>
|
||||
<th style="width:100px;">업무구분</th>
|
||||
<td>
|
||||
<input type="text" name="searchBizDivCode" value="${param.searchBizDivCode}">
|
||||
</td>
|
||||
<th>인스턴스</th>
|
||||
<th style="width:100px;">인스턴스</th>
|
||||
<td>
|
||||
<input type="text" name="searchGwInstanceId" value="${param.searchGwInstanceId}">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>클라이언트ID</th>
|
||||
<th style="width:100px;">클라이언트ID</th>
|
||||
<td>
|
||||
<input type="text" name="searchClientId" value="${param.searchClientId}">
|
||||
</td>
|
||||
<th>Inbound Adapter</th>
|
||||
<th style="width:100px;">Inbound Adapter</th>
|
||||
<td>
|
||||
<input type="text" name="searchInboundAdapter" value="${param.searchInboundAdapter}">
|
||||
</td>
|
||||
<th>Outbound Adapter</th>
|
||||
<th style="width:100px;">Outbound Adapter</th>
|
||||
<td>
|
||||
<input type="text" name="searchOutboundAdapter" value="${param.searchOutboundAdapter}">
|
||||
</td>
|
||||
@@ -681,17 +630,14 @@
|
||||
</table>
|
||||
<!-- 도넛 차트 (상단 50%씩) -->
|
||||
<div class="chart-container">
|
||||
<div class="chart" style="position:relative; padding:0;">
|
||||
<div id="totalDonutChart" style="width:100%; height:100%;"></div>
|
||||
<div id="totalDonutCenterText" style="position:absolute; left:44.5%; top:55%; transform:translate(-50%,-50%); font-size:24px; font-weight:bold; color:#333; pointer-events:none; z-index:10;">0</div>
|
||||
</div>
|
||||
<div id="totalDonutChart" class="chart"></div>
|
||||
<div id="callChart" class="chart"></div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- 요약 그리드 -->
|
||||
<div style="margin-top: 20px;">
|
||||
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px;">
|
||||
<div class="title" style="margin: 0;">API별 요약 통계</div>
|
||||
<div class="title" style="margin: 0;">API명별 요약 통계</div>
|
||||
<button type="button" class="cssbtn" id="btn_excel_export_summary" level="R">
|
||||
<i class="material-icons">file_download</i> Excel 다운로드 (요약)
|
||||
</button>
|
||||
|
||||
@@ -33,18 +33,6 @@
|
||||
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;
|
||||
}
|
||||
</style>
|
||||
<jsp:include page="/jsp/common/include/script.jsp"/>
|
||||
<script src="<c:url value="/addon/echarts/echarts.min.js"/>"></script>
|
||||
@@ -52,8 +40,6 @@
|
||||
var url = '<c:url value="/onl/kjb/statistics/apiStatsHourMan.json"/>';
|
||||
var url_view = '<c:url value="/onl/kjb/statistics/apiStatsHourMan.view"/>';
|
||||
var url_minute_view = '<c:url value="/onl/kjb/statistics/apiStatsMinuteMan.view"/>';
|
||||
var url_left_menu = '<c:url value="/leftMenu.do"/>';
|
||||
var DASHBOARD_MENU_ID = '0305001'; // apiStatsMinuteMan(대시보드) 메뉴 ID
|
||||
|
||||
var totalDonutChart, callChart;
|
||||
|
||||
@@ -73,7 +59,6 @@
|
||||
|
||||
// 총건수 도넛 차트
|
||||
var totalDonutOption = {
|
||||
color: ['#5470C6', '#FAC858', '#EE6666'],
|
||||
title: {
|
||||
text: '총 건수 분포',
|
||||
left: 'center',
|
||||
@@ -82,27 +67,22 @@
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
formatter: function (params) {
|
||||
return params.name + ': ' + params.value.toLocaleString() + ' (' + params.percent + '%)';
|
||||
}
|
||||
formatter: '{b}: {c} ({d}%)'
|
||||
},
|
||||
legend: {
|
||||
orient: 'vertical',
|
||||
right: 30,
|
||||
top: 'middle',
|
||||
orient: 'horizontal',
|
||||
bottom: 10,
|
||||
data: ['성공', 'Timeout', '시스템오류']
|
||||
},
|
||||
series: [{
|
||||
name: '총건수',
|
||||
type: 'pie',
|
||||
radius: ['38%', '65%'],
|
||||
center: ['44%', '55%'],
|
||||
radius: ['40%', '70%'],
|
||||
center: ['50%', '50%'],
|
||||
avoidLabelOverlap: true,
|
||||
label: {
|
||||
show: true,
|
||||
formatter: function (params) {
|
||||
return params.name + ': ' + params.value.toLocaleString();
|
||||
}
|
||||
formatter: '{b}: {c}'
|
||||
},
|
||||
emphasis: {
|
||||
label: {
|
||||
@@ -115,10 +95,22 @@
|
||||
show: true
|
||||
},
|
||||
data: [
|
||||
{ value: 0, name: '성공', itemStyle: { color: '#5470C6' } },
|
||||
{ value: 0, name: '성공', itemStyle: { color: '#91CC75' } },
|
||||
{ value: 0, name: 'Timeout', itemStyle: { color: '#FAC858' } },
|
||||
{ value: 0, name: '시스템오류', itemStyle: { color: '#EE6666' } }
|
||||
]
|
||||
}],
|
||||
graphic: [{
|
||||
type: 'text',
|
||||
left: 'center',
|
||||
top: 'center',
|
||||
style: {
|
||||
text: '0',
|
||||
textAlign: 'center',
|
||||
fill: '#333',
|
||||
fontSize: 24,
|
||||
fontWeight: 'bold'
|
||||
}
|
||||
}]
|
||||
};
|
||||
|
||||
@@ -128,7 +120,7 @@
|
||||
|
||||
// 호출량 차트
|
||||
var callOption = {
|
||||
title: { text: '호출량 추이', left: 'center', top: 10, textStyle: { fontSize: 14 } },
|
||||
title: { text: '호출량 추이', left: 'center', textStyle: { fontSize: 14 } },
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: { type: 'cross', label: { backgroundColor: '#6a7985' } }
|
||||
@@ -164,20 +156,23 @@
|
||||
var hour = parseInt(statTime.substring(8, 10));
|
||||
|
||||
var startDate = year + '-' + month + '-' + day;
|
||||
var hourStr = (hour < 10 ? '0' + hour : hour);
|
||||
var startTime = hourStr + ':00';
|
||||
// 해당 시간대 끝(정시~59분)으로 이동 (예: 10:00~10:59)
|
||||
var endTime = hourStr + ':59';
|
||||
var startTime = (hour < 10 ? '0' + hour : hour) + ':00';
|
||||
|
||||
// 1시간 후 계산 (23시인 경우 다음날 00시로 자동 계산)
|
||||
var d = new Date(parseInt(year), parseInt(month) - 1, parseInt(day), hour, 0, 0);
|
||||
d.setHours(d.getHours() + 1);
|
||||
|
||||
var endDate = d.getFullYear() + '-' +
|
||||
(d.getMonth() + 1 < 10 ? '0' : '') + (d.getMonth() + 1) + '-' +
|
||||
(d.getDate() < 10 ? '0' : '') + d.getDate();
|
||||
var endTime = (d.getHours() < 10 ? '0' : '') + d.getHours() + ':00';
|
||||
|
||||
var params = '?searchStartDate=' + startDate + '&searchStartTime=' + startTime
|
||||
+ '&searchEndDate=' + startDate + '&searchEndTime=' + endTime;
|
||||
params += '&menuId='+DASHBOARD_MENU_ID;
|
||||
+ '&searchEndDate=' + endDate + '&searchEndTime=' + endTime;
|
||||
params += '&menuId='+'${param.menuId}';
|
||||
params += '&cmd='+'LIST';
|
||||
params += '&serviceType='+'APIGW';
|
||||
|
||||
// 왼쪽 메뉴도 대시보드(분단위 통계) 메뉴로 전환
|
||||
parent.leftFrame.location.href = url_left_menu + '?menuId=' + DASHBOARD_MENU_ID
|
||||
+ '&serviceType=' + sessionStorage["serviceType"];
|
||||
location.href = url_minute_view + params;
|
||||
}
|
||||
|
||||
@@ -232,9 +227,13 @@
|
||||
{ value: totalTimeout, name: 'Timeout' },
|
||||
{ value: totalSystemErr, name: '시스템오류' }
|
||||
]
|
||||
}],
|
||||
graphic: [{
|
||||
style: {
|
||||
text: grandTotal.toLocaleString()
|
||||
}
|
||||
}]
|
||||
});
|
||||
$("#totalDonutCenterText").text(grandTotal.toLocaleString());
|
||||
|
||||
|
||||
// 호출량 차트 업데이트
|
||||
@@ -304,25 +303,6 @@
|
||||
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 + ')');
|
||||
@@ -449,13 +429,12 @@
|
||||
mtype: 'POST',
|
||||
postData: gridPostData,
|
||||
colNames: [
|
||||
'API ID', 'API 명',
|
||||
'API명',
|
||||
'총건수', '성공', 'Timeout', '시스템오류',
|
||||
'평균응답(ms)', '최소응답(ms)', '최대응답(ms)'
|
||||
],
|
||||
colModel: [
|
||||
{ name: 'apiName', align: 'left', width: '150', sortable: false },
|
||||
{ name: 'apiDesc', align: 'left', width: '250', sortable: false },
|
||||
{ name: 'apiName', 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 },
|
||||
@@ -478,9 +457,6 @@
|
||||
for (var i = 0; i < colModel.length; i++) {
|
||||
$(this).setColProp(colModel[i].name, { sortable: false });
|
||||
}
|
||||
// 조회(reloadGrid) 시 컬럼 폭이 고정폭(shrinkToFit:false)으로 남아
|
||||
// 실제 컨테이너 폭과 어긋나 가로 스크롤이 생기는 것을 방지
|
||||
$(this).jqGrid('setGridWidth', $('#content_middle').width(), true);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -491,8 +467,8 @@
|
||||
datatype: "json",
|
||||
mtype: 'POST',
|
||||
postData: gridPostData,
|
||||
colNames: [
|
||||
'통계시간', 'API ID', '인스턴스', '업무구분', '클라이언트ID',
|
||||
colNames: [
|
||||
'통계시간', 'API명', '인스턴스', '업무구분', '클라이언트ID',
|
||||
'Inbound Adapter', 'Outbound Adapter',
|
||||
'총건수', '성공', 'Timeout', '시스템오류',
|
||||
'평균응답(ms)', '최소응답(ms)', '최대응답(ms)'
|
||||
@@ -527,9 +503,6 @@
|
||||
for (var i = 0; i < colModel.length; i++) {
|
||||
$(this).setColProp(colModel[i].name, { sortable: false });
|
||||
}
|
||||
// 조회(reloadGrid) 시 컬럼 폭이 고정폭(shrinkToFit:false)으로 남아
|
||||
// 실제 컨테이너 폭과 어긋나 가로 스크롤이 생기는 것을 방지
|
||||
$(this).jqGrid('setGridWidth', $('#content_middle').width(), true);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -538,8 +511,6 @@
|
||||
// 날짜 입력 마스크
|
||||
$("input[name=searchDate]").inputmask("9999-99-99", { 'autoUnmask': true });
|
||||
|
||||
$("input[name=searchDate]").datepicker();
|
||||
|
||||
// 기본값 설정 (오늘)
|
||||
var today = getToday();
|
||||
if (!$("input[name=searchDate]").val()) {
|
||||
@@ -558,14 +529,6 @@
|
||||
search();
|
||||
});
|
||||
|
||||
$("#btnPrevDate").click(function() {
|
||||
shiftSearchDate(-1);
|
||||
});
|
||||
|
||||
$("#btnNextDate").click(function() {
|
||||
shiftSearchDate(1);
|
||||
});
|
||||
|
||||
$("#btn_excel_export_summary").click(function() {
|
||||
exportToExcel(true);
|
||||
});
|
||||
@@ -604,49 +567,41 @@
|
||||
</button>
|
||||
</div>
|
||||
<div class="title" id="title">API 시간별 통계</div>
|
||||
<table class="search_condition" cellspacing="0" style="table-layout:fixed;">
|
||||
<colgroup>
|
||||
<col style="width:150px;">
|
||||
<col>
|
||||
<col style="width:150px;">
|
||||
<col>
|
||||
<col style="width:150px;">
|
||||
<col>
|
||||
</colgroup>
|
||||
<table class="search_condition" cellspacing="0">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>조회일자</th>
|
||||
<td colspan="5">
|
||||
<th style="width:100px;">조회일자</th>
|
||||
<td colspan="3">
|
||||
<input type="text" name="searchDate" value="${param.searchDate}" style="width:100px;">
|
||||
<button type="button" id="btnPrevDate" class="cssbtn small"><</button>
|
||||
<button type="button" id="btnNextDate" class="cssbtn small">></button>
|
||||
<span style="color:#888; font-size:12px; margin-left:10px;">(선택일 00시 ~ 23시 조회)</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>API ID</th>
|
||||
<th style="width:100px;">API명</th>
|
||||
<td>
|
||||
<input type="text" name="searchApiName" value="${param.searchApiName}">
|
||||
</td>
|
||||
<th>업무구분</th>
|
||||
<td>
|
||||
<input type="text" name="searchBizDivCode" value="${param.searchBizDivCode}">
|
||||
</td>
|
||||
<th>인스턴스</th>
|
||||
<th style="width:100px;">인스턴스</th>
|
||||
<td>
|
||||
<input type="text" name="searchGwInstanceId" value="${param.searchGwInstanceId}">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>클라이언트ID</th>
|
||||
<th style="width:100px;">업무구분</th>
|
||||
<td>
|
||||
<input type="text" name="searchBizDivCode" value="${param.searchBizDivCode}">
|
||||
</td>
|
||||
<th style="width:100px;">클라이언트ID</th>
|
||||
<td>
|
||||
<input type="text" name="searchClientId" value="${param.searchClientId}">
|
||||
</td>
|
||||
<th>Inbound Adapter</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th style="width:100px;">Inbound Adapter</th>
|
||||
<td>
|
||||
<input type="text" name="searchInboundAdapter" value="${param.searchInboundAdapter}">
|
||||
</td>
|
||||
<th>Outbound Adapter</th>
|
||||
<th style="width:100px;">Outbound Adapter</th>
|
||||
<td>
|
||||
<input type="text" name="searchOutboundAdapter" value="${param.searchOutboundAdapter}">
|
||||
</td>
|
||||
@@ -655,17 +610,14 @@
|
||||
</table>
|
||||
<!-- 도넛 차트 (상단 50%씩) -->
|
||||
<div class="chart-container">
|
||||
<div class="chart" style="position:relative; padding:0;">
|
||||
<div id="totalDonutChart" style="width:100%; height:100%;"></div>
|
||||
<div id="totalDonutCenterText" style="position:absolute; left:44.5%; top:55%; transform:translate(-50%,-50%); font-size:24px; font-weight:bold; color:#333; pointer-events:none; z-index:10;">0</div>
|
||||
</div>
|
||||
<div id="totalDonutChart" class="chart"></div>
|
||||
<div id="callChart" class="chart"></div>
|
||||
</div>
|
||||
|
||||
<!-- 요약 그리드 -->
|
||||
<div style="margin-top: 20px;">
|
||||
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px;">
|
||||
<div class="title" style="margin: 0;">API별 요약 통계</div>
|
||||
<div class="title" style="margin: 0;">API명별 요약 통계</div>
|
||||
<button type="button" class="cssbtn" id="btn_excel_export_summary" level="R">
|
||||
<i class="material-icons">file_download</i> Excel 다운로드 (요약)
|
||||
</button>
|
||||
|
||||
@@ -26,18 +26,6 @@
|
||||
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;
|
||||
}
|
||||
</style>
|
||||
<jsp:include page="/jsp/common/include/script.jsp"/>
|
||||
<script src="<c:url value="/addon/echarts/echarts.min.js"/>"></script>
|
||||
@@ -63,9 +51,6 @@
|
||||
|
||||
// 총건수 도넛 차트
|
||||
var totalDonutOption = {
|
||||
// updateCharts()에서 series[].data를 itemStyle 없이 재설정하면 조각별 itemStyle.color가
|
||||
// 병합 과정에서 유실되므로, 옵션 레벨 color 팔레트(성공/Timeout/시스템오류 순)로 색을 고정한다.
|
||||
color: ['#5470C6', '#FAC858', '#EE6666'],
|
||||
title: {
|
||||
text: '총 건수 분포',
|
||||
left: 'center',
|
||||
@@ -74,27 +59,22 @@
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
formatter: function (params) {
|
||||
return params.name + ': ' + params.value.toLocaleString() + ' (' + params.percent + '%)';
|
||||
}
|
||||
formatter: '{b}: {c} ({d}%)'
|
||||
},
|
||||
legend: {
|
||||
orient: 'vertical',
|
||||
right: 30,
|
||||
top: 'middle',
|
||||
orient: 'horizontal',
|
||||
bottom: 10,
|
||||
data: ['성공', 'Timeout', '시스템오류']
|
||||
},
|
||||
series: [{
|
||||
name: '총건수',
|
||||
type: 'pie',
|
||||
radius: ['38%', '65%'],
|
||||
center: ['44%', '55%'],
|
||||
radius: ['40%', '70%'],
|
||||
center: ['50%', '50%'],
|
||||
avoidLabelOverlap: true,
|
||||
label: {
|
||||
show: true,
|
||||
formatter: function (params) {
|
||||
return params.name + ': ' + params.value.toLocaleString();
|
||||
}
|
||||
formatter: '{b}: {c}'
|
||||
},
|
||||
emphasis: {
|
||||
label: {
|
||||
@@ -107,10 +87,22 @@
|
||||
show: true
|
||||
},
|
||||
data: [
|
||||
{ value: 0, name: '성공', itemStyle: { color: '#5470C6' } },
|
||||
{ value: 0, name: '성공', itemStyle: { color: '#91CC75' } },
|
||||
{ value: 0, name: 'Timeout', itemStyle: { color: '#FAC858' } },
|
||||
{ value: 0, name: '시스템오류', itemStyle: { color: '#EE6666' } }
|
||||
]
|
||||
}],
|
||||
graphic: [{
|
||||
type: 'text',
|
||||
left: 'center',
|
||||
top: 'center',
|
||||
style: {
|
||||
text: '0',
|
||||
textAlign: 'center',
|
||||
fill: '#333',
|
||||
fontSize: 24,
|
||||
fontWeight: 'bold'
|
||||
}
|
||||
}]
|
||||
};
|
||||
|
||||
@@ -119,8 +111,8 @@
|
||||
|
||||
|
||||
// 호출량 차트
|
||||
var callOption = {
|
||||
title: { text: '호출량 추이', left: 'center', top: 10, textStyle: { fontSize: 14 } },
|
||||
var callOption = {
|
||||
title: { text: '호출량 추이', left: 'center', textStyle: { fontSize: 14 } },
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: { type: 'cross', label: { backgroundColor: '#6a7985' } }
|
||||
@@ -197,9 +189,13 @@
|
||||
{ value: totalTimeout, name: 'Timeout' },
|
||||
{ value: totalSystemErr, name: '시스템오류' }
|
||||
]
|
||||
}],
|
||||
graphic: [{
|
||||
style: {
|
||||
text: grandTotal.toLocaleString()
|
||||
}
|
||||
}]
|
||||
});
|
||||
$("#totalDonutCenterText").text(grandTotal.toLocaleString());
|
||||
|
||||
|
||||
// 호출량 차트 업데이트
|
||||
@@ -218,6 +214,7 @@
|
||||
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;
|
||||
@@ -233,43 +230,35 @@
|
||||
);
|
||||
}
|
||||
|
||||
// 종료일시(yyyyMMddHHmm) 포맷 헬퍼
|
||||
function formatDateTime(dt) {
|
||||
return dt.getFullYear() +
|
||||
// UI 업데이트 헬퍼
|
||||
function updateEndDateTime(dt) {
|
||||
var d = dt.getFullYear() +
|
||||
String(dt.getMonth() + 1).padStart(2, '0') +
|
||||
String(dt.getDate()).padStart(2, '0') +
|
||||
String(dt.getHours()).padStart(2, '0') +
|
||||
String(dt.getDate()).padStart(2, '0');
|
||||
var t = String(dt.getHours()).padStart(2, '0') +
|
||||
String(dt.getMinutes()).padStart(2, '0');
|
||||
}
|
||||
|
||||
// 종료시각 UI 업데이트 헬퍼
|
||||
function updateEndTime(dt) {
|
||||
$("input[name=searchEndTime]").val(
|
||||
String(dt.getHours()).padStart(2, '0') + ':' + String(dt.getMinutes()).padStart(2, '0'));
|
||||
return formatDateTime(dt);
|
||||
$("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;
|
||||
}
|
||||
|
||||
var start = parseDateTime(startDate, startTime);
|
||||
var startDateTime = startDate + startTime;
|
||||
var endDateTime;
|
||||
|
||||
if (endTime) {
|
||||
// 종료일은 시작일 기준. 종료시각이 시작시각보다 이르면 자정을 넘긴 것으로 보고 +1일
|
||||
var end = parseDateTime(startDate, endTime);
|
||||
if (end < start) {
|
||||
end = new Date(end.getTime() + 24 * 60 * 60 * 1000);
|
||||
}
|
||||
if (endDate && endTime) {
|
||||
var end = parseDateTime(endDate, endTime);
|
||||
var diffMinutes = (end - start) / (1000 * 60);
|
||||
|
||||
if (diffMinutes > 60) {
|
||||
// 1시간 초과 시 시작시각 기준 1시간치(정시~59분)로 조정
|
||||
endDateTime = updateEndTime(new Date(start.getTime() + 59 * 60 * 1000));
|
||||
// 1시간 초과 시 시작 + 1시간으로 조정
|
||||
endDateTime = updateEndDateTime(new Date(start.getTime() + 60 * 60 * 1000));
|
||||
} else {
|
||||
endDateTime = formatDateTime(end);
|
||||
endDateTime = endDate + endTime;
|
||||
}
|
||||
} else {
|
||||
// 종료시간 미입력 시 시작시각 기준 1시간치(정시~59분)로 설정
|
||||
endDateTime = updateEndTime(new Date(start.getTime() + 59 * 60 * 1000));
|
||||
// 종료시간 미입력 시 시작 + 1시간으로 설정
|
||||
endDateTime = updateEndDateTime(new Date(start.getTime() + 60 * 60 * 1000));
|
||||
}
|
||||
|
||||
return { start: startDateTime, end: endDateTime };
|
||||
@@ -324,45 +313,6 @@
|
||||
fetchChartData(range);
|
||||
}
|
||||
|
||||
// 조회기간을 정시 단위 1시간 구간으로 이동 (direction: -1 이전, 1 이후)
|
||||
// ex) 시작 08:35, 이전 클릭 -> 08:00~08:59, 다시 -> 07:00~07:59
|
||||
// ex) 시작 08:35, 이후 클릭 -> 09:00~09:59, 다시 -> 10:00~10:59
|
||||
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);
|
||||
}
|
||||
|
||||
// 종료시각은 같은 시간대의 59분으로 설정 (예: 10:00~10:59)
|
||||
var end = new Date(start.getTime() + 59 * 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 + ')');
|
||||
@@ -378,10 +328,13 @@
|
||||
serviceType: '${param.serviceType}'
|
||||
};
|
||||
|
||||
var range = validateAndAdjustDateRange();
|
||||
if (range) {
|
||||
postData.searchStartDateTime = range.start;
|
||||
postData.searchEndDateTime = range.end;
|
||||
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;
|
||||
}
|
||||
|
||||
console.log('[Excel Export] Request data:', postData);
|
||||
@@ -489,13 +442,12 @@
|
||||
mtype: 'POST',
|
||||
postData: gridPostData,
|
||||
colNames: [
|
||||
'API ID', 'API 명',
|
||||
'총건수', '성공', 'Timeout', '시스템오류',
|
||||
'API명',
|
||||
'총건수', '성공', 'Timeout', '시스템오류',
|
||||
'평균응답(ms)', '최소응답(ms)', '최대응답(ms)'
|
||||
],
|
||||
colModel: [
|
||||
{ name: 'apiName', align: 'left', width: '150', sortable: false },
|
||||
{ name: 'apiDesc', align: 'left', width: '250', sortable: false },
|
||||
{ name: 'apiName', 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 },
|
||||
@@ -518,9 +470,6 @@
|
||||
for (var i = 0; i < colModel.length; i++) {
|
||||
$(this).setColProp(colModel[i].name, { sortable: false });
|
||||
}
|
||||
// 조회(reloadGrid) 시 컬럼 폭이 고정폭(shrinkToFit:false)으로 남아
|
||||
// 실제 컨테이너 폭과 어긋나 가로 스크롤이 생기는 것을 방지
|
||||
$(this).jqGrid('setGridWidth', $('#content_middle').width(), true);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -532,7 +481,7 @@
|
||||
mtype: 'POST',
|
||||
postData: gridPostData,
|
||||
colNames: [
|
||||
'통계시간', 'API ID', '인스턴스', '업무구분', '클라이언트ID',
|
||||
'통계시간', 'API명', '인스턴스', '업무구분', '클라이언트ID',
|
||||
'Inbound Adapter', 'Outbound Adapter',
|
||||
'총건수', '성공', 'Timeout', '시스템오류',
|
||||
'평균응답(ms)', '최소응답(ms)', '최대응답(ms)'
|
||||
@@ -567,39 +516,44 @@
|
||||
for (var i = 0; i < colModel.length; i++) {
|
||||
$(this).setColProp(colModel[i].name, { sortable: false });
|
||||
}
|
||||
// 조회(reloadGrid) 시 컬럼 폭이 고정폭(shrinkToFit:false)으로 남아
|
||||
// 실제 컨테이너 폭과 어긋나 가로 스크롤이 생기는 것을 방지
|
||||
$(this).jqGrid('setGridWidth', $('#content_middle').width(), true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
// 날짜/시간 입력 마스크
|
||||
$("input[name=searchStartDate]").inputmask("9999-99-99", { 'autoUnmask': true });
|
||||
$("input[name=searchStartDate], input[name=searchEndDate]").inputmask("9999-99-99", { 'autoUnmask': true });
|
||||
$("input[name=searchStartTime], input[name=searchEndTime]").inputmask("99:99", { 'autoUnmask': true });
|
||||
|
||||
$("input[name=searchStartDate]").datepicker();
|
||||
|
||||
// 기본값 설정 (현재 시간(분) 기준 59분 전 ~ 현재 시간(분))
|
||||
// 기본값 설정 (현재 시간 기준 1시간 전 정각 ~ 현재 정각)
|
||||
var now = new Date();
|
||||
var oneHourAgo = new Date(now.getTime() - 59 * 60 * 1000);
|
||||
var endHour = now.getHours();
|
||||
var startHour = endHour - 1;
|
||||
var startDate, endDate;
|
||||
|
||||
function formatDate(d) {
|
||||
return d.getFullYear() + '-' +
|
||||
String(d.getMonth() + 1).padStart(2, '0') + '-' +
|
||||
String(d.getDate()).padStart(2, '0');
|
||||
}
|
||||
function formatTime(d) {
|
||||
return String(d.getHours()).padStart(2, '0') + ':' + String(d.getMinutes()).padStart(2, '0');
|
||||
if (startHour < 0) {
|
||||
startHour = 23;
|
||||
var yesterday = new Date(now);
|
||||
yesterday.setDate(yesterday.getDate() - 1);
|
||||
startDate = yesterday.getFullYear() + '-' +
|
||||
String(yesterday.getMonth() + 1).padStart(2, '0') + '-' +
|
||||
String(yesterday.getDate()).padStart(2, '0');
|
||||
endDate = now.getFullYear() + '-' +
|
||||
String(now.getMonth() + 1).padStart(2, '0') + '-' +
|
||||
String(now.getDate()).padStart(2, '0');
|
||||
} else {
|
||||
var today = getToday();
|
||||
startDate = today;
|
||||
endDate = today;
|
||||
}
|
||||
|
||||
if (!$("input[name=searchStartDate]").val()) {
|
||||
$("input[name=searchStartDate]").val(formatDate(oneHourAgo));
|
||||
$("input[name=searchStartTime]").val(formatTime(oneHourAgo));
|
||||
$("input[name=searchStartDate]").val(startDate);
|
||||
$("input[name=searchStartTime]").val(String(startHour).padStart(2, '0') + ':00');
|
||||
}
|
||||
if (!$("input[name=searchEndTime]").val()) {
|
||||
$("input[name=searchEndTime]").val(formatTime(now));
|
||||
if (!$("input[name=searchEndDate]").val()) {
|
||||
$("input[name=searchEndDate]").val(endDate);
|
||||
$("input[name=searchEndTime]").val(String(endHour).padStart(2, '0') + ':00');
|
||||
}
|
||||
|
||||
initCharts();
|
||||
@@ -614,14 +568,6 @@
|
||||
search();
|
||||
});
|
||||
|
||||
$("#btnPrevTime").click(function() {
|
||||
shiftTimeRange(-1);
|
||||
});
|
||||
|
||||
$("#btnNextTime").click(function() {
|
||||
shiftTimeRange(1);
|
||||
});
|
||||
|
||||
$("#btn_excel_export_summary").click(function() {
|
||||
exportToExcel(true);
|
||||
});
|
||||
@@ -636,8 +582,8 @@
|
||||
}
|
||||
});
|
||||
|
||||
// 종료시각 변경 시 1시간 초과 검증
|
||||
$("input[name=searchEndTime]").on('change blur', function() {
|
||||
// 종료일/시간 변경 시 1시간 초과 검증
|
||||
$("input[name=searchEndDate], input[name=searchEndTime]").on('change blur', function() {
|
||||
validateAndAdjustDateRange();
|
||||
});
|
||||
|
||||
@@ -669,28 +615,20 @@
|
||||
<tbody>
|
||||
<tr>
|
||||
<th style="width:100px;">조회기간</th>
|
||||
<td style="width:400px">
|
||||
<input type="text" name="searchStartDate" value="${param.searchStartDate}" style="width:80px;">
|
||||
<input type="text" name="searchStartTime" value="${param.searchStartTime}" style="width:40px;">
|
||||
<td colspan="3">
|
||||
<input type="text" name="searchStartDate" value="${param.searchStartDate}" style="width:100px;">
|
||||
<input type="text" name="searchStartTime" value="${param.searchStartTime}" style="width:60px;">
|
||||
~
|
||||
<input type="text" name="searchEndTime" value="${param.searchEndTime}" style="width:40px;">
|
||||
<button type="button" id="btnPrevTime" class="cssbtn small"><</button>
|
||||
<button type="button" id="btnNextTime" class="cssbtn small">></button>
|
||||
<input type="text" name="searchEndDate" value="${param.searchEndDate}" style="width:100px;">
|
||||
<input type="text" name="searchEndTime" value="${param.searchEndTime}" style="width:60px;">
|
||||
<span style="color:#888; font-size:12px; margin-left:10px;">(최대 1시간, 초과시 자동 조정)</span>
|
||||
</td>
|
||||
<th style="width:100px;">API ID</th>
|
||||
<td style="width:400px">
|
||||
<input type="text" name="searchApiName" value="${param.searchApiName}">
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</tbody>
|
||||
</table>
|
||||
<!-- 도넛 차트 (상단 50%씩) -->
|
||||
<div class="chart-container">
|
||||
<div class="chart" style="position:relative; padding:0;">
|
||||
<div id="totalDonutChart" style="width:100%; height:100%;"></div>
|
||||
<div id="totalDonutCenterText" style="position:absolute; left:44.5%; top:55%; transform:translate(-50%,-50%); font-size:24px; font-weight:bold; color:#333; pointer-events:none; z-index:10;">0</div>
|
||||
</div>
|
||||
<div id="totalDonutChart" class="chart"></div>
|
||||
<div id="callChart" class="chart"></div>
|
||||
</div>
|
||||
|
||||
@@ -707,17 +645,7 @@
|
||||
<div id="pagerSummary"></div>
|
||||
</div>
|
||||
|
||||
<!-- 상세 그리드 -->
|
||||
<div style="margin-top: 20px;">
|
||||
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px;">
|
||||
<div class="title" style="margin: 0;">상세 통계</div>
|
||||
<button type="button" class="cssbtn" id="btn_excel_export" level="R">
|
||||
<i class="material-icons">file_download</i> Excel 다운로드 (상세)
|
||||
</button>
|
||||
</div>
|
||||
<table id="grid"></table>
|
||||
<div id="pager"></div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
@@ -58,7 +58,6 @@
|
||||
|
||||
// 총건수 도넛 차트
|
||||
var totalDonutOption = {
|
||||
color: ['#5470C6', '#FAC858', '#EE6666'],
|
||||
title: {
|
||||
text: '총 건수 분포',
|
||||
left: 'center',
|
||||
@@ -67,27 +66,22 @@
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
formatter: function (params) {
|
||||
return params.name + ': ' + params.value.toLocaleString() + ' (' + params.percent + '%)';
|
||||
}
|
||||
formatter: '{b}: {c} ({d}%)'
|
||||
},
|
||||
legend: {
|
||||
orient: 'vertical',
|
||||
right: 30,
|
||||
top: 'middle',
|
||||
orient: 'horizontal',
|
||||
bottom: 10,
|
||||
data: ['성공', 'Timeout', '시스템오류']
|
||||
},
|
||||
series: [{
|
||||
name: '총건수',
|
||||
type: 'pie',
|
||||
radius: ['38%', '65%'],
|
||||
center: ['44%', '55%'],
|
||||
radius: ['40%', '70%'],
|
||||
center: ['50%', '50%'],
|
||||
avoidLabelOverlap: true,
|
||||
label: {
|
||||
show: true,
|
||||
formatter: function (params) {
|
||||
return params.name + ': ' + params.value.toLocaleString();
|
||||
}
|
||||
formatter: '{b}: {c}'
|
||||
},
|
||||
emphasis: {
|
||||
label: {
|
||||
@@ -100,10 +94,22 @@
|
||||
show: true
|
||||
},
|
||||
data: [
|
||||
{ value: 0, name: '성공', itemStyle: { color: '#5470C6' } },
|
||||
{ value: 0, name: '성공', itemStyle: { color: '#91CC75' } },
|
||||
{ value: 0, name: 'Timeout', itemStyle: { color: '#FAC858' } },
|
||||
{ value: 0, name: '시스템오류', itemStyle: { color: '#EE6666' } }
|
||||
]
|
||||
}],
|
||||
graphic: [{
|
||||
type: 'text',
|
||||
left: 'center',
|
||||
top: 'center',
|
||||
style: {
|
||||
text: '0',
|
||||
textAlign: 'center',
|
||||
fill: '#333',
|
||||
fontSize: 24,
|
||||
fontWeight: 'bold'
|
||||
}
|
||||
}]
|
||||
};
|
||||
|
||||
@@ -113,7 +119,7 @@
|
||||
|
||||
// 호출량 차트
|
||||
var callOption = {
|
||||
title: { text: '호출량 추이', left: 'center', top: 10, textStyle: { fontSize: 14 } },
|
||||
title: { text: '호출량 추이', left: 'center', textStyle: { fontSize: 14 } },
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: { type: 'cross', label: { backgroundColor: '#6a7985' } }
|
||||
@@ -183,9 +189,13 @@
|
||||
{ value: totalTimeout, name: 'Timeout' },
|
||||
{ value: totalSystemErr, name: '시스템오류' }
|
||||
]
|
||||
}],
|
||||
graphic: [{
|
||||
style: {
|
||||
text: grandTotal.toLocaleString()
|
||||
}
|
||||
}]
|
||||
});
|
||||
$("#totalDonutCenterText").text(grandTotal.toLocaleString());
|
||||
|
||||
|
||||
|
||||
@@ -422,13 +432,12 @@
|
||||
mtype: 'POST',
|
||||
postData: gridPostData,
|
||||
colNames: [
|
||||
'API ID', 'API 명',
|
||||
'API명',
|
||||
'총건수', '성공', 'Timeout', '시스템오류',
|
||||
'평균응답(ms)', '최소응답(ms)', '최대응답(ms)'
|
||||
],
|
||||
colModel: [
|
||||
{ name: 'apiName', align: 'left', width: '150', sortable: false },
|
||||
{ name: 'apiDesc', align: 'left', width: '250', sortable: false },
|
||||
{ name: 'apiName', 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 },
|
||||
@@ -451,9 +460,6 @@
|
||||
for (var i = 0; i < colModel.length; i++) {
|
||||
$(this).setColProp(colModel[i].name, { sortable: false });
|
||||
}
|
||||
// 조회(reloadGrid) 시 컬럼 폭이 고정폭(shrinkToFit:false)으로 남아
|
||||
// 실제 컨테이너 폭과 어긋나 가로 스크롤이 생기는 것을 방지
|
||||
$(this).jqGrid('setGridWidth', $('#content_middle').width(), true);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -465,7 +471,7 @@
|
||||
mtype: 'POST',
|
||||
postData: gridPostData,
|
||||
colNames: [
|
||||
'통계시간', 'API ID', '인스턴스', '업무구분', '클라이언트ID',
|
||||
'통계시간', 'API명', '인스턴스', '업무구분', '클라이언트ID',
|
||||
'Inbound Adapter', 'Outbound Adapter',
|
||||
'총건수', '성공', 'Timeout', '시스템오류',
|
||||
'평균응답(ms)', '최소응답(ms)', '최대응답(ms)'
|
||||
@@ -500,9 +506,6 @@
|
||||
for (var i = 0; i < colModel.length; i++) {
|
||||
$(this).setColProp(colModel[i].name, { sortable: false });
|
||||
}
|
||||
// 조회(reloadGrid) 시 컬럼 폭이 고정폭(shrinkToFit:false)으로 남아
|
||||
// 실제 컨테이너 폭과 어긋나 가로 스크롤이 생기는 것을 방지
|
||||
$(this).jqGrid('setGridWidth', $('#content_middle').width(), true);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -512,21 +515,11 @@
|
||||
$("input[name=searchStartDateTime]").inputmask("9999-99", { 'autoUnmask': true });
|
||||
$("input[name=searchEndDateTime]").inputmask("9999-99", { 'autoUnmask': true });
|
||||
|
||||
// 기본값 설정: 올해 1월 ~ 지난달 (현재가 1월이면 작년 1월 ~ 작년 12월)
|
||||
// 기본값 설정 (최근 12개월)
|
||||
var today = new Date();
|
||||
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);
|
||||
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);
|
||||
|
||||
if (!$("input[name=searchStartDateTime]").val()) {
|
||||
$("input[name=searchStartDateTime]").val(startMonth);
|
||||
@@ -585,19 +578,11 @@
|
||||
</button>
|
||||
</div>
|
||||
<div class="title" id="title">API 월별 통계</div>
|
||||
<table class="search_condition" cellspacing="0" style="table-layout:fixed;">
|
||||
<colgroup>
|
||||
<col style="width:150px;">
|
||||
<col>
|
||||
<col style="width:150px;">
|
||||
<col>
|
||||
<col style="width:150px;">
|
||||
<col>
|
||||
</colgroup>
|
||||
<table class="search_condition" cellspacing="0">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>조회기간</th>
|
||||
<td colspan="5">
|
||||
<th style="width:100px;">조회기간</th>
|
||||
<td colspan="3">
|
||||
<input type="text" name="searchStartDateTime" value="${param.searchStartDateTime}" style="width:100px;" placeholder="YYYY-MM">
|
||||
~
|
||||
<input type="text" name="searchEndDateTime" value="${param.searchEndDateTime}" style="width:100px;" placeholder="YYYY-MM">
|
||||
@@ -605,29 +590,31 @@
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>API ID</th>
|
||||
<th style="width:100px;">API명</th>
|
||||
<td>
|
||||
<input type="text" name="searchApiName" value="${param.searchApiName}">
|
||||
</td>
|
||||
<th>업무구분</th>
|
||||
<td>
|
||||
<input type="text" name="searchBizDivCode" value="${param.searchBizDivCode}">
|
||||
</td>
|
||||
<th>인스턴스</th>
|
||||
<th style="width:100px;">인스턴스</th>
|
||||
<td>
|
||||
<input type="text" name="searchGwInstanceId" value="${param.searchGwInstanceId}">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>클라이언트ID</th>
|
||||
<th style="width:100px;">업무구분</th>
|
||||
<td>
|
||||
<input type="text" name="searchBizDivCode" value="${param.searchBizDivCode}">
|
||||
</td>
|
||||
<th style="width:100px;">클라이언트ID</th>
|
||||
<td>
|
||||
<input type="text" name="searchClientId" value="${param.searchClientId}">
|
||||
</td>
|
||||
<th>Inbound Adapter</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th style="width:100px;">Inbound Adapter</th>
|
||||
<td>
|
||||
<input type="text" name="searchInboundAdapter" value="${param.searchInboundAdapter}">
|
||||
</td>
|
||||
<th>Outbound Adapter</th>
|
||||
<th style="width:100px;">Outbound Adapter</th>
|
||||
<td>
|
||||
<input type="text" name="searchOutboundAdapter" value="${param.searchOutboundAdapter}">
|
||||
</td>
|
||||
@@ -636,18 +623,15 @@
|
||||
</table>
|
||||
<!-- 도넛 차트 (상단 50%씩) -->
|
||||
<div class="chart-container">
|
||||
<div class="chart" style="position:relative; padding:0;">
|
||||
<div id="totalDonutChart" style="width:100%; height:100%;"></div>
|
||||
<div id="totalDonutCenterText" style="position:absolute; left:44.5%; top:55%; transform:translate(-50%,-50%); font-size:24px; font-weight:bold; color:#333; pointer-events:none; z-index:10;">0</div>
|
||||
</div>
|
||||
<div id="totalDonutChart" class="chart"></div>
|
||||
<div id="callChart" class="chart"></div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- 요약 그리드 -->
|
||||
<!-- 요약 그리드 -->
|
||||
<div style="margin-top: 20px;">
|
||||
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px;">
|
||||
<div class="title" style="margin: 0;">API별 요약 통계</div>
|
||||
<div class="title" style="margin: 0;">API명별 요약 통계</div>
|
||||
<button type="button" class="cssbtn" id="btn_excel_export_summary" level="R">
|
||||
<i class="material-icons">file_download</i> Excel 다운로드 (요약)
|
||||
</button>
|
||||
|
||||
@@ -58,7 +58,6 @@
|
||||
|
||||
// 총건수 도넛 차트
|
||||
var totalDonutOption = {
|
||||
color: ['#5470C6', '#FAC858', '#EE6666'],
|
||||
title: {
|
||||
text: '총 건수 분포',
|
||||
left: 'center',
|
||||
@@ -67,27 +66,22 @@
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
formatter: function (params) {
|
||||
return params.name + ': ' + params.value.toLocaleString() + ' (' + params.percent + '%)';
|
||||
}
|
||||
formatter: '{b}: {c} ({d}%)'
|
||||
},
|
||||
legend: {
|
||||
orient: 'vertical',
|
||||
right: 30,
|
||||
top: 'middle',
|
||||
orient: 'horizontal',
|
||||
bottom: 10,
|
||||
data: ['성공', 'Timeout', '시스템오류']
|
||||
},
|
||||
series: [{
|
||||
name: '총건수',
|
||||
type: 'pie',
|
||||
radius: ['38%', '65%'],
|
||||
center: ['44%', '55%'],
|
||||
radius: ['40%', '70%'],
|
||||
center: ['50%', '50%'],
|
||||
avoidLabelOverlap: true,
|
||||
label: {
|
||||
show: true,
|
||||
formatter: function (params) {
|
||||
return params.name + ': ' + params.value.toLocaleString();
|
||||
}
|
||||
formatter: '{b}: {c}'
|
||||
},
|
||||
emphasis: {
|
||||
label: {
|
||||
@@ -100,10 +94,22 @@
|
||||
show: true
|
||||
},
|
||||
data: [
|
||||
{ value: 0, name: '성공', itemStyle: { color: '#5470C6' } },
|
||||
{ value: 0, name: '성공', itemStyle: { color: '#91CC75' } },
|
||||
{ value: 0, name: 'Timeout', itemStyle: { color: '#FAC858' } },
|
||||
{ value: 0, name: '시스템오류', itemStyle: { color: '#EE6666' } }
|
||||
]
|
||||
}],
|
||||
graphic: [{
|
||||
type: 'text',
|
||||
left: 'center',
|
||||
top: 'center',
|
||||
style: {
|
||||
text: '0',
|
||||
textAlign: 'center',
|
||||
fill: '#333',
|
||||
fontSize: 24,
|
||||
fontWeight: 'bold'
|
||||
}
|
||||
}]
|
||||
};
|
||||
|
||||
@@ -113,7 +119,7 @@
|
||||
|
||||
// 호출량 차트
|
||||
var callOption = {
|
||||
title: { text: '호출량 추이', left: 'center', top: 10, textStyle: { fontSize: 14 } },
|
||||
title: { text: '호출량 추이', left: 'center', textStyle: { fontSize: 14 } },
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: { type: 'cross', label: { backgroundColor: '#6a7985' } }
|
||||
@@ -185,9 +191,13 @@
|
||||
{ value: totalTimeout, name: 'Timeout' },
|
||||
{ value: totalSystemErr, name: '시스템오류' }
|
||||
]
|
||||
}],
|
||||
graphic: [{
|
||||
style: {
|
||||
text: grandTotal.toLocaleString()
|
||||
}
|
||||
}]
|
||||
});
|
||||
$("#totalDonutCenterText").text(grandTotal.toLocaleString());
|
||||
|
||||
|
||||
|
||||
@@ -398,13 +408,12 @@
|
||||
mtype: 'POST',
|
||||
postData: gridPostData,
|
||||
colNames: [
|
||||
'API ID', 'API 명',
|
||||
'총건수', '성공', 'Timeout', '시스템오류',
|
||||
'API명',
|
||||
'총건수', '성공', 'Timeout', '시스템오류',
|
||||
'평균응답(ms)', '최소응답(ms)', '최대응답(ms)'
|
||||
],
|
||||
colModel: [
|
||||
{ name: 'apiName', align: 'left', width: '150', sortable: false },
|
||||
{ name: 'apiDesc', align: 'left', width: '250', sortable: false },
|
||||
{ name: 'apiName', 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 },
|
||||
@@ -427,9 +436,6 @@
|
||||
for (var i = 0; i < colModel.length; i++) {
|
||||
$(this).setColProp(colModel[i].name, { sortable: false });
|
||||
}
|
||||
// 조회(reloadGrid) 시 컬럼 폭이 고정폭(shrinkToFit:false)으로 남아
|
||||
// 실제 컨테이너 폭과 어긋나 가로 스크롤이 생기는 것을 방지
|
||||
$(this).jqGrid('setGridWidth', $('#content_middle').width(), true);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -441,7 +447,7 @@
|
||||
mtype: 'POST',
|
||||
postData: gridPostData,
|
||||
colNames: [
|
||||
'통계시간', 'API ID', '인스턴스', '업무구분', '클라이언트ID',
|
||||
'통계시간', 'API명', '인스턴스', '업무구분', '클라이언트ID',
|
||||
'Inbound Adapter', 'Outbound Adapter',
|
||||
'총건수', '성공', 'Timeout', '시스템오류',
|
||||
'평균응답(ms)', '최소응답(ms)', '최대응답(ms)'
|
||||
@@ -476,9 +482,6 @@
|
||||
for (var i = 0; i < colModel.length; i++) {
|
||||
$(this).setColProp(colModel[i].name, { sortable: false });
|
||||
}
|
||||
// 조회(reloadGrid) 시 컬럼 폭이 고정폭(shrinkToFit:false)으로 남아
|
||||
// 실제 컨테이너 폭과 어긋나 가로 스크롤이 생기는 것을 방지
|
||||
$(this).jqGrid('setGridWidth', $('#content_middle').width(), true);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -546,48 +549,42 @@
|
||||
</button>
|
||||
</div>
|
||||
<div class="title" id="title">API 연간 통계</div>
|
||||
<table class="search_condition" cellspacing="0" style="table-layout:fixed;">
|
||||
<colgroup>
|
||||
<col style="width:150px;">
|
||||
<col>
|
||||
<col style="width:150px;">
|
||||
<col>
|
||||
<col style="width:150px;">
|
||||
<col>
|
||||
</colgroup>
|
||||
<table class="search_condition" cellspacing="0">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>조회기간</th>
|
||||
<td colspan="5">
|
||||
<th style="width:100px;">조회기간</th>
|
||||
<td colspan="3">
|
||||
<input type="text" name="searchStartDateTime" value="${param.searchStartDateTime}" style="width:100px;" placeholder="YYYY">
|
||||
~
|
||||
<input type="text" name="searchEndDateTime" value="${param.searchEndDateTime}" style="width:100px;" placeholder="YYYY">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>API ID</th>
|
||||
<th style="width:100px;">API명</th>
|
||||
<td>
|
||||
<input type="text" name="searchApiName" value="${param.searchApiName}">
|
||||
</td>
|
||||
<th>업무구분</th>
|
||||
<td>
|
||||
<input type="text" name="searchBizDivCode" value="${param.searchBizDivCode}">
|
||||
</td>
|
||||
<th>인스턴스</th>
|
||||
<th style="width:100px;">인스턴스</th>
|
||||
<td>
|
||||
<input type="text" name="searchGwInstanceId" value="${param.searchGwInstanceId}">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>클라이언트ID</th>
|
||||
<th style="width:100px;">업무구분</th>
|
||||
<td>
|
||||
<input type="text" name="searchBizDivCode" value="${param.searchBizDivCode}">
|
||||
</td>
|
||||
<th style="width:100px;">클라이언트ID</th>
|
||||
<td>
|
||||
<input type="text" name="searchClientId" value="${param.searchClientId}">
|
||||
</td>
|
||||
<th>Inbound Adapter</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th style="width:100px;">Inbound Adapter</th>
|
||||
<td>
|
||||
<input type="text" name="searchInboundAdapter" value="${param.searchInboundAdapter}">
|
||||
</td>
|
||||
<th>Outbound Adapter</th>
|
||||
<th style="width:100px;">Outbound Adapter</th>
|
||||
<td>
|
||||
<input type="text" name="searchOutboundAdapter" value="${param.searchOutboundAdapter}">
|
||||
</td>
|
||||
@@ -596,17 +593,14 @@
|
||||
</table>
|
||||
<!-- 도넛 차트 (상단 50%씩) -->
|
||||
<div class="chart-container">
|
||||
<div class="chart" style="position:relative; padding:0;">
|
||||
<div id="totalDonutChart" style="width:100%; height:100%;"></div>
|
||||
<div id="totalDonutCenterText" style="position:absolute; left:44.5%; top:55%; transform:translate(-50%,-50%); font-size:24px; font-weight:bold; color:#333; pointer-events:none; z-index:10;">0</div>
|
||||
</div>
|
||||
<div id="totalDonutChart" class="chart"></div>
|
||||
<div id="callChart" class="chart"></div>
|
||||
</div>
|
||||
|
||||
<!-- 요약 그리드 -->
|
||||
<div style="margin-top: 20px;">
|
||||
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px;">
|
||||
<div class="title" style="margin: 0;">API별 요약 통계</div>
|
||||
<div class="title" style="margin: 0;">API명별 요약 통계</div>
|
||||
<button type="button" class="cssbtn" id="btn_excel_export_summary" level="R">
|
||||
<i class="material-icons">file_download</i> Excel 다운로드 (요약)
|
||||
</button>
|
||||
|
||||
@@ -15,19 +15,6 @@
|
||||
<jsp:include page="/jsp/common/include/css.jsp"/>
|
||||
<jsp:include page="/jsp/common/include/script.jsp"/>
|
||||
<script src="<c:url value="/addon/echarts/echarts.min.js"/>"></script>
|
||||
<style>
|
||||
#gbox_grid .ui-jqgrid-ftable tr.footrow td {
|
||||
height: 26px;
|
||||
}
|
||||
.cssbtn.small {
|
||||
height: 24px;
|
||||
border-radius: 2px;
|
||||
min-width: 24px;
|
||||
box-shadow: none;
|
||||
border-color: #ebebec;
|
||||
font-size: 11px;
|
||||
}
|
||||
</style>
|
||||
<script language="javascript">
|
||||
var url = '<c:url value="/onl/kjb/statistics/apiUseStatsMan.json"/>';
|
||||
var url_view = '<c:url value="/onl/kjb/statistics/apiUseStatsMan.view"/>';
|
||||
@@ -73,53 +60,13 @@
|
||||
}
|
||||
|
||||
|
||||
// 조회구분(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");
|
||||
$("#grid").setGridParam({ url: url, postData: postData, page: 1 }).trigger("reloadGrid");
|
||||
}
|
||||
}
|
||||
|
||||
// 조회기간을 시작일 기준 이전/다음 달의 1일~말일로 변경 후 조회 (direction: -1 이전달, 1 다음달)
|
||||
function shiftMonthRange(direction) {
|
||||
var v = $("input[name=searchStartDate]").val().replace(/-/g, "");
|
||||
if (!v || v.length < 6) return;
|
||||
|
||||
var year = parseInt(v.substring(0, 4));
|
||||
var month = parseInt(v.substring(4, 6)) - 1; // 0-based
|
||||
|
||||
var first = new Date(year, month + direction, 1);
|
||||
var last = new Date(year, month + direction + 1, 0); // 해당 달의 말일
|
||||
|
||||
function pad(n) { return String(n).padStart(2, '0'); }
|
||||
function fmt(d) {
|
||||
return d.getFullYear() + '' + pad(d.getMonth() + 1) + pad(d.getDate());
|
||||
}
|
||||
|
||||
$("input[name=searchStartDate]").val(fmt(first));
|
||||
$("input[name=searchEndDate]").val(fmt(last));
|
||||
|
||||
search();
|
||||
}
|
||||
|
||||
function exportToExcel() {
|
||||
|
||||
var postData = getPostData("cmd", "EXCEL_EXPORT");
|
||||
@@ -234,13 +181,12 @@
|
||||
mtype: 'POST',
|
||||
postData: gridPostData,
|
||||
colNames: [
|
||||
'제휴사', 'API명',
|
||||
'총건수', '성공', '성공율(%)', '실패율(%)', 'Timeout', '시스템오류',
|
||||
'구분',
|
||||
'총건수', '성공', '성공율(%)', '실패율(%)', '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 },
|
||||
@@ -252,18 +198,19 @@
|
||||
{ name: 'maxRespTime', align: 'right', width: '90', formatter: numberFormatter, sortable: false }
|
||||
],
|
||||
jsonReader: { repeatitems: false },
|
||||
height: 500,
|
||||
rowNum: 10000,
|
||||
pager: $('#pager'),
|
||||
page: '${param.page}',
|
||||
rowNum: '${rmsDefaultRowNum}',
|
||||
autoheight: true,
|
||||
height: 'auto',
|
||||
autowidth: true,
|
||||
footerrow: true,
|
||||
userDataOnFooter: true,
|
||||
viewrecords: true,
|
||||
rowList: eval('[${rmsDefaultRowList}]'),
|
||||
loadComplete: function(d) {
|
||||
var colModel = $(this).getGridParam("colModel");
|
||||
for (var i = 0; i < colModel.length; i++) {
|
||||
$(this).setColProp(colModel[i].name, { sortable: false });
|
||||
}
|
||||
|
||||
$(".ui-jqgrid-ftable td").css({ "font-weight": "bold", "background-color": "#f5f5f5" });
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -274,23 +221,10 @@
|
||||
$("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 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));
|
||||
}
|
||||
|
||||
var startDate = today.substring(0,6)+"01";
|
||||
var endDate = today;
|
||||
|
||||
|
||||
if (!$("input[name=searchStartDate]").val()) {
|
||||
$("input[name=searchStartDate]").val(startDate);
|
||||
@@ -308,18 +242,6 @@
|
||||
search();
|
||||
});
|
||||
|
||||
$("#btnPrevMonth").click(function() {
|
||||
shiftMonthRange(-1);
|
||||
});
|
||||
|
||||
$("#btnNextMonth").click(function() {
|
||||
shiftMonthRange(1);
|
||||
});
|
||||
|
||||
$("input[name=searchType]").click(function() {
|
||||
search();
|
||||
});
|
||||
|
||||
$("#btn_excel").click(function() {
|
||||
exportToExcel();
|
||||
});
|
||||
@@ -358,8 +280,6 @@
|
||||
<input type="text" name="searchStartDate" id="searchStartDate" value="${param.searchStartDate}" style="width:100px;">
|
||||
~
|
||||
<input type="text" name="searchEndDate" id="searchEndDate" value="${param.searchEndDate}" style="width:100px;">
|
||||
<button type="button" id="btnPrevMonth" class="cssbtn small"><</button>
|
||||
<button type="button" id="btnNextMonth" class="cssbtn small">></button>
|
||||
<span style="color:#888; font-size:12px; margin-left:10px;">(최대 31일)</span>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -374,16 +294,17 @@
|
||||
<td>
|
||||
<input type="text" name="searchOrgName" id="searchOrgName" value="${param.searchOrgName}">
|
||||
</td>
|
||||
<th style="width:120px;">API ID</th>
|
||||
<th style="width:120px;">API명</th>
|
||||
<td>
|
||||
<input type="text" name="searchApiName" id="searchApiName" value="${param.searchApiName}">
|
||||
</td>
|
||||
</tr>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div>
|
||||
<table id="grid"></table>
|
||||
<div id="pager"></div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
@@ -1,140 +0,0 @@
|
||||
<%@ page language="java" contentType="text/html; charset=utf-8" %>
|
||||
<%@ page import="java.io.*" %>
|
||||
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
|
||||
<%@ 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"/>
|
||||
<script language="javascript">
|
||||
var url = '<c:url value="/onl/transaction/apim/apiInterfaceMan.json"/>';
|
||||
var url_loader = '<c:url value="/loader.do"/>';
|
||||
|
||||
function loadList() {
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: url,
|
||||
dataType: "json",
|
||||
data: {cmd: 'LIST_DEPLOY_FILES'},
|
||||
success: function (json) {
|
||||
console.log('list', json)
|
||||
if (json) {
|
||||
$('#grid').jqGrid('clearGridData').jqGrid('setGridParam', {data: json.rows || []}).trigger('reloadGrid');
|
||||
}
|
||||
},
|
||||
error: function (e) {
|
||||
alert(e.responseText);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function callDeploy(filePath) {
|
||||
var postData = [];
|
||||
postData.push({name: "cmd", value: "interface"});
|
||||
postData.push({name: "control", value: "uploadfull"});
|
||||
postData.push({name: "filePath", value: filePath});
|
||||
return new Promise(function (resolve, reject) {
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: url_loader,
|
||||
data: postData,
|
||||
success: function (response) {
|
||||
resolve(response);
|
||||
},
|
||||
error: function (error) {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
$(document).ready(function () {
|
||||
$("#btn_close").click(function () {
|
||||
window.close();
|
||||
});
|
||||
|
||||
$('#grid').jqGrid({
|
||||
datatype: "local",
|
||||
colNames: ['업무그룹', '인터페이스', '생성일시', 'filePath'],
|
||||
colModel: [
|
||||
{name: 'bizCode', align: 'center', width: '80', sortable: true},
|
||||
{name: 'interfaceId', align: 'left', sortable: true},
|
||||
{name: 'createdDate', align: 'center', width: '130', sortable: true},
|
||||
{name: 'filePath', hidden: true}
|
||||
],
|
||||
pager: $('#pager'),
|
||||
rowNum: 20,
|
||||
rowList: [10, 20, 50, 100],
|
||||
autoheight: false,
|
||||
height: 460,
|
||||
width: 650,
|
||||
autowidth: false,
|
||||
viewrecords: true,
|
||||
shrinkToFit: true,
|
||||
multiselect: true,
|
||||
multiboxonly: false
|
||||
});
|
||||
|
||||
loadList();
|
||||
|
||||
$("#btn_deploy").click(async function () {
|
||||
var rowIds = $('#grid').jqGrid('getGridParam', 'selarrrow');
|
||||
if (rowIds.length === 0) {
|
||||
alert('배포할 인터페이스를 선택하세요.');
|
||||
return;
|
||||
}
|
||||
if (!confirm(rowIds.length + '건을 배포 하시겠습니까?')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$("#btn_deploy").prop("disabled", true);
|
||||
|
||||
var successCount = 0;
|
||||
var failedList = [];
|
||||
for (var i = 0; i < rowIds.length; i++) {
|
||||
var rowData = $("#grid").getRowData(rowIds[i]);
|
||||
var target = rowData['bizCode'] + '/' + rowData['interfaceId'];
|
||||
try {
|
||||
var response = await callDeploy(rowData['filePath']);
|
||||
if (response != null && response.includes('status=success')) {
|
||||
successCount++;
|
||||
} else {
|
||||
failedList.push(target);
|
||||
}
|
||||
} catch (error) {
|
||||
failedList.push(target);
|
||||
}
|
||||
}
|
||||
|
||||
$("#btn_deploy").prop("disabled", false);
|
||||
|
||||
var msg = '배포 완료 - 성공[' + successCount + ']건, 실패[' + failedList.length + ']건';
|
||||
if (failedList.length > 0) {
|
||||
msg += '\n실패목록 : ' + failedList.join(', ');
|
||||
}
|
||||
alert(msg);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="popup_box">
|
||||
<div class="search_wrap">
|
||||
<button type="button" class="cssbtn" id="btn_deploy" level="W"><i class="material-icons">cloud_upload</i> 배포
|
||||
</button>
|
||||
<button type="button" class="cssbtn" id="btn_close" level="R"><i class="material-icons">cancel</i> <%= localeMessage.getString("button.close") %>
|
||||
</button>
|
||||
</div>
|
||||
<div class="title">배포 대상 인터페이스 목록</div>
|
||||
<table id="grid"></table>
|
||||
<div id="pager"></div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,6 +1,5 @@
|
||||
<%@ page language="java" contentType="text/html; charset=utf-8" %>
|
||||
<%@ page import="java.io.*" %>
|
||||
<%@ page import="com.eactive.eai.common.util.SystemUtil" %>
|
||||
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
|
||||
<%@ include file="/jsp/common/include/localemessage.jsp" %>
|
||||
<%
|
||||
@@ -38,13 +37,6 @@
|
||||
success: function (json) {
|
||||
new makeOptions("BIZCODE", "BIZNAME").setObj($("select[name=searchEaiBzwkDstcd]")).setNoValueInclude(true).setNoValue("", "전체").setData(json.bizList).setFormat(codeName3OptionFormat).rendering();
|
||||
|
||||
var apiGroupSelect = $('select[name=searchApiGroupId]').empty();
|
||||
apiGroupSelect.append($('<option>').val('').text('전체'));
|
||||
apiGroupSelect.append($('<option>').val('UNREGISTERED').text('미등록'));
|
||||
(json.apiGroupList || []).forEach(function (group) {
|
||||
apiGroupSelect.append($('<option>').val(group.id).text(group.groupName));
|
||||
});
|
||||
|
||||
setSearchable(selectName); // 콤보에 searchable 설정
|
||||
putSelectFromParam();
|
||||
|
||||
@@ -84,26 +76,6 @@
|
||||
return cellvalue.substring(1, 4);
|
||||
}
|
||||
|
||||
function apiStatusFormatter(cellvalue, options, rowObject) {
|
||||
switch (cellvalue) {
|
||||
case 'E': return '장애';
|
||||
case 'D': return '지연';
|
||||
case 'C': return '점검';
|
||||
default: return '정상';
|
||||
}
|
||||
}
|
||||
|
||||
function specStatusFormatter(cellvalue, options, rowObject) {
|
||||
switch (cellvalue) {
|
||||
case 'PUBLISHED':
|
||||
var groupNames = rowObject.publishedGroupNames || [];
|
||||
var tooltip = groupNames.length ? groupNames.join('\n') : '게시된 그룹 없음';
|
||||
return $('<span>').text('게시(' + groupNames.length + ')').attr('title', tooltip).css('cursor', 'help').prop('outerHTML');
|
||||
case 'REGISTERED': return '등록';
|
||||
default: return '';
|
||||
}
|
||||
}
|
||||
|
||||
function list() {
|
||||
detail()
|
||||
var gridPostData = getSearchForJqgrid("cmd", "LIST"); //jqgrid에서는 object 로
|
||||
@@ -118,8 +90,6 @@
|
||||
'API FULL PATH',
|
||||
'요청',
|
||||
'응답',
|
||||
'상태',
|
||||
'스펙 상태',
|
||||
'작성자',
|
||||
'가상응답여부',
|
||||
'변경일시(*)',
|
||||
@@ -129,13 +99,11 @@
|
||||
{name: 'eaiSvcName', align: 'left', width: '100', sortable: true},
|
||||
{name: 'eaiSvcDesc', align: 'left', sortable: false},
|
||||
{name: 'apiFullPath', align: 'left', sortable: false},
|
||||
{name: 'fromAdapter', align: 'center', width: '30', formatter: adapterNameShortFormatter, sortable: false},
|
||||
{name: 'toAdapter', align: 'center', width: '30', formatter: adapterNameShortFormatter, sortable: false},
|
||||
{name: 'statusCode', align: 'center', width: '30', formatter: apiStatusFormatter, sortable: false},
|
||||
{name: 'specStatus', align: 'center', width: '40', formatter: specStatusFormatter, sortable: false, title: false},
|
||||
{name: 'author', align: 'center', width: '40', sortable: false},
|
||||
{name: 'fromAdapter', align: 'center', width: '40', formatter: adapterNameShortFormatter, sortable: false},
|
||||
{name: 'toAdapter', align: 'center', width: '40', formatter: adapterNameShortFormatter, sortable: false},
|
||||
{name: 'author', align: 'center', width: '60', sortable: false},
|
||||
{name : 'simYn', align : 'center' , width:'40', hidden: true },
|
||||
{name : 'lastModifiedDate', align : 'center' , width:'60', sortable: true},
|
||||
{name : 'lastModifiedDate', align : 'center' , width:'40', sortable: true},
|
||||
{name: 'syncAsyncType', align: 'center', width: '40', hidden: true},
|
||||
],
|
||||
jsonReader: {
|
||||
@@ -267,11 +235,6 @@
|
||||
window.alert('이관 대상 API 성공['+successCount+']건, 실패['+failedCount+']건 서버에 다운로드 완료');
|
||||
});
|
||||
|
||||
$("#btn_deploy").click(function(){
|
||||
var url2 = '<c:url value="/onl/transaction/apim/apiInterfaceMan.view"/>?cmd=DEPLOY_POPUP';
|
||||
showModal2(url2, {}, 663, 600);
|
||||
});
|
||||
|
||||
buttonControl();
|
||||
|
||||
// 25.11.25 파일 가져오기 할 때 중복 검사 기능추가.
|
||||
@@ -406,8 +369,7 @@
|
||||
</div><!-- end content_top -->
|
||||
<div class="content_middle" id="content_middle">
|
||||
<div class="search_wrap">
|
||||
<button type="button" class="cssbtn" id="btn_download" level="W" status="DETAIL"><i class="material-icons">cloud_download</i> 이관</button>
|
||||
<!-- <button type="button" class="cssbtn" id="btn_deploy" level="W" status="DETAIL"><i class="material-icons">cloud_upload</i> 배포</button> -->
|
||||
<button type="button" class="cssbtn" id="btn_download" level="W" status="DETAIL"><i class="material-icons">cloud_download</i> 이관</button>
|
||||
<button type="button" class="cssbtn" id="btn_import" level="R" data-bs-toggle="modal"
|
||||
data-bs-target="#uploadModal"><i
|
||||
class="material-icons">upload</i> <%=localeMessage.getString("button.import")%>
|
||||
@@ -423,10 +385,16 @@
|
||||
<table class="search_condition" cellspacing=0;>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th style="width:180px;">업무구분명</th>
|
||||
<td>
|
||||
<select name="searchEaiBzwkDstcd" value="${param.searchEaiBzwkDstcd}"></select>
|
||||
</td>
|
||||
<th style="width:180px;"><%= localeMessage.getString("eaiMessage.eaiSvcName")%></th>
|
||||
<td>
|
||||
<input type="text" name="searchEaiSvcName" value="${param.searchEaiSvcName}">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th style="width:180px;"><%= localeMessage.getString("eaiMessage.eaiSvcDesc")%></th>
|
||||
<td>
|
||||
<input type="text" name="searchEaiSvcDesc" value="${param.searchEaiSvcDesc}">
|
||||
@@ -436,49 +404,6 @@
|
||||
<input type="text" name="searchApiFullPath" value="${param.searchApiFullPath}">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th style="width:180px;">업무구분명</th>
|
||||
<td>
|
||||
<select name="searchEaiBzwkDstcd" value="${param.searchEaiBzwkDstcd}"></select>
|
||||
</td>
|
||||
<th style="width:180px;">당타발 구분</th>
|
||||
<td>
|
||||
<select name="searchInOutType" style="width: 100%">
|
||||
<option value="">전체</option>
|
||||
<option value="1">당발</option>
|
||||
<option value="2">타발</option>
|
||||
</select>
|
||||
</td>
|
||||
<th style="width:180px;">상태</th>
|
||||
<td>
|
||||
<select name="searchStatusCode" style="width: 100%">
|
||||
<option value="">전체</option>
|
||||
<option value="N">정상</option>
|
||||
<option value="D">지연</option>
|
||||
<option value="E">장애</option>
|
||||
<option value="C">점검</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th style="width:180px;">API 스펙 상태</th>
|
||||
<td>
|
||||
<select name="searchSpecStatus" style="width: 100%">
|
||||
<option value="">전체</option>
|
||||
<option value="UNREGISTERED">미등록</option>
|
||||
<option value="REGISTERED">등록</option>
|
||||
<option value="PUBLISHED">게시</option>
|
||||
</select>
|
||||
</td>
|
||||
<th style="width:180px;">API 그룹</th>
|
||||
<td>
|
||||
<select name="searchApiGroupId" style="width: 100%">
|
||||
<option value="">전체</option>
|
||||
<option value="UNREGISTERED">미등록</option>
|
||||
</select>
|
||||
</td>
|
||||
<th></th><td></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="help-text" style="color:#666; font-size:12px; margin-top:5px;">(*) : 정렬가능항목</div>
|
||||
@@ -513,4 +438,4 @@
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
@@ -77,23 +77,7 @@
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
/* iframe 모달(showModal) 콘텐츠 패딩 제거 — iframe 이 다이얼로그 폭을 최대한 사용해 내부 가로스크롤 방지 */
|
||||
.ui-dialog .ui-dialog-content { padding: 0 !important; }
|
||||
|
||||
/* OpenAPI Spec 버튼 상태 색상 (PTL_API_SPEC_INFO 기준) */
|
||||
#btn_openapi_spec.spec-exists { background:#ffe08a !important; border-color:#f0c040 !important; color:#5a4600 !important; }
|
||||
#btn_openapi_spec.spec-public { background:#b8e6a0 !important; border-color:#7cc257 !important; color:#2c4a15 !important; }
|
||||
/* 호버 상태 안내 레이어 */
|
||||
#specStatusLayer {
|
||||
position: fixed; z-index: 100000; display: none;
|
||||
background: rgba(30,30,30,0.92); color: #fff;
|
||||
padding: 8px 12px; border-radius: 6px;
|
||||
font-size: 12px; line-height: 1.6; max-width: 340px;
|
||||
white-space: pre-line; pointer-events: none;
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,0.3);
|
||||
}
|
||||
|
||||
|
||||
</style>
|
||||
<script language="javascript" >
|
||||
var isDetail = false;
|
||||
@@ -101,7 +85,6 @@
|
||||
const url_excel ='<c:url value="/onl/transaction/apim/apiInterfaceMan.excel" />';
|
||||
var url_spec = '<c:url value="/onl/transaction/apim/apiSpecMan.view"/>';
|
||||
var url_openapi_spec = '<c:url value="/onl/transaction/apim/djbApiSpecMan.view"/>';
|
||||
var url_openapi_spec_json = '<c:url value="/onl/transaction/apim/djbApiSpecMan.json"/>';
|
||||
var returnUrl ;
|
||||
let headerValues = [];
|
||||
let headerNames = [];
|
||||
@@ -504,7 +487,6 @@
|
||||
}
|
||||
|
||||
if(data.errResponseTransform != null && data.errResponseTransform != '' && data.errTransformYn == 'Y') {
|
||||
$('#toggleTransformYn').bootstrapToggle('on');
|
||||
$('#toggleErrTransformYn').bootstrapToggle('on');
|
||||
$('#rowErrResponseTransform').show();
|
||||
} else {
|
||||
@@ -554,8 +536,6 @@
|
||||
|
||||
$('#inboundHttpMethod').trigger('change');
|
||||
$('#outboundHttpMethod').trigger('change');
|
||||
|
||||
applyOpenApiSpecStatus(); // OpenAPI Spec 버튼 색상(PTL_API_SPEC_INFO)
|
||||
},
|
||||
error:function(e){
|
||||
alert(e.responseText);
|
||||
@@ -646,7 +626,7 @@
|
||||
const svcDesc = encodeURI(encodeURIComponent($('#eaiSvcDesc').val()));
|
||||
const cnvsnDesc = encodeURI(encodeURIComponent(transformDesc));
|
||||
var args = {title:transformName+' 수정', parent: true};
|
||||
var url='<c:url value="/onl/admin/rule/transformMan.view"/>';
|
||||
var url='<c:url value="/onl/admin/rule/transform2/transform2Man.view"/>';
|
||||
url += '?cmd=DETAIL';
|
||||
url += '&cnvsnName='+transformName;
|
||||
url += '&eaiSvcName='+svcName;
|
||||
@@ -793,7 +773,7 @@
|
||||
function updateFirstLayout(newTransformName, transformDesc){
|
||||
$.ajax({
|
||||
type : "POST",
|
||||
url: '<c:url value="/onl/admin/rule/transformMan.json" />',
|
||||
url: '<c:url value="/onl/admin/rule/transform2/transform2Man.json" />',
|
||||
data: { cmd: 'UPDATE',
|
||||
gridData: '[]',
|
||||
eaiSvcName: svcName,
|
||||
@@ -824,7 +804,7 @@
|
||||
};
|
||||
$.ajax({
|
||||
type : "POST",
|
||||
url: '<c:url value="/onl/admin/rule/transformMan.json" />',
|
||||
url: '<c:url value="/onl/admin/rule/transform2/transform2Man.json" />',
|
||||
data: { cmd: 'INSERT',
|
||||
gridData: '[]',
|
||||
cnvsnType,
|
||||
@@ -1031,46 +1011,10 @@
|
||||
window.open(urlAddServiceType(baseUrl + '&menuId=' + encodeURIComponent(getMenuId())), '_blank');
|
||||
} else {
|
||||
// 기본: 모달 + iframe. showModal 이 serviceType/menuId/pop 을 자동 부착.
|
||||
// 콘텐츠 고정폭 1600 + 다이얼로그 chrome(패딩/보더 ~40px) 때문에 iframe 내부폭이 1600 미만이면 가로스크롤 발생.
|
||||
// 뷰포트 내에서 최대한 크게 열어(캡 1720) iframe 내부폭이 1600 이상 확보되게 한다.
|
||||
var vw = $(window).width(), vh = $(window).height();
|
||||
var modalW = Math.min(vw - 12, 1720);
|
||||
var modalH = Math.min(vh - 20, 900);
|
||||
// 스펙 편집 모달 닫힘 시 버튼 색상 재조회(공개여부/등록여부 변경 반영)
|
||||
showModal(baseUrl, { title: 'OpenAPI Spec' }, modalW, modalH, function(){ applyOpenApiSpecStatus(); });
|
||||
showModal(baseUrl, { title: 'OpenAPI Spec' }, 1600, 880);
|
||||
}
|
||||
}
|
||||
|
||||
// OpenAPI Spec 버튼 색상 = PTL_API_SPEC_INFO(api_id = getFullSvcName()) 상태.
|
||||
// - 미등록: 기본색 - 등록·비공개(display_yn≠Y): 노랑 - 등록·공개(display_yn=Y): 하늘
|
||||
function applyOpenApiSpecStatus() {
|
||||
var $btn = $('#btn_openapi_spec');
|
||||
$btn.removeClass('spec-exists spec-public').data('specMsg', '');
|
||||
var eaiSvcName = getFullSvcName();
|
||||
if (!eaiSvcName || eaiSvcName.length < 2) return;
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: url_openapi_spec_json,
|
||||
dataType: 'json',
|
||||
data: { cmd: 'SPEC_STATUS', eaiSvcName: eaiSvcName },
|
||||
success: function(res) {
|
||||
var exists = res && (res.exists === true || res.exists === 'true');
|
||||
var isPublic = res && (res.displayYn === 'Y');
|
||||
var msg;
|
||||
if (!exists) {
|
||||
msg = 'OpenAPI Spec 미등록\n버튼을 눌러 스펙을 생성/편집하세요.';
|
||||
} else if (isPublic) {
|
||||
$btn.addClass('spec-public');
|
||||
msg = 'OpenAPI Spec 등록됨 · 포탈 공개중 (display_yn = Y)\n포탈 메인/문서에 노출됩니다.';
|
||||
} else {
|
||||
$btn.addClass('spec-exists');
|
||||
msg = 'OpenAPI Spec 등록됨 · 비공개 (display_yn ≠ Y)\n포탈에는 노출되지 않습니다.';
|
||||
}
|
||||
$btn.data('specMsg', msg);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
makeValidate();
|
||||
// var bootstrapButton = $.fn.button.noConflict()
|
||||
@@ -1094,23 +1038,6 @@
|
||||
showOpenApiSpecPopup(e);
|
||||
});
|
||||
|
||||
// OpenAPI Spec 버튼 호버 시 현재 상태 안내 레이어
|
||||
if ($('#specStatusLayer').length === 0) {
|
||||
$('body').append('<div id="specStatusLayer"></div>');
|
||||
}
|
||||
$("#btn_openapi_spec")
|
||||
.on('mouseenter', function() {
|
||||
var msg = $(this).data('specMsg');
|
||||
if (!msg) return;
|
||||
$('#specStatusLayer').text(msg).css('display', 'block');
|
||||
})
|
||||
.on('mousemove', function(e) {
|
||||
$('#specStatusLayer').css({ left: (e.clientX + 14) + 'px', top: (e.clientY + 16) + 'px' });
|
||||
})
|
||||
.on('mouseleave', function() {
|
||||
$('#specStatusLayer').hide();
|
||||
});
|
||||
|
||||
$("#btn_modify").click(modifyApi);
|
||||
$("#btn_delete").click(function(){
|
||||
if(!confirm("삭제하시겠습니까?")){
|
||||
@@ -1608,12 +1535,10 @@
|
||||
<button type="button" class="cssbtn" id="btn_json_export" level="R" status="DETAIL,NEW"><i class="material-icons">download</i> <%=localeMessage.getString("button.exportJson")%></button></button>
|
||||
<button type="button" class="cssbtn" id="btn_delete" level="W" status="DETAIL"><i class="material-icons">delete</i> <%= localeMessage.getString("button.delete") %></button>
|
||||
<button type="button" class="cssbtn" id="btn_modify" level="R" status="DETAIL,NEW"><i class="material-icons">save</i> <%= localeMessage.getString("button.modify") %></button>
|
||||
<%-- v1: 기존 API 스펙 (모달). PTL_PROPERTY(Portal / djb.apispec.v1.enabled) 로 노출 제어. --%>
|
||||
<c:if test="${apiSpecV1Enabled}">
|
||||
<button type="button" class="cssbtn" id="btn_api_spec" level="R" status="DETAIL"><i class="material-icons">description</i> API 스펙</button>
|
||||
</c:if>
|
||||
<!-- v1: 기존 API 스펙 (모달) — 기능 비교용 복원 -->
|
||||
<button type="button" class="cssbtn" id="btn_api_spec" level="R" status="DETAIL,NEW"><i class="material-icons">description</i> API 스펙</button>
|
||||
<!-- v2: 신규 OpenAPI Spec (새 탭) -->
|
||||
<button type="button" class="cssbtn" id="btn_openapi_spec" level="R" status="DETAIL"><i class="material-icons">api</i> OpenAPI Spec</button>
|
||||
<button type="button" class="cssbtn" id="btn_openapi_spec" level="R" status="DETAIL,NEW"><i class="material-icons">api</i> OpenAPI Spec</button>
|
||||
<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" style="font-size:1.4em">${rmsMenuName}</div>
|
||||
|
||||
@@ -67,9 +67,6 @@
|
||||
saveUrl: "<c:url value='/onl/transaction/apim/djbApiSpecMan.json'/>?cmd=SAVE&serviceType=APIGW",
|
||||
swaggerBase: "<c:url value='/plugins/swaggerUI/'/>",
|
||||
gwAddress: "${gwAddress}",
|
||||
useProxy: ${useProxy},
|
||||
tokenUseProxy: ${tokenUseProxy},
|
||||
exportEnabled: ${exportEnabled},
|
||||
orgPopupUrl: "<c:url value='/onl/transaction/apim/apiSpecMan.view'/>",
|
||||
groupPopupUrl: "<c:url value='/onl/transaction/apim/djbApiSpecMan.view'/>?cmd=GROUP_POPUP",
|
||||
jsonUrl: "<c:url value='/onl/transaction/apim/djbApiSpecMan.json'/>"
|
||||
@@ -94,8 +91,6 @@
|
||||
<button id="btn-newwin" class="hidden px-3 py-1.5 text-sm rounded border border-slate-300 bg-white hover:bg-slate-50">새창으로 띄우기</button>
|
||||
<button id="btn-regen" class="px-3 py-1.5 text-sm rounded border border-slate-300 bg-white hover:bg-slate-50">자동 생성(초기화)</button>
|
||||
<button id="btn-save" class="px-3 py-1.5 text-sm rounded bg-emerald-600 text-white hover:bg-emerald-700">저장</button>
|
||||
<%-- 내보내기 버튼은 PTL_PROPERTY(djb.openapi.export.enabled=true) 일 때만 노출. 기본 미노출. --%>
|
||||
<c:if test="${exportEnabled}">
|
||||
<div class="relative">
|
||||
<button id="btn-export" class="px-3 py-1.5 text-sm rounded bg-brand-600 text-white hover:bg-brand-700">내보내기 ▾</button>
|
||||
<div id="export-menu" class="hidden absolute right-0 mt-1 w-44 bg-white border border-slate-200 rounded shadow-lg z-40">
|
||||
@@ -103,7 +98,6 @@
|
||||
<button data-export="json" class="block w-full text-left px-3 py-2 text-sm hover:bg-slate-50">JSON 다운로드</button>
|
||||
</div>
|
||||
</div>
|
||||
</c:if>
|
||||
<button id="btn-close" class="px-3 py-1.5 text-sm rounded border border-slate-300 bg-white hover:bg-slate-50">닫기</button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
@@ -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: 4, titleText: '<%= localeMessage.getString("tranStat.errNo") %>'},
|
||||
{startColumnName: 'E9300', numberOfColumns: 2, titleText: '<%= localeMessage.getString("tranStat.timeout") %>'},
|
||||
{startColumnName: 'E100', numberOfColumns: 5, titleText: '<%= localeMessage.getString("tranStat.errNo") %>'},
|
||||
{startColumnName: 'E9300', numberOfColumns: 3, 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","8"));
|
||||
merge.push(makeMerge("타임아웃","0","0","9","11"));
|
||||
merge.push(makeMerge("평균처리시간(초)","0","0","12","14"));
|
||||
merge.push(makeMerge("구간별 에러건수","0","0","5","9"));
|
||||
merge.push(makeMerge("타임아웃","0","0","10","12"));
|
||||
merge.push(makeMerge("평균처리시간(초)","0","0","13","15"));
|
||||
|
||||
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","8"));
|
||||
merge.push(makeMerge("타임아웃","0","0","9","11"));
|
||||
merge.push(makeMerge("평균처리시간(초)","0","0","12","14"));
|
||||
merge.push(makeMerge("구간별 에러건수","0","0","5","9"));
|
||||
merge.push(makeMerge("타임아웃","0","0","10","12"));
|
||||
merge.push(makeMerge("평균처리시간(초)","0","0","13","15"));
|
||||
gridToExcelSubmit(url_excel,"LIST_GRID_TO_EXCEL",$("#grid"),$("#ajaxForm"),'업무별 거래현황',merge);
|
||||
return false;
|
||||
});
|
||||
@@ -273,6 +273,7 @@ $(document).ready(function() {
|
||||
<%-- <img src="<c:url value="/img/btn_10min.png" />" alt="" id="btn_10min" level="W" /> --%>
|
||||
<button type="button" class="cssbtn" id="btn_detail" level="R" ><i class="material-icons">details</i> <%= localeMessage.getString("button.detail") %></button>
|
||||
<button type="button" class="cssbtn" id="btn_excel" level="W" ><i class="material-icons">table_view</i> <%= localeMessage.getString("button.excel") %></button>
|
||||
<button type="button" class="cssbtn" id="btn_initialize" level="W" ><i class="material-icons">restart_alt</i> <%= localeMessage.getString("button.reset") %></button>
|
||||
<button type="button" class="cssbtn" id="btn_search" level="R" ><i class="material-icons">search</i> <%= localeMessage.getString("button.search") %></button>
|
||||
<%-- <img src="<c:url value="/img/btn_detail.png" />" alt="" id="btn_detail" level="R" /> --%>
|
||||
<%-- <img src="<c:url value="/img/btn_excel.png" />" alt="" id="btn_excel" level="W" /> --%>
|
||||
@@ -281,9 +282,6 @@ $(document).ready(function() {
|
||||
<%-- <img id="btn_excel" src="<c:url value="/img/btn_excel.png"/>" level="W" status="DETAIL"/> --%>
|
||||
</div>
|
||||
<div class="title"><%= localeMessage.getString("tranStat.title") %><span class="tooltip"><%= localeMessage.getString("tranStat.tooltip") %></span></div>
|
||||
<div style="padding-bottom: 10px; color: #555">
|
||||
※ 100 : 송신>APIM 200 : APIM>수신 300 : 수신>APIM 400 : APIM>송신
|
||||
</div>
|
||||
<ajax>
|
||||
<table id="grid" ></table>
|
||||
</ajax>
|
||||
|
||||
@@ -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 : 4, caption:'<%= localeMessage.getString("tranStat.errNo") %>'},
|
||||
{span : 2, caption:'<%= localeMessage.getString("tranStat.timeout") %>'}
|
||||
{span : 5, caption:'<%= localeMessage.getString("tranStat.errNo") %>'},
|
||||
{span : 3, 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","14"));
|
||||
merge.push(makeMerge("타임아웃","0","0","15","16"));
|
||||
merge.push(makeMerge("전문구간별 에러건수","0","0","11","15"));
|
||||
merge.push(makeMerge("타임아웃","0","0","16","18"));
|
||||
|
||||
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","14"));
|
||||
merge.push(makeMerge("타임아웃","0","0","15","16"));
|
||||
merge.push(makeMerge("구간별 에러건수","0","0","11","15"));
|
||||
merge.push(makeMerge("타임아웃","0","0","16","18"));
|
||||
|
||||
w2GridToExcelSubmit(url_excel,"LIST_GRID_TO_EXCEL",w2ui['myGrid'],$("#ajaxForm"),"전문별 거래현황",merge);
|
||||
return false;
|
||||
@@ -429,6 +429,7 @@ $( document ).ready(function() {
|
||||
<input type="checkbox" id="recentYnVal" value="N"/> <b><%= localeMessage.getString("tranStat.rec10Min") %></b>
|
||||
<button type="button" class="cssbtn" id="btn_detail" level="R" ><i class="material-icons">details</i> <%= localeMessage.getString("button.detail") %></button>
|
||||
<button type="button" class="cssbtn" id="btn_excel" level="W" ><i class="material-icons">table_view</i> <%= localeMessage.getString("button.excel") %></button>
|
||||
<button type="button" class="cssbtn" id="btn_initialize" level="W" ><i class="material-icons">restart_alt</i> <%= localeMessage.getString("button.reset") %></button>
|
||||
<button type="button" class="cssbtn" id="btn_search" level="R" ><i class="material-icons">search</i> <%= localeMessage.getString("button.search") %></button>
|
||||
<%-- <img src="<c:url value="/img/btn_10min.png"/>" alt="" id="btn_10min" level="W" /> --%>
|
||||
<%-- <img src="<c:url value="/img/btn_detail.png"/>" alt="" id="btn_detail" level="R" /> --%>
|
||||
@@ -437,9 +438,6 @@ $( document ).ready(function() {
|
||||
<%-- <img src="<c:url value="/img/btn_search.png"/>" alt="" id="btn_search" level="R" /> --%>
|
||||
</div>
|
||||
<div class="title"><%= localeMessage.getString("tranStatDetail.title") %><span class="tooltip"><%= localeMessage.getString("tranStat.tooltip") %></span></div>
|
||||
<div style="padding-bottom: 10px; color: #555">
|
||||
※ 100 : 송신>APIM 200 : APIM>수신 300 : 수신>APIM 400 : APIM>송신
|
||||
</div>
|
||||
<table class="search_condition" cellspacing=0;>
|
||||
<tbody>
|
||||
<tr>
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
var roleString = "<%=SessionManager.getRoleIdString(request)%>";
|
||||
let totalLogs = 0;
|
||||
let displayedLogs = 0;
|
||||
var bizErrCodeSet = new Set(); // ERRCODE_BIZ(업무 오류) 코드 목록 - 그리드 배경색 구분용
|
||||
|
||||
function adminChk() {
|
||||
var arrRoleID = roleString.split(',');
|
||||
@@ -107,8 +106,6 @@
|
||||
new makeOptions("CODE", "NAME").setObj($("select[name=searchExtBizCd]")).setNoValueInclude(true).setNoValue("", "전체").setData(json.extBizList).setFormat(codeName3OptionFormat).rendering();
|
||||
new makeOptions("CODE", "NAME").setObj($("select[name=searchLogPrcssSerno]")).setNoValueInclude(true).setNoValue("", "전체").setData(json.logPrcssSernoList).rendering();
|
||||
|
||||
bizErrCodeSet = new Set((json.bizErrCodeList || []).map(function (c) { return c.CODE; }));
|
||||
|
||||
setSearchable("searchEaiBzwkDstcd"); // 콤보에 searchable 설정
|
||||
setSearchable("searchExtBizCd");
|
||||
|
||||
@@ -225,6 +222,63 @@
|
||||
$("input[name=searchEndHHMM]").val(endHHMM);
|
||||
}
|
||||
|
||||
function guidEvent() {
|
||||
var searchGuid = $("input[name=searchGuid]").val();
|
||||
if (searchGuid.trim() == '')
|
||||
return;
|
||||
|
||||
var searchTm = searchGuid.trim();
|
||||
/*
|
||||
var yy = searchTm.substr(13,4);
|
||||
var mm = searchTm.substr(17,2);
|
||||
var dd = searchTm.substr(19,2);
|
||||
var hh = searchTm.substr(21,2);
|
||||
var MM = searchTm.substr(23,2);
|
||||
*/
|
||||
var yy = searchTm.substr(3,4);
|
||||
var mm = searchTm.substr(7,2);
|
||||
var dd = searchTm.substr(9,2);
|
||||
var hh = searchTm.substr(11,2);
|
||||
var MM = searchTm.substr(13,2);
|
||||
|
||||
var ms = Date.UTC(yy,mm-1,dd,hh,MM,00);
|
||||
var date = new Date(ms);
|
||||
var startYYYYMMDD;
|
||||
var startHHMM;
|
||||
var endYYYYMMDD;
|
||||
var endHHMM;
|
||||
//Local Time Setting
|
||||
date.setHours(date.getHours() - 9);
|
||||
//5분 간격
|
||||
date.setMinutes(date.getMinutes() - 5);
|
||||
|
||||
startYYYYMMDD = date.getFullYear() + right("0" + (date.getMonth() + 1), 2) + right("0" + (date.getDate()), 2);
|
||||
startHHMM = right("0" + date.getHours(), 2) + right("0" + date.getMinutes(), 2) + "00";
|
||||
|
||||
date.setMinutes(date.getMinutes() + 10);
|
||||
|
||||
endYYYYMMDD = date.getFullYear() + right("0" + (date.getMonth() + 1), 2) + right("0" + (date.getDate()), 2);
|
||||
endHHMM = right("0" + date.getHours(), 2) + right("0" + date.getMinutes(), 2) + "00";
|
||||
|
||||
//추가 코드 입력 2012.05.16 권세환 (일자가 바뀔경우 시간 간격을 +-5분 -> +-1분)
|
||||
if (startYYYYMMDD.substr(6, 2) != endYYYYMMDD.substr(6, 2)) {
|
||||
date.setMinutes(date.getMinutes() - 6);
|
||||
|
||||
startYYYYMMDD = date.getFullYear() + right("0" + (date.getMonth() + 1), 2) + right("0" + (date.getDate()), 2);
|
||||
startHHMM = right("0" + date.getHours(), 2) + right("0" + date.getMinutes(), 2) + "00";
|
||||
|
||||
date.setMinutes(date.getMinutes() + 2);
|
||||
|
||||
endYYYYMMDD = date.getFullYear() + right("0" + (date.getMonth() + 1), 2) + right("0" + (date.getDate()), 2);
|
||||
endHHMM = right("0" + date.getHours(), 2) + right("0" + date.getMinutes(), 2) + "00";
|
||||
}
|
||||
$("input[name=searchStartYYYYMMDD]").val(startYYYYMMDD);
|
||||
$("input[name=searchEndYYYYMMDD]").val(endYYYYMMDD);
|
||||
$("input[name=searchStartHHMM]").val(startHHMM);
|
||||
$("input[name=searchEndHHMM]").val(endHHMM);
|
||||
|
||||
}
|
||||
|
||||
function timeStampFormat2(cellvalue, options, rowObject) {
|
||||
if (cellvalue == null || cellvalue.length < 17)
|
||||
return '';
|
||||
@@ -314,8 +368,7 @@
|
||||
'<%=localeMessage.getString("tracking.companyitcontactname")%>',
|
||||
'<%=localeMessage.getString("tracking.companyitcontacttelno")%>',
|
||||
'<%=localeMessage.getString("tracking.itcontactpointperson1")%>',
|
||||
'<%=localeMessage.getString("tracking.bzwkcontactperson")%>',
|
||||
'EAIERRCD'
|
||||
'<%=localeMessage.getString("tracking.bzwkcontactperson")%>'
|
||||
],
|
||||
colModel: [
|
||||
{name: 'EAISVCSERNO', hidden: true},
|
||||
@@ -327,15 +380,15 @@
|
||||
{name: 'TRACKASISKEY2CTNT', hidden: true},
|
||||
|
||||
{name: 'EAISVCSERNO_TMP', align: 'left', width: '220', formatter: trimFormatter},
|
||||
{name: 'KEYMGTMSGCTNT', align: 'left', width: '300'},
|
||||
{name: 'KEYMGTMSGCTNT', align: 'left', width: '240'},
|
||||
{name: 'LOGPRCSSSERNO_TMP', align: 'center', width: '40'},
|
||||
{name: 'TELGMRECVTRANCD', align: 'left', width: '135', hidden: true},
|
||||
{name: 'EAISVCNAME', align: 'left', width: '160'},
|
||||
{name: 'EAISVCNAME', align: 'left', width: '130'},
|
||||
{name: 'EAISVCDESC', align: 'left', width: '280'},
|
||||
{name: 'EAIBZWKDSTCD_TMP', align: 'center', width: '50'},
|
||||
{name: 'EXTBIZCD', align: 'center', width: '50', hidden: isApiGw},
|
||||
{name: 'REFKEY', align: 'left', width: '280', hidden: isApiGw},
|
||||
{name: 'CLIENTNAME', align: 'left', width: '120', hidden: !isApiGw},
|
||||
{name: 'CLIENTNAME', align: 'left', width: '260', hidden: !isApiGw},
|
||||
{name: 'ORGNAME', align: 'left', width: '100', hidden: !isApiGw, formatter: orgNameFormatter},
|
||||
{name: 'MSGDPSTYMS_TMP', align: 'center', width: '150', formatter: timeStampFormat2},
|
||||
{name: 'MSGPRCSSYMS', align: 'center', width: '150', formatter: timeStampFormat2},
|
||||
@@ -345,8 +398,7 @@
|
||||
{name: 'COMPANYITCONTACTNAME', align: 'left', width: '110', hidden: true},
|
||||
{name: 'COMPANYITCONTACTTELNO', align: 'left', width: '120', hidden: true},
|
||||
{name: 'ITCONTACTPOINTPERSON1', align: 'left', width: '120', hidden: true},
|
||||
{name: 'BZWKCONTACTPERSON', align: 'left', width: '120', hidden: true},
|
||||
{name: 'EAIERRCD', hidden: true}
|
||||
{name: 'BZWKCONTACTPERSON', align: 'left', width: '120', hidden: true}
|
||||
],
|
||||
jsonReader: {
|
||||
repeatitems: false
|
||||
@@ -383,7 +435,7 @@
|
||||
|
||||
},
|
||||
loadComplete: function (d) {
|
||||
|
||||
$("#grid").tuiTableRowSpan("7");
|
||||
|
||||
// 거래 건수 표시
|
||||
var uniqueCount = calculateUniqueLogCount("#grid");
|
||||
@@ -392,22 +444,14 @@
|
||||
//에러 행 색상 표시
|
||||
var rows = $(this).getDataIDs();
|
||||
for (var i = 0; i < rows.length; i++) {
|
||||
var errYn = $("#grid").getCell(rows[i], 'ERRYN');
|
||||
var eaiErrCd = $("#grid").getCell(rows[i], 'EAIERRCD');
|
||||
var isBizErr = eaiErrCd != null && bizErrCodeSet.has(String(eaiErrCd).trim());
|
||||
|
||||
if (isBizErr) {
|
||||
// 업무(BIZ) 오류코드(ERRCODE_BIZ) - 하늘색 배경
|
||||
$("#" + rows[i]).find("td").css("background-color", "#BEEDFF");
|
||||
} else if (errYn == "Y") {
|
||||
var val = $("#grid").getCell(rows[i], 'ERRYN');
|
||||
if (val == "Y") {
|
||||
$("#" + rows[i]).find("td").css("background-color", "#FFCC99");
|
||||
}
|
||||
}
|
||||
|
||||
$("#grid").tuiTableRowSpan("7");
|
||||
|
||||
//1000건이상 alert
|
||||
if (d.records >= 1000) {
|
||||
if (d.records == 1000) {
|
||||
setTimeout(() => {
|
||||
alert("<%=localeMessage.getString("tracking.alert5")%>"); // 조회 결과 1,000건 초과
|
||||
}, 0); // Alert는 비동기적으로 처리
|
||||
@@ -437,6 +481,7 @@
|
||||
//$("#grid").jqGrid('setFrozenColumns');
|
||||
|
||||
$("#btn_search").click(function () {
|
||||
$("input[name=searchGuid]").blur();
|
||||
detail();
|
||||
getTransactionCounts();
|
||||
});
|
||||
@@ -506,6 +551,12 @@
|
||||
return false;
|
||||
});
|
||||
|
||||
//GUID
|
||||
$("input[name=searchGuid]").blur(function (event) {
|
||||
guidEvent();
|
||||
|
||||
});
|
||||
|
||||
buttonControl();
|
||||
|
||||
var local = localStorage.getItem("guid");
|
||||
@@ -650,7 +701,7 @@
|
||||
<td colspan="3"><input type="text" name="searchEaiSvcSerno" value="${param.searchEaiSvcSerno}"></td>
|
||||
|
||||
<th><%=localeMessage.getString("tracking.keymgtmsgctnt")%></th>
|
||||
<td colspan="2"><input type="text" name="searchGuid" value="${param.searchGuid}" ></td>
|
||||
<td colspan="2"><input type="text" name="searchKeyMgtMsgCtnt" value="${param.searchKeyMgtMsgCtnt}"></td>
|
||||
|
||||
<%-- <th><%=localeMessage.getString("tracking.orgGUID")%></th>
|
||||
<td colspan="4"><input type="text" name="searchGuid" value="" maxlength="<%=DbTrackingService.GUID_LENGTH%>"></td> --%>
|
||||
|
||||
@@ -23,9 +23,6 @@
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
.table_row td { padding: 0 5px; }
|
||||
.table_row input { box-sizing: border-box; }
|
||||
|
||||
</style>
|
||||
<script language="javascript" >
|
||||
// var eaiBzwkDstcd = window.dialogArguments["eaiBzwkDstcd"];
|
||||
@@ -256,7 +253,7 @@
|
||||
var name = $(this).attr("name");
|
||||
if ("${rmsMenuAuth}" =="W"){ //2020.07.08 admin 만 버튼보이게 수정
|
||||
$("#"+name).show();
|
||||
$("input[name="+name+"]").css("width","calc(100% - 60px)");
|
||||
$("input[name="+name+"]").css("width","185px");
|
||||
}
|
||||
});
|
||||
|
||||
@@ -264,7 +261,7 @@
|
||||
var name = $(this).attr("name");
|
||||
if($("input[name="+name+"]").val().trim() != ""){
|
||||
$("#"+name).show();
|
||||
$("input[name="+name+"]").css("width","calc(100% - 60px)");
|
||||
$("input[name="+name+"]").css("width","185px");
|
||||
}
|
||||
});
|
||||
|
||||
@@ -379,7 +376,7 @@
|
||||
const value = bodyCodeEditor.getValue();
|
||||
let prettyValue = value;
|
||||
if(syntax === 'JSN'){
|
||||
prettyValue = preserveExponential(value); //vkbeautify.json(value);
|
||||
prettyValue = vkbeautify.json(value);
|
||||
}else if(syntax === 'XML'){
|
||||
prettyValue = vkbeautify.xml(value).trim();
|
||||
}
|
||||
@@ -410,19 +407,6 @@
|
||||
}
|
||||
});
|
||||
}
|
||||
function preserveExponential(jsonString) {
|
||||
// 1. 지수 형태의 숫자(예: 1e+21, 5.43e-7)를 찾아 문자열 ""로 감쌉니다.
|
||||
// JSON의 key 뒤에 오는 value 위치의 지수 표기법을 매칭합니다.
|
||||
let protectedJson = jsonString.replace(/:\s*([+-]?\d+\.?\d*e[+-]?\d+)/gi, ': "$1"');
|
||||
|
||||
// 2. 보호된 상태로 vkbeautify를 실행하여 예쁘게 정렬합니다.
|
||||
let prettyJson = vkbeautify.json(protectedJson);
|
||||
|
||||
// 3. (선택사항) 만약 최종 결과물에서 반드시 큰따옴표("")를 다시 제거해야 한다면 아래 주석을 해제하세요.
|
||||
prettyJson = prettyJson.replace(/:\s*"([+-]?\d+\.?\d*e[+-]?\d+)"/gi, ': $1');
|
||||
|
||||
return prettyJson;
|
||||
}
|
||||
function doChangeSyntax(syntax){
|
||||
if(syntax === 'JSN'){
|
||||
$('#btn_pretty_body').css('display', 'block');
|
||||
@@ -805,35 +789,6 @@ $(document).ready(function() {
|
||||
$("html, hbody").css("overflow-x","scroll");
|
||||
}
|
||||
|
||||
function copyTextToClipboard(text, successMessage){
|
||||
if (typeof(navigator.clipboard) == 'undefined'){
|
||||
var textArea = document.createElement("textarea");
|
||||
textArea.value = text;
|
||||
textArea.style.position = "fixed";
|
||||
document.body.appendChild(textArea);
|
||||
textArea.focus();
|
||||
textArea.select();
|
||||
var successful = false;
|
||||
try{
|
||||
successful = document.execCommand('copy');
|
||||
}catch(err){
|
||||
|
||||
}
|
||||
document.body.removeChild(textArea);
|
||||
if (successful){
|
||||
alert(successMessage);
|
||||
}else{
|
||||
prompt("Ctrl+C 를 눌러 클립보드로 복사하세요.", text);
|
||||
}
|
||||
}else{
|
||||
navigator.clipboard.writeText(text).then(function(){
|
||||
alert(successMessage);
|
||||
}).catch(function(){
|
||||
prompt("클립보드 복사에 실패했습니다. Ctrl+C 를 눌러 복사하세요.", text);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
$('#headerToClipboard').click(function(){
|
||||
if($('#grid1').get(0) == null || $('#grid1').get(0).jexcel == null){
|
||||
return;
|
||||
@@ -845,11 +800,13 @@ $(document).ready(function() {
|
||||
paddedValues += row[5].padEnd(row[4], " ");
|
||||
}
|
||||
|
||||
copyTextToClipboard(paddedValues, '헤더값이 복사되었습니다.');
|
||||
navigator.clipboard.writeText(paddedValues);//클립보드로 복사
|
||||
alert('헤더값이 복사되었습니다.')
|
||||
});
|
||||
|
||||
$('#messageToClipboard').click(function() {
|
||||
copyTextToClipboard(fullMessageData, '전체 전문 복사되었습니다.');
|
||||
navigator.clipboard.writeText(fullMessageData);//클립보드로 복사
|
||||
alert('전체 전문 복사되었습니다.')
|
||||
});
|
||||
|
||||
$('#btn_pretty_print').click(function () {
|
||||
@@ -980,23 +937,15 @@ html,hbody {
|
||||
<input type=hidden name=realHeader>
|
||||
<div class="table_row_title"><%=localeMessage.getString("trackingDetail.common")%></div>
|
||||
<table class="table_row" cellspacing="0">
|
||||
<colgroup>
|
||||
<col style="width:180px"/>
|
||||
<col style="width:300px" />
|
||||
<col style="width:180px"/>
|
||||
<col style="width:300px" />
|
||||
</colgroup>
|
||||
<tr>
|
||||
<th><%=localeMessage.getString("trackingDetail.eaiSvcSerno")%></th>
|
||||
<td><input type="text" name="eaiSvcSerno" readonly="readonly"/> </td>
|
||||
<th><%=localeMessage.getString("trackingDetail.rspnsErrcdName")%></th>
|
||||
<td><input type="text" name="rspnsErrcdName" readonly="readonly"/> </td>
|
||||
<td colspan="3"><input type="text" name="eaiSvcSerno" readonly="readonly"/> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><%=localeMessage.getString("trackingDetail.eaiBzwkDstcd")%></th>
|
||||
<td><input type="text" name="eaiBzwkDstcd" readonly="readonly"/> </td>
|
||||
<th><%=localeMessage.getString("trackingDetail.msgDpstYMS")%></th>
|
||||
<td><input type="text" name="msgDpstYMS" readonly="readonly"/> </td>
|
||||
<th style="width:20%;"><%=localeMessage.getString("trackingDetail.eaiBzwkDstcd")%></th>
|
||||
<td style="width:30%;"><input type="text" name="eaiBzwkDstcd" readonly="readonly"/> </td>
|
||||
<th style="width:20%;"><%=localeMessage.getString("trackingDetail.msgDpstYMS")%></th>
|
||||
<td style="width:30%;"><input type="text" name="msgDpstYMS" readonly="readonly"/> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><%= localeMessage.getString("eaiMessage.eaiSvcName")%></th><td><input type="text" name="eaiSvcName" readonly="readonly" />
|
||||
@@ -1030,17 +979,11 @@ html,hbody {
|
||||
</table>
|
||||
<div class="table_row_title">API 정보</div>
|
||||
<table class="table_row" cellspacing="0">
|
||||
<colgroup>
|
||||
<col style="width:180px"/>
|
||||
<col style="width:300px" />
|
||||
<col style="width:180px"/>
|
||||
<col style="width:300px" />
|
||||
</colgroup>
|
||||
<tr>
|
||||
<th>APP(Client) ID</th>
|
||||
<td><input type="text" name="clientId" readonly="readonly"/> </td>
|
||||
<th>APP 명</th>
|
||||
<td><input type="text" name="clientName" readonly="readonly"/> </td>
|
||||
<th style="width:20%;">APP(Client) ID</th>
|
||||
<td style="width:30%;"><input type="text" name="clientId" readonly="readonly"/> </td>
|
||||
<th style="width:20%;">APP 명</th>
|
||||
<td style="width:30%;"><input type="text" name="clientName" readonly="readonly"/> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>법인 ID</th><td ><input type="text" name="orgId" readonly="readonly"/> </td>
|
||||
@@ -1162,27 +1105,20 @@ html,hbody {
|
||||
</div><!-- end.container-1 -->
|
||||
<div class="table_row_title"><%=localeMessage.getString("trackingDetail.inbound")%></div>
|
||||
<table class="table_row" cellspacing="0" >
|
||||
<colgroup>
|
||||
<col style="width:180px"/>
|
||||
<col style="width:300px" />
|
||||
<col style="width:180px"/>
|
||||
<col style="width:300px" />
|
||||
</colgroup>
|
||||
<tr>
|
||||
<%-- <th style="width:20%;"><%=localeMessage.getString("trackingDetail.svcMotivUseDstcd")%></th>
|
||||
<td style="width:30%;"><input type="text" name="svcMotivUseDstcd" readonly="readonly"/> </td> --%>
|
||||
<th style="width:20%;"><%=localeMessage.getString("trackingDetail.svcMotivUseDstcd")%></th>
|
||||
<td style="width:30%;"><input type="text" name="svcMotivUseDstcd" readonly="readonly"/> </td>
|
||||
<th style="width:20%;"><%=localeMessage.getString("trackingDetail.gstatSysAdptrBzwkGroupName")%></th>
|
||||
<td style="width:30%;"><input type="text" name="gstatSysAdptrBzwkGroupName" readonly="readonly" style="width: calc(100% - 60px);"/>
|
||||
<td style="width:30%;"><input type="text" name="gstatSysAdptrBzwkGroupName" readonly="readonly"/>
|
||||
<%-- <img src="<c:url value="/images/bt/pop_detail.gif"/>" alt="" id="gstatSysAdptrBzwkGroupName" class="adapter_btn" level="W"/> --%>
|
||||
<button type="button" class="cssbtn smallBtn2" id="gstatSysAdptrBzwkGroupName" style="vertical-align:middle; font-weight:bold;" level="W"><i class="material-icons">details</i><%= localeMessage.getString("button.detail") %></button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><%=localeMessage.getString("trackingDetail.flowCtrlRoutName")%></th><td><input type="text" name="flowCtrlRoutName" readonly="readonly"/> </td>
|
||||
<th><%=localeMessage.getString("trackingDetail.stndMsgUseYn")%></th><td><input type="text" name="stndMsgUseYn" readonly="readonly"/> </td>
|
||||
</tr>
|
||||
<%-- <tr>
|
||||
<th><%=localeMessage.getString("trackingDetail.flowCtrlRoutName")%></th><td><input type="text" name="flowCtrlRoutName" readonly="readonly"/> </td>
|
||||
|
||||
</tr> --%>
|
||||
<%-- <tr>
|
||||
<tr>
|
||||
<th><%=localeMessage.getString("trackingDetail.svcPrcssDsticName")%></th><td><input type="text" name="svcPrcssDsticName" readonly="readonly"/> </td>
|
||||
<th><%=localeMessage.getString("trackingDetail.svcBfClmnLogYn")%></th><td><input type="text" name="svcBfClmnLogYn" readonly="readonly"/> </td>
|
||||
</tr>
|
||||
@@ -1193,52 +1129,39 @@ html,hbody {
|
||||
<tr>
|
||||
<th><%=localeMessage.getString("trackingDetail.gstatSvcDsticName")%></th><td><input type="text" name="gstatSvcDsticName" readonly="readonly"/> </td>
|
||||
<th><%=localeMessage.getString("trackingDetail.prsntMsgIdName")%></th><td><input type="text" name="prsntMsgIdName" readonly="readonly"/> </td>
|
||||
</tr> --%>
|
||||
</tr>
|
||||
</table>
|
||||
<div class="table_row_title"><%=localeMessage.getString("trackingDetail.outbound")%></div>
|
||||
<table class="table_row" cellspacing="0" >
|
||||
<colgroup>
|
||||
<col style="width:180px"/>
|
||||
<col style="width:300px" />
|
||||
<col style="width:180px"/>
|
||||
<col style="width:300px" />
|
||||
</colgroup>
|
||||
<tr>
|
||||
<%-- <th style="width:20%;"><%=localeMessage.getString("trackingDetail.psvIntfacDsticName")%></th>--%>
|
||||
<%-- <td style="width:30%;"><input type="text" name="psvIntfacDsticName" readonly="readonly"/> </td> --%>
|
||||
<th style="width:20%;"><%=localeMessage.getString("trackingDetail.psvIntfacDsticName")%></th>
|
||||
<td style="width:30%;"><input type="text" name="psvIntfacDsticName" readonly="readonly"/> </td>
|
||||
<th style="width:20%;"><%=localeMessage.getString("trackingDetail.psvSysAdptrBzwkGroupName")%></th>
|
||||
<td style="width:30%;"><input type="text" name="psvSysAdptrBzwkGroupName" readonly="readonly" style="width: calc(100% - 60px);"/>
|
||||
<td style="width:30%;"><input type="text" name="psvSysAdptrBzwkGroupName" readonly="readonly"/>
|
||||
<%-- <img src="<c:url value="/images/bt/pop_detail.gif"/>" alt="" id="psvSysAdptrBzwkGroupName" class="adapter_btn" level="W"/> --%>
|
||||
<button type="button" class="cssbtn smallBtn2" id="psvSysAdptrBzwkGroupName" style="vertical-align:middle; font-weight:bold;" level="W"><i class="material-icons">details</i><%= localeMessage.getString("button.detail") %></button>
|
||||
</td>
|
||||
<th><%=localeMessage.getString("trackingDetail.psvBzwkSysName")%></th><td><input type="text" name="psvBzwkSysName" readonly="readonly"/> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><%=localeMessage.getString("trackingDetail.outbndRoutName")%></th><td><input type="text" name="outbndRoutName" readonly="readonly"/> </td>
|
||||
<th><%=localeMessage.getString("trackingDetail.toutVal")%></th><td><input type="text" name="toutVal" readonly="readonly"/> </td>
|
||||
</tr>
|
||||
<%-- <tr>
|
||||
<th><%=localeMessage.getString("trackingDetail.psvSysSvcDsticName")%></th><td><input type="text" name="psvSysSvcDsticName" readonly="readonly"/> </td>
|
||||
|
||||
</tr>--%>
|
||||
<%-- <tr>
|
||||
<tr>
|
||||
<th><%=localeMessage.getString("trackingDetail.psvSysSvcDsticName")%></th><td><input type="text" name="psvSysSvcDsticName" readonly="readonly"/> </td>
|
||||
<th><%=localeMessage.getString("trackingDetail.psvBzwkSysName")%></th><td><input type="text" name="psvBzwkSysName" readonly="readonly"/> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><%=localeMessage.getString("trackingDetail.rspnsErrFldName")%></th><td><input type="text" name="rspnsErrFldName" readonly="readonly"/> </td>
|
||||
<th><%=localeMessage.getString("trackingDetail.psvSysIdName")%></th><td><input type="text" name="psvSysIdName" readonly="readonly"/> </td>
|
||||
</tr> --%>
|
||||
</tr>
|
||||
</table>
|
||||
<div class="table_row_title"><%=localeMessage.getString("trackingDetail.MAPPING")%></div>
|
||||
<table class="table_row" cellspacing="0" >
|
||||
<colgroup>
|
||||
<col style="width:180px"/>
|
||||
<col style="width:300px" />
|
||||
<col style="width:180px"/>
|
||||
<col style="width:300px" />
|
||||
</colgroup>
|
||||
<tr>
|
||||
<th><%=localeMessage.getString("trackingDetail.chngYn")%></th>
|
||||
<td><input type="text" name="chngYn" readonly="readonly"/> </td>
|
||||
<th><%=localeMessage.getString("trackingDetail.chngMsgIdName")%></th>
|
||||
<td><input type="text" name="chngMsgIdName" readonly="readonly" style="width: calc(100% - 60px);"/>
|
||||
<th style="width:20%;"><%=localeMessage.getString("trackingDetail.chngYn")%></th>
|
||||
<td style="width:30%;"><input type="text" name="chngYn" readonly="readonly"/> </td>
|
||||
<th style="width:20%;"><%=localeMessage.getString("trackingDetail.chngMsgIdName")%></th>
|
||||
<td style="width:30%;"><input type="text" name="chngMsgIdName" readonly="readonly"/>
|
||||
<!-- img src="<c:url value="/images/bt/pop_detail.gif"/>" alt="" id="chngMsgIdName" class="layout_btn" /-->
|
||||
<button type="button" class="cssbtn smallBtn2" id="chngMsgIdName" style="vertical-align:middle; font-weight:bold;" level="W"><i class="material-icons">details</i><%= localeMessage.getString("button.detail") %></button>
|
||||
</td>
|
||||
@@ -1247,7 +1170,7 @@ html,hbody {
|
||||
<th><%=localeMessage.getString("trackingDetail.bascRspnsChngYn")%></th><td><input type="text" name="bascRspnsChngYn" readonly="readonly"/> </td>
|
||||
<th><%=localeMessage.getString("trackingDetail.bascRspnsChngMsgIdName")%></th>
|
||||
<td>
|
||||
<input type="text" name="bascRspnsChngMsgIdName" readonly="readonly" style="width: calc(100% - 60px);"/>
|
||||
<input type="text" name="bascRspnsChngMsgIdName" readonly="readonly"/>
|
||||
<!-- img src="<c:url value="/images/bt/pop_detail.gif"/>" alt="" id="bascRspnsChngMsgIdName" class="layout_btn" /-->
|
||||
<button type="button" class="cssbtn smallBtn2" id="bascRspnsChngMsgIdName" style="vertical-align:middle; font-weight:bold;" level="W"><i class="material-icons">details</i><%= localeMessage.getString("button.detail") %></button>
|
||||
</td>
|
||||
@@ -1257,10 +1180,10 @@ html,hbody {
|
||||
<th><%=localeMessage.getString("trackingDetail.errRspnsChngYn")%></th><td><input type="text" name="errRspnsChngYn" readonly="readonly"/> </td>
|
||||
<th><%=localeMessage.getString("trackingDetail.errRspnsChngMsgIdName")%></th>
|
||||
<td>
|
||||
<input type="text" name="errRspnsChngMsgIdName" readonly="readonly" style="width: calc(100% - 60px);"/>
|
||||
<input type="text" name="errRspnsChngMsgIdName" readonly="readonly"/>
|
||||
<img src="<c:url value="/images/bt/pop_detail.gif"/>" alt="" id="errRspnsChngMsgIdName" class="layout_btn" /> </td>
|
||||
</tr>
|
||||
<%-- <tr>
|
||||
<tr>
|
||||
<th><%=localeMessage.getString("trackingDetail.inptMsgIDName")%></th>
|
||||
<td>
|
||||
<input type="text" name="inptMsgIDName" readonly="readonly" />
|
||||
@@ -1270,9 +1193,9 @@ html,hbody {
|
||||
<tr>
|
||||
<th><%=localeMessage.getString("trackingDetail.bascRspnsMsgCmprCtnt")%></th><td><input type="text" name="bascRspnsMsgCmprCtnt" readonly="readonly"/> </td>
|
||||
<th><%=localeMessage.getString("trackingDetail.flovrYn")%></th><td><input type="text" name="flovrYn" readonly="readonly"/> </td>
|
||||
</tr> --%>
|
||||
</tr>
|
||||
</table>
|
||||
<%-- <div class="table_row_title"><%=localeMessage.getString("trackingDetail.etc")%></div>
|
||||
<div class="table_row_title"><%=localeMessage.getString("trackingDetail.etc")%></div>
|
||||
<table class="table_row" cellspacing="0" >
|
||||
<tr>
|
||||
<th style="width:20%;"><%=localeMessage.getString("trackingDetail.rspnsErrcdName")%></th>
|
||||
@@ -1304,7 +1227,7 @@ html,hbody {
|
||||
<th><%=localeMessage.getString("trackingDetail.trackAsisKey3Ctnt")%></th><td><input type="text" name="trackAsisKey3Ctnt" readonly="readonly"/> </td>
|
||||
<th><%=localeMessage.getString("trackingDetail.trackAsisKey4Ctnt")%></th><td><input type="text" name="trackAsisKey4Ctnt" readonly="readonly"/> </td>
|
||||
</tr>
|
||||
</table> --%>
|
||||
</table>
|
||||
</form>
|
||||
</div><!-- end.popup_box -->
|
||||
<div id="bizLogObjectDialog" style="white-space: pre-wrap; overflow-y:auto" title="Biz Log Object Detail">
|
||||
|
||||
@@ -1682,7 +1682,7 @@ $(document).ready(function () {
|
||||
var args = new Object();
|
||||
var url = '<c:url value="/onl/admin/rule/layoutMan.view" />';
|
||||
url += "?cmd=DETAILPOPUP";
|
||||
url += '&searchLoutName='+layoutName;
|
||||
url += '&loutName='+layoutName;
|
||||
url += '&pop=true';
|
||||
|
||||
showModal2(url,args,890,940,'','scroll:on');
|
||||
|
||||
@@ -1,343 +0,0 @@
|
||||
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %><%@
|
||||
page import="org.quartz.*,
|
||||
org.quartz.impl.matchers.GroupMatcher,
|
||||
org.springframework.web.context.WebApplicationContext,
|
||||
org.springframework.web.context.support.WebApplicationContextUtils,
|
||||
java.util.*,
|
||||
java.text.SimpleDateFormat"
|
||||
%><%!
|
||||
private String fmt(Date date) {
|
||||
if (date == null) return "-";
|
||||
return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(date);
|
||||
}
|
||||
|
||||
private String relative(Date date) {
|
||||
if (date == null) return "";
|
||||
long diff = date.getTime() - System.currentTimeMillis();
|
||||
String prefix = diff >= 0 ? "+" : "-";
|
||||
long abs = Math.abs(diff) / 1000;
|
||||
if (abs < 60) return prefix + abs + "s";
|
||||
if (abs < 3600) return prefix + (abs / 60) + "m " + (abs % 60) + "s";
|
||||
return prefix + (abs / 3600) + "h " + ((abs % 3600) / 60) + "m";
|
||||
}
|
||||
|
||||
private String stateClass(Trigger.TriggerState state) {
|
||||
if (state == Trigger.TriggerState.NORMAL) return "state-normal";
|
||||
if (state == Trigger.TriggerState.PAUSED) return "state-paused";
|
||||
if (state == Trigger.TriggerState.BLOCKED) return "state-blocked";
|
||||
if (state == Trigger.TriggerState.ERROR) return "state-error";
|
||||
return "";
|
||||
}
|
||||
|
||||
private Map<String, Object> buildTriggerInfo(Scheduler scheduler, Trigger trigger) throws Exception {
|
||||
Map<String, Object> info = new LinkedHashMap<>();
|
||||
Trigger.TriggerState state = scheduler.getTriggerState(trigger.getKey());
|
||||
info.put("triggerName", trigger.getKey().getName());
|
||||
info.put("triggerGroup", trigger.getKey().getGroup());
|
||||
info.put("state", state.name());
|
||||
info.put("stateClass", stateClass(state));
|
||||
info.put("prevFire", fmt(trigger.getPreviousFireTime()));
|
||||
info.put("prevFireRel", relative(trigger.getPreviousFireTime()));
|
||||
info.put("nextFire", fmt(trigger.getNextFireTime()));
|
||||
info.put("nextFireRel", relative(trigger.getNextFireTime()));
|
||||
info.put("priority", trigger.getPriority());
|
||||
if (trigger instanceof CronTrigger) {
|
||||
info.put("type", "Cron");
|
||||
info.put("cronExp", ((CronTrigger) trigger).getCronExpression());
|
||||
} else if (trigger instanceof SimpleTrigger) {
|
||||
SimpleTrigger st = (SimpleTrigger) trigger;
|
||||
info.put("type", "Simple");
|
||||
info.put("cronExp", "interval=" + st.getRepeatInterval() + "ms, repeat=" +
|
||||
(st.getRepeatCount() == SimpleTrigger.REPEAT_INDEFINITELY ? "INF" : st.getRepeatCount()));
|
||||
} else {
|
||||
info.put("type", trigger.getClass().getSimpleName());
|
||||
info.put("cronExp", "-");
|
||||
}
|
||||
if (trigger.getStartTime() != null) info.put("startTime", fmt(trigger.getStartTime()));
|
||||
if (trigger.getEndTime() != null) info.put("endTime", fmt(trigger.getEndTime()));
|
||||
return info;
|
||||
}
|
||||
|
||||
private List<Map<String, Object>> getJobList(Scheduler scheduler) throws Exception {
|
||||
List<Map<String, Object>> jobs = new ArrayList<>();
|
||||
for (String group : scheduler.getJobGroupNames()) {
|
||||
for (JobKey jobKey : scheduler.getJobKeys(GroupMatcher.jobGroupEquals(group))) {
|
||||
JobDetail detail = scheduler.getJobDetail(jobKey);
|
||||
List<? extends Trigger> triggers = scheduler.getTriggersOfJob(jobKey);
|
||||
|
||||
Map<String, Object> job = new LinkedHashMap<>();
|
||||
job.put("name", jobKey.getName());
|
||||
job.put("group", jobKey.getGroup());
|
||||
job.put("jobClass", detail.getJobClass().getSimpleName());
|
||||
job.put("disallowConcurrent", detail.isConcurrentExectionDisallowed());
|
||||
|
||||
StringBuilder dataStr = new StringBuilder();
|
||||
for (String key : detail.getJobDataMap().getKeys()) {
|
||||
if (dataStr.length() > 0) dataStr.append(", ");
|
||||
dataStr.append(key).append("=").append(detail.getJobDataMap().getString(key));
|
||||
}
|
||||
job.put("jobData", dataStr.toString());
|
||||
job.put("triggerCount", triggers.size());
|
||||
|
||||
List<Map<String, Object>> triggerList = new ArrayList<>();
|
||||
for (Trigger trigger : triggers) {
|
||||
triggerList.add(buildTriggerInfo(scheduler, trigger));
|
||||
}
|
||||
job.put("triggerList", triggerList);
|
||||
|
||||
if (!triggers.isEmpty()) {
|
||||
Trigger trigger = triggers.get(0);
|
||||
Trigger.TriggerState state = scheduler.getTriggerState(trigger.getKey());
|
||||
job.put("state", state.name());
|
||||
job.put("stateClass", stateClass(state));
|
||||
job.put("prevFire", fmt(trigger.getPreviousFireTime()));
|
||||
job.put("prevFireRel", relative(trigger.getPreviousFireTime()));
|
||||
job.put("nextFire", fmt(trigger.getNextFireTime()));
|
||||
job.put("nextFireRel", relative(trigger.getNextFireTime()));
|
||||
if (trigger instanceof CronTrigger) {
|
||||
job.put("cronExp", ((CronTrigger) trigger).getCronExpression());
|
||||
} else {
|
||||
job.put("cronExp", "-");
|
||||
}
|
||||
} else {
|
||||
job.put("state", "NO_TRIGGER");
|
||||
job.put("stateClass", "state-error");
|
||||
job.put("prevFire", "-");
|
||||
job.put("prevFireRel", "");
|
||||
job.put("nextFire", "-");
|
||||
job.put("nextFireRel", "");
|
||||
job.put("cronExp", "-");
|
||||
}
|
||||
jobs.add(job);
|
||||
}
|
||||
}
|
||||
jobs.sort(Comparator.comparing(m -> (String) m.get("name")));
|
||||
return jobs;
|
||||
}
|
||||
%><%
|
||||
WebApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(application);
|
||||
|
||||
Scheduler scheduler = null;
|
||||
Scheduler clusteredScheduler = null;
|
||||
String error = null;
|
||||
|
||||
try {
|
||||
scheduler = (Scheduler) ctx.getBean("scheduler");
|
||||
} catch (Exception e) {
|
||||
error = "scheduler bean not found: " + e.getMessage();
|
||||
}
|
||||
try {
|
||||
clusteredScheduler = (Scheduler) ctx.getBean("clusteredScheduler");
|
||||
} catch (Exception e) {
|
||||
// clusteredScheduler is optional
|
||||
}
|
||||
|
||||
// 자동 새로고침 간격 (초), 기본 10초
|
||||
String refreshParam = request.getParameter("refresh");
|
||||
int refreshSec = 10;
|
||||
if (refreshParam != null) {
|
||||
try { refreshSec = Integer.parseInt(refreshParam); } catch (NumberFormatException e) { /* ignore */ }
|
||||
if (refreshSec < 1) refreshSec = 1;
|
||||
}
|
||||
boolean autoRefresh = refreshParam != null;
|
||||
%>
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<% if (autoRefresh) { %>
|
||||
<meta http-equiv="refresh" content="<%= refreshSec %>;url=quartzStatus.jsp?refresh=<%= refreshSec %>">
|
||||
<% } %>
|
||||
<title>Quartz Scheduler Status</title>
|
||||
<style>
|
||||
body { font-family: 'Segoe UI', Arial, sans-serif; margin: 20px; background: #f5f5f5; color: #333; }
|
||||
h1 { font-size: 20px; margin-bottom: 5px; }
|
||||
h2 { font-size: 16px; color: #555; margin: 20px 0 8px 0; border-bottom: 2px solid #ddd; padding-bottom: 4px; }
|
||||
.info { font-size: 13px; color: #666; margin-bottom: 15px; }
|
||||
table { width: 100%%; border-collapse: collapse; font-size: 13px; background: #fff; box-shadow: 0 1px 3px rgba(0,0,0,0.1); margin-bottom: 20px; }
|
||||
th { background: #4a5568; color: #fff; padding: 8px 10px; text-align: left; font-weight: 500; }
|
||||
td { padding: 7px 10px; border-bottom: 1px solid #eee; }
|
||||
tr:hover td { background: #f7fafc; }
|
||||
.state-normal { color: #38a169; font-weight: bold; }
|
||||
.state-paused { color: #d69e2e; font-weight: bold; }
|
||||
.state-blocked { color: #e53e3e; font-weight: bold; }
|
||||
.state-error { color: #e53e3e; font-weight: bold; }
|
||||
.mono { font-family: 'Consolas', 'Monaco', monospace; font-size: 12px; }
|
||||
.rel { color: #718096; font-size: 11px; margin-left: 4px; }
|
||||
.badge { display: inline-block; padding: 2px 6px; border-radius: 3px; font-size: 11px; }
|
||||
.badge-yes { background: #c6f6d5; color: #276749; }
|
||||
.badge-no { background: #fed7d7; color: #9b2c2c; }
|
||||
.scheduler-meta { display: flex; gap: 20px; flex-wrap: wrap; margin-bottom: 10px; }
|
||||
.scheduler-meta span { font-size: 13px; }
|
||||
.scheduler-meta strong { color: #4a5568; }
|
||||
.error { color: #e53e3e; padding: 10px; background: #fff5f5; border: 1px solid #fc8181; border-radius: 4px; }
|
||||
.toolbar { font-size: 12px; color: #718096; }
|
||||
.toolbar a { color: #4299e1; text-decoration: none; margin: 0 4px; }
|
||||
.toolbar a:hover { text-decoration: underline; }
|
||||
.toolbar .active { font-weight: bold; color: #2b6cb0; background: #ebf4ff; padding: 2px 6px; border-radius: 3px; }
|
||||
.auto-badge { display: inline-block; padding: 1px 6px; border-radius: 3px; font-size: 11px; background: #bee3f8; color: #2a4365; margin-left: 6px; }
|
||||
.trigger-toggle { cursor: pointer; user-select: none; }
|
||||
.trigger-toggle:hover td { background: #edf2f7; }
|
||||
.trigger-row { display: none; }
|
||||
.trigger-row.open { display: table-row; }
|
||||
.trigger-row td { background: #f7fafc; padding: 5px 10px 5px 30px; border-bottom: 1px solid #e2e8f0; font-size: 12px; }
|
||||
.trigger-detail-table { width: 100%; border-collapse: collapse; font-size: 12px; margin: 0; }
|
||||
.trigger-detail-table th { background: #e2e8f0; color: #4a5568; padding: 5px 8px; font-weight: 500; font-size: 11px; }
|
||||
.trigger-detail-table td { padding: 4px 8px; border-bottom: 1px solid #e2e8f0; }
|
||||
.trigger-detail-table tr:last-child td { border-bottom: none; }
|
||||
.toggle-icon { display: inline-block; width: 16px; font-size: 11px; color: #718096; transition: transform 0.15s; }
|
||||
.toggle-icon.open { transform: rotate(90deg); }
|
||||
.trigger-count { font-size: 11px; color: #718096; font-weight: normal; margin-left: 4px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Quartz Scheduler Status
|
||||
<% if (autoRefresh) { %><span class="auto-badge">Auto <%= refreshSec %>s</span><% } %>
|
||||
</h1>
|
||||
<p class="info">
|
||||
Instance: <strong><%= System.getProperty("inst.Name", "N/A") %></strong>
|
||||
| Server Time: <strong><%= new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) %></strong>
|
||||
</p>
|
||||
<p class="toolbar">
|
||||
Auto Refresh:
|
||||
<% if (!autoRefresh) { %><span class="active">OFF</span><% } else { %><a href="quartzStatus.jsp">OFF</a><% } %>
|
||||
|
|
||||
<% int[] intervals = {5, 10, 30, 60}; %>
|
||||
<% for (int iv : intervals) { %>
|
||||
<% if (autoRefresh && refreshSec == iv) { %><span class="active"><%= iv %>s</span><% } else { %><a href="quartzStatus.jsp?refresh=<%= iv %>"><%= iv %>s</a><% } %>
|
||||
<% } %>
|
||||
</p>
|
||||
|
||||
<% if (error != null) { %>
|
||||
<div class="error"><%= error %></div>
|
||||
<% } %>
|
||||
|
||||
<% if (scheduler != null) { %>
|
||||
<h2>Scheduler: <%= scheduler.getSchedulerName() %> (RAM)</h2>
|
||||
<div class="scheduler-meta">
|
||||
<span><strong>Status:</strong> <span class="<%= scheduler.isStarted() ? "state-normal" : "state-error" %>"><%= scheduler.isStarted() ? "STARTED" : "STOPPED" %></span></span>
|
||||
<span><strong>Instance ID:</strong> <%= scheduler.getSchedulerInstanceId() %></span>
|
||||
<span><strong>Job Count:</strong> <%= getJobList(scheduler).size() %></span>
|
||||
</div>
|
||||
<table>
|
||||
<tr>
|
||||
<th>Job Name</th>
|
||||
<th>Job Class</th>
|
||||
<th>Cron</th>
|
||||
<th>State</th>
|
||||
<th>Concurrent</th>
|
||||
<th>Prev Fire</th>
|
||||
<th>Next Fire</th>
|
||||
<th>Job Data</th>
|
||||
</tr>
|
||||
<% int jobIdx = 0; for (Map<String, Object> job : getJobList(scheduler)) { String rowId = "s_" + jobIdx; List<Map<String, Object>> tList = (List<Map<String, Object>>) job.get("triggerList"); %>
|
||||
<tr class="trigger-toggle" onclick="toggleTriggers('<%= rowId %>')">
|
||||
<td><span class="toggle-icon" id="icon_<%= rowId %>">▶</span><strong><%= job.get("name") %></strong><span class="trigger-count">(<%= job.get("triggerCount") %> triggers)</span></td>
|
||||
<td class="mono"><%= job.get("jobClass") %></td>
|
||||
<td class="mono"><%= job.get("cronExp") %></td>
|
||||
<td class="<%= job.get("stateClass") %>"><%= job.get("state") %></td>
|
||||
<td><span class="badge <%= (Boolean)job.get("disallowConcurrent") ? "badge-yes" : "badge-no" %>">
|
||||
<%= (Boolean)job.get("disallowConcurrent") ? "NO (protected)" : "YES (allowed)" %></span></td>
|
||||
<td class="mono"><%= job.get("prevFire") %><span class="rel"><%= job.get("prevFireRel") %></span></td>
|
||||
<td class="mono"><%= job.get("nextFire") %><span class="rel"><%= job.get("nextFireRel") %></span></td>
|
||||
<td class="mono"><%= job.get("jobData") %></td>
|
||||
</tr>
|
||||
<tr class="trigger-row" id="triggers_<%= rowId %>">
|
||||
<td colspan="8" style="padding: 6px 10px 6px 30px;">
|
||||
<% if (tList.isEmpty()) { %>
|
||||
<em style="color:#a0aec0;">No triggers</em>
|
||||
<% } else { %>
|
||||
<table class="trigger-detail-table">
|
||||
<tr><th>Trigger Name</th><th>Group</th><th>Type</th><th>Expression / Interval</th><th>State</th><th>Priority</th><th>Prev Fire</th><th>Next Fire</th></tr>
|
||||
<% for (Map<String, Object> t : tList) { %>
|
||||
<tr>
|
||||
<td><%= t.get("triggerName") %></td>
|
||||
<td><%= t.get("triggerGroup") %></td>
|
||||
<td><%= t.get("type") %></td>
|
||||
<td class="mono"><%= t.get("cronExp") %></td>
|
||||
<td class="<%= t.get("stateClass") %>"><%= t.get("state") %></td>
|
||||
<td><%= t.get("priority") %></td>
|
||||
<td class="mono"><%= t.get("prevFire") %><span class="rel"><%= t.get("prevFireRel") %></span></td>
|
||||
<td class="mono"><%= t.get("nextFire") %><span class="rel"><%= t.get("nextFireRel") %></span></td>
|
||||
</tr>
|
||||
<% } %>
|
||||
</table>
|
||||
<% } %>
|
||||
</td>
|
||||
</tr>
|
||||
<% jobIdx++; } %>
|
||||
</table>
|
||||
<% } %>
|
||||
|
||||
<% if (clusteredScheduler != null) { %>
|
||||
<h2>Clustered Scheduler: <%= clusteredScheduler.getSchedulerName() %> (DB)</h2>
|
||||
<div class="scheduler-meta">
|
||||
<span><strong>Status:</strong> <span class="<%= clusteredScheduler.isStarted() ? "state-normal" : "state-error" %>"><%= clusteredScheduler.isStarted() ? "STARTED" : "STOPPED" %></span></span>
|
||||
<span><strong>Instance ID:</strong> <%= clusteredScheduler.getSchedulerInstanceId() %></span>
|
||||
<span><strong>Job Count:</strong> <%= getJobList(clusteredScheduler).size() %></span>
|
||||
</div>
|
||||
<table>
|
||||
<tr>
|
||||
<th>Job Name</th>
|
||||
<th>Job Class</th>
|
||||
<th>Cron</th>
|
||||
<th>State</th>
|
||||
<th>Concurrent</th>
|
||||
<th>Prev Fire</th>
|
||||
<th>Next Fire</th>
|
||||
<th>Job Data</th>
|
||||
</tr>
|
||||
<% int cJobIdx = 0; for (Map<String, Object> job : getJobList(clusteredScheduler)) { String rowId = "c_" + cJobIdx; List<Map<String, Object>> tList = (List<Map<String, Object>>) job.get("triggerList"); %>
|
||||
<tr class="trigger-toggle" onclick="toggleTriggers('<%= rowId %>')">
|
||||
<td><span class="toggle-icon" id="icon_<%= rowId %>">▶</span><strong><%= job.get("name") %></strong><span class="trigger-count">(<%= job.get("triggerCount") %> triggers)</span></td>
|
||||
<td class="mono"><%= job.get("jobClass") %></td>
|
||||
<td class="mono"><%= job.get("cronExp") %></td>
|
||||
<td class="<%= job.get("stateClass") %>"><%= job.get("state") %></td>
|
||||
<td><span class="badge <%= (Boolean)job.get("disallowConcurrent") ? "badge-yes" : "badge-no" %>">
|
||||
<%= (Boolean)job.get("disallowConcurrent") ? "NO (protected)" : "YES (allowed)" %></span></td>
|
||||
<td class="mono"><%= job.get("prevFire") %><span class="rel"><%= job.get("prevFireRel") %></span></td>
|
||||
<td class="mono"><%= job.get("nextFire") %><span class="rel"><%= job.get("nextFireRel") %></span></td>
|
||||
<td class="mono"><%= job.get("jobData") %></td>
|
||||
</tr>
|
||||
<tr class="trigger-row" id="triggers_<%= rowId %>">
|
||||
<td colspan="8" style="padding: 6px 10px 6px 30px;">
|
||||
<% if (tList.isEmpty()) { %>
|
||||
<em style="color:#a0aec0;">No triggers</em>
|
||||
<% } else { %>
|
||||
<table class="trigger-detail-table">
|
||||
<tr><th>Trigger Name</th><th>Group</th><th>Type</th><th>Expression / Interval</th><th>State</th><th>Priority</th><th>Prev Fire</th><th>Next Fire</th></tr>
|
||||
<% for (Map<String, Object> t : tList) { %>
|
||||
<tr>
|
||||
<td><%= t.get("triggerName") %></td>
|
||||
<td><%= t.get("triggerGroup") %></td>
|
||||
<td><%= t.get("type") %></td>
|
||||
<td class="mono"><%= t.get("cronExp") %></td>
|
||||
<td class="<%= t.get("stateClass") %>"><%= t.get("state") %></td>
|
||||
<td><%= t.get("priority") %></td>
|
||||
<td class="mono"><%= t.get("prevFire") %><span class="rel"><%= t.get("prevFireRel") %></span></td>
|
||||
<td class="mono"><%= t.get("nextFire") %><span class="rel"><%= t.get("nextFireRel") %></span></td>
|
||||
</tr>
|
||||
<% } %>
|
||||
</table>
|
||||
<% } %>
|
||||
</td>
|
||||
</tr>
|
||||
<% cJobIdx++; } %>
|
||||
</table>
|
||||
<% } %>
|
||||
|
||||
<script>
|
||||
function toggleTriggers(rowId) {
|
||||
var row = document.getElementById('triggers_' + rowId);
|
||||
var icon = document.getElementById('icon_' + rowId);
|
||||
if (row.classList.contains('open')) {
|
||||
row.classList.remove('open');
|
||||
icon.classList.remove('open');
|
||||
} else {
|
||||
row.classList.add('open');
|
||||
icon.classList.add('open');
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
+87
-105
@@ -1,10 +1,9 @@
|
||||
plugins {
|
||||
id 'java-library'
|
||||
id 'java-library'
|
||||
id 'eclipse'
|
||||
id 'eclipse-wtp'
|
||||
id 'eclipse-wtp'
|
||||
id 'idea'
|
||||
id 'war'
|
||||
id 'org.cyclonedx.bom' version '3.2.4'
|
||||
}
|
||||
|
||||
group 'com.eactive'
|
||||
@@ -32,11 +31,11 @@ allprojects {
|
||||
project.webAppDirName = 'WebContent'
|
||||
|
||||
java {
|
||||
toolchain {
|
||||
languageVersion = JavaLanguageVersion.of(8)
|
||||
}
|
||||
toolchain {
|
||||
languageVersion = JavaLanguageVersion.of(8)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
java {
|
||||
@@ -46,51 +45,56 @@ sourceSets {
|
||||
}
|
||||
|
||||
compileJava {
|
||||
options.encoding = 'UTF-8'
|
||||
options.compilerArgs = ['-parameters']
|
||||
options.generatedSourceOutputDirectory = project.file(generatedJavaDir)
|
||||
options.encoding = 'UTF-8'
|
||||
options.compilerArgs = ['-parameters']
|
||||
options.generatedSourceOutputDirectory = project.file(generatedJavaDir)
|
||||
}
|
||||
|
||||
war {
|
||||
def profile = project.findProperty("profile") ?: "tomcat"
|
||||
|
||||
if (profile == "weblogic") {
|
||||
webXml = file("WebContent/WEB-INF/weblogic-web.xml")
|
||||
exclude 'WEB-INF/web.xml'
|
||||
}
|
||||
|
||||
|
||||
// rootSpec.exclude '**/persistence.xml'
|
||||
exclude '**/context.xml'
|
||||
exclude '**/context-*.xml'
|
||||
archiveFileName = "eapim-admin.war"
|
||||
duplicatesStrategy = DuplicatesStrategy.WARN
|
||||
def profile = project.findProperty("profile") ?: "tomcat"
|
||||
|
||||
if (profile == "weblogic") {
|
||||
webXml = file("WebContent/WEB-INF/weblogic-web.xml")ㅇ
|
||||
exclude 'WEB-INF/web.xml'
|
||||
}
|
||||
|
||||
|
||||
// rootSpec.exclude '**/persistence.xml'
|
||||
exclude '**/context.xml'
|
||||
exclude '**/context-*.xml'
|
||||
archiveFileName = "eapim-admin.war"
|
||||
duplicatesStrategy = DuplicatesStrategy.WARN
|
||||
}
|
||||
|
||||
|
||||
tasks.withType(JavaCompile) {
|
||||
options.encoding = 'UTF-8'
|
||||
options.compilerArgs += ['-parameters', "-Xlint:unchecked", "-Xlint:deprecation"]
|
||||
options.encoding = 'UTF-8'
|
||||
options.compilerArgs += ['-parameters', "-Xlint:unchecked", "-Xlint:deprecation"]
|
||||
}
|
||||
|
||||
|
||||
dependencies {
|
||||
|
||||
annotationProcessor "org.projectlombok:lombok:1.18.28", "org.projectlombok:lombok-mapstruct-binding:0.2.0", "org.mapstruct:mapstruct-processor:1.5.5.Final"
|
||||
annotationProcessor "com.querydsl:querydsl-apt:${queryDslVersion}:jpa", "jakarta.persistence:jakarta.persistence-api:2.2.3", "jakarta.annotation:jakarta.annotation-api:1.3.5"
|
||||
|
||||
|
||||
annotationProcessor "org.projectlombok:lombok:1.18.28",
|
||||
"org.projectlombok:lombok-mapstruct-binding:0.2.0",
|
||||
"org.mapstruct:mapstruct-processor:1.5.5.Final"
|
||||
annotationProcessor "com.querydsl:querydsl-apt:${queryDslVersion}:jpa",
|
||||
"jakarta.persistence:jakarta.persistence-api:2.2.3",
|
||||
"jakarta.annotation:jakarta.annotation-api:1.3.5"
|
||||
|
||||
implementation project(':elink-online-core')
|
||||
implementation project(':elink-online-transformer')
|
||||
implementation project(':elink-online-common')
|
||||
implementation project(':elink-online-emsclient')
|
||||
implementation project(':elink-portal-common')
|
||||
//implementation project(':kjb-safedb')
|
||||
//implementation project(":eapim-admin-djb")
|
||||
implementation project(':elink-online-emsclient')
|
||||
implementation project(':elink-portal-common')
|
||||
//implementation project(':kjb-safedb')
|
||||
implementation project(":eapim-admin-djb")
|
||||
|
||||
/* Custom Libs */
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
/* Custom Libs (damo-manager.jar 는 Tomcat lib 가 런타임 제공 → compileOnly, WAR 미포함) */
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar'], exclude: ['damo-manager.jar'])
|
||||
compileOnly files('libs/damo-manager.jar')
|
||||
|
||||
//implementation "org.dom4j:dom4j:2.1.3"
|
||||
//implementation "org.dom4j:dom4j:2.1.3"
|
||||
//implementation "com.rabbitmq:amqp-client:3.6.6"
|
||||
|
||||
implementation "commons-primitives:commons-primitives:1.0"
|
||||
@@ -112,16 +116,10 @@ dependencies {
|
||||
|
||||
// https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api
|
||||
compileOnly group: 'javax.servlet', name: 'javax.servlet-api', version: '4.0.1'
|
||||
|
||||
implementation group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.13.2.2'
|
||||
//implementation group: 'com.fasterxml.jackson.dataformat', name: 'jackson-dataformat-xml', version: '2.13.1'
|
||||
implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.13.2'
|
||||
|
||||
// apistatus-draft.yml (자동 탐지 초안 양식) 로딩용.
|
||||
// admin 은 Spring Boot 가 아니라 yml 자동 바인딩이 없어 직접 파싱한다.
|
||||
// 2.x 를 쓴다 - 1.x 는 CVE-2022-1471(Constructor 역직렬화 RCE) 및
|
||||
// CVE-2022-25857/38749~38752(DoS) 영향. 로딩도 SafeConstructor 로 한다.
|
||||
implementation 'org.yaml:snakeyaml:2.4'
|
||||
implementation group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.13.1'
|
||||
//implementation group: 'com.fasterxml.jackson.dataformat', name: 'jackson-dataformat-xml', version: '2.13.1'
|
||||
implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.13.1'
|
||||
|
||||
implementation "javax.jms:javax.jms-api:2.0"
|
||||
implementation "org.snmp4j:snmp4j:1.10.1"
|
||||
@@ -140,18 +138,13 @@ dependencies {
|
||||
|
||||
implementation "org.apache.ibatis:ibatis-sqlmap:2.3.4.726"
|
||||
implementation "org.jdom:jdom:1.1"
|
||||
implementation "com.google.code.gson:gson:2.8.9"
|
||||
implementation "com.google.code.gson:gson:2.3.1"
|
||||
implementation "javax.annotation:javax.annotation-api:1.2"
|
||||
implementation "net.sf.json-lib:json-lib-ext-spring:1.0.2"
|
||||
implementation "taglibs:standard:1.1.2"
|
||||
implementation "javax.servlet:jstl:1.1.2"
|
||||
implementation group: 'com.jcraft', name: 'jsch', version: '0.1.51'
|
||||
|
||||
// springapp-servlet.xml 의 CommonsMultipartResolver 가 사용.
|
||||
// 기존엔 elink-online-common(api 'commons-fileupload:1.5')에서 전이적으로 받아왔으나,
|
||||
// eapim-online 쪽에서 해당 의존성을 제거할 예정이라 직접 선언으로 전환.
|
||||
implementation "commons-fileupload:commons-fileupload:1.6.0"
|
||||
|
||||
implementation "org.quartz-scheduler:quartz:${quartzVersion}"
|
||||
|
||||
// https://mvnrepository.com/artifact/org.apache.poi/poi
|
||||
@@ -193,32 +186,32 @@ dependencies {
|
||||
implementation 'ch.qos.logback:logback-classic:1.2.10'
|
||||
implementation 'ch.qos.logback:logback-access:1.2.10'
|
||||
implementation 'ch.qos.logback:logback-core:1.2.10'
|
||||
|
||||
implementation files("libs/eai-bap-client.jar")
|
||||
|
||||
compileOnly 'javax.xml.bind:jaxb-api:2.3.1'
|
||||
|
||||
//implementation 'org.postgresql:postgresql:42.2.23'
|
||||
|
||||
implementation 'backport-util-concurrent:backport-util-concurrent:3.0'
|
||||
|
||||
implementation ('software.amazon.awssdk:s3:2.20.142') {
|
||||
exclude group: 'org.slf4j', module: 'slf4j-api'
|
||||
exclude group: 'org.slf4j', module: 'logback-classic'
|
||||
}
|
||||
implementation 'software.amazon.awssdk:sso:2.20.142'
|
||||
implementation 'software.amazon.awssdk:sts:2.20.142'
|
||||
|
||||
implementation group: 'commons-net', name: 'commons-net', version: '3.5'
|
||||
implementation files("libs/eai-bap-client.jar")
|
||||
|
||||
// JDK 8 의 rt.jar 에는 org.w3c.dom.ElementTraversal 이 없어 xercesImpl 가 NCDFE 를 일으킴.
|
||||
// xml-apis 1.4.01 에 그 클래스가 포함됨. 직접 의존성으로 묶어 WAR 에 패키징되도록 함.
|
||||
implementation 'xml-apis:xml-apis:1.4.01'
|
||||
compileOnly 'javax.xml.bind:jaxb-api:2.3.1'
|
||||
|
||||
testRuntimeOnly 'com.h2database:h2:2.1.214'
|
||||
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1'
|
||||
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1'
|
||||
testImplementation 'org.springframework.boot:spring-boot-starter-test:2.6.15'
|
||||
//implementation 'org.postgresql:postgresql:42.2.23'
|
||||
|
||||
implementation 'backport-util-concurrent:backport-util-concurrent:3.0'
|
||||
|
||||
implementation ('software.amazon.awssdk:s3:2.20.142') {
|
||||
exclude group: 'org.slf4j', module: 'slf4j-api'
|
||||
exclude group: 'org.slf4j', module: 'logback-classic'
|
||||
}
|
||||
implementation 'software.amazon.awssdk:sso:2.20.142'
|
||||
implementation 'software.amazon.awssdk:sts:2.20.142'
|
||||
|
||||
implementation group: 'commons-net', name: 'commons-net', version: '3.5'
|
||||
|
||||
// JDK 8 의 rt.jar 에는 org.w3c.dom.ElementTraversal 이 없어 xercesImpl 가 NCDFE 를 일으킴.
|
||||
// xml-apis 1.4.01 에 그 클래스가 포함됨. 직접 의존성으로 묶어 WAR 에 패키징되도록 함.
|
||||
implementation 'xml-apis:xml-apis:1.4.01'
|
||||
|
||||
testRuntimeOnly 'com.h2database:h2:2.1.214'
|
||||
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1'
|
||||
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1'
|
||||
testImplementation 'org.springframework.boot:spring-boot-starter-test:2.6.15'
|
||||
}
|
||||
|
||||
test {
|
||||
@@ -226,9 +219,9 @@ test {
|
||||
}
|
||||
|
||||
configurations.all {
|
||||
exclude group: 'log4j', module: 'log4j'
|
||||
exclude group: 'org.apache.activemq'
|
||||
exclude group: 'org.codehaus.jackson'
|
||||
exclude group: 'log4j', module: 'log4j'
|
||||
exclude group: 'org.apache.activemq'
|
||||
exclude group: 'org.codehaus.jackson'
|
||||
resolutionStrategy {
|
||||
// 10 minute cache of dynamic version navigation
|
||||
cacheDynamicVersionsFor 10, 'minutes'
|
||||
@@ -239,11 +232,6 @@ configurations.all {
|
||||
// 일부 transitive 가 끌어오는 xml-apis:1.0.b2 (2002, DOM L2) 는 이 클래스를 포함하지 않아
|
||||
// xercesImpl 가 NoClassDefFoundError 를 일으킴 → 1.4.01 로 강제 통일.
|
||||
force 'xml-apis:xml-apis:1.4.01'
|
||||
|
||||
// logback 1.2.10 은 SLF4J 1.7 바인딩(StaticLoggerBinder)만 제공한다.
|
||||
// transitive (Java-WebSocket:1.5.7 등) 가 slf4j-api 2.x 를 끌어올리면
|
||||
// SLF4J 가 provider 를 못 찾아 NOP 로거로 폴백 → 콘솔/파일 로그가 전부 사라진다.
|
||||
force 'org.slf4j:slf4j-api:1.7.36'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -257,35 +245,29 @@ task settingEclipseEncoding {
|
||||
}
|
||||
|
||||
task initDirs() {
|
||||
file(generatedJavaDir).mkdirs()
|
||||
file(generatedJavaDir).mkdirs()
|
||||
}
|
||||
|
||||
eclipse {
|
||||
wtp {
|
||||
component {
|
||||
contextPath = 'monitoring'
|
||||
}
|
||||
}
|
||||
synchronizationTasks settingEclipseEncoding, initDirs
|
||||
jdt {
|
||||
// apt {
|
||||
// // generated 된 패스 경로를 .setting/org.eclipse.jdt.apt.core.prefs 의 org.eclipse.jdt.apt.genSrcDir에 적용한다.
|
||||
// // project > properties > Java Compiler > Annoation Processing 화면에서 확인 가능하다.
|
||||
// genSrcDir = file(generatedJavaDir)
|
||||
// }
|
||||
}
|
||||
project {
|
||||
if (!natures.contains('org.eclipse.buildship.core.gradleprojectnature')) {
|
||||
natures.add('org.eclipse.buildship.core.gradleprojectnature')
|
||||
buildCommand 'org.eclipse.buildship.core.gradleprojectbuilder'
|
||||
}
|
||||
}
|
||||
wtp {
|
||||
component {
|
||||
contextPath = 'monitoring'
|
||||
}
|
||||
}
|
||||
synchronizationTasks settingEclipseEncoding, initDirs
|
||||
jdt {
|
||||
|
||||
}
|
||||
project {
|
||||
if (!natures.contains('org.eclipse.buildship.core.gradleprojectnature')) {
|
||||
natures.add('org.eclipse.buildship.core.gradleprojectnature')
|
||||
buildCommand 'org.eclipse.buildship.core.gradleprojectbuilder'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tasks.withType(JavaCompile) {
|
||||
options.fork = true // 컴파일을 별도 프로세스로 분리
|
||||
options.forkOptions.memoryMaximumSize = '4g' // 컴파일러에 2GB 할당 (필요시 4g로 증량)
|
||||
options.encoding = 'UTF-8'
|
||||
}
|
||||
// CycloneDX SBOM -> xlsx 변환 (gradle sbomXlsx)
|
||||
apply from: "$projectDir/gradle/sbom-xlsx.gradle"
|
||||
}
|
||||
@@ -1,326 +0,0 @@
|
||||
/*
|
||||
* CycloneDX SBOM(bom.json) -> Excel(xlsx) 변환 태스크.
|
||||
*
|
||||
* gradle sbomXlsx # cyclonedxBom 실행 후 변환
|
||||
* gradle sbomXlsx -PsbomJson=path.json # 기존 bom.json 사용(cyclonedxBom 생략)
|
||||
* gradle sbomXlsx -PsbomOut=out.xlsx # 출력 경로 지정
|
||||
*
|
||||
* buildscript 블록이 이 스크립트에만 적용되므로 POI 의존성이 메인 빌드
|
||||
* classpath 나 WAR 산출물에는 포함되지 않는다.
|
||||
*
|
||||
* 시트: 요약 / WAR 기준
|
||||
* 산출 기준은 war 태스크의 classpath(= runtimeClasspath) 이므로
|
||||
* test·annotationProcessor·developmentOnly·compileOnly 의존은 모두 제외된다.
|
||||
* bom.json 은 라이선스/해시/설명/직접-전이 판별을 위한 메타 소스로만 쓴다.
|
||||
*/
|
||||
buildscript {
|
||||
repositories {
|
||||
maven {
|
||||
url "https://nexus.eactive.synology.me:8090/repository/maven-public/"
|
||||
allowInsecureProtocol = true
|
||||
}
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'org.apache.poi:poi-ooxml:3.17'
|
||||
}
|
||||
}
|
||||
|
||||
import groovy.json.JsonSlurper
|
||||
import org.apache.poi.ss.usermodel.BorderStyle
|
||||
import org.apache.poi.ss.usermodel.FillPatternType
|
||||
import org.apache.poi.ss.usermodel.HorizontalAlignment
|
||||
import org.apache.poi.ss.usermodel.IndexedColors
|
||||
import org.apache.poi.ss.usermodel.VerticalAlignment
|
||||
import org.apache.poi.ss.util.CellRangeAddress
|
||||
import org.apache.poi.xssf.usermodel.XSSFWorkbook
|
||||
|
||||
// 엑셀 셀 문자열 상한(32767)보다 여유를 둔 절단 길이
|
||||
ext.SBOM_CELL_LIMIT = 32000
|
||||
|
||||
task sbomXlsx {
|
||||
group = 'sbom'
|
||||
description = 'CycloneDX bom.json 을 WAR 수록 기준 xlsx 로 변환한다'
|
||||
|
||||
// -PsbomJson 으로 기존 산출물을 지정하면 재생성하지 않는다
|
||||
if (!project.hasProperty('sbomJson')) {
|
||||
dependsOn 'cyclonedxBom'
|
||||
}
|
||||
|
||||
doLast {
|
||||
File src = resolveBomJson(project)
|
||||
File out = project.hasProperty('sbomOut')
|
||||
? project.file(project.property('sbomOut'))
|
||||
: new File(project.buildDir, "reports/sbom/${sbomFileName(project)}")
|
||||
out.parentFile.mkdirs()
|
||||
|
||||
def bom = new JsonSlurper().parse(src, 'UTF-8')
|
||||
def deploy = collectDeployJars(project)
|
||||
def warRows = joinWarRows(deploy.jars, indexComponents(bom))
|
||||
|
||||
def wb = new XSSFWorkbook()
|
||||
def st = createStyles(wb)
|
||||
writeSummarySheet(wb, st, bom, warRows, src, deploy.label)
|
||||
writeWarSheet(wb, st, warRows)
|
||||
|
||||
out.withOutputStream { os -> wb.write(os) }
|
||||
wb.close()
|
||||
|
||||
int unmatched = warRows.count { it.matched == 'N' }
|
||||
logger.lifecycle("SBOM xlsx 생성: ${out.absolutePath} " +
|
||||
"(배포 수록 ${warRows.size()}개, SBOM 미매칭 ${unmatched}개, 원본 ${src.name})")
|
||||
}
|
||||
}
|
||||
|
||||
/** 산출 파일명: 배포 패키지명 기준 (war 있으면 war 파일명, 없으면 project 이름[-버전]) */
|
||||
String sbomFileName(Project p) {
|
||||
def warTask = p.tasks.findByName('war')
|
||||
if (warTask != null) {
|
||||
String archive = warTask.archiveFileName.get()
|
||||
return archive.replaceAll(/\.(war|jar|ear)$/, '') + '-sbom.xlsx'
|
||||
}
|
||||
String ver = (p.version == null || p.version.toString() in ['', 'unspecified']) ? '' : "-${p.version}"
|
||||
return "${p.name}${ver}-sbom.xlsx"
|
||||
}
|
||||
|
||||
/** bom.json 위치 결정: -PsbomJson > cyclonedxBom 산출 경로 후보 */
|
||||
File resolveBomJson(Project p) {
|
||||
if (p.hasProperty('sbomJson')) {
|
||||
File f = p.file(p.property('sbomJson'))
|
||||
if (!f.exists()) {
|
||||
throw new GradleException("bom.json 없음: ${f.absolutePath}")
|
||||
}
|
||||
return f
|
||||
}
|
||||
def candidates = [
|
||||
new File(p.buildDir, 'reports/cyclonedx/bom.json'),
|
||||
new File(p.buildDir, 'reports/bom.json'),
|
||||
]
|
||||
File found = candidates.find { it.exists() }
|
||||
if (found == null) {
|
||||
throw new GradleException(
|
||||
"bom.json 을 찾지 못했다. 확인한 경로: " + candidates*.absolutePath.join(', ') +
|
||||
"\n'gradle cyclonedxBom' 실행 후 재시도하거나 -PsbomJson=<경로> 로 지정한다.")
|
||||
}
|
||||
return found
|
||||
}
|
||||
|
||||
/**
|
||||
* 실제 배포물에 packaging 되는 jar 목록.
|
||||
* war 프로젝트는 war 태스크 classpath(= runtimeClasspath), 그 외는 runtimeClasspath 를
|
||||
* 기준으로 하므로 test/annotationProcessor/developmentOnly/compileOnly 는 자동으로 빠진다.
|
||||
*
|
||||
* @return [label: 기준 설명, jars: 행 목록]
|
||||
*/
|
||||
Map collectDeployJars(Project p) {
|
||||
def cfg = p.configurations.findByName('runtimeClasspath')
|
||||
if (cfg == null) {
|
||||
p.logger.warn("[${p.name}] runtimeClasspath 가 없어 배포 기준 시트를 비운다")
|
||||
return [label: '(없음)', jars: []]
|
||||
}
|
||||
|
||||
def warTask = p.tasks.findByName('war')
|
||||
def files
|
||||
String label
|
||||
if (warTask != null) {
|
||||
files = warTask.classpath.files
|
||||
label = 'WAR WEB-INF/lib (war 태스크 classpath)'
|
||||
} else {
|
||||
files = cfg.files
|
||||
label = 'runtimeClasspath (war 태스크 없음)'
|
||||
}
|
||||
|
||||
def coordByFile = [:]
|
||||
cfg.resolvedConfiguration.resolvedArtifacts.each { a ->
|
||||
def id = a.moduleVersion.id
|
||||
coordByFile[a.file] = [group: id.group, name: id.name, version: id.version]
|
||||
}
|
||||
def jars = files.findAll { it.name.endsWith('.jar') }.collect { f ->
|
||||
def c = coordByFile[f]
|
||||
[
|
||||
file : f.name,
|
||||
group : c?.group ?: '',
|
||||
name : c?.name ?: f.name.replaceAll(/\.jar$/, ''),
|
||||
version: c?.version ?: '',
|
||||
coord : c ? "${c.group}:${c.name}:${c.version}".toString() : '',
|
||||
]
|
||||
}.sort { it.file }
|
||||
|
||||
return [label: label, jars: jars]
|
||||
}
|
||||
|
||||
/** bom.json 컴포넌트를 'group:name:version' 키로 색인 (라이선스/해시/설명/직접-전이) */
|
||||
Map indexComponents(bom) {
|
||||
String rootRef = bom.metadata?.component?.'bom-ref'
|
||||
Set directRefs = (bom.dependencies?.find { it.ref == rootRef }?.dependsOn ?: []) as Set
|
||||
|
||||
def index = [:]
|
||||
bom.components?.each { c ->
|
||||
def hashes = [:]
|
||||
c.hashes?.each { h -> hashes[h.alg] = h.content }
|
||||
|
||||
def licenses = (c.licenses ?: []).collect { l ->
|
||||
l.license?.id ?: l.license?.name ?: l.expression ?: ''
|
||||
}.findAll { it }
|
||||
|
||||
index["${c.group ?: ''}:${c.name ?: ''}:${c.version ?: ''}".toString()] = [
|
||||
direct : directRefs.contains(c.'bom-ref') ? '직접' : '전이',
|
||||
licenses : licenses.join('; '),
|
||||
licenseList: licenses.isEmpty() ? ['(미상)'] : licenses,
|
||||
purl : c.purl ?: '',
|
||||
sha256 : hashes['SHA-256'] ?: '',
|
||||
sha1 : hashes['SHA-1'] ?: '',
|
||||
description: c.description ?: '',
|
||||
]
|
||||
}
|
||||
return index
|
||||
}
|
||||
|
||||
/** WAR jar 목록에 SBOM 메타를 좌표로 결합 */
|
||||
List joinWarRows(List warJars, Map index) {
|
||||
def result = []
|
||||
warJars.eachWithIndex { j, i ->
|
||||
def m = j.coord ? index[j.coord] : null
|
||||
result << [
|
||||
no : i + 1,
|
||||
file : j.file,
|
||||
group : j.group,
|
||||
name : j.name,
|
||||
version : j.version,
|
||||
direct : m?.direct ?: '',
|
||||
licenses : m?.licenses ?: '',
|
||||
licenseList: m?.licenseList ?: ['(미상)'],
|
||||
purl : m?.purl ?: '',
|
||||
sha256 : m?.sha256 ?: '',
|
||||
sha1 : m?.sha1 ?: '',
|
||||
matched : (m != null) ? 'Y' : 'N',
|
||||
description: m?.description ?: '',
|
||||
]
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
Map createStyles(wb) {
|
||||
def headFont = wb.createFont()
|
||||
headFont.setBold(true)
|
||||
headFont.setColor(IndexedColors.WHITE.getIndex())
|
||||
|
||||
def head = wb.createCellStyle()
|
||||
head.setFont(headFont)
|
||||
head.setFillForegroundColor(IndexedColors.DARK_BLUE.getIndex())
|
||||
head.setFillPattern(FillPatternType.SOLID_FOREGROUND)
|
||||
head.setAlignment(HorizontalAlignment.CENTER)
|
||||
head.setVerticalAlignment(VerticalAlignment.CENTER)
|
||||
head.setBorderBottom(BorderStyle.THIN)
|
||||
|
||||
def body = wb.createCellStyle()
|
||||
body.setVerticalAlignment(VerticalAlignment.TOP)
|
||||
|
||||
def wrap = wb.createCellStyle()
|
||||
wrap.setVerticalAlignment(VerticalAlignment.TOP)
|
||||
wrap.setWrapText(true)
|
||||
|
||||
def labelFont = wb.createFont()
|
||||
labelFont.setBold(true)
|
||||
def label = wb.createCellStyle()
|
||||
label.setFont(labelFont)
|
||||
|
||||
return [head: head, body: body, wrap: wrap, label: label]
|
||||
}
|
||||
|
||||
/** 헤더 행 생성 + 폭 지정 + 틀고정 */
|
||||
def writeHeader(sheet, style, List<String> headers, List<Integer> widths) {
|
||||
def row = sheet.createRow(0)
|
||||
row.setHeightInPoints(20f)
|
||||
headers.eachWithIndex { h, i ->
|
||||
def cell = row.createCell(i)
|
||||
cell.setCellValue(h)
|
||||
cell.setCellStyle(style)
|
||||
sheet.setColumnWidth(i, widths[i] * 256)
|
||||
}
|
||||
sheet.createFreezePane(0, 1)
|
||||
}
|
||||
|
||||
def cellOf(row, int idx, value, style) {
|
||||
def cell = row.createCell(idx)
|
||||
String s = (value == null) ? '' : value.toString()
|
||||
if (s.length() > SBOM_CELL_LIMIT) {
|
||||
s = s.substring(0, SBOM_CELL_LIMIT) + '…(생략)'
|
||||
}
|
||||
cell.setCellValue(s)
|
||||
cell.setCellStyle(style)
|
||||
return cell
|
||||
}
|
||||
|
||||
def writeSummarySheet(wb, st, bom, List warRows, File src, String basisLabel) {
|
||||
def sheet = wb.createSheet('요약')
|
||||
def comp = bom.metadata?.component ?: [:]
|
||||
def tool = bom.metadata?.tools?.components?.getAt(0)
|
||||
Set licenseKinds = warRows.collectMany { it.licenseList } as Set
|
||||
|
||||
def items = [
|
||||
['대상 프로젝트', "${comp.group ?: ''}:${comp.name ?: ''}:${comp.version ?: ''}"],
|
||||
['산출 기준', "${basisLabel} — test/annotationProcessor/compileOnly 제외"],
|
||||
['BOM 포맷', "${bom.bomFormat ?: ''} ${bom.specVersion ?: ''}"],
|
||||
['serialNumber', bom.serialNumber ?: ''],
|
||||
['생성 시각', bom.metadata?.timestamp ?: ''],
|
||||
['생성 도구', tool ? "${tool.name} ${tool.version}" : ''],
|
||||
['원본 파일', src.absolutePath],
|
||||
['배포 수록 jar', warRows.size()],
|
||||
[' └ 직접 의존', warRows.count { it.direct == '직접' }],
|
||||
[' └ 전이 의존', warRows.count { it.direct == '전이' }],
|
||||
[' └ SBOM 미매칭', warRows.count { it.matched == 'N' }],
|
||||
['라이선스 종류', licenseKinds.size()],
|
||||
['라이선스 미상', warRows.count { it.licenses.isEmpty() }],
|
||||
]
|
||||
|
||||
writeHeader(sheet, st.head, ['항목', '값'], [30, 90])
|
||||
items.eachWithIndex { item, i ->
|
||||
def row = sheet.createRow(i + 1)
|
||||
cellOf(row, 0, item[0], st.label)
|
||||
cellOf(row, 1, item[1], st.body)
|
||||
}
|
||||
|
||||
// 라이선스 분포 (요약 하단)
|
||||
def byLicense = [:].withDefault { 0 }
|
||||
warRows.each { r -> r.licenseList.each { lic -> byLicense[lic] = byLicense[lic] + 1 } }
|
||||
def sorted = byLicense.entrySet().sort { a, b -> (b.value <=> a.value) ?: (a.key <=> b.key) }
|
||||
|
||||
int base = items.size() + 2
|
||||
def hdr = sheet.createRow(base)
|
||||
cellOf(hdr, 0, '라이선스', st.head)
|
||||
cellOf(hdr, 1, 'jar 수', st.head)
|
||||
sorted.eachWithIndex { e, i ->
|
||||
def row = sheet.createRow(base + 1 + i)
|
||||
cellOf(row, 0, e.key, st.body)
|
||||
cellOf(row, 1, e.value, st.body)
|
||||
}
|
||||
}
|
||||
|
||||
/** 실제 배포물(WAR WEB-INF/lib) 기준 시트 */
|
||||
def writeWarSheet(wb, st, List warRows) {
|
||||
def sheet = wb.createSheet('WAR 기준')
|
||||
def headers = ['No', 'jar 파일명', 'Group', 'Name', 'Version', '구분',
|
||||
'License', 'purl', 'SHA-256', 'SHA-1', 'SBOM매칭', 'Description']
|
||||
def widths = [6, 46, 32, 34, 16, 7, 30, 60, 40, 30, 10, 60]
|
||||
writeHeader(sheet, st.head, headers, widths)
|
||||
|
||||
warRows.eachWithIndex { r, i ->
|
||||
def row = sheet.createRow(i + 1)
|
||||
cellOf(row, 0, r.no, st.body)
|
||||
cellOf(row, 1, r.file, st.body)
|
||||
cellOf(row, 2, r.group, st.body)
|
||||
cellOf(row, 3, r.name, st.body)
|
||||
cellOf(row, 4, r.version, st.body)
|
||||
cellOf(row, 5, r.direct, st.body)
|
||||
cellOf(row, 6, r.licenses, st.body)
|
||||
cellOf(row, 7, r.purl, st.body)
|
||||
cellOf(row, 8, r.sha256, st.body)
|
||||
cellOf(row, 9, r.sha1, st.body)
|
||||
cellOf(row, 10, r.matched, st.body)
|
||||
cellOf(row, 11, r.description, st.wrap)
|
||||
}
|
||||
if (!warRows.isEmpty()) {
|
||||
sheet.setAutoFilter(new CellRangeAddress(0, warRows.size(), 0, headers.size() - 1))
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
POST http://localhost:7090/monitoring/_onl/admin/authserver/clientMan.json?serviceType=APIGW
|
||||
Content-Type: application/json
|
||||
User-Agent: insomnia/10.1.1
|
||||
X-Internal-Token: {{internalApiToken}}
|
||||
X-API-KEY: EAPIM_KEY
|
||||
action: insert
|
||||
target_host: stg
|
||||
|
||||
@@ -118,4 +118,4 @@ target_host: stg
|
||||
"errTransformYn": "N"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
Binary file not shown.
@@ -1,368 +1,470 @@
|
||||
# eapim-admin
|
||||
# EAPIM Admin
|
||||
|
||||
제주은행(DJB) eLink EMS 관리 콘솔
|
||||
광주은행 엔터프라이즈 API 관리 시스템 - 관리 콘솔
|
||||
|
||||
## 개요
|
||||
## 프로젝트 개요
|
||||
|
||||
**eapim-admin** 은 eLink EMS(eLink Management System)의 웹 기반 관리 콘솔입니다.
|
||||
API 게이트웨이 구성, 실시간 모니터링/대시보드, 통계, 트랜잭션 로그 조회, 인터페이스·레이아웃·어댑터·라우팅 관리 기능을 제공합니다.
|
||||
|
||||
eLink EMS 는 세 가지 애플리케이션으로 구성됩니다.
|
||||
|
||||
| 애플리케이션 | 설명 |
|
||||
|---|---|
|
||||
| **eapim-admin** | 관리 콘솔 (이 저장소) |
|
||||
| eapim-online | 실시간 트랜잭션 처리 API 게이트웨이 |
|
||||
| eapim-portal | API 소비자용 개발자 포털 (Spring Boot) |
|
||||
|
||||
> 패키지·클래스에 남아 있는 `kjb` / `KJB` 명칭은 초기 광주은행 커스터마이징에서 유래한 것으로,
|
||||
> 현재 배포 대상은 제주은행(DJB)입니다. 제주은행 전용 코드는 `com.eactive.eai.rms.ext.djb` 하위에 있습니다.
|
||||
**EAPIM Admin**은 광주은행의 eLink EMS (eLink Management System) 관리 콘솔로, API 게이트웨이 구성, 모니터링, 통계, 트랜잭션 로그 조회 기능을 제공하는 웹 기반 플랫폼입니다.
|
||||
|
||||
## 기술 스택
|
||||
|
||||
| 구분 | 버전 / 내용 |
|
||||
|---|---|
|
||||
| Java | **8** (Gradle toolchain 으로 강제, `JavaLanguageVersion.of(8)`) |
|
||||
| 빌드 | Gradle **8.7** (wrapper), `war` 패키징 |
|
||||
| Spring Framework | 5.3.27 (MVC / ORM / JDBC) |
|
||||
| Spring Data JPA | 2.5.2 |
|
||||
| Hibernate | 5.6.15.Final |
|
||||
| QueryDSL | 5.0.0 (빌드 시 Q-class 생성) |
|
||||
| iBATIS | 2.3.4 (레거시 SQL 매핑) |
|
||||
| Quartz | 2.2.1 (스케줄링) |
|
||||
| 로깅 | SLF4J + Logback 1.2.10 (모든 `log4j` 의존성 제외) |
|
||||
| 기타 | Apache POI 3.17(Excel), Jackson 2.13.1, AWS SDK 2.x(S3), EhCache, Lombok, MapStruct |
|
||||
- Spring Framework 5.3.27
|
||||
- Java 17
|
||||
- Gradle 8.7
|
||||
- Oracle 19c
|
||||
- Spring Data JPA 2.5.2
|
||||
- Hibernate 5.4.33/5.6.15
|
||||
- QueryDSL 5.0.0
|
||||
- iBATIS 2.3.4 (레거시 SQL 매핑)
|
||||
- Quartz 2.2.1
|
||||
- Logback 1.2.10
|
||||
|
||||
애플리케이션 프레임워크는 Spring Boot 가 아닌 전통적 Spring 5.3 WAR 이며 Tomcat / WebLogic 등에 배포됩니다.
|
||||
(Spring Boot 는 테스트 유틸리티 `spring-boot-starter-test:2.6.15` 에만 사용)
|
||||
## 초기 세팅 가이드
|
||||
|
||||
## 사전 요구사항
|
||||
### 사전 요구사항
|
||||
|
||||
- **JDK 8** - `.envrc`(direnv) 또는 셸 rc 에서 `JAVA_HOME` 을 JDK 8 위치로 export. Gradle 8 toolchain auto-detect 가 `JAVA_HOME` 도 후보로 인식합니다. `gradle.properties` 에 JDK 경로를 넣지 않습니다.
|
||||
- JDK 17 이상
|
||||
- Gradle 8.7
|
||||
- Git
|
||||
- Oracle 접근 (개발/테스트 환경에 따라). 테스트는 H2 인메모리 DB 사용.
|
||||
- **형제 디렉토리 체크아웃 필요** - 멀티 모듈 프로젝트로 아래 저장소가 같은 부모 디렉토리에 있어야 합니다.
|
||||
```
|
||||
<workspace>/
|
||||
├── eapim-admin/ # 이 저장소
|
||||
├── eapim-online/ # elink-online-* 모듈
|
||||
└── elink-portal-common/ # 포털 공통 JPA 엔티티
|
||||
```
|
||||
- Oracle 19c (또는 개발 환경에 따라 접근 가능한 DB)
|
||||
|
||||
## 멀티 모듈 구조
|
||||
### 환경별 설정 파일
|
||||
프로젝트 클론 후 필요한 환경 설정을 수정하세요:
|
||||
|
||||
`settings.gradle` 에서 형제 디렉토리의 소스 프로젝트를 참조합니다.
|
||||
- `WebContent/WEB-INF/properties/env.D.properties` - 개발 환경
|
||||
- `WebContent/WEB-INF/properties/env.T.properties` - 테스트/스테이징 환경
|
||||
- `WebContent/WEB-INF/properties/env.P.properties` - 운영 환경
|
||||
|
||||
| 모듈 | 위치 | 설명 |
|
||||
|---|---|---|
|
||||
| elink-online-core | `../eapim-online/elink-online-core` | 핵심 인터페이스 / 도메인 모델 |
|
||||
| elink-online-core-jpa | `../eapim-online/elink-online-core-jpa` | JPA 엔티티 / 리포지토리 |
|
||||
| elink-online-transformer | `../eapim-online/elink-online-transformer` | 메시지 변환 로직 |
|
||||
| elink-online-common | `../eapim-online/elink-online-common` | 공통 유틸리티 |
|
||||
| elink-online-emsclient | `../eapim-online/elink-online-emsclient` | EMS 통신 클라이언트 |
|
||||
| elink-portal-common | `../elink-portal-common` | 포털 공통 컴포넌트 (JPA 엔티티) |
|
||||
|
||||
> `kjb-safedb`, `eapim-admin-djb` 는 `settings.gradle` 에 주석 처리되어 있으며,
|
||||
> 필요 시 주석을 해제하고 해당 디렉토리를 체크아웃합니다.
|
||||
### 주의사항
|
||||
- **SafeDB**: 암호화 기능이 필요한 경우 `-Dkjb_safedb.mode=fake` 설정 (개발 환경) 또는 실제 SafeDB 라이브러리 설치
|
||||
- **멀티 모듈 구조**: eapim-online의 여러 모듈들을 참조하므로 반드시 eapim-online도 클론해야 합니다
|
||||
|
||||
|
||||
## 빌드 및 실행
|
||||
|
||||
### Gradle 명령어
|
||||
|
||||
프로젝트 루트의 Gradle wrapper(`./gradlew`) 를 사용합니다.
|
||||
|
||||
### 빌드 및 실행 명령어
|
||||
|
||||
```bash
|
||||
# 표준 빌드
|
||||
./gradlew build
|
||||
|
||||
# Weblogic 배포용 빌드 (테스트 제외)
|
||||
./gradlew build -x test -Pprofile=weblogic
|
||||
|
||||
# WAR 파일 빌드[ems_stdout.log](../../logs/emsSvr-RinjaeMA/ems_stdout.log)
|
||||
./gradlew war
|
||||
|
||||
# 클린 빌드
|
||||
./gradlew clean build
|
||||
|
||||
# WAR 빌드 (build/libs/eapim-admin.war)
|
||||
./gradlew war
|
||||
|
||||
# WebLogic 배포용 빌드 (테스트 제외, weblogic-web.xml 사용)
|
||||
./gradlew build -x test -Pprofile=weblogic
|
||||
|
||||
# 패키징 없이 컴파일만
|
||||
./gradlew classes
|
||||
|
||||
# QueryDSL Q-class 등 생성 코드만 생성
|
||||
./gradlew compileJava
|
||||
|
||||
# 전체 테스트
|
||||
# 테스트 실행
|
||||
./gradlew test
|
||||
|
||||
# 특정 테스트
|
||||
./gradlew test --tests "com.eactive.eai.rms.*"
|
||||
# 패키징 없이 클래스만 컴파일
|
||||
./gradlew classes
|
||||
|
||||
# 의존성 트리 / 태스크 목록
|
||||
# QueryDSL Q-classes 및 기타 애노테이션 생성
|
||||
./gradlew compileJava
|
||||
|
||||
# 의존성 트리 보기
|
||||
./gradlew dependencies
|
||||
|
||||
# 사용 가능한 모든 태스크 목록
|
||||
./gradlew tasks --all
|
||||
```
|
||||
|
||||
- WAR 파일명: `eapim-admin.war`, 컨텍스트 경로: `/monitoring`
|
||||
- 프로필: `-Pprofile=weblogic` 지정 시 `web.xml` 대신 `weblogic-web.xml` 사용 (DefaultServlet 문제 회피)
|
||||
|
||||
## 로컬 개발 빠른 시작
|
||||
|
||||
1. **생성 코드 생성** - 최초 1회 및 pull 이후
|
||||
```bash
|
||||
./gradlew compileJava # Q-class IDE 오류 시: ./gradlew clean compileJava
|
||||
```
|
||||
2. **빌드/테스트**
|
||||
```bash
|
||||
./gradlew build
|
||||
```
|
||||
3. **IDE 열기** - 아래 "IDE 설정" 참조
|
||||
4. **WAS(Tomcat 등) 실행 구성** 에 `eapim-admin` 배포 + 아래 VM 옵션 추가 후 기동
|
||||
|
||||
### 로컬 Tomcat VM 옵션 예시
|
||||
|
||||
전체 목록과 설명은 `CLAUDE.md` 의 "필수 환경 변수 (Tomcat)" 를 참조하세요.
|
||||
|
||||
```
|
||||
-Deai.datasource.type=DEV
|
||||
-Dinst.Name=emsSvr11
|
||||
-Deai.tableowner=EMSADM
|
||||
-Deai.systemmode=D
|
||||
-Dfile.encoding=utf-8
|
||||
-Dlogin.mode=db
|
||||
-DLOGBACK_LOG_LEVEL=DEBUG
|
||||
-Ddamo-manager.enabled=true
|
||||
-Dlogging.log-path=/logs/prod/eapim/emsSvr11
|
||||
-Dlogback.configurationFile=classpath:logback-dev.xml
|
||||
-Dhibernate.dialect=org.hibernate.dialect.Oracle12cDialect
|
||||
```
|
||||
|
||||
- `inst.Name` 은 클러스터 내에서 인스턴스마다 고유해야 합니다.
|
||||
- `eai.tableowner` 는 Oracle 스키마 소유자.
|
||||
|
||||
## 프로젝트 구조
|
||||
|
||||
### 전체 프로젝트 구조
|
||||
```
|
||||
kjb-eapim/
|
||||
├── eapim-admin/ # Admin 관리 콘솔 (현재 프로젝트)
|
||||
├── eapim-online/ # Online 게이트웨이 코어 모듈
|
||||
│ ├── elink-online-core/
|
||||
│ ├── elink-online-core-jpa/
|
||||
│ ├── elink-online-transformer/
|
||||
│ ├── elink-online-common/
|
||||
│ ├── elink-online-emsclient/
|
||||
│ └── elink-online-adapter/
|
||||
├── elink-portal-common/ # 포털 공통 컴포넌트 (JPA 엔티티)
|
||||
├── kjb-safedb/ # SafeDB 암호화 라이브러리
|
||||
└── eapim-portal/ # Portal 개발자 포털 (선택사항)
|
||||
```
|
||||
|
||||
### eapim-admin 내부 구조
|
||||
```
|
||||
src/main/java/com/eactive/eai/
|
||||
├── agent/command - 커맨드 패턴 구현
|
||||
├── common - 공통 유틸리티, iBatis, JSON 직렬화
|
||||
├── custom - 고객사별 커스터마이제이션
|
||||
├── rms - 메인 애플리케이션 코드
|
||||
│ ├── bap - 배치 처리(BAP): adaptor / manage / tansaction
|
||||
│ ├── bat - 추가 배치 기능
|
||||
│ ├── common - RMS 공통 (필터, 시작, 서비스)
|
||||
│ ├── data - 데이터 엔티티, 리포지토리
|
||||
│ ├── env - 환경 설정
|
||||
│ ├── kakao - 카카오 연동
|
||||
│ ├── onl - 온라인 트랜잭션 관리 (apim / manage / transaction)
|
||||
│ ├── service - 비즈니스 서비스
|
||||
│ └── ext
|
||||
│ ├── djb - 제주은행(DJB) 확장 (webhook, UMS 연동, 통계 등)
|
||||
│ └── kjb - 광주은행(KJB) 유래 확장
|
||||
└── (com.eactive.ext.kjb) - 통계 화면 등 일부 확장 코드
|
||||
|
||||
WebContent/
|
||||
├── WEB-INF/
|
||||
│ ├── applicationContext.xml - Root Spring 설정
|
||||
│ ├── springapp-servlet.xml - Servlet Spring 설정
|
||||
│ ├── web.xml / weblogic-web.xml
|
||||
│ └── properties/ - env.D / env.T / env.P / env.L .properties
|
||||
├── jsp/ - 화면
|
||||
└── guide/ - 사용자 가이드(docsify)
|
||||
├── common/ - 공통 유틸리티, iBatis, JSON 직렬화
|
||||
├── custom/ - 고객사별 커스터마이제이션
|
||||
├── rms/ - 메인 애플리케이션 코드
|
||||
│ ├── bap/ - 배치 처리 (BAP)
|
||||
│ ├── bat/ - 추가 배치 기능
|
||||
│ ├── common/ - RMS 공통 (필터, 시작, 서비스)
|
||||
│ ├── data/ - 데이터 엔티티, 리포지토리
|
||||
│ ├── env/ - 환경 설정
|
||||
│ ├── kakao/ - 카카오 연동
|
||||
│ ├── onl/ - 온라인 트랜잭션 관리
|
||||
│ └── service/ - 비즈니스 서비스
|
||||
└── ext.kjb/ - 광주은행(KJB) 확장
|
||||
```
|
||||
|
||||
## 데이터 액세스 (하이브리드 ORM)
|
||||
### 웹 리소스 구조
|
||||
```
|
||||
WebContent/
|
||||
├── WEB-INF/
|
||||
│ ├── applicationContext.xml - Root Spring 설정
|
||||
│ ├── springapp-servlet.xml - Servlet Spring 설정
|
||||
│ ├── web.xml - Tomcat용 배포 디스크립터
|
||||
│ ├── weblogic-web.xml - WebLogic용 배포 디스크립터
|
||||
│ └── properties/ - 환경별 프로퍼티
|
||||
│ ├── env.D.properties - 개발
|
||||
│ ├── env.T.properties - 테스트
|
||||
│ └── env.P.properties - 운영
|
||||
└── guide/ - 사용자 가이드
|
||||
```
|
||||
|
||||
JPA 와 iBATIS 를 함께 사용합니다.
|
||||
## 주요 기능
|
||||
|
||||
- **신규 기능**: JPA + Spring Data 리포지토리 (`com.eactive.eai.rms.data.entity`, `elink-portal-common`)
|
||||
- **레거시**: iBATIS SQL 매퍼 `src/main/resources/com/eactive/eai/**/*.xml` (Spring XML 설정, 점진적 마이그레이션 대상)
|
||||
- 벤더별 매핑: Oracle `*-oracle.xml`, MariaDB `*-mariadb.xml`, PostgreSQL `*-postgresql.xml`
|
||||
다음과 같은 관리 기능을 수행하며, 모든 관리 대상은 실시간으로 DB, 메모리에 있는 정보가 수정, 반영됩니다.
|
||||
|
||||
### Multi-tenancy (런타임 스키마 전환)
|
||||
| 기능 | 설명 |
|
||||
| -------------------- | ------------------------------------------------------------ |
|
||||
| 사용자 관리 | 사용자 및 권한을 관리하는 기능 |
|
||||
| 로그 조회 | 거래 수행 내역을 조회 하는 기능(온라인/배치/일괄) |
|
||||
| 통계 조회 | 거래 통계 조회 기능 |
|
||||
| 대시보드 조회 | 거래 내역 및 장애상황을 모니터링 할 수 있는 대시보드 조회 기능 |
|
||||
| 인터페이스 관리 | 인터페이스 등록/관리 기능 |
|
||||
| 레이아웃 관리 | 레이아웃 등록/관리 기능 |
|
||||
| 변환 관리 | 변환 등록/관리 기능 |
|
||||
| 어댑터 관리 | 통신 어댑터 등록/관리 기능 |
|
||||
| 라우팅룰 관리 | 라우팅 룰 등록/관리 기능 |
|
||||
| 배치 어댑터 관리 | 배치 어댑터 등록/관리 기능 |
|
||||
| 배치 인터페이스 관리 | 배치 인터페이스 등록/관리 기능 |
|
||||
| 일괄 관리 | 일괄 등록/관리 기능 |
|
||||
|
||||
Hibernate Multi-tenancy 로 요청 시점에 스키마를 전환합니다.
|
||||
`DataSourceContextHolder`(ThreadLocal) → `TenantIdentifierResolver` → `ConfigurableMultiTenantConnectionProvider.setSchema()`.
|
||||
요청 파라미터 `serviceType`(예: `APIGW`) 를 `DataSourceTypeInterceptor` 가 읽어 자동 설정하거나, 컨트롤러에서 명시 설정합니다.
|
||||
자세한 내용은 `CLAUDE.md` 의 "Multi-tenancy" 절 참조.
|
||||
## 외부 모듈
|
||||
|
||||
이 프로젝트는 다음 모듈에 의존합니다 (settings.gradle 참조):
|
||||
|
||||
| 이름 | 버전 | 설명 |
|
||||
| ------------------------ | ------------ | ---------------------------------------------- |
|
||||
| elink-online-core | 4.5-SNAPSHOT | 코어 모듈로 인터페이스 클래스 정의 |
|
||||
| elink-online-core-jpa | 4.5-SNAPSHOT | JPA 엔티티 및 리포지토리 |
|
||||
| elink-online-transformer | 4.5-SNAPSHOT | 변환 모듈로 메시지 및 변환 관련 클래스 정의 |
|
||||
| elink-online-adapter | 4.5-SNAPSHOT | 어댑터 모듈로 통신에 필요한 어댑터 클래스 정의 |
|
||||
| elink-online-common | 4.5-SNAPSHOT | 나머지 공통 프로그램이 정의되어있다 |
|
||||
| elink-online-emsclient | 4.5-SNAPSHOT | ems와 통신하기 위한 모듈 |
|
||||
| elink-portal-common | - | 포털 공통 컴포넌트 (JPA 엔티티) |
|
||||
| kjb-safedb | - | 커스텀 암호화 라이브러리 |
|
||||
| eapim-admin-kjb | - | KJB 은행 특화 커스터마이제이션 |
|
||||
|
||||
## 하이브리드 ORM 방식
|
||||
|
||||
이 애플리케이션은 **JPA와 iBATIS 모두를 사용**합니다:
|
||||
- **JPA/Hibernate**: `elink-portal-common`을 통한 현대적인 데이터 액세스 (Spring Data repositories)
|
||||
- **iBATIS**: 기존 코드를 위한 레거시 SQL 매핑 (점진적으로 마이그레이션 예정)
|
||||
|
||||
데이터 액세스 작업 시:
|
||||
- 새로운 기능은 JPA와 Spring Data repositories를 사용해야 합니다
|
||||
- 레거시 iBATIS 매퍼는 `src/main/resources/com/eactive/eai/**/*.xml`에 있으며 Spring XML을 통해 설정됩니다
|
||||
|
||||
## 코드 생성
|
||||
|
||||
빌드 시 생성됩니다.
|
||||
빌드 프로세스에서 생성되는 것들:
|
||||
- **QueryDSL Q-classes**: 타입 안전 쿼리 클래스
|
||||
- **Lombok**: 애노테이션 프로세싱을 통한 Getters/setters/builders
|
||||
- **MapStruct**: DTO와 엔티티 간의 객체 매퍼
|
||||
|
||||
### 이중 Q-Class 생성 (Gradle + Eclipse)
|
||||
|
||||
이 프로젝트는 **Gradle과 Eclipse 애노테이션 프로세서 모두를 사용**하므로, Q-classes가 **두 위치에 생성**됩니다:
|
||||
|
||||
1. **`build/generated/java/`** - `gradle compileJava` 또는 `gradle build` 실행 시 Gradle이 생성
|
||||
2. **`WebContent/generated/`** - Eclipse IDE에서 빌드 시 Eclipse APT가 생성
|
||||
|
||||
**이는 예상된 동작이며 의도적입니다.** 두 디렉토리 모두 `.gitignore`에 포함되어 있으며 커밋해서는 안 됩니다.
|
||||
|
||||
## AI 코딩 어시스턴트 지침 파일
|
||||
|
||||
이 프로젝트는 여러 AI 코딩 어시스턴트를 지원합니다. 각 도구는 다음 파일을 참조합니다:
|
||||
|
||||
### 지침 파일 위치
|
||||
|
||||
| AI 도구 | 지침 파일 경로 | 용도 |
|
||||
|---------|---------------|------|
|
||||
| **Claude Code** | `CLAUDE.md` | 마스터 지침 파일 (한글) |
|
||||
| **GitHub Copilot** | `.github/copilot-instructions.md` | Copilot용 지침 |
|
||||
| **Cursor** | `.cursorrules` | Cursor용 간결한 규칙 |
|
||||
|
||||
### 지침 파일 관리 방법
|
||||
|
||||
1. **마스터 파일**: `CLAUDE.md`
|
||||
- 가장 상세한 프로젝트 지침을 포함
|
||||
- 한글로 작성되어 한국 개발팀이 이해하기 쉬움
|
||||
- 수정 시 이 파일을 먼저 업데이트
|
||||
|
||||
2. **동기화**:
|
||||
```bash
|
||||
# CLAUDE.md 수정 후 다른 파일들 동기화
|
||||
cp CLAUDE.md .github/copilot-instructions.md
|
||||
# .cursorrules는 간결한 버전이므로 필요시 수동 업데이트
|
||||
```
|
||||
|
||||
3. **새로운 AI 도구 지원 추가**:
|
||||
- Windsurf: `.windsurfrules` 파일 생성
|
||||
- 기타 도구: 해당 도구의 규칙 파일명 확인 후 생성
|
||||
|
||||
- **QueryDSL Q-class** - `build/generated/java/` 에 생성, `.gitignore` 대상 (커밋 금지)
|
||||
- **Lombok** - getter/setter/builder 등
|
||||
- **MapStruct** - DTO ↔ 엔티티 매퍼
|
||||
|
||||
pull 이후 또는 Q-class IDE 오류 시 `./gradlew compileJava` (또는 `clean compileJava`) 로 재생성합니다.
|
||||
|
||||
## 환경 설정
|
||||
|
||||
### 시스템 모드
|
||||
|
||||
`-Deai.systemmode` 로 지정하며 해당 프로퍼티 파일을 로드합니다.
|
||||
- **D (DEV)**: 개발 환경
|
||||
- **T (TEST)**: 테스트/스테이징 환경
|
||||
- **P (PROD)**: 운영 환경
|
||||
|
||||
| 값 | 환경 | 파일 |
|
||||
|---|---|---|
|
||||
| D | 개발 | `env.D.properties` |
|
||||
| T | 테스트 / 스테이징 | `env.T.properties` |
|
||||
| P | 운영 | `env.P.properties` |
|
||||
| L | 로컬 | `env.L.properties` |
|
||||
시스템 모드는 `-Deai.systemmode` JVM 옵션으로 설정하며, 해당 환경의 프로퍼티 파일(`env.D.properties` 등)을 로드합니다.
|
||||
|
||||
### 데이터베이스
|
||||
### TOMCAT 기동 옵션 참조
|
||||
|
||||
- **Oracle**: 운영 주 DB (`hibernate.dialect=org.hibernate.dialect.Oracle12cDialect`)
|
||||
- **MariaDB / PostgreSQL**: 벤더별 SQL 매핑으로 지원 (JDBC 의존성은 필요 시 활성화)
|
||||
- **H2**: 테스트용 인메모리 DB (`src/test/resources`)
|
||||
- 연결 타입은 `-Deai.datasource.type`(DEV/STG/PROD) 로 선택
|
||||
#### 은행 개발 서버용
|
||||
```
|
||||
-Deai.datasource.type=DEV
|
||||
-Dinst.Name=emsSvr11
|
||||
-Deai.tableowner=EMSAPP
|
||||
-Deai.systemmode=D
|
||||
-Dfile.encoding=utf-8
|
||||
-DLOGBACK_LOG_LEVEL=info
|
||||
```
|
||||
|
||||
### DB 암호화
|
||||
|
||||
`damo-manager.jar` 모듈은 환경에 따라 다른 암호화로직을 제공한다.
|
||||
|
||||
| 환경 | 설명 |
|
||||
|---|---|
|
||||
| 운영 | 실제 D'amo 네이티브 라이브러리 |
|
||||
| 개발 | SHA-256 |
|
||||
|
||||
로컬 개발 시 `-Ddamo-manager.enabled=false`
|
||||
|
||||
## DJB (제주은행) 전용 설정
|
||||
|
||||
DJB 전용 프로퍼티는 DB 테이블 `TSEAIRM24` 에 그룹명 `'Monitoring'` 과
|
||||
DB 테이블 `PTL_PROPERTY` 에 그룹명 `'Portal'` 로 저장된다.
|
||||
|
||||
> Monitoring 프라퍼티 (환경정보 > EMS관리 > 모니터링 프라퍼티)
|
||||
|
||||
**계정관리**
|
||||
- `djb.sms_auth.enabled` - 로그인시 2-factor 인증 적용여부
|
||||
- `djb.sms_auth.fixed_value` - sms_auth.mode=fixed일 경우 고정인증값
|
||||
- `djb.sms_auth.mode` - 2-factor 인증 모드 (real:실제, fixed:고정값-개발환경에서 사용)
|
||||
- `rms.DUAL_LOGIN_ENABLED` - 중복 로그인 허용 여부
|
||||
- `rms.auto.logout.timeout` - 자동 로그아웃 시간(분)
|
||||
- `rms.password.combi.check` - 비밀번호 영문/숫자/특수문자 조합 종류수
|
||||
- `rms.password.fail.count` - 비밀번호 실패 허용 횟수(이 값 이상 실패시 계정 잠김)
|
||||
- `rms.password.history.count` - 이전 비밀번호 변경 불가 횟수
|
||||
- `rms.password.init.subfix` - 관리자가 비밀번호 초기화시 추가 문자 ex) 행번@!
|
||||
- `rms.password.length.check` - 비밀번호 최소 자리수
|
||||
- `rms.password.repeat.check` - 비밀번호 동일문자,연속문자 반복 불가 횟수
|
||||
- `rms.password.sms_auth.enabled` - 비밀번호 변경시 2-factor 인증 여부
|
||||
|
||||
**인터페이스 배포**
|
||||
- `iomap.download.path` - 인터페이스.json 파일 다운로드 서버 경로
|
||||
- `iomap.upload.path` - 인터페이스.json 파일 업로드 서버 경로
|
||||
|
||||
**UMS(공통) 연동**
|
||||
- `djb.ums.was_ip_address` - 관리자포탈 WAS IP 주소 (UMS 연동시 필요)
|
||||
- `djb.ums.was_mac_address` - 관리자포탈 WAS MAC 주소 (UMS 연동 시 필요)
|
||||
|
||||
**UMS(이메일) 연동**
|
||||
- `djb.ums.email.url` - 이메일 API 엔드포인트 (EAI)
|
||||
- `djb.ums.email.cstno` - 고객번호
|
||||
- `djb.ums.email.ums_evnt_id` - UMS이벤트 ID
|
||||
|
||||
**UMS(사내메신저) 연동**
|
||||
- `djb.ums.messenger.url` - 사내메신저 API 앤드포인트 (FEP)
|
||||
- `djb.ums.messenger.client_id` - 사내메신저 클라이언트 아이디
|
||||
- `djb.ums.messenger.client_secret` - 사내메신저 클라이언트 시크릿
|
||||
- `djb.ums.messenger.api-monitor.enabled` - API 상태변화시 직원에게 사내메신저 발송 여부
|
||||
|
||||
**UMS(SMS) 연동**
|
||||
- `djb.ums.sms.url` - SMS API 엔드포인트 (EAI)
|
||||
- `djb.ums.sms.almtk_snd_prtl_key.intra` - 카톡 알림톡 채널 ID (제주은행 임직원)
|
||||
- `djb.ums.sms.almtk_snd_prtl_key.public` - 카톡 알림톡 채널 ID (제주은행)
|
||||
|
||||
**Webhook 발송**
|
||||
- `rms.webhook.enabled` - 웹훅 발송 여부
|
||||
- `rms.webhook.retry_count` - 웹훅 재발송 횟수
|
||||
- `rms.webhook.retry_time` - 웹훅 재발송 시간간격
|
||||
- `rms.webhook.reverse_proxy.url` - 리버스 프록시 경로
|
||||
#### 로컬 개발용
|
||||
```
|
||||
-Deai.datasource.type=DEV
|
||||
-Dinst.Name=emsSvr99
|
||||
-Deai.tableowner=EMSAPP
|
||||
-Deai.systemmode=D
|
||||
-Dfile.encoding=utf-8
|
||||
-Dlogin.mode=db
|
||||
-DLOGBACK_LOG_LEVEL=DEBUG
|
||||
-Dlogback.configurationFile=classpath:logback-dev.xml
|
||||
-Dhibernate.dialect=org.hibernate.dialect.Oracle12cDialect
|
||||
-Dkjb_safedb.mode=fake
|
||||
|
||||
|
||||
> Portal 프라퍼티 (파트너포탈 > 포탈관리 > Property관리)
|
||||
-Dlogback.configurationFile="C:\eactive\workspaces\kjb-eapim\eapim-admin\src\main\resources\logback-rinjae.xml"
|
||||
```
|
||||
|
||||
**UMS 연동**
|
||||
- `djb.ums.email.if_id` - 이메일 연동 인터페이스 ID
|
||||
- `djb.ums.email.tx_id` - 이메일 거래 ID
|
||||
- `djb.ums.messenger.if_id` - 사내메신저 인터페이스 ID
|
||||
- `djb.ums.messenger.tx_id` - 사내메신저 거래 ID
|
||||
- `djb.ums.sms.if_id` - SMS 인터페이스 ID
|
||||
- `djb.ums.sms.tx_id` - SMS 거래 ID
|
||||
### 데이터베이스 지원
|
||||
|
||||
- **Oracle**: 운영 데이터베이스 (주)
|
||||
- **PostgreSQL/MariaDB**: 대체 운영 DB
|
||||
- **H2**: 테스트용 인메모리 데이터베이스
|
||||
|
||||
## 스케쥴러
|
||||
데이터베이스 벤더는 프로퍼티 파일의 `db.vendor`와 `hibernate.dialect`를 통해 설정됩니다.
|
||||
|
||||
**API 상태 모니터링**
|
||||
- `ApiStatusMonitorJob11, ApiStatusMonitorJob21` - API 상태 변화를 모니터링한다. API 상태 변화 발생시 직원에게 메신저 및 제휴사에게 웹훅을 발송한다.
|
||||
- 실행간격(1분)이 짧아서 CLUSTERED는 적용을 못하고, 서버수만큼 등록하여 번갈아 가면서 실행하도록 설정
|
||||
- `api.status.error.range_minute` - API 장애 판단을 위한 로그 구간(분)
|
||||
- `api.status.error.rate` - API 장애판단을 위한 오류율 (ex : 10분동안 90% 이상 오류 발생시 장애로 판단)
|
||||
- `api.status.delay.range_minute` - API 지연 상태 판단을 위한 로그 구간(분)
|
||||
- `api.status.delay.avg_resp_time` - API 지연 상태 판단을 위한 평균 응답속도 (ex: 10분동안 평균 응답속도가 10초이상이면 지연으로 판단)
|
||||
### SafeDB 암호화 라이브러리
|
||||
|
||||
**유량제어토큰실패감시**
|
||||
- `InflowTokenMonitorJob` - 유량제어 토큰 획득 실패(TSEAIFR11) 발생시 직원에게 메신저를 발송한다
|
||||
- `api.inflow.fail.range_minute` - 최근 몇분동안 감시하는지 설정
|
||||
`kjb-safedb` 모듈은 세 가지 동작 모드를 제공합니다:
|
||||
- **REAL**: 실제 SafeDB 네이티브 라이브러리 사용
|
||||
- **FAKE**: SHA-256/AES-256을 사용하는 개발 모드 (SafeDB 설치 불필요)
|
||||
- **NONE**: 암호화 우회 (테스트 전용)
|
||||
|
||||
**API 통계**
|
||||
- `ApiStatsHourlyAggregationJob` - 당일 API 로그 정보를 시간별로 집계 (TSEAILG00 > API_STATS_HOUR)
|
||||
- `APISTATSDAILYJOB` - API 사용통계 일집계 (API_STATS_HOUR > API_STATS_DAY)
|
||||
- `APISTATSMONTHLYJOB` - API 사용통계 월집계 (API_STATS_DAY > API_STATS_MONTH)
|
||||
- `APISTATSYEARLYJOB` - API 사용통계 년집계 (API_STATS_MONTH > API_STATS_YEAR)
|
||||
|
||||
**UMS발송**
|
||||
- `UMSDISPATCHJOB11, UMSDISPATCHJOB21` - UMS를 발송한다.
|
||||
- 실행간격(5초)이 짧아서 CLUSTERED는 적용을 못하고, 서버수만큼 등록하여 번갈아 가면서 실행하도록 설정
|
||||
|
||||
**게시글종료**
|
||||
- `PortalInquiryClosingJob` - 답변완료 이후 일정기간 댓글이 없는 경우 게시글을 종료한다
|
||||
- `inquiry.comment.closing_day` - 게시글 종료 대기일수
|
||||
|
||||
**메모리초기화**
|
||||
- `MEMORYTRINIT` - 관리자포탈 서버 메모리 초기화(G/W에서 받은 거래현황)
|
||||
|
||||
**토큰발급이력삭제**
|
||||
- `TOKENISSUANCELOGCLEANUPJOB` - 7일이 지난 토큰 발급내역을 삭제한다
|
||||
개발 시 SafeDB가 로컬에 설치되지 않은 경우 `-Dkjb_safedb.mode=fake`를 설정합니다.
|
||||
|
||||
## 배포
|
||||
|
||||
| 대상 | 비고 |
|
||||
|---|---|
|
||||
| Tomcat | 기본, 개발 |
|
||||
| WebLogic 14.1.2 | 운영, `-Pprofile=weblogic` 필요 |
|
||||
| JEUS | 지원 |
|
||||
| JBoss / WildFly | descriptor 포함 지원 |
|
||||
### 지원 서버
|
||||
- **Tomcat** (기본, 개발)
|
||||
- **WebLogic 14.1.2** (운영, `-Pprofile=weblogic` 필요)
|
||||
- **JEUS** (지원)
|
||||
- **JBoss/WildFly** (descriptors와 함께 지원)
|
||||
|
||||
- 모든 관리 변경사항은 DB 와 메모리에 즉시 반영됩니다.
|
||||
- 인코딩은 UTF-8. `CharacterEncodingFilter` 가 `*.excel` 을 제외한 모든 요청에 적용.
|
||||
- XSS 보호: `com.eactive.eai.rms.common.filter.CrossScriptingFilter` 전역 적용.
|
||||
### WebLogic 전용 빌드
|
||||
|
||||
WebLogic용 빌드 시, DefaultServlet 문제를 해결하기 위해 `web.xml`을 `weblogic-web.xml`로 교체하는 `weblogic` 프로필을 사용합니다:
|
||||
|
||||
```bash
|
||||
gradle build -x test -Pprofile=weblogic
|
||||
```
|
||||
|
||||
### WAR 배포
|
||||
|
||||
WAR 파일은 context path `/monitoring`으로 `eapim-admin.war`로 빌드됩니다.
|
||||
|
||||
## IDE 설정
|
||||
|
||||
`build.gradle` 은 `eclipse` / `eclipse-wtp` / `idea` 플러그인을 모두 포함합니다.
|
||||
IntelliJ 전용 변형은 `build.gradle.intellij` 로 별도 관리합니다 (필요 시 `build.gradle` 로 교체).
|
||||
### Eclipse
|
||||
|
||||
```bash
|
||||
# Eclipse
|
||||
./gradlew eclipse # .project / .classpath, WTP(context path: /monitoring), APT 설정 생성
|
||||
|
||||
# IntelliJ IDEA
|
||||
./gradlew idea # 또는 IDE 에서 "Import Gradle Project"
|
||||
gradle eclipse
|
||||
```
|
||||
|
||||
다음을 생성합니다:
|
||||
- `.project` 및 `.classpath` 파일
|
||||
- Eclipse WTP 설정 (context path: `/monitoring`)
|
||||
- UTF-8 인코딩 설정
|
||||
- QueryDSL/Lombok을 위한 애노테이션 프로세서 설정
|
||||
|
||||
### IntelliJ IDEA
|
||||
|
||||
```bash
|
||||
gradle idea
|
||||
```
|
||||
|
||||
또는 IntelliJ에서 직접 "Import Gradle Project"를 사용합니다.
|
||||
|
||||
## KJB (광주은행) 전용 설정
|
||||
|
||||
### 프로퍼티 관리
|
||||
KJB 전용 프로퍼티는 데이터베이스 테이블 `TSEAIRM24`에 그룹명 `'Monitoring'`으로 저장됩니다.
|
||||
|
||||
|
||||
### 주요 프로퍼티 설정
|
||||
|
||||
**이메일 알림 (EAI Batch를 통해)**
|
||||
- `kjb.eai_batch.url` - 이메일 발송을 위한 EAI 배치 서버 URL
|
||||
- 개발: http://172.31.32.111:10230/BATAppWeb/EaiBatCall
|
||||
- QA: http://172.31.33.111:10430/BATAppWeb/EaiBatCall
|
||||
- 운영: http://172.21.1.40:10860/BATAppWeb/EaiBatCall
|
||||
- 입력하지 않을 경우 EAI 배치 호출을 하지 않고 내부적으로 통신하지 않으며 warning 로그만 남김
|
||||
- `kjb.ums.eai_batch.send_dir` - 이메일 파일 발송 디렉토리 (기본값: `/Data/eapim/portal/sendmail/snd`)
|
||||
- `kjb.ums.eai_batch.backup_dir` - 이메일 백업 디렉토리 (기본값: `/Data/eapim/portal/sendmail/bak`)
|
||||
- `kjb.ums.eai_batch.interface_id` - 고객 이메일용 인터페이스 ID (예: `UAGFF00001UIE`)
|
||||
|
||||
**UMS (SMS/LMS/카카오톡) 연동**
|
||||
- `kjb.ums.host_url` - UMS API 엔드포인트
|
||||
- `kjb.ums.api_key` - UMS API 인증 키
|
||||
- `kjb.ums.connection_timeout` - 연결 타임아웃 (초 단위)
|
||||
- `kjb.ums.response_timeout` - 응답 타임아웃 (초 단위)
|
||||
|
||||
### 보안
|
||||
- 커스텀 패스워드 인코더: SafeDB 연동을 위한 `KjbSafedbPasswordEncoder`
|
||||
|
||||
## 테스트
|
||||
|
||||
- **JUnit 5 (Jupiter)** - 테스트 프레임워크
|
||||
- **Spring Boot Test 2.6.15** - 통합 테스트 유틸리티
|
||||
- **H2** - 인메모리 DB (`src/test/resources`)
|
||||
- **Mockito** - 모킹 (Spring Boot Test 경유)
|
||||
테스트에 사용되는 것들:
|
||||
- **JUnit 5 (Jupiter)**: 테스트 프레임워크
|
||||
- **Spring Boot Test 2.6.15**: 통합 테스트 유틸리티
|
||||
- **H2 Database**: 테스트용 인메모리 데이터베이스
|
||||
- **Mockito**: 모킹 프레임워크 (Spring Boot Test를 통해)
|
||||
|
||||
```bash
|
||||
./gradlew test
|
||||
./gradlew test --tests "com.eactive.eai.rms.*"
|
||||
# 모든 테스트 실행
|
||||
gradle test
|
||||
|
||||
# 특정 테스트 클래스 실행
|
||||
gradle test --tests "com.example.ClassName"
|
||||
|
||||
# 특정 테스트 패키지 실행 (JUnit 플랫폼 사용)
|
||||
gradle test --tests "com.eactive.eai.rms.*"
|
||||
```
|
||||
|
||||
## 관련 프로젝트
|
||||
|
||||
프로젝트 구조 (kjb-eapim 디렉토리 기준):
|
||||
- `eapim-admin/` - Admin 관리 콘솔 (현재 프로젝트)
|
||||
- `eapim-online/` - Online 게이트웨이 (코어 모듈 포함)
|
||||
- `eapim-portal/` - Portal 개발자 포털
|
||||
- `elink-portal-common/` - 공통 유틸리티 라이브러리
|
||||
- `kjb-safedb/` - SafeDB 암호화 라이브러리
|
||||
- `kjb-eapim-sql/` - SQL 스크립트 (선택사항)
|
||||
|
||||
## 문서
|
||||
|
||||
- **프로젝트 상세 지침**: `CLAUDE.md` (AI 어시스턴트 및 개발자용 마스터 문서, 한글)
|
||||
- **사용자 가이드**: `WebContent/guide/` (docsify)
|
||||
- **프로젝트 상세 지침**: `CLAUDE.md` (한글)
|
||||
- **사용자 가이드**: `WebContent/guide/` (한글)
|
||||
|
||||
## 참고: KJB 데이터베이스 파라미터 설정
|
||||
|
||||
### 추가 파라미터 SQL
|
||||
```sql
|
||||
-- 로컬 개발용 (외부)
|
||||
-- EAI, UMS 주소 미기입시 내부적으론 호출 시도 하지 않고 warning 로그만 남기며 정상 처리
|
||||
--insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.eai_batch.url', '');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.eai.batch.connection_timeout', '5');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.eai.batch.response_timeout', '5');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.eai.batch.timeout', '10');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.eai.batch.charset', 'euc-kr');
|
||||
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.eai_batch.send_dir', '/Data/eapim/portal/sendmail/snd');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.eai_batch.backup_dir', '/Data/eapim/portal/sendmail/bak');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.eai_batch.error_dir', '/Data/eapim/portal/sendmail/bak');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.eai_batch.retention_date', '60');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.eai_batch.interface_id', 'UIEFF00001UAG');
|
||||
|
||||
-- insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.host_url', '');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.api_key', '7cca6d58-e4f7-474d-9edd-525806bc99ff');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.connection_timeout', '5');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.response_timeout', '5');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.timeout', '10');
|
||||
|
||||
--insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.obp.host_url', '');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.obp.trust_system', 'APIMPT-0002-QVBJTVBULTAwMDI=');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.obp.commission_uri', '/api/billing/findBillingForCondition');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.obp.commission_print_uri', '/api/billing/billing/findBillingForCondition/print');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.obp.query_partnercode_uri', '/api/customer/findCustomerByBusinessManRegistrationNo/%s');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.obp.create_partnercode_uri', '/not-yet-implement/%s');
|
||||
commit;
|
||||
|
||||
|
||||
-- 은행 개발 서버용
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.eai_batch.url', 'http://172.31.32.111:10230/BATAppWeb/EaiBatCall');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.eai.batch.connection_timeout', '5');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.eai.batch.response_timeout', '5');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.eai.batch.timeout', '10');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.eai.batch.charset', 'euc-kr');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.eai_batch.send_dir', '/Data/eapim/portal/sendmail/snd');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.eai_batch.backup_dir', '/Data/eapim/portal/sendmail/bak');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.eai_batch.error_dir', '/Data/eapim/portal/sendmail/bak');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.eai_batch.retention_date', '60');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.eai_batch.interface_id', 'UIEFF00001UAG');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.host_url', 'http://172.31.35.144:9021');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.api_key', '7cca6d58-e4f7-474d-9edd-525806bc99ff');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.connection_timeout', '5');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.response_timeout', '5');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.timeout', '10');
|
||||
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.obp.host_url', 'http://192.168.246.15:8181');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.obp.trust_system', 'APIMPT-0002-QVBJTVBULTAwMDI=');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.obp.commission_uri', '/api/billing/findBillingForCondition');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.obp.commission_print_uri', '/api/billing/billing/findBillingForCondition/print');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.obp.query_partnercode_uri', '/api/customer/findCustomerByBusinessManRegistrationNo/%s');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.obp.create_partnercode_uri', '/not-yet-implement/%s');
|
||||
commit;
|
||||
|
||||
|
||||
-- 은행 운영 서버용
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.eai_batch.url', 'http://172.21.1.40:10860/BATAppWeb/EaiBatCall');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.eai.batch.connection_timeout', '5');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.eai.batch.response_timeout', '5');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.eai.batch.timeout', '10');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.eai.batch.charset', 'euc-kr');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.eai_batch.send_dir', '/Data/eapim/portal/sendmail/snd');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.eai_batch.backup_dir', '/Data/eapim/portal/sendmail/bak');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.eai_batch.error_dir', '/Data/eapim/portal/sendmail/bak');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.eai_batch.retention_date', '60');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.eai_batch.interface_id', 'UIEFF00001UAG');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.host_url', 'http://172.24.128.66:9021');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.api_key', '7cca6d58-e4f7-474d-9edd-525806bc99ff');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.connection_timeout', '5');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.response_timeout', '5');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.timeout', '10');
|
||||
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.obp.host_url', 'http://192.168.246.15:8181');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.obp.trust_system', 'APIMPT-0002-QVBJTVBULTAwMDI=');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.obp.commission_uri', '/api/billing/findBillingForCondition');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.obp.commission_print_uri', '/api/billing/billing/findBillingForCondition/print');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.obp.query_partnercode_uri', '/api/customer/findCustomerByBusinessManRegistrationNo/%s');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.obp.create_partnercode_uri', '/not-yet-implement/%s');
|
||||
commit;
|
||||
```
|
||||
## 라이선스
|
||||
|
||||
사내 전용(proprietary). 제주은행 eLink EMS.
|
||||
© 광주은행 (Kwangju Bank)
|
||||
|
||||
+2
-2
@@ -7,7 +7,7 @@ include 'elink-online-common'
|
||||
include 'elink-online-emsclient'
|
||||
include 'elink-portal-common'
|
||||
//include 'kjb-safedb'
|
||||
//include 'eapim-admin-djb'
|
||||
include 'eapim-admin-djb'
|
||||
|
||||
project (':elink-online-core-jpa').projectDir = new File(settingsDir, "../eapim-online/elink-online-core-jpa")
|
||||
project (':elink-online-core').projectDir = new File(settingsDir, "../eapim-online/elink-online-core")
|
||||
@@ -16,4 +16,4 @@ project (':elink-online-common').projectDir = new File(settingsDir, "../eapim-on
|
||||
project (':elink-online-emsclient').projectDir = new File(settingsDir, "../eapim-online/elink-online-emsclient")
|
||||
project (':elink-portal-common').projectDir = new File(settingsDir, "../elink-portal-common")
|
||||
//project (':kjb-safedb').projectDir = new File(settingsDir, "../kjb-safedb")
|
||||
//project (':eapim-admin-djb').projectDir = new File(settingsDir, "../eapim-admin-djb")
|
||||
project (':eapim-admin-djb').projectDir = new File(settingsDir, "../eapim-admin-djb")
|
||||
+1
-3
@@ -41,12 +41,10 @@ public class AuditLogInterceptor implements HandlerInterceptor {
|
||||
|
||||
if (isAuditableUsecase.isAuditable(userId, command, logType, systemCode)) {
|
||||
String parameters = getParametersAsString(request);
|
||||
String message = request.getParameter("auditReason");
|
||||
log
|
||||
.debug("audit log save : logType={}, cmd={}, user={}, parameters={}", logType, command,
|
||||
userId, parameters);
|
||||
saveAuditLogUseCase
|
||||
.log(systemCode, logType, command, remoteAddress, userId, parameters, message);
|
||||
saveAuditLogUseCase.log(systemCode, logType, command, remoteAddress, userId, parameters);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
-3
@@ -51,9 +51,6 @@ public class AuditLogPageRepositoryImpl implements AuditLogPageRepository {
|
||||
if (StringUtils.isNotBlank(command.getSearchRemoteAddress())) {
|
||||
jpaQuery.where(qAuditLogEntity.remoteAddress.containsIgnoreCase(command.getSearchRemoteAddress()));
|
||||
}
|
||||
if (StringUtils.isNotBlank(command.getSearchParameters())) {
|
||||
jpaQuery.where(qAuditLogEntity.parameters.containsIgnoreCase(command.getSearchParameters()));
|
||||
}
|
||||
|
||||
long totalCount = jpaQuery.fetchOne();
|
||||
List<AuditLogEntity> auditLogs = jpaQuery
|
||||
|
||||
@@ -23,8 +23,6 @@ public class AuditLog {
|
||||
|
||||
private String logTypeText;
|
||||
|
||||
private String message;
|
||||
|
||||
private String parameters;
|
||||
|
||||
private LocalDateTime lastModifiedDate;
|
||||
|
||||
@@ -3,6 +3,6 @@ package com.eactive.eai.rms.common.acl.audit.port.in;
|
||||
public interface SaveAuditLogUseCase {
|
||||
|
||||
public void log(String systemCode, String logType, String command, String remoteAddress, String userId,
|
||||
String parameters, String message);
|
||||
String parameters);
|
||||
|
||||
}
|
||||
|
||||
-1
@@ -18,7 +18,6 @@ public class SelectListAuditLogCommand {
|
||||
|
||||
private String searchUserId;
|
||||
private String searchRemoteAddress;
|
||||
private String searchParameters;
|
||||
|
||||
@JsonFormat(pattern = "yyyyMMddHHmmss")
|
||||
@DateTimeFormat(pattern = "yyyyMMddHHmmss")
|
||||
|
||||
@@ -59,16 +59,15 @@ class AuditLogService
|
||||
|
||||
@Override
|
||||
public void log(String systemCode, String logType, String command, String remoteAddress, String userId,
|
||||
String parameters, String message) {
|
||||
String parameters) {
|
||||
|
||||
AuditLog auditLog = new AuditLog(systemCode, logType, command, userId, remoteAddress);
|
||||
String auditPointKey = AuditPoint.generateKey(logType, systemCode, command);
|
||||
AuditPoint auditPoint = auditPoints.get(auditPointKey);
|
||||
|
||||
|
||||
auditLog.setLogTypeText(auditPoint.getLogTypeText());
|
||||
auditLog.setLastModifiedDate(LocalDateTime.now());
|
||||
auditLog.setParameters(parameters);
|
||||
auditLog.setMessage(message);
|
||||
|
||||
saveAuditLogPort.save(auditLog);
|
||||
}
|
||||
|
||||
@@ -15,7 +15,6 @@ import org.springframework.web.bind.annotation.GetMapping;
|
||||
import com.eactive.eai.rms.common.acl.menu.ui.LeftMenuUI;
|
||||
import com.eactive.eai.rms.common.acl.role.RoleManService;
|
||||
import com.eactive.eai.rms.common.base.BaseAnnotationController;
|
||||
import com.eactive.eai.rms.common.context.MonitoringContext;
|
||||
import com.eactive.eai.rms.common.datasource.DataSourceContextHolder;
|
||||
import com.eactive.eai.rms.common.login.SessionManager;
|
||||
import com.eactive.eai.rms.data.entity.man.menu.MenuService;
|
||||
@@ -32,9 +31,6 @@ public class MenuRenderController extends BaseAnnotationController {
|
||||
@Autowired
|
||||
private MenuRenderService menuRenderService;
|
||||
|
||||
@Autowired
|
||||
private MonitoringContext monitoringContext;
|
||||
|
||||
@GetMapping("/left_top.do")
|
||||
public String left_top(HttpServletRequest request, ModelMap modelMap) {
|
||||
return "/common/screen/left_top";
|
||||
@@ -91,9 +87,6 @@ public class MenuRenderController extends BaseAnnotationController {
|
||||
List<DataSourceType> roleServiceTypeList = roleManService.selectRoleServiceTypeList(roleIds);
|
||||
modelMap.addAttribute("roleServiceTypeList", roleServiceTypeList);
|
||||
|
||||
modelMap.addAttribute("autoLogoutTimeout",
|
||||
monitoringContext.getIntProperty(MonitoringContext.RMS_AUTO_LOGOUT_TIMEOUT, 30));
|
||||
|
||||
if (StringUtils.isNotBlank(mainPage)) {
|
||||
for (Map<String, Object> map : dataList) {
|
||||
String url = (String) map.get("LEV4_URL");
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
package com.eactive.eai.rms.common.acl.sitemap;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
|
||||
import com.eactive.eai.rms.common.base.BaseAnnotationController;
|
||||
import com.eactive.eai.rms.common.login.SessionManager;
|
||||
|
||||
@Controller
|
||||
@RequiredArgsConstructor
|
||||
public class SitemapController extends BaseAnnotationController {
|
||||
|
||||
private final SitemapService sitemapService;
|
||||
|
||||
@GetMapping(value = "/common/acl/sitemap/sitemapMan.view")
|
||||
public String view(@RequestParam(value = "serviceType", required = false) String serviceType,
|
||||
HttpServletRequest request, Model model) {
|
||||
List<String> roleIds = SessionManager.getRoleId(request);
|
||||
model.addAttribute("sitemapTree", sitemapService.getSitemapTree(roleIds, serviceType));
|
||||
return "/common/acl/sitemap/sitemapMan";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
package com.eactive.eai.rms.common.acl.sitemap;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.eactive.eai.rms.common.acl.sitemap.ui.SitemapNode;
|
||||
|
||||
public interface SitemapService {
|
||||
|
||||
public List<SitemapNode> getSitemapTree(List<String> roleIds, String serviceType);
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user