Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b7c8e7f41c | |||
| 6c0fca22f3 |
Vendored
+153
-147
@@ -1,174 +1,180 @@
|
|||||||
pipeline {
|
pipeline {
|
||||||
agent none
|
agent { label 'djb-vm' }
|
||||||
|
|
||||||
options {
|
options {
|
||||||
timestamps()
|
timestamps()
|
||||||
disableConcurrentBuilds()
|
disableConcurrentBuilds()
|
||||||
skipDefaultCheckout() // stage별 agent의 자동 SCM checkout 방지 (Deploy 노드는 git 접근 불필요, unstash로만 WAR 수신)
|
|
||||||
buildDiscarder(logRotator(numToKeepStr: '20', artifactNumToKeepStr: '20'))
|
buildDiscarder(logRotator(numToKeepStr: '20', artifactNumToKeepStr: '20'))
|
||||||
}
|
}
|
||||||
|
|
||||||
environment {
|
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'
|
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 {
|
stages {
|
||||||
stage('Build (djb-vm)') {
|
stage('Checkout') {
|
||||||
agent { label 'djb-vm' }
|
steps {
|
||||||
environment {
|
checkout scm
|
||||||
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'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
stage('Deploy (weblogic)') {
|
stage('Checkout dependencies') {
|
||||||
agent { label 'weblogic' }
|
steps {
|
||||||
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') {
|
|
||||||
sh '''
|
sh '''
|
||||||
set +e
|
set -eu
|
||||||
payload=$(printf '{"text":"[%s #%s](%s) SUCCESS"}' "$JOB_NAME" "$BUILD_NUMBER" "$BUILD_URL")
|
cd "$WORKSPACE/.."
|
||||||
curl -sS --fail --max-time 5 -H 'Content-Type: application/json' -X POST --data "$payload" "$WEBHOOK_URL" >/dev/null || echo "Webhook failed"
|
|
||||||
|
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 '''
|
sh '''
|
||||||
set +e
|
set +e
|
||||||
payload=$(printf '{"text":"[%s #%s](%s) FAILURE"}' "$JOB_NAME" "$BUILD_NUMBER" "$BUILD_URL")
|
systemctl --user stop eapim-admin 2>/dev/null
|
||||||
curl -sS --fail --max-time 5 -H 'Content-Type: application/json' -X POST --data "$payload" "$WEBHOOK_URL" >/dev/null || echo "Webhook failed"
|
|
||||||
|
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
|
||||||
'''
|
'''
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,4 +16,3 @@ PortalPartnershipManController_피드백/개선요청관리_APIGW_UNMASK,DELETE
|
|||||||
PortalPropertyController_포탈 프로퍼티 관리_APIGW_UPDATE
|
PortalPropertyController_포탈 프로퍼티 관리_APIGW_UPDATE
|
||||||
PortalUserTermsManController_약관동의 이력_APIGW_UNMASK
|
PortalUserTermsManController_약관동의 이력_APIGW_UNMASK
|
||||||
MessageRequestManController_메세지발송내역_APIGW_UPDATE_STATUS
|
MessageRequestManController_메세지발송내역_APIGW_UPDATE_STATUS
|
||||||
PortalInquiryManController_Q&A문의관리_APIGW_VISIBILITY
|
|
||||||
|
|||||||
@@ -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"?>
|
<?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>
|
<context-root>monitoring</context-root>
|
||||||
<session-descriptor>
|
<session-descriptor>
|
||||||
<timeout-secs>1800</timeout-secs>
|
<timeout-secs>1800</timeout-secs>
|
||||||
<cookie-name>JSESSIONID_EMS</cookie-name>
|
|
||||||
<persistent-store-type>replicated_if_clustered</persistent-store-type>
|
<persistent-store-type>replicated_if_clustered</persistent-store-type>
|
||||||
</session-descriptor>
|
</session-descriptor>
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
@charset "utf-8";
|
@charset "utf-8";
|
||||||
|
|
||||||
.gnb .depth1 > li:hover > a{color:#008cc5;} /* theme */
|
.gnb .depth1 > li:hover > a{color:#008cc5;} /* theme */
|
||||||
.gnb .sitemap-link:hover{color:#008cc5;} /* theme */
|
|
||||||
.gnb .depth1 > li > .red_box{background:#008cc5;}/* theme */
|
.gnb .depth1 > li > .red_box{background:#008cc5;}/* theme */
|
||||||
.gnb .depth2 > li > a:hover{color:#008cc5;}/* theme */
|
.gnb .depth2 > li > a:hover{color:#008cc5;}/* theme */
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
@charset "utf-8";
|
@charset "utf-8";
|
||||||
|
|
||||||
.gnb .depth1 > li:hover > a{color:#16a085;} /* theme */
|
.gnb .depth1 > li:hover > a{color:#16a085;} /* theme */
|
||||||
.gnb .sitemap-link:hover{color:#16a085;} /* theme */
|
|
||||||
.gnb .depth1 > li > .red_box{background:#16a085;}/* theme */
|
.gnb .depth1 > li > .red_box{background:#16a085;}/* theme */
|
||||||
.gnb .depth2 > li > a:hover{color:#16a085;}/* theme */
|
.gnb .depth2 > li > a:hover{color:#16a085;}/* theme */
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
@charset "utf-8";
|
@charset "utf-8";
|
||||||
|
|
||||||
.gnb .depth1 > li:hover > a{color:#e74c3c;} /* theme */
|
.gnb .depth1 > li:hover > a{color:#e74c3c;} /* theme */
|
||||||
.gnb .sitemap-link:hover{color:#e74c3c;} /* theme */
|
|
||||||
.gnb .depth1 > li > .red_box{background:#e74c3c;}/* theme */
|
.gnb .depth1 > li > .red_box{background:#e74c3c;}/* theme */
|
||||||
.gnb .depth2 > li > a:hover{color:#e74c3c;}/* theme */
|
.gnb .depth2 > li > a:hover{color:#e74c3c;}/* theme */
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
@charset "utf-8";
|
@charset "utf-8";
|
||||||
|
|
||||||
.gnb .depth1 > li:hover > a{color:#ff80c0;} /* theme */
|
.gnb .depth1 > li:hover > a{color:#ff80c0;} /* theme */
|
||||||
.gnb .sitemap-link:hover{color:#ff80c0;} /* theme */
|
|
||||||
.gnb .depth1 > li > .red_box{background:#ff80c0;}/* theme */
|
.gnb .depth1 > li > .red_box{background:#ff80c0;}/* theme */
|
||||||
.gnb .depth2 > li > a:hover{color:#ff80c0;}/* theme */
|
.gnb .depth2 > li > a:hover{color:#ff80c0;}/* theme */
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
@charset "utf-8";
|
@charset "utf-8";
|
||||||
|
|
||||||
.gnb .depth1 > li:hover > a{color:#be8200;} /* theme */
|
.gnb .depth1 > li:hover > a{color:#be8200;} /* theme */
|
||||||
.gnb .sitemap-link:hover{color:#be8200;} /* theme */
|
|
||||||
.gnb .depth1 > li > .red_box{background:#be8200;}/* theme */
|
.gnb .depth1 > li > .red_box{background:#be8200;}/* theme */
|
||||||
.gnb .depth2 > li > a:hover{color:#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); }
|
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_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:180px; display:inline-block; height:80px; overflow-y:hidden;}
|
.gnb{position:absolute; top:0px; left:230px; display:inline-block; height:80px; overflow-y:hidden;}
|
||||||
.gnb:hover{height:auto; overflow-y:visible;}
|
.gnb:hover{height:auto; overflow-y:visible;}
|
||||||
.gnb:hover + .gnb_bg{display:block;}
|
.gnb:hover + .gnb_bg{display:block;}
|
||||||
.gnb h1{float:left; display:block; width:auto; height:100%; line-height:80px;}
|
.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{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{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 > 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; }
|
.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 .red_box{transform:scaleX(1);}
|
||||||
.gnb .depth1 > li:hover > a{opacity: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 .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 .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 .depth1 > li:first-child .depth2{border-left:1px solid #eee;}
|
.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{display:block; color:#000; font-size:13px; line-height:30px;}
|
||||||
.gnb .depth2 > li > a:hover{color:#ff0000;}
|
.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;}
|
.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{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{}
|
||||||
.content_top .path > li{display:inline-block; margin:0; padding:0;}
|
.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; text-decoration:none;}
|
.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:hover{color:#666;}
|
||||||
.content_top .path > li a:before{content:'>'; margin-right:10px;}
|
.content_top .path > li a:before{content:'>'; margin-right:10px;}
|
||||||
.content_top .path > li:first-child a:before{content:'';}
|
.content_top .path > li:first-child a:before{content:'';}
|
||||||
|
|||||||
@@ -25,10 +25,6 @@
|
|||||||
components: [], // 재사용 스키마(POC: 빈 상태)
|
components: [], // 재사용 스키마(POC: 빈 상태)
|
||||||
};
|
};
|
||||||
|
|
||||||
// GW authtype(oauth/api_key 등, 서버 DETAIL_SPEC 응답). 인증 badge 라벨 판별용.
|
|
||||||
// (enricher 는 oauth·api_key 를 모두 apiKey-in-header 로 모델링하므로 scheme.type 만으로 구분 불가)
|
|
||||||
var djbAuthType = '';
|
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
// 2. 유틸
|
// 2. 유틸
|
||||||
// ============================================================
|
// ============================================================
|
||||||
@@ -193,23 +189,15 @@
|
|||||||
|
|
||||||
// 경로(op.path.value)에는 어댑터경로가 이미 baking 됨(백엔드 applyAutoRules). 서버에는 응답유형별 호스트만 붙인다.
|
// 경로(op.path.value)에는 어댑터경로가 이미 baking 됨(백엔드 applyAutoRules). 서버에는 응답유형별 호스트만 붙인다.
|
||||||
// - gw : djb.gateway.base-url (PortalProperty) → [gw호스트 + 어댑터경로 + 경로]
|
// - gw : djb.gateway.base-url (PortalProperty) → [gw호스트 + 어댑터경로 + 경로]
|
||||||
// - mock : Mock Server URL(전체 URL) → [mock URL 그대로] (어댑터/오퍼레이션 경로 미부착)
|
// - mock : Mock Server URL → [mock호스트 + 어댑터경로 + 경로]
|
||||||
// - 그 외 : 호스트 없음 → [어댑터경로 + 경로]
|
// - 그 외 : 호스트 없음 → [어댑터경로 + 경로]
|
||||||
var _rt = (d.portal && d.portal.responseType) || 'sample';
|
var _rt = (d.portal && d.portal.responseType) || 'sample';
|
||||||
var _host;
|
var _host;
|
||||||
var _pathKey = op.path.value;
|
if (_rt === 'gw') _host = (window.DJB_CTX && window.DJB_CTX.gwAddress) || 'http://127.0.0.1:39310';
|
||||||
if (_rt === 'gw') {
|
else if (_rt === 'mock') _host = (d.portal && d.portal.mockUrl) || '';
|
||||||
_host = (window.DJB_CTX && window.DJB_CTX.gwAddress) || 'http://127.0.0.1:39310';
|
else _host = '';
|
||||||
} else if (_rt === 'mock') {
|
|
||||||
// Mock 은 전체 호출 URL. origin=서버, pathname(+query)=경로로 분해해 경로가 뒤에 덧붙지 않도록 한다.
|
|
||||||
var _mk = splitFullUrl((d.portal && d.portal.mockUrl) || '');
|
|
||||||
_host = _mk.origin;
|
|
||||||
_pathKey = _mk.path;
|
|
||||||
} else {
|
|
||||||
_host = '';
|
|
||||||
}
|
|
||||||
if (_host) spec.servers = [{ url: _host }];
|
if (_host) spec.servers = [{ url: _host }];
|
||||||
spec.paths[_pathKey] = { [op.method.value.toLowerCase()]: operation };
|
spec.paths[op.path.value] = { [op.method.value.toLowerCase()]: operation };
|
||||||
|
|
||||||
// 보안 스킴
|
// 보안 스킴
|
||||||
d.securitySchemes.forEach(s => {
|
d.securitySchemes.forEach(s => {
|
||||||
@@ -260,11 +248,9 @@
|
|||||||
if (row.description && row.description.value) obj.properties[name].description = row.description.value;
|
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;
|
if (row.name && row.name.locked) obj.properties[name]['x-djb-gw'] = true;
|
||||||
} else if (row.type.value === 'array') {
|
} else if (row.type.value === 'array') {
|
||||||
const itemType = (row.itemsType && row.itemsType.value) || 'string';
|
|
||||||
obj.properties[name] = {
|
obj.properties[name] = {
|
||||||
type: 'array',
|
type: 'array',
|
||||||
// array<object>(GW GRID): 자식 행을 items.properties 로 직렬화. (누락 시 저장 왕복에서 자식 유실)
|
items: { type: (row.itemsType && row.itemsType.value) || 'string' }
|
||||||
items: itemType === 'object' ? schemaArrayToObject(row.children || []) : { type: itemType }
|
|
||||||
};
|
};
|
||||||
if (row.description && row.description.value) obj.properties[name].description = row.description.value;
|
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;
|
if (row.name && row.name.locked) obj.properties[name]['x-djb-gw'] = true;
|
||||||
@@ -315,7 +301,7 @@
|
|||||||
function goStep(n) {
|
function goStep(n) {
|
||||||
if (n < 1 || n > STEPS.length) return;
|
if (n < 1 || n > STEPS.length) return;
|
||||||
state.step = n;
|
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';
|
if (n === 3) state.resInfoTab = 'body';
|
||||||
renderStepper();
|
renderStepper();
|
||||||
renderForm();
|
renderForm();
|
||||||
@@ -349,9 +335,8 @@
|
|||||||
}
|
}
|
||||||
function fieldRow(label, controlHtml, opts) {
|
function fieldRow(label, controlHtml, opts) {
|
||||||
opts = opts || {};
|
opts = opts || {};
|
||||||
const rowSpacing = opts.bordered ? 'px-3 py-3' : 'mb-3';
|
|
||||||
return `
|
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' : ''}">
|
<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>' : ''}
|
${escapeHtml(label)}${opts.required ? ' <span class="text-red-500">*</span>' : ''}
|
||||||
${opts.locked ? '<span class="ml-1 text-slate-400 text-xs">🔒</span>' : ''}
|
${opts.locked ? '<span class="ml-1 text-slate-400 text-xs">🔒</span>' : ''}
|
||||||
@@ -381,7 +366,7 @@
|
|||||||
`<div class="flex items-center gap-3 mb-4 p-3 bg-slate-50 border border-slate-200 rounded">
|
`<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>
|
<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>
|
<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>` +
|
</div>` +
|
||||||
fieldRow('Operation ID', lockedInput((window.DJB_CTX && window.DJB_CTX.eaiSvcName) || op.operationId.value), { locked: true }) +
|
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 }) +
|
fieldRow('API 타이틀', input(i.title.value, 'data-path="info.title.value"', { required: true }), { required: true }) +
|
||||||
@@ -454,19 +439,6 @@
|
|||||||
+ '</div>';
|
+ '</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))
|
// Step 2 — 요청 정보 (Header / Parameter / Body / API 설명(HTML))
|
||||||
function renderReqInfo(root) {
|
function renderReqInfo(root) {
|
||||||
const tab = state.reqInfoTab || 'body';
|
const tab = state.reqInfoTab || 'body';
|
||||||
@@ -687,7 +659,6 @@
|
|||||||
const isLocked = row.name.locked;
|
const isLocked = row.name.locked;
|
||||||
const isObject = row.type.value === 'object';
|
const isObject = row.type.value === 'object';
|
||||||
const isArray = row.type.value === 'array';
|
const isArray = row.type.value === 'array';
|
||||||
const isArrayOfObject = isArray && (row.itemsType && row.itemsType.value) === 'object';
|
|
||||||
const expandedKey = `${path}.__expanded`;
|
const expandedKey = `${path}.__expanded`;
|
||||||
const isExpanded = state[expandedKey] !== false;
|
const isExpanded = state[expandedKey] !== false;
|
||||||
|
|
||||||
@@ -697,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 text-center">${isLocked ? '<span class="text-slate-400 text-xs" title="게이트웨이 동기화">🔒</span>' : ''}</td>
|
||||||
<td class="py-2 px-2">
|
<td class="py-2 px-2">
|
||||||
<div class="flex items-center gap-1" style="padding-left:${depth * 18}px;">
|
<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>`
|
? `<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="w-4 inline-block"></span>`}
|
||||||
<span class="text-xs text-slate-400 mr-1">${isObject ? '{}' : isArray ? '[]' : '·'}</span>
|
<span class="text-xs text-slate-400 mr-1">${isObject ? '{}' : isArray ? '[]' : '·'}</span>
|
||||||
@@ -728,7 +699,7 @@
|
|||||||
isArray
|
isArray
|
||||||
? `<div class="flex gap-1 items-center">
|
? `<div class="flex gap-1 items-center">
|
||||||
<span class="text-xs text-slate-400">items:</span>
|
<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>`
|
</div>`
|
||||||
: input(row.example.value, `data-path="${path}.example.value"`, { placeholder: '예제값' })}
|
: input(row.example.value, `data-path="${path}.example.value"`, { placeholder: '예제값' })}
|
||||||
</td>
|
</td>
|
||||||
@@ -740,8 +711,8 @@
|
|||||||
</tr>
|
</tr>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
// children (object + array<object>(GRID) 공통)
|
// children
|
||||||
if ((isObject || isArrayOfObject) && isExpanded) {
|
if (isObject && isExpanded) {
|
||||||
if (row.children && row.children.length) {
|
if (row.children && row.children.length) {
|
||||||
html += renderSchemaRows(row.children, `${path}.children`, depth + 1, seq);
|
html += renderSchemaRows(row.children, `${path}.children`, depth + 1, seq);
|
||||||
}
|
}
|
||||||
@@ -760,44 +731,22 @@
|
|||||||
// ============================================================
|
// ============================================================
|
||||||
// 9. 인증 / 보안 (기본정보 Step 에 병합된 섹션)
|
// 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() {
|
function renderAuthSection() {
|
||||||
var schemes = state.data.securitySchemes || [];
|
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(
|
return card(
|
||||||
sectionTitle('인증 / 보안', '게이트웨이(GW)에 설정된 인증만 표시됩니다. (읽기 전용)') +
|
sectionTitle('인증 / 보안', '게이트웨이(GW)에 설정된 인증만 표시됩니다. (읽기 전용)') +
|
||||||
notice +
|
|
||||||
(schemes.length
|
(schemes.length
|
||||||
? schemes.map((s, i) => renderSecuritySchemeCard(s, i)).join('')
|
? schemes.map((s, i) => renderSecuritySchemeCard(s, i)).join('')
|
||||||
: '<div class="text-sm text-slate-500 py-2">인증 없음</div>')
|
: '<div class="text-sm text-slate-500 py-2">인증 없음</div>')
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
function renderSecuritySchemeCard(s, idx) {
|
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 = '';
|
let body = '';
|
||||||
if (s.type === 'apiKey') {
|
if (s.type === 'apiKey') {
|
||||||
@@ -824,7 +773,7 @@
|
|||||||
<div class="border border-slate-200 rounded-lg p-3 mb-3 locked-card">
|
<div class="border border-slate-200 rounded-lg p-3 mb-3 locked-card">
|
||||||
<div class="flex items-center gap-2 mb-3">
|
<div class="flex items-center gap-2 mb-3">
|
||||||
<code class="text-sm font-semibold text-slate-800">${escapeHtml(s.key)}</code>
|
<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>
|
<span class="text-xs text-slate-400 ml-1">🔒 GW</span>
|
||||||
</div>
|
</div>
|
||||||
${body}
|
${body}
|
||||||
@@ -981,27 +930,14 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 전체 URL → { origin, path }. Mock Server URL 을 서버+경로로 분해(경로를 별도로 덧붙이지 않기 위함).
|
// 응답유형별 실제 호출 주소. 경로(op.path.value)에 어댑터경로가 이미 포함, 호스트만 응답유형별로 앞에 붙임.
|
||||||
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 그대로(경로 미부착).
|
|
||||||
function resolvedCallUrl() {
|
function resolvedCallUrl() {
|
||||||
var d = state.data;
|
var d = state.data;
|
||||||
var rt = (d.portal && d.portal.responseType) || 'sample';
|
var rt = (d.portal && d.portal.responseType) || 'sample';
|
||||||
if (rt === 'mock') return (((d.portal && d.portal.mockUrl) || '').trim()) || '(주소 없음)';
|
var host;
|
||||||
var host = (rt === 'gw') ? ((window.DJB_CTX && window.DJB_CTX.gwAddress) || 'http://127.0.0.1:39310') : '';
|
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) || '';
|
var path = (d.operation && d.operation.path && d.operation.path.value) || '';
|
||||||
if (!host) return path || '(주소 없음)';
|
if (!host) return path || '(주소 없음)';
|
||||||
return host.replace(/\/+$/, '') + (path.charAt(0) === '/' ? path : '/' + path);
|
return host.replace(/\/+$/, '') + (path.charAt(0) === '/' ? path : '/' + path);
|
||||||
@@ -1024,26 +960,24 @@
|
|||||||
root.innerHTML =
|
root.innerHTML =
|
||||||
card(
|
card(
|
||||||
sectionTitle('포탈 게시 정보', '개발자포탈 공개 설정. 기존 API 스펙 화면의 값을 불러와 적용합니다.') +
|
sectionTitle('포탈 게시 정보', '개발자포탈 공개 설정. 기존 API 스펙 화면의 값을 불러와 적용합니다.') +
|
||||||
'<div class="border border-slate-200 rounded-lg divide-y divide-slate-200">' +
|
|
||||||
fieldRow('API 그룹',
|
fieldRow('API 그룹',
|
||||||
`<div class="flex flex-col gap-1">
|
`<div class="flex flex-col gap-1">
|
||||||
<div class="flex items-center gap-2 flex-wrap">
|
<div class="flex items-center gap-2 flex-wrap">
|
||||||
<span id="group-chips">${groupChips || '<span class="text-xs text-slate-400">선택된 그룹 없음</span>'}</span>
|
<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>
|
<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>
|
||||||
<div class="text-xs text-slate-400">API 그룹을 설정하지 않으면 포탈에 게시되지 않습니다.</div>
|
|
||||||
<div class="text-xs text-slate-400">API 그룹은 apiGroupMan(API 그룹 관리)에서도 관리됩니다.</div>
|
<div class="text-xs text-slate-400">API 그룹은 apiGroupMan(API 그룹 관리)에서도 관리됩니다.</div>
|
||||||
</div>`, { bordered: true }) +
|
</div>`) +
|
||||||
fieldRow('포탈 게시 활성화', `<label class="inline-flex items-center gap-2 text-sm">${checkbox(p.displayYn === 'Y', 'data-portal-yn')} 활성화</label>`, { bordered: true }) +
|
fieldRow('공개 여부', `<label class="inline-flex items-center gap-2 text-sm">${checkbox(p.displayYn === 'Y', 'data-portal-yn')} 공개</label>`) +
|
||||||
fieldRow('역할별 공개 제한',
|
fieldRow('공개 권한',
|
||||||
`<div class="flex flex-col gap-1">
|
`<div class="flex flex-col gap-1">
|
||||||
<div class="flex gap-4">
|
<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_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_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>
|
<label class="inline-flex items-center gap-1 text-sm">${checkbox(!!roles.ROLE_CORP_MANAGER, 'data-role="ROLE_CORP_MANAGER"')} 법인관리자</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="text-xs text-slate-400">선택시 비 로그인 사용자에게 공개 되지 않습니다.</div>
|
<div class="text-xs text-slate-400">전부 선택 시 로그인 사용자 전체에게 공개됩니다.</div>
|
||||||
</div>`, { bordered: true }) +
|
</div>`) +
|
||||||
(showOrg ? fieldRow('공개 법인',
|
(showOrg ? fieldRow('공개 법인',
|
||||||
`<div class="flex flex-col gap-1">
|
`<div class="flex flex-col gap-1">
|
||||||
<div class="flex items-center gap-2 flex-wrap">
|
<div class="flex items-center gap-2 flex-wrap">
|
||||||
@@ -1051,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>
|
<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>
|
||||||
<div class="text-xs text-slate-400">특정 법인에게만 공개하고 싶을 때 공개 법인을 선택하시면 됩니다.</div>
|
<div class="text-xs text-slate-400">특정 법인에게만 공개하고 싶을 때 공개 법인을 선택하시면 됩니다.</div>
|
||||||
</div>`, { bordered: true }) : '') +
|
</div>`) : '') +
|
||||||
fieldRow('응답 유형',
|
fieldRow('응답 유형',
|
||||||
'<div>' +
|
'<div>' +
|
||||||
['sample', 'mock', 'gw'].map(v =>
|
['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>`
|
`<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('') +
|
).join('') +
|
||||||
(rt === 'sample'
|
`<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-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>`) +
|
|
||||||
(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>` : '') +
|
(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 }) +
|
'</div>') +
|
||||||
(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 }) : '') +
|
(rt === 'mock' ? fieldRow('Mock Server URL', input(p.mockUrl || '', 'data-portal="mockUrl"', { placeholder: 'https://mock.example.com' }), { hint: 'Mock 응답을 제공할 서버의 기본 URL. 응답 유형이 Mock 일 때 개발자포탈이 이 주소로 요청을 전달하고, Swagger 스펙의 서버 주소로도 사용됩니다.' }) : '')
|
||||||
'</div>'
|
|
||||||
) +
|
) +
|
||||||
card(
|
card(
|
||||||
sectionTitle('설명 (API 소개 페이지)', 'API 소개 페이지에만 표시됩니다. OpenAPI 스펙 파일에는 포함되지 않습니다.') +
|
sectionTitle('설명 (API 소개 페이지)', 'API 소개 페이지에만 표시됩니다. OpenAPI 스펙 파일에는 포함되지 않습니다.') +
|
||||||
'<div id="info-desc-editor">' + (state.data.info.description.value || '') + '</div>'
|
'<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(
|
card(
|
||||||
sectionTitle('내보내기', 'OpenAPI 스펙을 파일로 저장합니다.') +
|
sectionTitle('내보내기', 'OpenAPI 스펙을 파일로 저장합니다.') +
|
||||||
`<div class="flex gap-2">
|
`<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="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>
|
<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>`
|
</div>`
|
||||||
) : '');
|
);
|
||||||
initInfoDescEditor(); // 설명 리치 에디터 (API 소개 페이지 전용)
|
initInfoDescEditor(); // 설명 리치 에디터 (API 소개 페이지 전용)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1659,7 +1586,7 @@
|
|||||||
$('#btn-prev').addEventListener('click', () => goStep(state.step - 1));
|
$('#btn-prev').addEventListener('click', () => goStep(state.step - 1));
|
||||||
$('#btn-next').addEventListener('click', () => {
|
$('#btn-next').addEventListener('click', () => {
|
||||||
if (state.step < STEPS.length) goStep(state.step + 1);
|
if (state.step < STEPS.length) goStep(state.step + 1);
|
||||||
else saveSpec(false); // 마지막 단계의 "완료" → 저장 동작
|
else toast('모든 단계를 완료했습니다. 우측 미리보기 또는 내보내기를 사용하세요.');
|
||||||
});
|
});
|
||||||
|
|
||||||
$('#btn-toggle-preview').addEventListener('click', () => setPreviewVisibility(false));
|
$('#btn-toggle-preview').addEventListener('click', () => setPreviewVisibility(false));
|
||||||
@@ -1692,18 +1619,14 @@
|
|||||||
toast('변경을 취소했습니다');
|
toast('변경을 취소했습니다');
|
||||||
});
|
});
|
||||||
|
|
||||||
// 내보내기 버튼은 djb.openapi.export.enabled=true 일 때만 DOM 에 존재 → 없으면 바인딩 생략
|
$('#btn-export').addEventListener('click', () => $('#export-menu').classList.toggle('hidden'));
|
||||||
var bExport = $('#btn-export');
|
document.addEventListener('click', e => {
|
||||||
if (bExport) {
|
if (!e.target.closest('#btn-export') && !e.target.closest('#export-menu')) $('#export-menu').classList.add('hidden');
|
||||||
bExport.addEventListener('click', () => $('#export-menu').classList.toggle('hidden'));
|
});
|
||||||
document.addEventListener('click', e => {
|
$('#export-menu').addEventListener('click', e => {
|
||||||
if (!e.target.closest('#btn-export') && !e.target.closest('#export-menu')) $('#export-menu').classList.add('hidden');
|
const b = e.target.closest('[data-export]');
|
||||||
});
|
if (b) { downloadSpec(b.dataset.export); $('#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 bSave = $('#btn-save'); if (bSave) bSave.addEventListener('click', () => saveSpec(false));
|
||||||
var bRegen = $('#btn-regen'); if (bRegen) bRegen.addEventListener('click', regenerate);
|
var bRegen = $('#btn-regen'); if (bRegen) bRegen.addEventListener('click', regenerate);
|
||||||
@@ -1745,12 +1668,7 @@
|
|||||||
example: wrap(p.example == null ? '' : p.example), children: null
|
example: wrap(p.example == null ? '' : p.example), children: null
|
||||||
};
|
};
|
||||||
if (t === 'object') row.children = schemaToRows(p);
|
if (t === 'object') row.children = schemaToRows(p);
|
||||||
else if (t === 'array') {
|
else if (t === 'array') row.itemsType = wrap((p.items && p.items.type) || 'string');
|
||||||
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);
|
|
||||||
}
|
|
||||||
return row;
|
return row;
|
||||||
}
|
}
|
||||||
function schemaToRows(schema) {
|
function schemaToRows(schema) {
|
||||||
@@ -1988,7 +1906,6 @@
|
|||||||
if (res && res.testbedSpec) {
|
if (res && res.testbedSpec) {
|
||||||
try { state.data = specToData(JSON.parse(res.testbedSpec)); } catch (e) { console.error('specToData', e); }
|
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)은 백엔드에서 적용됨 → 그대로 로드
|
// 자동생성 규칙(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');
|
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 || '';
|
var _nm = decodeEntities(ctx.apiName || '') || state.data.info.title.value || '';
|
||||||
@@ -2006,16 +1923,12 @@
|
|||||||
if (res.responseType) pp.responseType = res.responseType;
|
if (res.responseType) pp.responseType = res.responseType;
|
||||||
if (res.mockUrl) pp.mockUrl = decodeEntities(res.mockUrl);
|
if (res.mockUrl) pp.mockUrl = decodeEntities(res.mockUrl);
|
||||||
if (res.apiGroups) { try { pp.apiGroups = JSON.parse(res.apiGroups) || []; } catch (e) { pp.apiGroups = []; } }
|
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) — 저장본은 저장값, 자동생성분은 프론트에서 생성
|
// 요청/응답 메시지 스펙(HTML) — 저장본은 저장값, 자동생성분은 프론트에서 생성
|
||||||
state.data.msgSpec = state.data.msgSpec || { request: '', response: '' };
|
state.data.msgSpec = state.data.msgSpec || { request: '', response: '' };
|
||||||
if (res.apiRequestSpec) state.data.msgSpec.request = decodeEntities(res.apiRequestSpec);
|
if (res.apiRequestSpec) state.data.msgSpec.request = decodeEntities(res.apiRequestSpec);
|
||||||
if (res.apiResponseSpec) state.data.msgSpec.response = decodeEntities(res.apiResponseSpec);
|
if (res.apiResponseSpec) state.data.msgSpec.response = decodeEntities(res.apiResponseSpec);
|
||||||
// 저장본이 아니면(최초 작성) 자동생성 여부를 confirm 으로 확인 후 진행
|
// 저장본이 아니면(자동생성/최초 생성) 추가자료(예제·설명자료)를 프론트에서 스키마 기준 생성
|
||||||
if ((res.source || '') !== 'saved') {
|
if ((res.source || '') !== 'saved') frontAutoGenerate();
|
||||||
if (window.confirm('저장된 스펙이 없습니다. 게이트웨이 레이아웃 기반으로 예제·설명자료를 자동 생성할까요?')) frontAutoGenerate();
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.catch(e => console.error('loadInitialData', e));
|
.catch(e => console.error('loadInitialData', e));
|
||||||
}
|
}
|
||||||
@@ -2072,7 +1985,6 @@
|
|||||||
try { state.data = specToData(JSON.parse(res.testbedSpec)); }
|
try { state.data = specToData(JSON.parse(res.testbedSpec)); }
|
||||||
catch (e) { toast('자동 생성 실패: 스펙 파싱 오류', 'error'); return; }
|
catch (e) { toast('자동 생성 실패: 스펙 파싱 오류', 'error'); return; }
|
||||||
}
|
}
|
||||||
djbAuthType = (res && res.authType) || ''; // 인증 badge 라벨 판별용(oauth/api_key)
|
|
||||||
// 구조(title/tag/summary/opId/스키마/보안/헤더)는 백엔드(generateSpec)가 적용한 기초 스펙 그대로 로드.
|
// 구조(title/tag/summary/opId/스키마/보안/헤더)는 백엔드(generateSpec)가 적용한 기초 스펙 그대로 로드.
|
||||||
// 추가자료(예제·설명자료)는 프론트에서 스키마 기준 생성 → 재생성 버튼과 동일 결과.
|
// 추가자료(예제·설명자료)는 프론트에서 스키마 기준 생성 → 재생성 버튼과 동일 결과.
|
||||||
frontAutoGenerate();
|
frontAutoGenerate();
|
||||||
|
|||||||
@@ -1,63 +1,298 @@
|
|||||||
// OpenAPI 에디터 초기 상태 골격(빈 껍데기).
|
// 계좌이체 API 모의 데이터
|
||||||
// - specToData() 가 deepClone 하여 서버 spec 값으로 덮어쓰는 구조 기준값.
|
// locked: true 인 필드는 게이트웨이가 자동 주입한 값(회색 + 자물쇠)
|
||||||
// - 실제 데모/샘플 값은 두지 않는다(팝업은 항상 서버 spec 로드 후 렌더).
|
// locked: false / 미지정 인 필드는 사용자가 자유롭게 편집
|
||||||
// - docOptions 만 기능 기본값으로 유지(문서 미리보기 옵션).
|
|
||||||
// locked:true = 게이트웨이 자동 주입(회색+자물쇠) / locked:false = 사용자 편집.
|
|
||||||
|
|
||||||
window.SAMPLE_DATA = (function () {
|
window.SAMPLE_DATA = (function () {
|
||||||
function f() { return { value: '', locked: false }; }
|
|
||||||
return {
|
return {
|
||||||
info: {
|
info: {
|
||||||
title: f(),
|
title: { value: '계좌이체 API', locked: false },
|
||||||
version: f(),
|
version: { value: '1.0.0', locked: false },
|
||||||
summary: f(),
|
summary: { value: '', locked: false },
|
||||||
description: f(),
|
description: { value: '', locked: false },
|
||||||
termsOfService: f(),
|
termsOfService: { value: '', locked: false },
|
||||||
contact: { name: f(), email: f(), url: f() },
|
contact: {
|
||||||
license: { name: f(), url: f() }
|
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: {
|
operation: {
|
||||||
method: f(),
|
method: { value: 'POST', locked: true },
|
||||||
path: f(),
|
path: { value: '/v1/accounts/transfer', locked: true },
|
||||||
operationId: f(),
|
operationId: { value: 'transferAccount', locked: false },
|
||||||
summary: f(),
|
summary: { value: '', locked: false },
|
||||||
description: f(),
|
description: { value: '', locked: false },
|
||||||
tags: { value: [], locked: false },
|
tags: { value: ['transfer'], locked: false },
|
||||||
deprecated: { value: false, locked: false }
|
deprecated: { value: false, locked: false }
|
||||||
},
|
},
|
||||||
|
|
||||||
// 파라미터 (Path/Query/Header/Cookie)
|
// 파라미터 (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: {
|
requestBody: {
|
||||||
mediaType: 'application/json',
|
mediaType: 'application/json',
|
||||||
required: { value: false, locked: false },
|
required: { value: true, locked: true },
|
||||||
schema: []
|
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: {
|
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: [],
|
securitySchemes: [
|
||||||
globalSecurity: [],
|
{
|
||||||
|
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: {
|
examples: {
|
||||||
request: {},
|
request: {
|
||||||
response: {}
|
'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' } ] }
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// 문서 옵션
|
// 문서 옵션
|
||||||
|
|||||||
@@ -184,9 +184,8 @@ function getEndTime(){
|
|||||||
</td>
|
</td>
|
||||||
<th style="width:180px;"><%= localeMessage.getString("auditLogMan.remoteAddress") %></th>
|
<th style="width:180px;"><%= localeMessage.getString("auditLogMan.remoteAddress") %></th>
|
||||||
<td><input type="text" name="searchRemoteAddress" value="${param.searchRemoteAddress}" style="width:100%"></td>
|
<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>
|
<td>
|
||||||
<input type="text" name="searchParameters" value="${param.searchParameters}" style="width:100%">
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|||||||
@@ -54,18 +54,10 @@ function detail(key){
|
|||||||
$('#systemCode').val(data.systemCode);
|
$('#systemCode').val(data.systemCode);
|
||||||
$('#logTypeText').val(data.logTypeText);
|
$('#logTypeText').val(data.logTypeText);
|
||||||
$('#remoteAddress').val(data.remoteAddress);
|
$('#remoteAddress').val(data.remoteAddress);
|
||||||
|
$('#logMsg').val(data.message);
|
||||||
$('#logSubMsg').val(data.command);
|
$('#logSubMsg').val(data.command);
|
||||||
$('#parameters').val(data.parameters);
|
$('#parameters').val(data.parameters);
|
||||||
|
|
||||||
// 사유(message)는 값이 있을 때만 노출
|
|
||||||
if (data.message) {
|
|
||||||
$('#logMsg').val(data.message);
|
|
||||||
$('#messageRow').show();
|
|
||||||
} else {
|
|
||||||
$('#logMsg').val('');
|
|
||||||
$('#messageRow').hide();
|
|
||||||
}
|
|
||||||
|
|
||||||
},
|
},
|
||||||
error:function(e){
|
error:function(e){
|
||||||
alert(e.responseText);
|
alert(e.responseText);
|
||||||
@@ -129,9 +121,6 @@ $(document).ready(function() {
|
|||||||
<tr>
|
<tr>
|
||||||
<th><%= localeMessage.getString("auditLogMan.command") %></th><td><input type="text" id="logSubMsg" name="logSubMsg" readonly="readonly"/></td>
|
<th><%= localeMessage.getString("auditLogMan.command") %></th><td><input type="text" id="logSubMsg" name="logSubMsg" readonly="readonly"/></td>
|
||||||
</tr>
|
</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">
|
<tr height="100px">
|
||||||
<th><%= localeMessage.getString("auditLogMan.parameters") %></th><td><textarea id="parameters" name="parameters" style="width:100%;height:200px" readonly="readonly"></textarea></td>
|
<th><%= localeMessage.getString("auditLogMan.parameters") %></th><td><textarea id="parameters" name="parameters" style="width:100%;height:200px" readonly="readonly"></textarea></td>
|
||||||
</tr>
|
</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,16 +18,6 @@
|
|||||||
<script language="javascript" >
|
<script language="javascript" >
|
||||||
var url = '<c:url value="/common/acl/user/userMan.json" />';
|
var url = '<c:url value="/common/acl/user/userMan.json" />';
|
||||||
var url_view = '<c:url value="/common/acl/user/userMan.view" />';
|
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 로
|
var gridPostData = getSearchForJqgrid("cmd","LIST"); //jqgrid에서는 object 로
|
||||||
$('#grid').jqGrid({
|
$('#grid').jqGrid({
|
||||||
@@ -38,20 +28,16 @@ $(document).ready(function() {
|
|||||||
colNames:['<%= localeMessage.getString("login.userId") %>',
|
colNames:['<%= localeMessage.getString("login.userId") %>',
|
||||||
'<%= localeMessage.getString("user.name") %>',
|
'<%= localeMessage.getString("user.name") %>',
|
||||||
'<%= localeMessage.getString("user.roleName") %>',
|
'<%= localeMessage.getString("user.roleName") %>',
|
||||||
'<%= localeMessage.getString("user.status") %>',
|
|
||||||
'<%= localeMessage.getString("user.createOn") %>',
|
'<%= localeMessage.getString("user.createOn") %>',
|
||||||
'<%= localeMessage.getString("user.lastLoginYms") %>',
|
|
||||||
'<%= localeMessage.getString("user.depart") %>',
|
'<%= localeMessage.getString("user.depart") %>',
|
||||||
'<%= localeMessage.getString("user.teamName") %>',
|
'<%= localeMessage.getString("user.teamName") %>',
|
||||||
'<%= localeMessage.getString("user.position") %>'
|
'<%= localeMessage.getString("user.position") %>'
|
||||||
],
|
],
|
||||||
colModel:[
|
colModel:[
|
||||||
{ name : 'USERID' , align:'center', width: 100, sortable:false },
|
{ name : 'USERID' , align:'center' ,sortable:false },
|
||||||
{ name : 'USERNAME' , align:'center', width: 100 },
|
{ name : 'USERNAME' , align:'center' },
|
||||||
{ name : 'ROLEIDNFINAME' , align:'center', width: 300 },
|
{ name : 'ROLEIDNFINAME' , align:'center' },
|
||||||
{ name : 'STATUS' , align:'center', width: 80, formatter: userStatusFormat },
|
{ name : 'LASTAMNDYMS' , align:'center', formatter: timeStampFormat },
|
||||||
{ name : 'REGDYMS' , align:'center', width: 120, formatter: timeStampFormat },
|
|
||||||
{ name : 'LASTLOGINYMS' , align:'center', width: 120, formatter: timeStampFormat },
|
|
||||||
{ name : 'DVSNNAME' , align:'center', hidden: true },
|
{ name : 'DVSNNAME' , align:'center', hidden: true },
|
||||||
{ name : 'TEAMNAME' , align:'center', hidden: true },
|
{ name : 'TEAMNAME' , align:'center', hidden: true },
|
||||||
{ name : 'JOBTLNAME' , 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():"");
|
$(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=LASTAMNDYMS]").inputmask("9999-99-99 99:99:99",{'autoUnmask':true});
|
||||||
$("input[name=LASTLOGINYMS]").inputmask("9999-99-99 99:99:99",{'autoUnmask':true});
|
|
||||||
/* $("input[name=SECONDMENTSTDT]").inputmask("9999-99-99",{'autoUnmask':true});
|
/* $("input[name=SECONDMENTSTDT]").inputmask("9999-99-99",{'autoUnmask':true});
|
||||||
$("input[name=SECONDMENTENDT]").inputmask("9999-99-99",{'autoUnmask':true}); */
|
$("input[name=SECONDMENTENDT]").inputmask("9999-99-99",{'autoUnmask':true}); */
|
||||||
|
|
||||||
@@ -217,7 +216,6 @@ $(document).ready(function() {
|
|||||||
dataType:"json",
|
dataType:"json",
|
||||||
data:{cmd: 'UPDATE_PASSWORD', userId : $("input[name=userId]").val()},
|
data:{cmd: 'UPDATE_PASSWORD', userId : $("input[name=userId]").val()},
|
||||||
success:function(json){
|
success:function(json){
|
||||||
$('select[name="status"]').val("1")
|
|
||||||
alert(json.result);
|
alert(json.result);
|
||||||
},
|
},
|
||||||
error:function(e){
|
error:function(e){
|
||||||
@@ -279,10 +277,10 @@ $(document).ready(function() {
|
|||||||
<table class="table_row" cellspacing="0">
|
<table class="table_row" cellspacing="0">
|
||||||
<input type="hidden" name="jobclcd">
|
<input type="hidden" name="jobclcd">
|
||||||
<tr>
|
<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>
|
||||||
<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>
|
||||||
<tr class="datailT">
|
<tr class="datailT">
|
||||||
<th><%= localeMessage.getString("user.auth") %> </th><td><input type="text" name="roleidnfiname" /></td>
|
<th><%= localeMessage.getString("user.auth") %> </th><td><input type="text" name="roleidnfiname" /></td>
|
||||||
@@ -319,6 +317,7 @@ $(document).ready(function() {
|
|||||||
<td>
|
<td>
|
||||||
<div class="select-style">
|
<div class="select-style">
|
||||||
<select name="status">
|
<select name="status">
|
||||||
|
<option value=""><%= localeMessage.getString("code.normal") %></option>
|
||||||
<option value="1"><%= localeMessage.getString("code.normal") %></option>
|
<option value="1"><%= localeMessage.getString("code.normal") %></option>
|
||||||
<option value="2"><%= localeMessage.getString("code.lock") %></option>
|
<option value="2"><%= localeMessage.getString("code.lock") %></option>
|
||||||
</select>
|
</select>
|
||||||
@@ -335,10 +334,7 @@ $(document).ready(function() {
|
|||||||
<th><%= localeMessage.getString("user.fieldAgentName") %> </th><td><input type="text" name="spotprxyname" /></td>
|
<th><%= localeMessage.getString("user.fieldAgentName") %> </th><td><input type="text" name="spotprxyname" /></td>
|
||||||
</tr> --%>
|
</tr> --%>
|
||||||
<tr>
|
<tr>
|
||||||
<th><%= localeMessage.getString("user.createOn") %> </th><td><input type="text" name="REGDYMS" readonly/></td>
|
<th><%= localeMessage.getString("user.createOn") %> </th><td><input type="text" name="LASTAMNDYMS" readonly/></td>
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th><%= localeMessage.getString("user.lastLoginYms") %> </th><td><input type="text" name="LASTLOGINYMS" readonly/></td>
|
|
||||||
</tr>
|
</tr>
|
||||||
<%-- <tr>
|
<%-- <tr>
|
||||||
<th><%= localeMessage.getString("user.secondmentbrncd") %> </th><td><input type="text" name="SECONDMENTBRNCD" readonly/></td>
|
<th><%= localeMessage.getString("user.secondmentbrncd") %> </th><td><input type="text" name="SECONDMENTBRNCD" readonly/></td>
|
||||||
|
|||||||
@@ -531,88 +531,6 @@
|
|||||||
}
|
}
|
||||||
}, 200);
|
}, 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>
|
</script>
|
||||||
|
|
||||||
<!-- 공용 Alert 모달 스타일 -->
|
<!-- 공용 Alert 모달 스타일 -->
|
||||||
|
|||||||
@@ -91,10 +91,7 @@
|
|||||||
data: $('#loginForm').serialize(),
|
data: $('#loginForm').serialize(),
|
||||||
dataType: 'json',
|
dataType: 'json',
|
||||||
success: function(response) {
|
success: function(response) {
|
||||||
if (response.changePassword) {
|
if (response.smsAuthRequired) {
|
||||||
showChangeInitPassword(response);
|
|
||||||
}
|
|
||||||
else if (response.smsAuthRequired) {
|
|
||||||
// SMS 인증 필요
|
// SMS 인증 필요
|
||||||
showSmsAuthModal(response);
|
showSmsAuthModal(response);
|
||||||
} else if (response.success) {
|
} else if (response.success) {
|
||||||
@@ -112,13 +109,6 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
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 인증 모달 표시
|
// SMS 인증 모달 표시
|
||||||
function showSmsAuthModal(data) {
|
function showSmsAuthModal(data) {
|
||||||
$('#smsAuthMaskedPhone').text(data.maskedPhone);
|
$('#smsAuthMaskedPhone').text(data.maskedPhone);
|
||||||
@@ -228,39 +218,44 @@
|
|||||||
clearInterval(resendTimer);
|
clearInterval(resendTimer);
|
||||||
$('#smsAuthModal').modal('hide');
|
$('#smsAuthModal').modal('hide');
|
||||||
}
|
}
|
||||||
|
function changePwd(){
|
||||||
function checkPwd(){
|
|
||||||
if ($("input[name=resetUserId]").val() == null || $("input[name=resetUserId]").val().length == 0 ){
|
if ($("input[name=resetUserId]").val() == null || $("input[name=resetUserId]").val().length == 0 ){
|
||||||
alert("<%= localeMessage.getString("login.checkid") %>");
|
alert("<%= localeMessage.getString("login.checkid") %>");
|
||||||
$("input[name=resetUserId]").trigger("focus");
|
$("input[name=resetUserId]").trigger("focus");
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
if ($("input[name=resetPassword]").val() == null || $("input[name=resetPassword]").val().trim().length == 0 ){
|
if ($("input[name=resetPassword]").val() == null || $("input[name=resetPassword]").val().trim().length == 0 ){
|
||||||
alert("<%= localeMessage.getString("login.checkpwd1") %>");
|
alert("<%= localeMessage.getString("login.checkpwd1") %>");
|
||||||
$("input[name=resetPassword]").trigger("focus");
|
$("input[name=resetPassword]").trigger("focus");
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
if ($("input[name=changePassword]").val() == null || $("input[name=changePassword]").val().trim().length == 0 ){
|
if ($("input[name=changePassword]").val() == null || $("input[name=changePassword]").val().trim().length == 0 ){
|
||||||
alert("<%= localeMessage.getString("login.checkpwd2") %>");
|
alert("<%= localeMessage.getString("login.checkpwd2") %>");
|
||||||
$("input[name=changePassword]").trigger("focus");
|
$("input[name=changePassword]").trigger("focus");
|
||||||
return false;
|
return ;
|
||||||
}
|
}
|
||||||
if ($("input[name=confirmPassword]").val() == null || $("input[name=confirmPassword]").val().trim().length == 0 ){
|
if ($("input[name=confirmPassword]").val() == null || $("input[name=confirmPassword]").val().trim().length == 0 ){
|
||||||
alert("<%= localeMessage.getString("login.checkpwd3") %>");
|
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") %>");
|
alert("<%= localeMessage.getString("login.checkpwd4") %>");
|
||||||
$("input[name=confirmPassword]").trigger("focus");
|
$("input[name=confirmPassword]").trigger("focus");
|
||||||
return false;
|
return ;
|
||||||
}
|
}
|
||||||
if ($("input[name=resetUserId]").val() == $("input[name=changePassword]").val()){
|
if ($("input[name=resetUserId]").val() == $("input[name=changePassword]").val()){
|
||||||
alert("<%= localeMessage.getString("login.checkpwd5") %>");
|
alert("<%= localeMessage.getString("login.checkpwd5") %>");
|
||||||
$("input[name=changePassword]").trigger("focus");
|
$("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();
|
||||||
}
|
}
|
||||||
|
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
@@ -276,22 +271,14 @@
|
|||||||
});
|
});
|
||||||
$("input[name=changePassword]").keydown(function(event){
|
$("input[name=changePassword]").keydown(function(event){
|
||||||
if ( event.which == 13 ) {
|
if ( event.which == 13 ) {
|
||||||
$('#modalLoginForm').submit();
|
changePwd();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
$("input[name=confirmPassword]").keydown(function(event){
|
$("input[name=confirmPassword]").keydown(function(event){
|
||||||
if ( event.which == 13 ) {
|
if ( event.which == 13 ) {
|
||||||
$('#modalLoginForm').submit();
|
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(){
|
$("select[name=serviceType]").change(function(){
|
||||||
fncPreMain();
|
fncPreMain();
|
||||||
});
|
});
|
||||||
@@ -299,7 +286,9 @@
|
|||||||
$("#btn_login").click(function(){
|
$("#btn_login").click(function(){
|
||||||
fncPreMain();
|
fncPreMain();
|
||||||
});
|
});
|
||||||
|
$("#changePwd").click(function(){
|
||||||
|
changePwd();
|
||||||
|
});
|
||||||
$("#btn_sso_login").click(function(){
|
$("#btn_sso_login").click(function(){
|
||||||
fncSsoLogin();
|
fncSsoLogin();
|
||||||
});
|
});
|
||||||
@@ -377,7 +366,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 비밀번호 변경 모달 -->
|
|
||||||
<div class="modal fade" id="pwdChgModal" tabindex="-1" role="dialog" aria-labelledby="pwdChgModalLabel" aria-hidden="true">
|
<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-dialog" role="document">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
@@ -387,15 +376,13 @@
|
|||||||
<span aria-hidden="true">X</span>
|
<span aria-hidden="true">X</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<form id="modalLoginForm" action="<c:url value="/changePassword.do"/>" method="post" onsubmit="return checkPwd()" novalidate>
|
<form name="form" id="modalLoginForm" action="<c:url value="/changePassword.do"/>" method="post">
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<div id="changeInitPassword">
|
<div class="form-group d-flex">
|
||||||
<div class="form-group d-flex">
|
<input type="text" name="resetUserId" class="form-control rounded-left" placeholder="User Id" required>
|
||||||
<input type="text" name="resetUserId" class="form-control rounded-left" placeholder="User Id" required>
|
</div>
|
||||||
</div>
|
<div class="form-group d-flex">
|
||||||
<div class="form-group d-flex">
|
<input type="password" name="resetPassword" class="form-control rounded-left" placeholder="<%= localeMessage.getString("login.placeholderCurrentPassword") %>" autocomplete="off" required>
|
||||||
<input type="password" name="resetPassword" class="form-control rounded-left" placeholder="<%= localeMessage.getString("login.placeholderCurrentPassword") %>" autocomplete="off" required>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<p><%= localeMessage.getString("login.toChangePasswordMessage") %></p>
|
<p><%= localeMessage.getString("login.toChangePasswordMessage") %></p>
|
||||||
<div class="form-group d-flex">
|
<div class="form-group d-flex">
|
||||||
@@ -404,12 +391,9 @@
|
|||||||
<div class="form-group d-flex">
|
<div class="form-group d-flex">
|
||||||
<input type="password" name="confirmPassword" class="form-control rounded-left" placeholder="<%= localeMessage.getString("login.placeholderConfirmationPassword") %>" autocomplete="off" required>
|
<input type="password" name="confirmPassword" class="form-control rounded-left" placeholder="<%= localeMessage.getString("login.placeholderConfirmationPassword") %>" autocomplete="off" required>
|
||||||
</div>
|
</div>
|
||||||
<span style="font-size:0.8em; color:red">
|
|
||||||
※ 7글자 이상 & 영문/숫자/특수문자 2종류 이상
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<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>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -9,8 +9,6 @@
|
|||||||
<%@page import="com.eactive.eai.rms.common.util.StringUtils" %>
|
<%@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.DataSourceType" %>
|
||||||
<%@page import="com.eactive.eai.rms.common.datasource.DataSourceTypeManager" %>
|
<%@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 prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
|
||||||
<%@ taglib uri="http://www.springframework.org/tags" prefix="spring" %>
|
<%@ taglib uri="http://www.springframework.org/tags" prefix="spring" %>
|
||||||
<%@ include file="/jsp/common/include/localemessage.jsp" %>
|
<%@ include file="/jsp/common/include/localemessage.jsp" %>
|
||||||
@@ -18,12 +16,6 @@
|
|||||||
<%
|
<%
|
||||||
String serviceKey = SessionManager.getServiceTypeKey(request);
|
String serviceKey = SessionManager.getServiceTypeKey(request);
|
||||||
String serviceText = DataSourceTypeManager.getDataSourceType(serviceKey).getText();
|
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 roleScreenId = (String) request.getAttribute("roleScreenId");
|
||||||
String mainPage = (String) request.getAttribute("mainPage");
|
String mainPage = (String) request.getAttribute("mainPage");
|
||||||
String menuId = (String) request.getAttribute("menuId");
|
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/web_ui.css"/>"/>
|
||||||
<link rel="stylesheet" type="text/css" media="screen" href="<c:url value="/css/theme_${themeColor}.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/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/prefixfree.min.js"/>"></script>
|
||||||
<script language="javascript" src="<c:url value="/js/jquery.cookie.js"/>"></script>
|
<script language="javascript" src="<c:url value="/js/jquery.cookie.js"/>"></script>
|
||||||
@@ -543,7 +529,6 @@
|
|||||||
console.log("selectLocaleValue : " + selectLocaleValue);
|
console.log("selectLocaleValue : " + selectLocaleValue);
|
||||||
parent.changeLocale($(this).val());
|
parent.changeLocale($(this).val());
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
$(function() {
|
$(function() {
|
||||||
@@ -574,20 +559,13 @@
|
|||||||
<span style="display:block;font-size:10px;"><%=localeMessage.getString("screen.lastLogin") %> : <%=LastLoginYms%> [ <%=LastLoginIp%> ]</span>
|
<span style="display:block;font-size:10px;"><%=localeMessage.getString("screen.lastLogin") %> : <%=LastLoginYms%> [ <%=LastLoginIp%> ]</span>
|
||||||
<% } %>
|
<% } %>
|
||||||
</a></li>
|
</a></li>
|
||||||
<li style="width:100px; line-height:80px; text-align:center;">
|
<li>
|
||||||
<select id="selectLocale" name="locale" style="width:60px; padding: 2px; text-align:center;">
|
<select id="selectLocale" name="locale" style="margin-top: 20px; width:80px">
|
||||||
<option value="en">English</option>
|
<option value="en">English</option>
|
||||||
<option value="ko">한글</option>
|
<option value="ko">한글</option>
|
||||||
</select>
|
</select>
|
||||||
</li>
|
</li>
|
||||||
<% if (showSitemapLink) { %>
|
<li onclick="logout()"><a href="#" class=""><img src="<c:url value="/img/icon_logout.png"/>"
|
||||||
<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"/>"
|
|
||||||
alt=""/><%= localeMessage.getString("screen.logout") %>
|
alt=""/><%= localeMessage.getString("screen.logout") %>
|
||||||
</a></li>
|
</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|||||||
@@ -82,10 +82,8 @@
|
|||||||
apiDesc: api.eaiSvcDesc
|
apiDesc: api.eaiSvcDesc
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
// addJSONData는 datatype:"local" 그리드의 p.data를 채우지 않아, 헤더 클릭 정렬 시 p.data(빈 배열) 기준으로 다시 그려지며 행이 사라짐.
|
|
||||||
// setGridParam({data:...}).trigger('reloadGrid')로 채워야 정렬이 정상 동작함.
|
|
||||||
$("#selectedApiGrid").jqGrid("clearGridData");
|
$("#selectedApiGrid").jqGrid("clearGridData");
|
||||||
$("#selectedApiGrid").jqGrid('setGridParam', {data: gridData}).trigger('reloadGrid');
|
$("#selectedApiGrid")[0].addJSONData(gridData);
|
||||||
|
|
||||||
var data = json;
|
var data = json;
|
||||||
$("#btn_gen_id").hide();
|
$("#btn_gen_id").hide();
|
||||||
@@ -135,14 +133,15 @@
|
|||||||
var selectedApiGrid = $("#selectedApiGrid");
|
var selectedApiGrid = $("#selectedApiGrid");
|
||||||
selectedApiGrid.jqGrid({
|
selectedApiGrid.jqGrid({
|
||||||
datatype: "local",
|
datatype: "local",
|
||||||
rownumbers: true, // 가상 인덱스
|
gridview: true, // 성능 향상
|
||||||
|
rownumbers: true, // 가상 인덱스
|
||||||
rownumWidth: 40,
|
rownumWidth: 40,
|
||||||
colNames: ['API ID', 'API 이름'],
|
colNames: ['API ID', 'API 이름'],
|
||||||
colModel: [
|
colModel: [
|
||||||
{name: 'apiId', index: 'apiId', width: 100, align: 'center'},
|
{name: 'apiId', index: 'apiId', width: 100, align: 'center'},
|
||||||
{name: 'apiDesc', index: 'apiDesc', width: 100, align: 'center'}
|
{name: 'apiDesc', index: 'apiDesc', width: 100, align: 'center'}
|
||||||
],
|
],
|
||||||
rowNum: 9999,
|
rowNum: -1,
|
||||||
// rowList: [10, 20, 30],
|
// rowList: [10, 20, 30],
|
||||||
// pager: '#selectedApiGridPager',
|
// pager: '#selectedApiGridPager',
|
||||||
// sortname: 'apiId',
|
// sortname: 'apiId',
|
||||||
@@ -451,8 +450,8 @@
|
|||||||
</div><!-- end content_top -->
|
</div><!-- end content_top -->
|
||||||
<div class="content_middle">
|
<div class="content_middle">
|
||||||
<div class="search_wrap">
|
<div class="search_wrap">
|
||||||
<!-- <button type="button" class="cssbtn" id="btn_sync_portal" level="W" status="DETAIL"><i class="material-icons">sync</i> 개발자포탈 정보 반영
|
<button type="button" class="cssbtn" id="btn_sync_portal" level="W" status="DETAIL"><i class="material-icons">sync</i> 개발자포탈 정보 반영
|
||||||
</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 type="button" class="cssbtn" id="btn_delete" level="W" status="DETAIL"><i class="material-icons">delete</i> <%= localeMessage.getString("button.delete") %>
|
||||||
</button>
|
</button>
|
||||||
<button type="button" class="cssbtn" id="btn_modify" level="W" status="DETAIL,NEW"><i
|
<button type="button" class="cssbtn" id="btn_modify" level="W" status="DETAIL,NEW"><i
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
var url = '<c:url value="/onl/admin/authserver/scopeMan.json" />';
|
var url = '<c:url value="/onl/admin/authserver/scopeMan.json" />';
|
||||||
var url_view = '<c:url value="/onl/admin/authserver/scopeMan.view" />';
|
var url_view = '<c:url value="/onl/admin/authserver/scopeMan.view" />';
|
||||||
var api_url_view = '<c:url value="/onl/apim/apigroup/apiGroupMan.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;
|
var isDetail = false;
|
||||||
function isValid(){
|
function isValid(){
|
||||||
@@ -64,9 +65,9 @@ function mappingInfo(key){
|
|||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
type : "POST",
|
type : "POST",
|
||||||
url: url,
|
url: mapping_url,
|
||||||
dataType: "json",
|
dataType: "json",
|
||||||
data: {cmd: 'API_LIST', searchScopeId : key},
|
data: {cmd: 'LIST', searchScopeId : key},
|
||||||
success: function(json){
|
success: function(json){
|
||||||
var mappingData = json.rows;
|
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() {
|
$(document).ready(function() {
|
||||||
var returnUrl = getReturnUrlForReturn();
|
var returnUrl = getReturnUrlForReturn();
|
||||||
var key ="${param.scopeId}";
|
var key ="${param.scopeId}";
|
||||||
@@ -142,39 +182,28 @@ $(document).ready(function() {
|
|||||||
if (!isValid()){
|
if (!isValid()){
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var scopeId = $('input[name=scopeId]').val();
|
|
||||||
var postData = $('#ajaxForm').serializeArray();
|
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){
|
if (isDetail){
|
||||||
postData.push({ name: "cmd" , value:"UPDATE"});
|
postData.push({ name: "cmd" , value:"UPDATE"});
|
||||||
}else{
|
}else{
|
||||||
postData.push({ name: "cmd" , value:"INSERT"});
|
postData.push({ name: "cmd" , value:"INSERT"});
|
||||||
}
|
}
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
type : "POST",
|
type : "POST",
|
||||||
url:url,
|
url:url,
|
||||||
data:postData,
|
data:postData,
|
||||||
beforeSend: function() {
|
|
||||||
$("[id^='btn_']").prop("disabled", true);
|
|
||||||
$("#btn_modify").text("처리중 . . . . .");
|
|
||||||
},
|
|
||||||
success:function(args){
|
success:function(args){
|
||||||
alert("저장 되었습니다.");
|
alert("SCOPE 정보가 저장 되었습니다. 이어서 API 리스트를 저장합니다.");
|
||||||
goNav(returnUrl);//LIST로 이동
|
|
||||||
|
if(!isDetail) { // INSERT 일 경우 ScopeId를 업데이트
|
||||||
|
key = $('input[name=scopeId]').val();
|
||||||
|
}
|
||||||
|
saveApiGridData(key, returnUrl); // API LIST 저장.
|
||||||
|
|
||||||
|
|
||||||
},
|
},
|
||||||
error:function(e){
|
error:function(e){
|
||||||
alert(e.responseText);
|
alert(e.responseText);
|
||||||
},
|
|
||||||
complete: function() {
|
|
||||||
$("[id^='btn_']").prop("disabled", false);
|
|
||||||
$("#btn_modify").text("수정");
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ $(document).ready(function() {
|
|||||||
datatype:"json",
|
datatype:"json",
|
||||||
mtype: 'POST',
|
mtype: 'POST',
|
||||||
url: url,
|
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") %>',
|
colNames:['<%= localeMessage.getString("infAdpConMan.adaNm") %>',
|
||||||
'<%= localeMessage.getString("infAdpConMan.adaDes") %>',
|
'<%= localeMessage.getString("infAdpConMan.adaDes") %>',
|
||||||
'<%= localeMessage.getString("infAdpConMan.thrPerSecond") %>',
|
'<%= localeMessage.getString("infAdpConMan.thrPerSecond") %>',
|
||||||
@@ -62,7 +62,6 @@ $(document).ready(function() {
|
|||||||
url2 += '&menuId='+'${param.menuId}';
|
url2 += '&menuId='+'${param.menuId}';
|
||||||
//검색값
|
//검색값
|
||||||
url2 += '&searchName='+$("input[name=searchName]").val();
|
url2 += '&searchName='+$("input[name=searchName]").val();
|
||||||
url2 += '&searchUseYn='+$("select[name=searchUseYn]").val();
|
|
||||||
//key값
|
//key값
|
||||||
url2 += '&name='+name;
|
url2 += '&name='+name;
|
||||||
goNav(url2);
|
goNav(url2);
|
||||||
@@ -81,7 +80,7 @@ $(document).ready(function() {
|
|||||||
resizeJqGridWidth('grid','content_middle','1000');
|
resizeJqGridWidth('grid','content_middle','1000');
|
||||||
|
|
||||||
$("#btn_search").click(function(){
|
$("#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(){
|
$("#btn_new").click(function(){
|
||||||
var url2 = url_view;
|
var url2 = url_view;
|
||||||
@@ -127,16 +126,6 @@ $(document).ready(function() {
|
|||||||
<tr>
|
<tr>
|
||||||
<th style="width:180px;"><%= localeMessage.getString("infAdpConMan.adaNm") %></th>
|
<th style="width:180px;"><%= localeMessage.getString("infAdpConMan.adaNm") %></th>
|
||||||
<td><input type="text" name="searchName" value="${param.searchName}"></td>
|
<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>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ $(document).ready(function() {
|
|||||||
datatype:"json",
|
datatype:"json",
|
||||||
mtype: 'POST',
|
mtype: 'POST',
|
||||||
url: url,
|
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',
|
colNames:['클라이언트 ID',
|
||||||
'클라이언트명',
|
'클라이언트명',
|
||||||
'<%= localeMessage.getString("infAdpConMan.thrPerSecond") %>',
|
'<%= localeMessage.getString("infAdpConMan.thrPerSecond") %>',
|
||||||
@@ -62,7 +62,6 @@ $(document).ready(function() {
|
|||||||
url2 += '&menuId='+'${param.menuId}';
|
url2 += '&menuId='+'${param.menuId}';
|
||||||
//검색값
|
//검색값
|
||||||
url2 += '&searchName='+$("input[name=searchName]").val();
|
url2 += '&searchName='+$("input[name=searchName]").val();
|
||||||
url2 += '&searchUseYn='+$("select[name=searchUseYn]").val();
|
|
||||||
//key값
|
//key값
|
||||||
url2 += '&name='+name;
|
url2 += '&name='+name;
|
||||||
goNav(url2);
|
goNav(url2);
|
||||||
@@ -81,7 +80,7 @@ $(document).ready(function() {
|
|||||||
resizeJqGridWidth('grid','content_middle','1000');
|
resizeJqGridWidth('grid','content_middle','1000');
|
||||||
|
|
||||||
$("#btn_search").click(function(){
|
$("#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(){
|
$("#btn_new").click(function(){
|
||||||
var url2 = url_view;
|
var url2 = url_view;
|
||||||
@@ -91,7 +90,6 @@ $(document).ready(function() {
|
|||||||
url2 += '&menuId='+'${param.menuId}';
|
url2 += '&menuId='+'${param.menuId}';
|
||||||
//검색값
|
//검색값
|
||||||
url2 += '&searchName=';
|
url2 += '&searchName=';
|
||||||
url2 += '&searchUseYn='+$('select[name=searchUseYn]').val();
|
|
||||||
|
|
||||||
goNav(url2);
|
goNav(url2);
|
||||||
});
|
});
|
||||||
@@ -126,16 +124,6 @@ $(document).ready(function() {
|
|||||||
<tr>
|
<tr>
|
||||||
<th style="width:180px;">클라이언트명</th>
|
<th style="width:180px;">클라이언트명</th>
|
||||||
<td><input type="text" name="searchName" value="${param.searchName}"></td>
|
<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>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|||||||
@@ -298,7 +298,6 @@ $(document).ready(function() {
|
|||||||
url: url,
|
url: url,
|
||||||
data: postData,
|
data: postData,
|
||||||
success: function(args) {
|
success: function(args) {
|
||||||
console.log('mod', args);
|
|
||||||
showAlert("<%= localeMessage.getString("common.saveMsg") %>", {
|
showAlert("<%= localeMessage.getString("common.saveMsg") %>", {
|
||||||
type: 'success',
|
type: 'success',
|
||||||
title: '저장 완료',
|
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-value" id="summaryThreshold">0<span class="unit">req</span></span>
|
||||||
<span class="bucket-summary-sub" id="summaryThresholdCalc"></span>
|
<span class="bucket-summary-sub" id="summaryThresholdCalc"></span>
|
||||||
</div>
|
</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>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -49,7 +49,6 @@ function groupNameFormat(cellvalue, options, rowObject) {
|
|||||||
|
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
$("input[name=searchStartYYYYMMDD],input[name=searchEndYYYYMMDD]").inputmask("yyyy-mm-dd",{'autoUnmask':true});
|
$("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(){
|
$("input[name=searchStartYYYYMMDD]").each(function(){
|
||||||
if ($(this).val() == undefined || $(this).val() == null || $(this).val() == ""){
|
if ($(this).val() == undefined || $(this).val() == null || $(this).val() == ""){
|
||||||
@@ -63,18 +62,6 @@ $(document).ready(function() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
$("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 로
|
||||||
|
|
||||||
@@ -149,10 +136,8 @@ $(document).ready(function() {
|
|||||||
|
|
||||||
var start = $("input[name=searchStartYYYYMMDD]").val().replace(/-/gi,"");
|
var start = $("input[name=searchStartYYYYMMDD]").val().replace(/-/gi,"");
|
||||||
var end = $("input[name=searchEndYYYYMMDD]").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("조회기간을 확인해주세요.");
|
alert("조회기간을 확인해주세요.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -212,24 +197,14 @@ $(document).ready(function() {
|
|||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<th style="width:180px;"><%= localeMessage.getString("infConHstMan.date") %></th>
|
<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="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="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="searchStartDate" value="" style="width:0px;">
|
||||||
<input type="hidden" name="searchEndDate" value="" style="width:0px;">
|
<input type="hidden" name="searchEndDate" value="" style="width:0px;">
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</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>
|
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ $(document).ready(function() {
|
|||||||
datatype:"json",
|
datatype:"json",
|
||||||
mtype: 'POST',
|
mtype: 'POST',
|
||||||
url: url,
|
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',
|
colNames:['그룹 ID',
|
||||||
'그룹명',
|
'그룹명',
|
||||||
'초당 임계치',
|
'초당 임계치',
|
||||||
@@ -125,7 +125,6 @@ $(document).ready(function() {
|
|||||||
url2 += '&menuId='+'${param.menuId}';
|
url2 += '&menuId='+'${param.menuId}';
|
||||||
//검색값
|
//검색값
|
||||||
url2 += '&searchGroupName='+$("input[name=searchGroupName]").val();
|
url2 += '&searchGroupName='+$("input[name=searchGroupName]").val();
|
||||||
url2 += '&searchUseYn='+$("select[name=searchUseYn]").val();
|
|
||||||
//key값
|
//key값
|
||||||
url2 += '&groupId='+groupId;
|
url2 += '&groupId='+groupId;
|
||||||
goNav(url2);
|
goNav(url2);
|
||||||
@@ -144,7 +143,7 @@ $(document).ready(function() {
|
|||||||
resizeJqGridWidth('grid','content_middle','1000');
|
resizeJqGridWidth('grid','content_middle','1000');
|
||||||
|
|
||||||
$("#btn_search").click(function(){
|
$("#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(){
|
$("#btn_new").click(function(){
|
||||||
var url2 = url_view;
|
var url2 = url_view;
|
||||||
@@ -189,16 +188,6 @@ $(document).ready(function() {
|
|||||||
<tr>
|
<tr>
|
||||||
<th style="width:180px;">그룹명</th>
|
<th style="width:180px;">그룹명</th>
|
||||||
<td><input type="text" name="searchGroupName" value="${param.searchGroupName}"></td>
|
<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>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ $(document).ready(function() {
|
|||||||
datatype:"json",
|
datatype:"json",
|
||||||
mtype: 'POST',
|
mtype: 'POST',
|
||||||
url: url,
|
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") %>',
|
colNames:['<%= localeMessage.getString("infIfConMan.ifNm") %>',
|
||||||
'<%= localeMessage.getString("infIfConMan.ifDesc") %>',
|
'<%= localeMessage.getString("infIfConMan.ifDesc") %>',
|
||||||
'<%= localeMessage.getString("infAdpConMan.thrPerSecond") %>',
|
'<%= localeMessage.getString("infAdpConMan.thrPerSecond") %>',
|
||||||
@@ -62,7 +62,6 @@ $(document).ready(function() {
|
|||||||
url2 += '&menuId='+'${param.menuId}';
|
url2 += '&menuId='+'${param.menuId}';
|
||||||
//검색값
|
//검색값
|
||||||
url2 += '&searchName='+$("input[name=searchName]").val();
|
url2 += '&searchName='+$("input[name=searchName]").val();
|
||||||
url2 += '&searchUseYn='+$("select[name=searchUseYn]").val();
|
|
||||||
//key값
|
//key값
|
||||||
url2 += '&name='+name;
|
url2 += '&name='+name;
|
||||||
goNav(url2);
|
goNav(url2);
|
||||||
@@ -81,7 +80,7 @@ $(document).ready(function() {
|
|||||||
resizeJqGridWidth('grid','content_middle','1000');
|
resizeJqGridWidth('grid','content_middle','1000');
|
||||||
|
|
||||||
$("#btn_search").click(function(){
|
$("#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(){
|
$("#btn_new").click(function(){
|
||||||
var url2 = url_view;
|
var url2 = url_view;
|
||||||
@@ -127,16 +126,6 @@ $(document).ready(function() {
|
|||||||
<tr>
|
<tr>
|
||||||
<th style="width:180px;"><%= localeMessage.getString("infIfConMan.ifNm") %></th>
|
<th style="width:180px;"><%= localeMessage.getString("infIfConMan.ifNm") %></th>
|
||||||
<td><input type="text" name="searchName" value="${param.searchName}"></td>
|
<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>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|||||||
@@ -71,11 +71,11 @@
|
|||||||
|
|
||||||
$('#grid').children().remove();
|
$('#grid').children().remove();
|
||||||
|
|
||||||
const itemTypes = ['Field', 'Grid', 'Group', 'Attr', 'Array'];
|
const itemTypes = ['Field', 'Grid', 'Group', 'Attr'];
|
||||||
|
|
||||||
columns = [ // for columnData prop
|
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("layoutMan.itmeEn") %>', name: 'loutItemName', type: 'text', width:280, resize:true, minWidth:120 },
|
||||||
|
{ 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.itemLevel")%>', name: 'loutItemDepth', type: 'text', width:50 },
|
||||||
{ title: '<%= localeMessage.getString("standardLayout.itemType") %>', name: 'loutItemType', type: 'autocomplete', source: itemTypes, width:70 },
|
{ 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.arraySize") %>', name: 'loutItemOccCnt', type: 'text', width:90 },
|
||||||
|
|||||||
@@ -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>
|
|
||||||
|
|
||||||
@@ -54,7 +54,6 @@
|
|||||||
const LAYOUT_ITEM_TYPE_GROUP = "Group";
|
const LAYOUT_ITEM_TYPE_GROUP = "Group";
|
||||||
const LAYOUT_ITEM_TYPE_GRID = "Grid";
|
const LAYOUT_ITEM_TYPE_GRID = "Grid";
|
||||||
const LAYOUT_ITEM_TYPE_FIELD = "Field";
|
const LAYOUT_ITEM_TYPE_FIELD = "Field";
|
||||||
const LAYOUT_ITEM_TYPE_ARRAY = "Array";
|
|
||||||
|
|
||||||
var isDetail = false;
|
var isDetail = false;
|
||||||
|
|
||||||
@@ -551,14 +550,6 @@
|
|||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 항목 자신이 Array인 경우, 경로/변환명령 끝에 반복 표시 [*]를 추가
|
|
||||||
function addArrayTag(loutItemType, data) {
|
|
||||||
if (loutItemType == LAYOUT_ITEM_TYPE_ARRAY && !data.endsWith("[*]")) {
|
|
||||||
data = data + "[*]";
|
|
||||||
}
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
//변환명에 있는 서비스코드와 if서비스 코드 매칭
|
//변환명에 있는 서비스코드와 if서비스 코드 매칭
|
||||||
function validation(){
|
function validation(){
|
||||||
if ($('input[name=cnvsnName]').val() == '') {
|
if ($('input[name=cnvsnName]').val() == '') {
|
||||||
@@ -898,10 +889,10 @@
|
|||||||
let selectValue = "";
|
let selectValue = "";
|
||||||
let targetValue = "";
|
let targetValue = "";
|
||||||
for ( var i = 0; i < sourceData.length; i++) {
|
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);
|
selectValue = (sourceData[i]['LOUTITEMPATH']).substr(sourceData[i]['loutName'].length+1);
|
||||||
for (var j = 0; j <targetData.length; j++) {
|
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);
|
targetValue = (targetData[j]['LOUTITEMPATH']).substr(targetData[j]['loutName'].length+1);
|
||||||
if (targetValue == selectValue ) {
|
if (targetValue == selectValue ) {
|
||||||
var data = sourceData[i]['LOUTITEMPATH'];
|
var data = sourceData[i]['LOUTITEMPATH'];
|
||||||
@@ -925,8 +916,8 @@
|
|||||||
//field, ATTR 여부 판단
|
//field, ATTR 여부 판단
|
||||||
var sourceRow = sourceGrid.getRowData( srcRowId );
|
var sourceRow = sourceGrid.getRowData( srcRowId );
|
||||||
var targetRow = targetGrid.getRowData( tgtRowId );
|
var targetRow = targetGrid.getRowData( tgtRowId );
|
||||||
if ( (sourceRow["loutItemType"] == 'Field' || sourceRow["loutItemType"] == 'Attr' || sourceRow["loutItemType"] == 'Array') &&
|
if ( (sourceRow["loutItemType"] == 'Field' || sourceRow["loutItemType"] == 'Attr') &&
|
||||||
(targetRow["loutItemType"] == 'Field' || targetRow["loutItemType"] == 'Attr' || targetRow["loutItemType"] == 'Array') ){
|
(targetRow["loutItemType"] == 'Field' || targetRow["loutItemType"] == 'Attr') ){
|
||||||
;
|
;
|
||||||
}else{
|
}else{
|
||||||
alert('<%=localeMessage.getString("transformManDetail.alert3")%>');
|
alert('<%=localeMessage.getString("transformManDetail.alert3")%>');
|
||||||
@@ -945,15 +936,15 @@
|
|||||||
var targetValue = "";
|
var targetValue = "";
|
||||||
var j=tgtIndex-1-1;
|
var j=tgtIndex-1-1;
|
||||||
for ( var i = srcIndex-1; i < sourceData.length; i++) {
|
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++;
|
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++;
|
j++;
|
||||||
}
|
}
|
||||||
if (targetData.length -1 < j) break;
|
if (targetData.length -1 < j) break;
|
||||||
var targetRowId = targetData[j]['id'];
|
var targetRowId = targetData[j]['id'];
|
||||||
targetGrid.jqGrid('setCell',targetRowId,'CNVSNCMDNAME',addArrayTag(sourceData[i]['loutItemType'], addGroupTag(srcGroup,sourceData[i]['LOUTITEMPATH'])));
|
targetGrid.jqGrid('setCell',targetRowId,'CNVSNCMDNAME',addGroupTag(srcGroup,sourceData[i]['LOUTITEMPATH']));
|
||||||
targetGrid.jqGrid('setCell',targetRowId,'CNVSNRSULTITEMPATHNAME',addArrayTag(targetData[j]['loutItemType'], addGroupTag(tgtGroup,targetData[j]['LOUTITEMPATH'])));
|
targetGrid.jqGrid('setCell',targetRowId,'CNVSNRSULTITEMPATHNAME',addGroupTag(tgtGroup,targetData[j]['LOUTITEMPATH']));
|
||||||
targetGrid.jqGrid('setCell',targetRowId,'CNVSNITEMSERNO',targetData[j]['loutItemSerno']);
|
targetGrid.jqGrid('setCell',targetRowId,'CNVSNITEMSERNO',targetData[j]['loutItemSerno']);
|
||||||
|
|
||||||
const sourceEndpointElementId = sourceGrid.getEndpointElementId(sourceData[i]['id']);
|
const sourceEndpointElementId = sourceGrid.getEndpointElementId(sourceData[i]['id']);
|
||||||
@@ -1024,7 +1015,7 @@
|
|||||||
var targetData = targetGrid.getRowData();
|
var targetData = targetGrid.getRowData();
|
||||||
var gridData = new Array();
|
var gridData = new Array();
|
||||||
for (var i = 0; i <targetData.length; i++) {
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
if (targetData[i]['CNVSNCMDNAME']==null
|
if (targetData[i]['CNVSNCMDNAME']==null
|
||||||
@@ -1167,7 +1158,7 @@
|
|||||||
function srcformatterFunction(cellvalue,options,rowObject){
|
function srcformatterFunction(cellvalue,options,rowObject){
|
||||||
var rowId = options["rowId"];
|
var rowId = options["rowId"];
|
||||||
var loutItemType = rowObject["loutItemType"];
|
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;' />";
|
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{
|
}else{
|
||||||
return "<div id='div_srcgroup_"+rowId+"' name='srcgroup_"+rowId+"' style='width:28px;height:17px;' />";
|
return "<div id='div_srcgroup_"+rowId+"' name='srcgroup_"+rowId+"' style='width:28px;height:17px;' />";
|
||||||
@@ -1179,7 +1170,7 @@
|
|||||||
function tgtformatterFunction(cellvalue,options,rowObject){
|
function tgtformatterFunction(cellvalue,options,rowObject){
|
||||||
var rowId = options["rowId"];
|
var rowId = options["rowId"];
|
||||||
var loutItemType = rowObject["loutItemType"];
|
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;' >"
|
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 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>";
|
+ "</div></div>";
|
||||||
@@ -1678,8 +1669,8 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((sourceRow["loutItemType"] != 'Field' && sourceRow["loutItemType"] != 'Attr' && sourceRow["loutItemType"] != 'Array')
|
if ((sourceRow["loutItemType"] != 'Field' && sourceRow["loutItemType"] != 'Attr')
|
||||||
|| (targetRow["loutItemType"] != 'Field' && targetRow["loutItemType"] != 'Attr' && targetRow["loutItemType"] != 'Array')) {
|
|| (targetRow["loutItemType"] != 'Field' && targetRow["loutItemType"] != 'Attr')) {
|
||||||
alert('<%=localeMessage.getString("transformManDetail.alert3")%>');
|
alert('<%=localeMessage.getString("transformManDetail.alert3")%>');
|
||||||
return ;
|
return ;
|
||||||
}
|
}
|
||||||
@@ -1693,7 +1684,7 @@
|
|||||||
|
|
||||||
if (targetRow['CNVSNCMDNAME'] == '') {
|
if (targetRow['CNVSNCMDNAME'] == '') {
|
||||||
// 변환 명령 값이 한번 설정 되면 변경 되지 않게 함.
|
// 변환 명령 값이 한번 설정 되면 변경 되지 않게 함.
|
||||||
var cnvsnCmdName = addArrayTag(sourceRow["loutItemType"], addGroupTag(srcGroup,sourceRow["LOUTITEMPATH"]));
|
var cnvsnCmdName = addGroupTag(srcGroup,sourceRow["LOUTITEMPATH"]);
|
||||||
// function까지 처리되도록 수정했으나, parameter가 하나인 경우에만 정상처리됨
|
// function까지 처리되도록 수정했으나, parameter가 하나인 경우에만 정상처리됨
|
||||||
if($('#functionCombo').val() != "") {
|
if($('#functionCombo').val() != "") {
|
||||||
cnvsnCmdName = $('#functionCombo').val() + "(" + cnvsnCmdName + ")";
|
cnvsnCmdName = $('#functionCombo').val() + "(" + cnvsnCmdName + ")";
|
||||||
@@ -1702,7 +1693,7 @@
|
|||||||
targetGrid.jqGrid('setCell',tgtRowId,'CNVSNCMDNAME', cnvsnCmdName);
|
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,'CNVSNITEMSERNO',targetRow['loutItemSerno']);
|
||||||
targetGrid.jqGrid('setCell',tgtRowId,'CNVSNSRCITEMS', cnvsnSrcItems);
|
targetGrid.jqGrid('setCell',tgtRowId,'CNVSNSRCITEMS', cnvsnSrcItems);
|
||||||
targetGrid.jqGrid('setCell',tgtRowId,'ISVALIDMAPPING', true);
|
targetGrid.jqGrid('setCell',tgtRowId,'ISVALIDMAPPING', true);
|
||||||
@@ -1894,8 +1885,8 @@
|
|||||||
if (loutItemType == LAYOUT_ITEM_TYPE_GRID || loutItemType == LAYOUT_ITEM_TYPE_GROUP) {
|
if (loutItemType == LAYOUT_ITEM_TYPE_GRID || loutItemType == LAYOUT_ITEM_TYPE_GROUP) {
|
||||||
// group 인 경우
|
// group 인 경우
|
||||||
items = createGridGroupContextMenuItems(grid, rowId);
|
items = createGridGroupContextMenuItems(grid, rowId);
|
||||||
} else if(loutItemType == LAYOUT_ITEM_TYPE_FIELD || loutItemType == LAYOUT_ITEM_TYPE_ARRAY) {
|
} else if(loutItemType == LAYOUT_ITEM_TYPE_FIELD) {
|
||||||
// field, array 인 경우
|
// field 인 경우
|
||||||
items = createFieldContextMenuItems(grid, positionElement.attr('id'));
|
items = createFieldContextMenuItems(grid, positionElement.attr('id'));
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
@@ -1947,8 +1938,8 @@
|
|||||||
var key = "";
|
var key = "";
|
||||||
var args = new Object();
|
var args = new Object();
|
||||||
args['eaiSvcName'] = $('input[name=eaiSvcName]').val();
|
args['eaiSvcName'] = $('input[name=eaiSvcName]').val();
|
||||||
var url=url_view; //'<c:url value="/onl/transaction/extnl/interfaceMan.view"/>';
|
var url='<c:url value="/onl/transaction/extnl/interfaceMan.view"/>';
|
||||||
url = url + "?cmd=API_POPUP";
|
url = url + "?cmd=POPUP";
|
||||||
var ret = showModal(url,args,1020,630, function(arg){
|
var ret = showModal(url,args,1020,630, function(arg){
|
||||||
var args = null;
|
var args = null;
|
||||||
if(arg == null || arg == undefined ) {//chrome
|
if(arg == null || arg == undefined ) {//chrome
|
||||||
@@ -1962,11 +1953,11 @@
|
|||||||
|
|
||||||
var ret = args.returnValue;
|
var ret = args.returnValue;
|
||||||
console.log("ret",ret);
|
console.log("ret",ret);
|
||||||
key = ret['apiId'];
|
key = ret['key'];
|
||||||
|
|
||||||
$("input[name=eaiSvcName]").val(key);
|
$("input[name=eaiSvcName]").val(key);
|
||||||
$("input[name=eaiSvcDesc]").val(ret['apiDesc']);
|
$("input[name=eaiSvcDesc]").val(ret['eaiSvcDesc']);
|
||||||
bzwkDstCd = ret['bizCode'];
|
bzwkDstCd = ret['eaiBzwkDstCd'];
|
||||||
|
|
||||||
$("input[name=eaiSvcName]").change();
|
$("input[name=eaiSvcName]").change();
|
||||||
});
|
});
|
||||||
@@ -1998,7 +1989,7 @@
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (loutItemType == LAYOUT_ITEM_TYPE_FIELD || loutItemType == LAYOUT_ITEM_TYPE_ARRAY) {
|
if (loutItemType == LAYOUT_ITEM_TYPE_FIELD) {
|
||||||
loutItemPath = loutItemPath.replace(new RegExp("^" + prefix), "");
|
loutItemPath = loutItemPath.replace(new RegExp("^" + prefix), "");
|
||||||
console.debug(grid.getId() + " : loutItemPath : " + loutItemPath);
|
console.debug(grid.getId() + " : loutItemPath : " + loutItemPath);
|
||||||
rowData['GROUP_LOUTITEMPATH'] = loutItemPath;
|
rowData['GROUP_LOUTITEMPATH'] = loutItemPath;
|
||||||
|
|||||||
@@ -20,7 +20,6 @@
|
|||||||
<jsp:include page="/jsp/common/include/script.jsp"/>
|
<jsp:include page="/jsp/common/include/script.jsp"/>
|
||||||
|
|
||||||
<script language="javascript" >
|
<script language="javascript" >
|
||||||
var url_view = '<c:url value="/onl/admin/rule/transform2/transform2Man.view" />';
|
|
||||||
var $ = jQuery.noConflict();
|
var $ = jQuery.noConflict();
|
||||||
var bzwkDstCd = "";
|
var bzwkDstCd = "";
|
||||||
|
|
||||||
@@ -82,9 +81,8 @@ $(document).ready(function() {
|
|||||||
var key = "";
|
var key = "";
|
||||||
var args = new Object();
|
var args = new Object();
|
||||||
args['eaiSvcName'] = $('input[name=eaiSvcName]').val();
|
args['eaiSvcName'] = $('input[name=eaiSvcName]').val();
|
||||||
var url=url_view; //'<c:url value="/onl/transaction/extnl/interfaceMan.view"/>';
|
var url='<c:url value="/onl/transaction/extnl/interfaceMan.view"/>';
|
||||||
url = url + "?cmd=API_POPUP";
|
url = url + "?cmd=POPUP";
|
||||||
console.log('[new]', url);
|
|
||||||
var ret = showModal(url,args,1020,630, function(arg){
|
var ret = showModal(url,args,1020,630, function(arg){
|
||||||
var args = null;
|
var args = null;
|
||||||
if(arg == null || arg == undefined ) {//chrome
|
if(arg == null || arg == undefined ) {//chrome
|
||||||
@@ -98,11 +96,11 @@ $(document).ready(function() {
|
|||||||
|
|
||||||
var ret = args.returnValue;
|
var ret = args.returnValue;
|
||||||
console.log("ret",ret);
|
console.log("ret",ret);
|
||||||
key = ret['apiId'];
|
key = ret['key'];
|
||||||
|
|
||||||
$("input[name=eaiSvcName]").val(key);
|
$("input[name=eaiSvcName]").val(key);
|
||||||
$("input[name=eaiSvcDesc]").val(ret['apiDesc']);
|
$("input[name=eaiSvcDesc]").val(ret['eaiSvcDesc']);
|
||||||
bzwkDstCd = ret['bizCode'];
|
bzwkDstCd = ret['eaiBzwkDstCd'];
|
||||||
|
|
||||||
$("input[name=eaiSvcName]").change();
|
$("input[name=eaiSvcName]").change();
|
||||||
//$("select[name=eaiSevrDstcd]").val(ret['eaiSevrDstcd']);
|
//$("select[name=eaiSevrDstcd]").val(ret['eaiSevrDstcd']);
|
||||||
|
|||||||
@@ -415,12 +415,6 @@
|
|||||||
<input type="text" name="groupName" data-required data-warning="API 그룹 명을 입력하여 주십시오.">
|
<input type="text" name="groupName" data-required data-warning="API 그룹 명을 입력하여 주십시오.">
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
|
||||||
<th style="width:150px;">그룹 설명</th>
|
|
||||||
<td colspan="7">
|
|
||||||
<input type="text" name="groupDesc">
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
<tr>
|
||||||
<th style="width:150px;">공개 여부</th>
|
<th style="width:150px;">공개 여부</th>
|
||||||
<td style="width:100px;">
|
<td style="width:100px;">
|
||||||
@@ -467,6 +461,14 @@
|
|||||||
<img id="mainIconPreview" src="#" alt="Main Icon Preview" style="display:none;height:20px">
|
<img id="mainIconPreview" src="#" alt="Main Icon Preview" style="display:none;height:20px">
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
|
|
||||||
|
<%--<tr>
|
||||||
|
<th>컨텐츠 내용</th>
|
||||||
|
<td colspan="5">
|
||||||
|
<textarea name="contentDetail" id="contentDetail" rows="10" cols="50"></textarea>
|
||||||
|
</td>
|
||||||
|
</tr>--%>
|
||||||
</table>
|
</table>
|
||||||
<div style="margin-top:15px;">
|
<div style="margin-top:15px;">
|
||||||
<div style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 5px ">
|
<div style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 5px ">
|
||||||
|
|||||||
@@ -56,16 +56,11 @@
|
|||||||
$("#expectEndDate").datepicker().inputmask("yyyy-mm-dd", {'autoUnmask': true});
|
$("#expectEndDate").datepicker().inputmask("yyyy-mm-dd", {'autoUnmask': true});
|
||||||
|
|
||||||
$("#btn_app_approve").click(function () {
|
$("#btn_app_approve").click(function () {
|
||||||
var isDeleteReq = (currentRequestType === 'DELETE');
|
if (confirm("승인하시겠습니까?")) {
|
||||||
var gwAction = (isDeleteReq && $('#chkGwDelete').is(':checked')) ? 'DELETE' : 'BLOCK';
|
|
||||||
var confirmMsg = isDeleteReq
|
|
||||||
? "해지 승인 시 포털 인증키(Credential)는 삭제되며,\n게이트웨이 클라이언트는 [" + (gwAction === 'DELETE' ? "완전 삭제" : "차단(비활성화)") + "] 처리됩니다.\n승인하시겠습니까?"
|
|
||||||
: "승인하시겠습니까?";
|
|
||||||
if (confirm(confirmMsg)) {
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
type: "POST",
|
type: "POST",
|
||||||
url: url,
|
url: url,
|
||||||
data: {cmd: "APPROVE", id: key, gwAction: gwAction},
|
data: {cmd: "APPROVE", id: key},
|
||||||
success: function (args) {
|
success: function (args) {
|
||||||
<%--alert("<%= localeMessage.getString("approval.approveSuccess") %>");--%>
|
<%--alert("<%= localeMessage.getString("approval.approveSuccess") %>");--%>
|
||||||
alert("승인되었습니다.");
|
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 () {
|
$('#btn_app_redeploy').click(function () {
|
||||||
if (confirm("재배포 하시겠습니까?")) {
|
if (confirm("재배포 하시겠습니까?")) {
|
||||||
$.ajax({
|
$.ajax({
|
||||||
@@ -181,8 +150,6 @@
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
var currentRequestType = null; // 현재 신청 유형 (DELETE 승인 시 GW 처리방식 선택 노출용)
|
|
||||||
|
|
||||||
function detail(key) {
|
function detail(key) {
|
||||||
$.ajax({
|
$.ajax({
|
||||||
type: "POST",
|
type: "POST",
|
||||||
@@ -211,11 +178,10 @@
|
|||||||
$("#createdDate").text(data.createdDate).inputmask("9999-99-99 99:99:99.999", {'autoUnmask': true});
|
$("#createdDate").text(data.createdDate).inputmask("9999-99-99 99:99:99.999", {'autoUnmask': true});
|
||||||
$("#expectEndDate").val(data.expectEndDate);
|
$("#expectEndDate").val(data.expectEndDate);
|
||||||
|
|
||||||
var isCurrentApprover = data.approvers.some(approver =>
|
if (data.approvers.some(approver =>
|
||||||
approver.approverStatus === 'CURRENT' &&
|
approver.approverStatus === 'CURRENT' &&
|
||||||
approver.user && approver.user.USERID === loginUser
|
approver.user.USERID === loginUser
|
||||||
);
|
)) {
|
||||||
if (isCurrentApprover) {
|
|
||||||
document.getElementById("btn_app_approve").style.display = "";
|
document.getElementById("btn_app_approve").style.display = "";
|
||||||
document.getElementById("btn_app_reject").style.display = "";
|
document.getElementById("btn_app_reject").style.display = "";
|
||||||
} else {
|
} else {
|
||||||
@@ -223,29 +189,6 @@
|
|||||||
document.getElementById("btn_app_reject").style.display = "none";
|
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') {
|
if (data.approvalStatus.code === 'FAILED') {
|
||||||
document.getElementById("btn_app_redeploy").style.display = "";
|
document.getElementById("btn_app_redeploy").style.display = "";
|
||||||
}
|
}
|
||||||
@@ -392,13 +335,6 @@
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</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">
|
<div class="popup_button">
|
||||||
<button type="button" class="cssbtn" id="btn_modify" level="W"><i
|
<button type="button" class="cssbtn" id="btn_modify" level="W"><i
|
||||||
class="material-icons">save</i>수정
|
class="material-icons">save</i>수정
|
||||||
@@ -409,9 +345,6 @@
|
|||||||
<button type="button" class="cssbtn" style="display: none;" id="btn_app_reject" level="W"><i
|
<button type="button" class="cssbtn" style="display: none;" id="btn_app_reject" level="W"><i
|
||||||
class="material-icons">close</i>반려
|
class="material-icons">close</i>반려
|
||||||
</button>
|
</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_redeploy" level="W"><i
|
<button type="button" class="cssbtn" style="display: none;" id="btn_app_redeploy" level="W"><i
|
||||||
class="material-icons">close</i>재처리
|
class="material-icons">close</i>재처리
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -131,24 +131,6 @@
|
|||||||
// var reverseNumber = records - ((page - 1) * rowNum + i);
|
// var reverseNumber = records - ((page - 1) * rowNum + i);
|
||||||
$(this).setCell(rows[i], 'rowNum', number);
|
$(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) {
|
loadError: function(jqXHR, textStatus, errorThrown) {
|
||||||
var location = '<%=request.getContextPath()%>/';
|
var location = '<%=request.getContextPath()%>/';
|
||||||
@@ -194,7 +176,7 @@
|
|||||||
<div class="title">승인 요청 목록<span class="tooltip">승인 요청을 관리합니다.</span></div>
|
<div class="title">승인 요청 목록<span class="tooltip">승인 요청을 관리합니다.</span></div>
|
||||||
<c:if test="${hardDeleteEnabled}">
|
<c:if test="${hardDeleteEnabled}">
|
||||||
<div style="background:#fff3cd; border:1px solid #ffc107; padding:8px 15px; margin:5px 0; border-radius:4px; color:#856404;">
|
<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>
|
</div>
|
||||||
</c:if>
|
</c:if>
|
||||||
<form id="ajaxForm" onsubmit="return false;">
|
<form id="ajaxForm" onsubmit="return false;">
|
||||||
|
|||||||
@@ -45,10 +45,6 @@
|
|||||||
padding: 2px 5px;
|
padding: 2px 5px;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
}
|
}
|
||||||
.cssbtn:disabled {
|
|
||||||
opacity: 0.45;
|
|
||||||
cursor: not-allowed;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
<script language="javascript">
|
<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이 승인 요청을 삭제하시겠습니까?")) {
|
|
||||||
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(e.responseText);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
$('#btn_user_redeploy').click(function() {
|
$('#btn_user_redeploy').click(function() {
|
||||||
if (confirm("재배포 하시겠습니까?")) {
|
if (confirm("재배포 하시겠습니까?")) {
|
||||||
$.ajax({
|
$.ajax({
|
||||||
@@ -270,55 +217,20 @@
|
|||||||
|
|
||||||
// Display Portal User details
|
// Display Portal User details
|
||||||
var portalUser = data.portalUser;
|
var portalUser = data.portalUser;
|
||||||
var portalOrg = portalUser ? portalUser.portalOrgUIs : null;
|
$("#loginId").text(portalUser.loginId);
|
||||||
|
$("#userName").text(portalUser.userName);
|
||||||
// 삭제된 대상 사용자/법인 판별
|
$("#mobileNumber").text(portalUser.mobileNumber);
|
||||||
var userDeleted = !portalUser
|
$("#userStatusDescription").text(portalUser.userStatusDescription);
|
||||||
|| portalUser.userName === '반려된 사용자'
|
$("#roleCodeDescription").text(portalUser.roleCodeDescription);
|
||||||
|| portalUser.userName === '삭제된 사용자';
|
if (data.approvers.some(approver =>
|
||||||
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 =>
|
|
||||||
approver.approverStatus === 'CURRENT' &&
|
approver.approverStatus === 'CURRENT' &&
|
||||||
approver.user && approver.user.USERID === loginUser
|
approver.user.USERID === loginUser
|
||||||
);
|
)) {
|
||||||
|
document.getElementById("btn_user_approve").style.display = "";
|
||||||
if (deletedTarget) {
|
document.getElementById("btn_user_reject").style.display = "";
|
||||||
// 삭제된 사용자/법인 포함 → 승인·반려 불가. 버튼은 비활성(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 = "";
|
|
||||||
} else {
|
} else {
|
||||||
$("#btn_user_approve, #btn_user_reject").prop("disabled", false);
|
document.getElementById("btn_user_approve").style.display = "none";
|
||||||
document.getElementById("btn_user_delete").style.display = "none";
|
document.getElementById("btn_user_reject").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";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.approvalStatus.description === '배포실패'){
|
if (data.approvalStatus.description === '배포실패'){
|
||||||
@@ -326,27 +238,23 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Display Portal Org details
|
// Display Portal Org details
|
||||||
if (portalOrg) {
|
var portalOrg = portalUser.portalOrgUIs;
|
||||||
$("#orgName").text(portalOrg.orgName);
|
$("#orgName").text(portalOrg.orgName);
|
||||||
$("#orgStatusDescription").text(portalOrg.orgStatusDescription);
|
$("#orgStatusDescription").text(portalOrg.orgStatusDescription);
|
||||||
$("#serviceName").text(portalOrg.serviceName);
|
$("#serviceName").text(portalOrg.serviceName);
|
||||||
$("#corpRegNo").text(formatCorpRegNo(portalOrg.corpRegNo));
|
$("#corpRegNo").text(formatCorpRegNo(portalOrg.corpRegNo));
|
||||||
$("#compRegNo").text(formatCompanyRegNo(portalOrg.compRegNo));
|
$("#compRegNo").text(formatCompanyRegNo(portalOrg.compRegNo));
|
||||||
|
|
||||||
$("#scPhoneNumber").text(portalOrg.scPhoneNumber); // 고객센터 전화번호
|
$("#scPhoneNumber").text(portalOrg.scPhoneNumber); // 고객센터 전화번호
|
||||||
$("#orgPhoneNumber").text(portalOrg.orgPhoneNumber); // 회사 전화번호
|
$("#orgPhoneNumber").text(portalOrg.orgPhoneNumber); // 회사 전화번호
|
||||||
$("#ceoName").text(portalOrg.ceoName); // 대표자 성명
|
$("#ceoName").text(portalOrg.ceoName); // 대표자 성명
|
||||||
$("#orgIndustryType").text(portalOrg.orgIndustryType || ""); // 업종
|
$("#orgIndustryType").text(portalOrg.orgIndustryType || ""); // 업종
|
||||||
$("#orgAddr").text(portalOrg.orgAddr); // 소재지
|
$("#orgAddr").text(portalOrg.orgAddr); // 소재지
|
||||||
$("#orgSectors").text(portalOrg.orgSectors || ""); // 업태
|
$("#orgSectors").text(portalOrg.orgSectors || ""); // 업태
|
||||||
$("#orgCode").text(portalOrg.orgCode);
|
$("#orgCode").text(portalOrg.orgCode);
|
||||||
$("#ipWhitelist").text(portalOrg.ipWhitelist || "");
|
$("#ipWhitelist").text(portalOrg.ipWhitelist || "");
|
||||||
} else {
|
|
||||||
$("#orgName").text("삭제된법인");
|
|
||||||
$("#orgStatusDescription, #serviceName, #corpRegNo, #compRegNo, #scPhoneNumber, #orgPhoneNumber, #ceoName, #orgIndustryType, #orgAddr, #orgSectors, #orgCode, #ipWhitelist").text("");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (portalOrg && portalOrg.compRegFile) {
|
if (portalOrg.compRegFile) {
|
||||||
$("#fileName").text(portalOrg.fileName);
|
$("#fileName").text(portalOrg.fileName);
|
||||||
if (portalOrg.fileName) {
|
if (portalOrg.fileName) {
|
||||||
$("#fileWrapper").show().off('click').on('click', function() {
|
$("#fileWrapper").show().off('click').on('click', function() {
|
||||||
@@ -386,18 +294,15 @@
|
|||||||
|
|
||||||
// 사용자 정보 업데이트
|
// 사용자 정보 업데이트
|
||||||
var portalUser = data.portalUser;
|
var portalUser = data.portalUser;
|
||||||
if (portalUser) {
|
$("#loginId").text(portalUser.loginId);
|
||||||
$("#loginId").text(portalUser.loginId);
|
$("#userName").text(portalUser.userName);
|
||||||
$("#userName").text(portalUser.userName);
|
$("#mobileNumber").text(portalUser.mobileNumber);
|
||||||
$("#mobileNumber").text(portalUser.mobileNumber);
|
$("#userStatusDescription").text(portalUser.userStatusDescription);
|
||||||
$("#userStatusDescription").text(portalUser.userStatusDescription);
|
$("#roleCodeDescription").text(portalUser.roleCodeDescription);
|
||||||
$("#roleCodeDescription").text(portalUser.roleCodeDescription);
|
|
||||||
} else {
|
|
||||||
$("#loginId, #userName, #mobileNumber, #userStatusDescription, #roleCodeDescription").text("삭제된 사용자");
|
|
||||||
}
|
|
||||||
|
|
||||||
// 법인 정보 업데이트
|
// 법인 정보 업데이트
|
||||||
updateOrgInfo(portalUser ? portalUser.portalOrgUIs : null);
|
var portalOrg = portalUser.portalOrgUIs;
|
||||||
|
updateOrgInfo(portalOrg);
|
||||||
|
|
||||||
// 마스킹 상태에 따라 데이터를 다르게 표시
|
// 마스킹 상태에 따라 데이터를 다르게 표시
|
||||||
var isMasked = $("#btn_unmask").attr("status") === "DETAIL";
|
var isMasked = $("#btn_unmask").attr("status") === "DETAIL";
|
||||||
@@ -439,11 +344,6 @@
|
|||||||
|
|
||||||
// 법인 정보 업데이트 함수
|
// 법인 정보 업데이트 함수
|
||||||
function updateOrgInfo(portalOrg) {
|
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);
|
$("#orgName").text(portalOrg.orgName);
|
||||||
$("#orgStatusDescription").text(portalOrg.orgStatusDescription);
|
$("#orgStatusDescription").text(portalOrg.orgStatusDescription);
|
||||||
$("#serviceName").text(portalOrg.serviceName);
|
$("#serviceName").text(portalOrg.serviceName);
|
||||||
@@ -586,8 +486,6 @@
|
|||||||
<div class="popup_button">
|
<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_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_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" 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 type="button" class="cssbtn" id="btn_close" level="R"><i class="material-icons">cancel</i> <%= localeMessage.getString("button.close") %>
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -22,8 +22,6 @@
|
|||||||
function formatInquiryStatus(cellvalue, options, rowObject) {
|
function formatInquiryStatus(cellvalue, options, rowObject) {
|
||||||
if (cellvalue == 'PENDING') {
|
if (cellvalue == 'PENDING') {
|
||||||
return '<span style="color: red;">문의중</span>';
|
return '<span style="color: red;">문의중</span>';
|
||||||
} else if (cellvalue == 'REVIEWING') {
|
|
||||||
return '<span >문의검토중</span>'
|
|
||||||
} else if (cellvalue == 'RESPONDED') {
|
} else if (cellvalue == 'RESPONDED') {
|
||||||
return '<span >답변완료</span>'
|
return '<span >답변완료</span>'
|
||||||
} else if (cellvalue == 'CLOSED') {
|
} 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) {
|
function formatFile(cellvalue, options, rowObject) {
|
||||||
var iconPath = '${pageContext.request.contextPath}/images/icon_file.png';
|
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;">' : '';
|
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',
|
mtype: 'POST',
|
||||||
url: url,
|
url: url,
|
||||||
postData : { cmd : 'LIST' },
|
postData : { cmd : 'LIST' },
|
||||||
colNames:['No.', 'id', '제목', '법인명', '상태', '공개범위', '작성자', '등록일', '답변자','답변일'],
|
colNames:['No.', 'id', '제목', '법인명', '상태', '작성자', '등록일', '답변자','답변일'],
|
||||||
colModel:[
|
colModel:[
|
||||||
{ name : 'rowNum' , align:'center', width:50, sortable:false },
|
{ name : 'rowNum' , align:'center', width:50, sortable:false },
|
||||||
{ name : 'id' , align:'center', key:true, hidden:true},
|
{ name : 'id' , align:'center', key:true, hidden:true},
|
||||||
{ name : 'inquirySubject' , align:'left' , width:200 , formatter: formatFile},
|
{ name : 'inquirySubject' , align:'left' , width:200 , formatter: formatFile},
|
||||||
{ name : 'orgName' , align:'center' , width:100 , },
|
{ name : 'orgName' , align:'center' , width:100 , },
|
||||||
{ name : 'inquiryStatus' , align:'center', width:70, formatter: formatInquiryStatus },
|
{ name : 'inquiryStatus' , align:'center', width:70, formatter: formatInquiryStatus },
|
||||||
{ name : 'visibility' , align:'center', width:70, formatter: formatVisibility },
|
|
||||||
{ name : 'inquirer.userName', align:'center', width:70 },
|
{ name : 'inquirer.userName', align:'center', width:70 },
|
||||||
{ name : 'createdDate' , align:'center', width:100, formatter: timeStampFormat },
|
{ name : 'createdDate' , align:'center', width:100, formatter: timeStampFormat },
|
||||||
{ name : 'responderName' , align:'center', width:70 },
|
{ name : 'responderName' , align:'center', width:70 },
|
||||||
@@ -221,7 +207,6 @@
|
|||||||
<select name="searchInquiryStatus">
|
<select name="searchInquiryStatus">
|
||||||
<option value="">전체</option>
|
<option value="">전체</option>
|
||||||
<option value="PENDING">문의중</option>
|
<option value="PENDING">문의중</option>
|
||||||
<option value="REVIEWING">문의검토중</option>
|
|
||||||
<option value="RESPONDED">답변완료</option>
|
<option value="RESPONDED">답변완료</option>
|
||||||
<option value="CLOSED">답변종료</option>
|
<option value="CLOSED">답변종료</option>
|
||||||
</select>
|
</select>
|
||||||
|
|||||||
@@ -79,46 +79,12 @@
|
|||||||
margin-left: 2px;
|
margin-left: 2px;
|
||||||
font-weight: bold;
|
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>
|
</style>
|
||||||
<script language="javascript">
|
<script language="javascript">
|
||||||
var url = '<c:url value="/onl/apim/portalinquiry/portalInquiryMan.json" />';
|
var url = '<c:url value="/onl/apim/portalinquiry/portalInquiryMan.json" />';
|
||||||
var url_view = '<c:url value="/onl/apim/portalinquiry/portalInquiryMan.view" />';
|
var url_view = '<c:url value="/onl/apim/portalinquiry/portalInquiryMan.view" />';
|
||||||
var file_download = '<c:url value="/file/download.do" />';
|
var file_download = '<c:url value="/file/download.do" />';
|
||||||
var inquiryStatus = 'PENDING';
|
var inquiryStatus = 'PENDING';
|
||||||
var originalVisibility = 'ORG';
|
|
||||||
var reasonModalCallback = null;
|
|
||||||
|
|
||||||
function decodeHTMLEntities(text) {
|
function decodeHTMLEntities(text) {
|
||||||
var textArea = document.createElement('textarea');
|
var textArea = document.createElement('textarea');
|
||||||
@@ -143,8 +109,6 @@
|
|||||||
inquiryStatus = data.inquiryStatus;
|
inquiryStatus = data.inquiryStatus;
|
||||||
if (data.inquiryStatus == 'PENDING') {
|
if (data.inquiryStatus == 'PENDING') {
|
||||||
$("#inquiryStatus").text('문의중');
|
$("#inquiryStatus").text('문의중');
|
||||||
} else if (data.inquiryStatus == 'REVIEWING') {
|
|
||||||
$("#inquiryStatus").text('문의검토중');
|
|
||||||
} else if (data.inquiryStatus == 'RESPONDED') {
|
} else if (data.inquiryStatus == 'RESPONDED') {
|
||||||
$("#inquiryStatus").text('답변완료');
|
$("#inquiryStatus").text('답변완료');
|
||||||
} else if (data.inquiryStatus == 'CLOSED') {
|
} else if (data.inquiryStatus == 'CLOSED') {
|
||||||
@@ -158,24 +122,6 @@
|
|||||||
var decodedContent = decodeHTMLEntities(data.responseDetail);
|
var decodedContent = decodeHTMLEntities(data.responseDetail);
|
||||||
$("#responseDetail").summernote('code', decodedContent || '');
|
$("#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);
|
listComment(key);
|
||||||
|
|
||||||
@@ -201,17 +147,8 @@
|
|||||||
if (!data || data.length === 0) return;
|
if (!data || data.length === 0) return;
|
||||||
$.each(data, function (i, comment) {
|
$.each(data, function (i, comment) {
|
||||||
var date = comment.createdDate ? timeStampFormat(comment.createdDate) : '';
|
var date = comment.createdDate ? timeStampFormat(comment.createdDate) : '';
|
||||||
var isPrivate = comment.visibility === 'PRIVATE';
|
|
||||||
var row = $('<div>').addClass('info-row');
|
var row = $('<div>').addClass('info-row');
|
||||||
if (isPrivate) {
|
$('<div>').addClass('info-label').css('color', '#555').text(comment.writerName || '').appendTo(row);
|
||||||
// 비공개 댓글은 색상으로 구별
|
|
||||||
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);
|
|
||||||
var content = $('<div>').addClass('info-content');
|
var content = $('<div>').addClass('info-content');
|
||||||
$('<div>').css('white-space', 'pre-wrap').text(comment.commentDetail).appendTo(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'});
|
var meta = $('<div>').css({'font-size': '0.9em', 'color': '#999', 'margin-top': '2px'});
|
||||||
@@ -222,19 +159,6 @@
|
|||||||
})(comment.id, comment.inquiryId))
|
})(comment.id, comment.inquiryId))
|
||||||
.appendTo(meta);
|
.appendTo(meta);
|
||||||
meta.appendTo(content);
|
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);
|
content.appendTo(row);
|
||||||
row.appendTo($commentList);
|
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) {
|
function deleteComment(commentId, inquiryId) {
|
||||||
if (inquiryStatus == 'CLOSED') {
|
if (inquiryStatus == 'CLOSED') {
|
||||||
alert('답변이 종료되어 삭제할 수 없습니다');
|
alert('답변이 종료되어 삭제할 수 없습니다');
|
||||||
@@ -362,38 +208,24 @@
|
|||||||
alert('답변이 종료되어 수정할 수 없습니다');
|
alert('답변이 종료되어 수정할 수 없습니다');
|
||||||
return;
|
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 (!checkRequired("ajaxForm")) return;
|
||||||
if (!confirm("<%= localeMessage.getString("common.checkSave")%>")) return;
|
if (!confirm("<%= localeMessage.getString("common.checkSave")%>")) return;
|
||||||
saveInquiryAnswer(returnUrl);
|
|
||||||
});
|
|
||||||
|
|
||||||
$("#reasonModalOk").click(function () {
|
var postData = $('#ajaxForm').serializeArray();
|
||||||
var reason = ($('#reasonModalInput').val() || '').trim();
|
postData.push({name: "cmd", value: "INSERT"});
|
||||||
if (!reason) {
|
|
||||||
alert('변경 사유를 입력하세요.');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var cb = reasonModalCallback;
|
|
||||||
closeReasonModal();
|
|
||||||
if (cb) cb(reason);
|
|
||||||
});
|
|
||||||
|
|
||||||
$("#reasonModalCancel").click(function () {
|
$.ajax({
|
||||||
closeReasonModal();
|
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 () {
|
$("#btn_comment").click(function () {
|
||||||
@@ -476,22 +308,7 @@
|
|||||||
<div class="info-label">작성일</div>
|
<div class="info-label">작성일</div>
|
||||||
<div id="createdDate" class="info-content"></div>
|
<div id="createdDate" class="info-content"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="info-row">
|
<div class="info-row" id="attachFileRow" style="display: none;">
|
||||||
<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-label">첨부파일</div>
|
<div class="info-label">첨부파일</div>
|
||||||
<div id="attachFiles" class="info-content"></div>
|
<div id="attachFiles" class="info-content"></div>
|
||||||
</div>
|
</div>
|
||||||
@@ -539,17 +356,5 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</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>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@@ -312,9 +312,7 @@ $(document).ready(function() {
|
|||||||
<div class="content_middle" id="content_middle">
|
<div class="content_middle" id="content_middle">
|
||||||
<div class="search_wrap">
|
<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>
|
<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>
|
<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>
|
<button type="button" class="cssbtn" id="btn_search" level="R"><i class="material-icons">search</i> <%= localeMessage.getString("button.search") %></button>
|
||||||
</div>
|
</div>
|
||||||
<%--법인정보관리--%>
|
<%--법인정보관리--%>
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
response.setHeader("Expires", "0");
|
response.setHeader("Expires", "0");
|
||||||
|
|
||||||
MonitoringContext monitoringContext = (MonitoringContext)CommonUtil.getBean(request, "monitoringContext");
|
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) {
|
public String getRequiredLabel(String label) {
|
||||||
@@ -138,11 +138,7 @@
|
|||||||
$("#btn_delete").hide();
|
$("#btn_delete").hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
$("select[name=approvalStatus]").val(portalOrgData.approvalStatus);
|
$("#approvalStatus").val(portalOrgData.approvalStatus);
|
||||||
// 승인상태는 읽기전용(수정 불가) - 값은 그대로 제출되도록 disabled 대신 잠금 처리
|
|
||||||
$("select[name=approvalStatus]")
|
|
||||||
.css({'pointer-events':'none', 'background-color':'#eee'})
|
|
||||||
.attr('tabindex', '-1');
|
|
||||||
$("#compRegNo").val(formatCompanyRegNo(portalOrgData.compRegNo));
|
$("#compRegNo").val(formatCompanyRegNo(portalOrgData.compRegNo));
|
||||||
$("#corpRegNo").val(formatCorpRegNo(portalOrgData.corpRegNo));
|
$("#corpRegNo").val(formatCorpRegNo(portalOrgData.corpRegNo));
|
||||||
$("#createdDate").inputmask("9999-99-99 99:99:99", {'autoUnmask': true});
|
$("#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 생성 전에 분리된 전화번호 필드 제거
|
// FormData 생성 전에 분리된 전화번호 필드 제거
|
||||||
$("#scPhonePrefix, #scPhoneMiddle, #scPhoneLast, #orgPhonePrefix, #orgPhoneMiddle, #orgPhoneLast").removeAttr('name');
|
$("#scPhonePrefix, #scPhoneMiddle, #scPhoneLast, #orgPhonePrefix, #orgPhoneMiddle, #orgPhoneLast").removeAttr('name');
|
||||||
@@ -519,47 +516,23 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// cmd 및 사유(auditReason) 첨부 후 전송
|
// cmd 파라미터 추가
|
||||||
function submitOrg(cmd, auditReason) {
|
formData.append("cmd", isDetail ? "UPDATE" : "INSERT");
|
||||||
formData.append("cmd", cmd);
|
|
||||||
// 감사로그(AUDIT_LOG) 발화를 위한 systemCode. 법인/가입자 리포지토리는 고정 EMS 스키마라 스키마 전환 영향 없음
|
$.ajax({
|
||||||
formData.append("serviceType", "APIGW");
|
type : "POST",
|
||||||
if (auditReason) {
|
url:url,
|
||||||
formData.append("auditReason", auditReason);
|
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() {
|
$("#btn_previous").click(function() {
|
||||||
@@ -568,28 +541,25 @@
|
|||||||
|
|
||||||
$("#btn_delete").click(function(){
|
$("#btn_delete").click(function(){
|
||||||
|
|
||||||
showReasonPrompt("해당 법인을 삭제 처리합니다. 사유를 입력해 주세요.", {
|
if(confirm("<%= localeMessage.getString("common.confirmMsg")%>")){
|
||||||
title: "삭제 사유",
|
var postData = $('#ajaxForm').serializeArray();
|
||||||
onConfirm: function(reason) {
|
postData.push({name: "cmd" , value:"DELETE"});
|
||||||
var postData = $('#ajaxForm').serializeArray();
|
|
||||||
postData.push({name: "cmd" , value:"DELETE"});
|
|
||||||
postData.push({name: "serviceType", value: "APIGW"});
|
|
||||||
postData.push({name: "auditReason", value: reason});
|
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
type : "POST",
|
type : "POST",
|
||||||
url:url,
|
url:url,
|
||||||
data:postData,
|
data:postData,
|
||||||
success:function(args){
|
success:function(args){
|
||||||
alert("<%= localeMessage.getString("common.deleteMsg") %>");
|
alert("<%= localeMessage.getString("common.deleteMsg") %>");
|
||||||
goNav(returnUrl);
|
goNav(returnUrl);
|
||||||
},
|
},
|
||||||
error:function(e){
|
error:function(e){
|
||||||
alert(e.responseText);
|
alert(e.responseText);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}else{
|
||||||
});
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,6 @@
|
|||||||
var url = '<c:url value="/onl/apim/portalproperty/propertyMan.json" />';
|
var url = '<c:url value="/onl/apim/portalproperty/propertyMan.json" />';
|
||||||
var url_view = '<c:url value="/onl/apim/portalproperty/propertyMan.view" />';
|
var url_view = '<c:url value="/onl/apim/portalproperty/propertyMan.view" />';
|
||||||
var lastsel2;
|
var lastsel2;
|
||||||
var clickedCol; //클릭한 셀의 컬럼 인덱스(포커스 이동용)
|
|
||||||
|
|
||||||
function isValid() {
|
function isValid() {
|
||||||
if ($('input[name=prptyGroupName]').val() == "") {
|
if ($('input[name=prptyGroupName]').val() == "") {
|
||||||
@@ -104,10 +103,6 @@
|
|||||||
},
|
},
|
||||||
loadComplete: function () {
|
loadComplete: function () {
|
||||||
},
|
},
|
||||||
onCellSelect: function (rowid, iCol) {
|
|
||||||
//onSelectRow보다 먼저 발화 → 클릭한 컬럼 인덱스 저장
|
|
||||||
clickedCol = iCol;
|
|
||||||
},
|
|
||||||
onSelectRow: function (rowid, status) {
|
onSelectRow: function (rowid, status) {
|
||||||
if (lastsel2 != undefined) {
|
if (lastsel2 != undefined) {
|
||||||
if ($("#grid tr#" + lastsel2).attr("editable") == 1) { //editable=1 means row in edit mode
|
if ($("#grid tr#" + lastsel2).attr("editable") == 1) { //editable=1 means row in edit mode
|
||||||
@@ -115,8 +110,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
$('#grid').restoreRow(lastsel2);
|
$('#grid').restoreRow(lastsel2);
|
||||||
//focusField로 클릭한 셀에 포커스(미지정 시 첫 편집 컬럼으로 감)
|
$('#grid').editRow(rowid, true);
|
||||||
$('#grid').editRow(rowid, {keys: true, focusField: clickedCol});
|
|
||||||
lastsel2 = rowid;
|
lastsel2 = rowid;
|
||||||
},
|
},
|
||||||
onSortCol: function () {
|
onSortCol: function () {
|
||||||
|
|||||||
@@ -347,11 +347,9 @@
|
|||||||
<button type="button" class="cssbtn" id="btn_new" level="W"><i
|
<button type="button" class="cssbtn" id="btn_new" level="W"><i
|
||||||
class="material-icons">add</i> <%= localeMessage.getString("button.new") %>
|
class="material-icons">add</i> <%= localeMessage.getString("button.new") %>
|
||||||
</button>
|
</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
|
<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> 선택 삭제
|
class="material-icons">delete_forever</i> 선택 삭제
|
||||||
</button>
|
</button>
|
||||||
</c:if>
|
|
||||||
<button type="button" class="cssbtn" id="btn_search" level="R"><i
|
<button type="button" class="cssbtn" id="btn_search" level="R"><i
|
||||||
class="material-icons">search</i> <%= localeMessage.getString("button.search") %>
|
class="material-icons">search</i> <%= localeMessage.getString("button.search") %>
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -165,10 +165,6 @@
|
|||||||
$("#userStatus").val(data.userStatus);
|
$("#userStatus").val(data.userStatus);
|
||||||
$("#roleCode").val(data.roleCode).trigger('change');
|
$("#roleCode").val(data.roleCode).trigger('change');
|
||||||
$("#approvalStatus").val(data.approvalStatus);
|
$("#approvalStatus").val(data.approvalStatus);
|
||||||
// 승인상태는 읽기전용(수정 불가) - 값은 그대로 제출되도록 disabled 대신 잠금 처리
|
|
||||||
$("#approvalStatus")
|
|
||||||
.css({'pointer-events':'none', 'background-color':'#eee'})
|
|
||||||
.attr('tabindex', '-1');
|
|
||||||
|
|
||||||
// 탈퇴 상태 처리
|
// 탈퇴 상태 처리
|
||||||
if (originalUserStatus === STATUS.REMOVED) {
|
if (originalUserStatus === STATUS.REMOVED) {
|
||||||
@@ -285,7 +281,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
const fullLoginId = id + "@" + domain;
|
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)) {
|
if (!emailPattern.test(fullLoginId)) {
|
||||||
alert(fullLoginId + " 은 올바른 이메일 형식이 아닙니다.");
|
alert(fullLoginId + " 은 올바른 이메일 형식이 아닙니다.");
|
||||||
return;
|
return;
|
||||||
@@ -317,7 +313,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function validateEmailId(input) {
|
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) {
|
function validateDomainInput(input) {
|
||||||
@@ -492,49 +488,29 @@
|
|||||||
formData.push({name: 'mobileNumber', value: mobileNumber});
|
formData.push({name: 'mobileNumber', value: mobileNumber});
|
||||||
}
|
}
|
||||||
|
|
||||||
// cmd 및 사유(auditReason) 첨부 후 전송
|
formData.push({
|
||||||
function submitUser(cmd, auditReason) {
|
name: 'cmd',
|
||||||
formData.push({ name: 'cmd', value: cmd });
|
value: isDetail ? "UPDATE" : "INSERT"
|
||||||
// 감사로그(AUDIT_LOG) 발화를 위한 systemCode. 가입자 리포지토리는 고정 EMS 스키마라 스키마 전환 영향 없음
|
});
|
||||||
formData.push({ name: 'serviceType', value: 'APIGW' });
|
|
||||||
if (auditReason) {
|
if (!confirm("<%= localeMessage.getString("common.checkSave")%>")) return;
|
||||||
formData.push({ name: 'auditReason', value: auditReason });
|
|
||||||
|
$.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 () {
|
$("#btn_previous").click(function () {
|
||||||
@@ -542,32 +518,27 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
$("#btn_delete").click(function () {
|
$("#btn_delete").click(function () {
|
||||||
showReasonPrompt("해당 가입자를 삭제 처리합니다. 사유를 입력해 주세요.", {
|
if (!confirm("<%= localeMessage.getString("common.confirmMsg")%>")) return;
|
||||||
title: "삭제 사유",
|
|
||||||
onConfirm: function(reason) {
|
$.ajax({
|
||||||
$.ajax({
|
type: "POST",
|
||||||
type: "POST",
|
url: url,
|
||||||
url: url,
|
data: {
|
||||||
data: {
|
cmd: "DELETE",
|
||||||
cmd: "DELETE",
|
id: $('input[name=id]').val()
|
||||||
id: $('input[name=id]').val(),
|
},
|
||||||
serviceType: "APIGW",
|
success: function () {
|
||||||
auditReason: reason
|
alert("<%= localeMessage.getString("common.deleteMsg") %>");
|
||||||
},
|
goNav(returnUrl);
|
||||||
success: function () {
|
},
|
||||||
alert("<%= localeMessage.getString("common.deleteMsg") %>");
|
error: function (e) {
|
||||||
goNav(returnUrl);
|
alert(e.responseText);
|
||||||
},
|
|
||||||
error: function (e) {
|
|
||||||
alert(e.responseText);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
$("#btn_password_reset").click(function () {
|
$("#btn_password_reset").click(function () {
|
||||||
if (!confirm("패스워드를 초기화 하시겠습니까?\n초기 패스워드는 '이메일ID${passwordInitSubfix}' 입니다.")) return;
|
if (!confirm("패스워드를 초기화 하시겠습니까?\n초기 패스워드는 '!' + 이메일ID 입니다.")) return;
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
type: "POST",
|
type: "POST",
|
||||||
|
|||||||
@@ -172,9 +172,6 @@ $(document).ready(function() {
|
|||||||
<form id="ajaxForm">
|
<form id="ajaxForm">
|
||||||
<input type="hidden" name="id">
|
<input type="hidden" name="id">
|
||||||
<table class="table_row" cellspacing="0">
|
<table class="table_row" cellspacing="0">
|
||||||
<colgroup>
|
|
||||||
<col width="200px">
|
|
||||||
</colgroup>
|
|
||||||
<tr>
|
<tr>
|
||||||
<th><%= localeMessage.getString("messageTemplate.messageCode") %> <font color="red"> *</font></th>
|
<th><%= localeMessage.getString("messageTemplate.messageCode") %> <font color="red"> *</font></th>
|
||||||
<td><input type="text" name="messageCode" data-required data-warning="메세지 코드는 필수입니다"/></td>
|
<td><input type="text" name="messageCode" data-required data-warning="메세지 코드는 필수입니다"/></td>
|
||||||
|
|||||||
@@ -1,242 +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: '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:40%; border:1px solid #ebebec;">
|
|
||||||
~
|
|
||||||
<input type="text" name="searchEndYYYYMMDD" value="" id="endDatepicker" size="10" readonly="readonly" style="width:40%; 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,137 +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 || '');
|
|
||||||
$('#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 colspan="3"><span id="signature"></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>
|
|
||||||
@@ -66,27 +66,22 @@
|
|||||||
},
|
},
|
||||||
tooltip: {
|
tooltip: {
|
||||||
trigger: 'item',
|
trigger: 'item',
|
||||||
formatter: function (params) {
|
formatter: '{b}: {c} ({d}%)'
|
||||||
return params.name + ': ' + params.value.toLocaleString() + ' (' + params.percent + '%)';
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
legend: {
|
legend: {
|
||||||
orient: 'vertical',
|
orient: 'horizontal',
|
||||||
right: 30,
|
bottom: 10,
|
||||||
top: 'middle',
|
|
||||||
data: ['성공', 'Timeout', '시스템오류']
|
data: ['성공', 'Timeout', '시스템오류']
|
||||||
},
|
},
|
||||||
series: [{
|
series: [{
|
||||||
name: '총건수',
|
name: '총건수',
|
||||||
type: 'pie',
|
type: 'pie',
|
||||||
radius: ['38%', '65%'],
|
radius: ['40%', '70%'],
|
||||||
center: ['44%', '55%'],
|
center: ['50%', '50%'],
|
||||||
avoidLabelOverlap: true,
|
avoidLabelOverlap: true,
|
||||||
label: {
|
label: {
|
||||||
show: true,
|
show: true,
|
||||||
formatter: function (params) {
|
formatter: '{b}: {c}'
|
||||||
return params.name + ': ' + params.value.toLocaleString();
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
emphasis: {
|
emphasis: {
|
||||||
label: {
|
label: {
|
||||||
@@ -103,6 +98,18 @@
|
|||||||
{ value: 0, name: 'Timeout', itemStyle: { color: '#FAC858' } },
|
{ value: 0, name: 'Timeout', itemStyle: { color: '#FAC858' } },
|
||||||
{ value: 0, name: '시스템오류', itemStyle: { color: '#EE6666' } }
|
{ value: 0, name: '시스템오류', itemStyle: { color: '#EE6666' } }
|
||||||
]
|
]
|
||||||
|
}],
|
||||||
|
graphic: [{
|
||||||
|
type: 'text',
|
||||||
|
left: 'center',
|
||||||
|
top: 'center',
|
||||||
|
style: {
|
||||||
|
text: '0',
|
||||||
|
textAlign: 'center',
|
||||||
|
fill: '#333',
|
||||||
|
fontSize: 24,
|
||||||
|
fontWeight: 'bold'
|
||||||
|
}
|
||||||
}]
|
}]
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -112,7 +119,7 @@
|
|||||||
|
|
||||||
// 호출량 차트
|
// 호출량 차트
|
||||||
var callOption = {
|
var callOption = {
|
||||||
title: { text: '호출량 추이', left: 'center', top: 10, textStyle: { fontSize: 14 } },
|
title: { text: '호출량 추이', left: 'center', textStyle: { fontSize: 14 } },
|
||||||
tooltip: {
|
tooltip: {
|
||||||
trigger: 'axis',
|
trigger: 'axis',
|
||||||
axisPointer: { type: 'cross', label: { backgroundColor: '#6a7985' } }
|
axisPointer: { type: 'cross', label: { backgroundColor: '#6a7985' } }
|
||||||
@@ -184,9 +191,13 @@
|
|||||||
{ value: totalTimeout, name: 'Timeout' },
|
{ value: totalTimeout, name: 'Timeout' },
|
||||||
{ value: totalSystemErr, name: '시스템오류' }
|
{ value: totalSystemErr, name: '시스템오류' }
|
||||||
]
|
]
|
||||||
|
}],
|
||||||
|
graphic: [{
|
||||||
|
style: {
|
||||||
|
text: grandTotal.toLocaleString()
|
||||||
|
}
|
||||||
}]
|
}]
|
||||||
});
|
});
|
||||||
$("#totalDonutCenterText").text(grandTotal.toLocaleString());
|
|
||||||
|
|
||||||
|
|
||||||
// 호출량 차트 업데이트
|
// 호출량 차트 업데이트
|
||||||
@@ -444,9 +455,6 @@
|
|||||||
for (var i = 0; i < colModel.length; i++) {
|
for (var i = 0; i < colModel.length; i++) {
|
||||||
$(this).setColProp(colModel[i].name, { sortable: false });
|
$(this).setColProp(colModel[i].name, { sortable: false });
|
||||||
}
|
}
|
||||||
// 조회(reloadGrid) 시 컬럼 폭이 고정폭(shrinkToFit:false)으로 남아
|
|
||||||
// 실제 컨테이너 폭과 어긋나 가로 스크롤이 생기는 것을 방지
|
|
||||||
$(this).jqGrid('setGridWidth', $('#content_middle').width(), true);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -493,9 +501,6 @@
|
|||||||
for (var i = 0; i < colModel.length; i++) {
|
for (var i = 0; i < colModel.length; i++) {
|
||||||
$(this).setColProp(colModel[i].name, { sortable: false });
|
$(this).setColProp(colModel[i].name, { sortable: false });
|
||||||
}
|
}
|
||||||
// 조회(reloadGrid) 시 컬럼 폭이 고정폭(shrinkToFit:false)으로 남아
|
|
||||||
// 실제 컨테이너 폭과 어긋나 가로 스크롤이 생기는 것을 방지
|
|
||||||
$(this).jqGrid('setGridWidth', $('#content_middle').width(), true);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -582,18 +587,10 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="title" id="title">API 일별 통계</div>
|
<div class="title" id="title">API 일별 통계</div>
|
||||||
<table class="search_condition" cellspacing="0" style="table-layout:fixed;">
|
<table class="search_condition" cellspacing="0">
|
||||||
<colgroup>
|
|
||||||
<col style="width:150px;">
|
|
||||||
<col>
|
|
||||||
<col style="width:150px;">
|
|
||||||
<col>
|
|
||||||
<col style="width:150px;">
|
|
||||||
<col>
|
|
||||||
</colgroup>
|
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<th>조회기간</th>
|
<th style="width:100px;">조회기간</th>
|
||||||
<td colspan="5">
|
<td colspan="5">
|
||||||
<input type="text" name="searchStartDateTime" value="${param.searchStartDateTime}" style="width:100px;">
|
<input type="text" name="searchStartDateTime" value="${param.searchStartDateTime}" style="width:100px;">
|
||||||
~
|
~
|
||||||
@@ -602,29 +599,29 @@
|
|||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>API명</th>
|
<th style="width:100px;">API명</th>
|
||||||
<td>
|
<td>
|
||||||
<input type="text" name="searchApiName" value="${param.searchApiName}">
|
<input type="text" name="searchApiName" value="${param.searchApiName}">
|
||||||
</td>
|
</td>
|
||||||
<th>업무구분</th>
|
<th style="width:100px;">업무구분</th>
|
||||||
<td>
|
<td>
|
||||||
<input type="text" name="searchBizDivCode" value="${param.searchBizDivCode}">
|
<input type="text" name="searchBizDivCode" value="${param.searchBizDivCode}">
|
||||||
</td>
|
</td>
|
||||||
<th>인스턴스</th>
|
<th style="width:100px;">인스턴스</th>
|
||||||
<td>
|
<td>
|
||||||
<input type="text" name="searchGwInstanceId" value="${param.searchGwInstanceId}">
|
<input type="text" name="searchGwInstanceId" value="${param.searchGwInstanceId}">
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>클라이언트ID</th>
|
<th style="width:100px;">클라이언트ID</th>
|
||||||
<td>
|
<td>
|
||||||
<input type="text" name="searchClientId" value="${param.searchClientId}">
|
<input type="text" name="searchClientId" value="${param.searchClientId}">
|
||||||
</td>
|
</td>
|
||||||
<th>Inbound Adapter</th>
|
<th style="width:100px;">Inbound Adapter</th>
|
||||||
<td>
|
<td>
|
||||||
<input type="text" name="searchInboundAdapter" value="${param.searchInboundAdapter}">
|
<input type="text" name="searchInboundAdapter" value="${param.searchInboundAdapter}">
|
||||||
</td>
|
</td>
|
||||||
<th>Outbound Adapter</th>
|
<th style="width:100px;">Outbound Adapter</th>
|
||||||
<td>
|
<td>
|
||||||
<input type="text" name="searchOutboundAdapter" value="${param.searchOutboundAdapter}">
|
<input type="text" name="searchOutboundAdapter" value="${param.searchOutboundAdapter}">
|
||||||
</td>
|
</td>
|
||||||
@@ -633,10 +630,7 @@
|
|||||||
</table>
|
</table>
|
||||||
<!-- 도넛 차트 (상단 50%씩) -->
|
<!-- 도넛 차트 (상단 50%씩) -->
|
||||||
<div class="chart-container">
|
<div class="chart-container">
|
||||||
<div class="chart" style="position:relative; padding:0;">
|
<div id="totalDonutChart" class="chart"></div>
|
||||||
<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="callChart" class="chart"></div>
|
<div id="callChart" class="chart"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -67,27 +67,22 @@
|
|||||||
},
|
},
|
||||||
tooltip: {
|
tooltip: {
|
||||||
trigger: 'item',
|
trigger: 'item',
|
||||||
formatter: function (params) {
|
formatter: '{b}: {c} ({d}%)'
|
||||||
return params.name + ': ' + params.value.toLocaleString() + ' (' + params.percent + '%)';
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
legend: {
|
legend: {
|
||||||
orient: 'vertical',
|
orient: 'horizontal',
|
||||||
right: 30,
|
bottom: 10,
|
||||||
top: 'middle',
|
|
||||||
data: ['성공', 'Timeout', '시스템오류']
|
data: ['성공', 'Timeout', '시스템오류']
|
||||||
},
|
},
|
||||||
series: [{
|
series: [{
|
||||||
name: '총건수',
|
name: '총건수',
|
||||||
type: 'pie',
|
type: 'pie',
|
||||||
radius: ['38%', '65%'],
|
radius: ['40%', '70%'],
|
||||||
center: ['44%', '55%'],
|
center: ['50%', '50%'],
|
||||||
avoidLabelOverlap: true,
|
avoidLabelOverlap: true,
|
||||||
label: {
|
label: {
|
||||||
show: true,
|
show: true,
|
||||||
formatter: function (params) {
|
formatter: '{b}: {c}'
|
||||||
return params.name + ': ' + params.value.toLocaleString();
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
emphasis: {
|
emphasis: {
|
||||||
label: {
|
label: {
|
||||||
@@ -104,6 +99,18 @@
|
|||||||
{ value: 0, name: 'Timeout', itemStyle: { color: '#FAC858' } },
|
{ value: 0, name: 'Timeout', itemStyle: { color: '#FAC858' } },
|
||||||
{ value: 0, name: '시스템오류', itemStyle: { color: '#EE6666' } }
|
{ 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 +120,7 @@
|
|||||||
|
|
||||||
// 호출량 차트
|
// 호출량 차트
|
||||||
var callOption = {
|
var callOption = {
|
||||||
title: { text: '호출량 추이', left: 'center', top: 10, textStyle: { fontSize: 14 } },
|
title: { text: '호출량 추이', left: 'center', textStyle: { fontSize: 14 } },
|
||||||
tooltip: {
|
tooltip: {
|
||||||
trigger: 'axis',
|
trigger: 'axis',
|
||||||
axisPointer: { type: 'cross', label: { backgroundColor: '#6a7985' } }
|
axisPointer: { type: 'cross', label: { backgroundColor: '#6a7985' } }
|
||||||
@@ -220,9 +227,13 @@
|
|||||||
{ value: totalTimeout, name: 'Timeout' },
|
{ value: totalTimeout, name: 'Timeout' },
|
||||||
{ value: totalSystemErr, name: '시스템오류' }
|
{ value: totalSystemErr, name: '시스템오류' }
|
||||||
]
|
]
|
||||||
|
}],
|
||||||
|
graphic: [{
|
||||||
|
style: {
|
||||||
|
text: grandTotal.toLocaleString()
|
||||||
|
}
|
||||||
}]
|
}]
|
||||||
});
|
});
|
||||||
$("#totalDonutCenterText").text(grandTotal.toLocaleString());
|
|
||||||
|
|
||||||
|
|
||||||
// 호출량 차트 업데이트
|
// 호출량 차트 업데이트
|
||||||
@@ -446,9 +457,6 @@
|
|||||||
for (var i = 0; i < colModel.length; i++) {
|
for (var i = 0; i < colModel.length; i++) {
|
||||||
$(this).setColProp(colModel[i].name, { sortable: false });
|
$(this).setColProp(colModel[i].name, { sortable: false });
|
||||||
}
|
}
|
||||||
// 조회(reloadGrid) 시 컬럼 폭이 고정폭(shrinkToFit:false)으로 남아
|
|
||||||
// 실제 컨테이너 폭과 어긋나 가로 스크롤이 생기는 것을 방지
|
|
||||||
$(this).jqGrid('setGridWidth', $('#content_middle').width(), true);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -495,9 +503,6 @@
|
|||||||
for (var i = 0; i < colModel.length; i++) {
|
for (var i = 0; i < colModel.length; i++) {
|
||||||
$(this).setColProp(colModel[i].name, { sortable: false });
|
$(this).setColProp(colModel[i].name, { sortable: false });
|
||||||
}
|
}
|
||||||
// 조회(reloadGrid) 시 컬럼 폭이 고정폭(shrinkToFit:false)으로 남아
|
|
||||||
// 실제 컨테이너 폭과 어긋나 가로 스크롤이 생기는 것을 방지
|
|
||||||
$(this).jqGrid('setGridWidth', $('#content_middle').width(), true);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -562,47 +567,41 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="title" id="title">API 시간별 통계</div>
|
<div class="title" id="title">API 시간별 통계</div>
|
||||||
<table class="search_condition" cellspacing="0" style="table-layout:fixed;">
|
<table class="search_condition" cellspacing="0">
|
||||||
<colgroup>
|
|
||||||
<col style="width:150px;">
|
|
||||||
<col>
|
|
||||||
<col style="width:150px;">
|
|
||||||
<col>
|
|
||||||
<col style="width:150px;">
|
|
||||||
<col>
|
|
||||||
</colgroup>
|
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<th>조회일자</th>
|
<th style="width:100px;">조회일자</th>
|
||||||
<td colspan="5">
|
<td colspan="3">
|
||||||
<input type="text" name="searchDate" value="${param.searchDate}" style="width:100px;">
|
<input type="text" name="searchDate" value="${param.searchDate}" style="width:100px;">
|
||||||
<span style="color:#888; font-size:12px; margin-left:10px;">(선택일 00시 ~ 23시 조회)</span>
|
<span style="color:#888; font-size:12px; margin-left:10px;">(선택일 00시 ~ 23시 조회)</span>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>API명</th>
|
<th style="width:100px;">API명</th>
|
||||||
<td>
|
<td>
|
||||||
<input type="text" name="searchApiName" value="${param.searchApiName}">
|
<input type="text" name="searchApiName" value="${param.searchApiName}">
|
||||||
</td>
|
</td>
|
||||||
<th>업무구분</th>
|
<th style="width:100px;">인스턴스</th>
|
||||||
<td>
|
|
||||||
<input type="text" name="searchBizDivCode" value="${param.searchBizDivCode}">
|
|
||||||
</td>
|
|
||||||
<th>인스턴스</th>
|
|
||||||
<td>
|
<td>
|
||||||
<input type="text" name="searchGwInstanceId" value="${param.searchGwInstanceId}">
|
<input type="text" name="searchGwInstanceId" value="${param.searchGwInstanceId}">
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<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>
|
<td>
|
||||||
<input type="text" name="searchClientId" value="${param.searchClientId}">
|
<input type="text" name="searchClientId" value="${param.searchClientId}">
|
||||||
</td>
|
</td>
|
||||||
<th>Inbound Adapter</th>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th style="width:100px;">Inbound Adapter</th>
|
||||||
<td>
|
<td>
|
||||||
<input type="text" name="searchInboundAdapter" value="${param.searchInboundAdapter}">
|
<input type="text" name="searchInboundAdapter" value="${param.searchInboundAdapter}">
|
||||||
</td>
|
</td>
|
||||||
<th>Outbound Adapter</th>
|
<th style="width:100px;">Outbound Adapter</th>
|
||||||
<td>
|
<td>
|
||||||
<input type="text" name="searchOutboundAdapter" value="${param.searchOutboundAdapter}">
|
<input type="text" name="searchOutboundAdapter" value="${param.searchOutboundAdapter}">
|
||||||
</td>
|
</td>
|
||||||
@@ -611,10 +610,7 @@
|
|||||||
</table>
|
</table>
|
||||||
<!-- 도넛 차트 (상단 50%씩) -->
|
<!-- 도넛 차트 (상단 50%씩) -->
|
||||||
<div class="chart-container">
|
<div class="chart-container">
|
||||||
<div class="chart" style="position:relative; padding:0;">
|
<div id="totalDonutChart" class="chart"></div>
|
||||||
<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="callChart" class="chart"></div>
|
<div id="callChart" class="chart"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -59,27 +59,22 @@
|
|||||||
},
|
},
|
||||||
tooltip: {
|
tooltip: {
|
||||||
trigger: 'item',
|
trigger: 'item',
|
||||||
formatter: function (params) {
|
formatter: '{b}: {c} ({d}%)'
|
||||||
return params.name + ': ' + params.value.toLocaleString() + ' (' + params.percent + '%)';
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
legend: {
|
legend: {
|
||||||
orient: 'vertical',
|
orient: 'horizontal',
|
||||||
right: 30,
|
bottom: 10,
|
||||||
top: 'middle',
|
|
||||||
data: ['성공', 'Timeout', '시스템오류']
|
data: ['성공', 'Timeout', '시스템오류']
|
||||||
},
|
},
|
||||||
series: [{
|
series: [{
|
||||||
name: '총건수',
|
name: '총건수',
|
||||||
type: 'pie',
|
type: 'pie',
|
||||||
radius: ['38%', '65%'],
|
radius: ['40%', '70%'],
|
||||||
center: ['44%', '55%'],
|
center: ['50%', '50%'],
|
||||||
avoidLabelOverlap: true,
|
avoidLabelOverlap: true,
|
||||||
label: {
|
label: {
|
||||||
show: true,
|
show: true,
|
||||||
formatter: function (params) {
|
formatter: '{b}: {c}'
|
||||||
return params.name + ': ' + params.value.toLocaleString();
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
emphasis: {
|
emphasis: {
|
||||||
label: {
|
label: {
|
||||||
@@ -96,6 +91,18 @@
|
|||||||
{ value: 0, name: 'Timeout', itemStyle: { color: '#FAC858' } },
|
{ value: 0, name: 'Timeout', itemStyle: { color: '#FAC858' } },
|
||||||
{ value: 0, name: '시스템오류', itemStyle: { color: '#EE6666' } }
|
{ value: 0, name: '시스템오류', itemStyle: { color: '#EE6666' } }
|
||||||
]
|
]
|
||||||
|
}],
|
||||||
|
graphic: [{
|
||||||
|
type: 'text',
|
||||||
|
left: 'center',
|
||||||
|
top: 'center',
|
||||||
|
style: {
|
||||||
|
text: '0',
|
||||||
|
textAlign: 'center',
|
||||||
|
fill: '#333',
|
||||||
|
fontSize: 24,
|
||||||
|
fontWeight: 'bold'
|
||||||
|
}
|
||||||
}]
|
}]
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -105,7 +112,7 @@
|
|||||||
|
|
||||||
// 호출량 차트
|
// 호출량 차트
|
||||||
var callOption = {
|
var callOption = {
|
||||||
title: { text: '호출량 추이', left: 'center', top: 10, textStyle: { fontSize: 14 } },
|
title: { text: '호출량 추이', left: 'center', textStyle: { fontSize: 14 } },
|
||||||
tooltip: {
|
tooltip: {
|
||||||
trigger: 'axis',
|
trigger: 'axis',
|
||||||
axisPointer: { type: 'cross', label: { backgroundColor: '#6a7985' } }
|
axisPointer: { type: 'cross', label: { backgroundColor: '#6a7985' } }
|
||||||
@@ -182,9 +189,13 @@
|
|||||||
{ value: totalTimeout, name: 'Timeout' },
|
{ value: totalTimeout, name: 'Timeout' },
|
||||||
{ value: totalSystemErr, name: '시스템오류' }
|
{ value: totalSystemErr, name: '시스템오류' }
|
||||||
]
|
]
|
||||||
|
}],
|
||||||
|
graphic: [{
|
||||||
|
style: {
|
||||||
|
text: grandTotal.toLocaleString()
|
||||||
|
}
|
||||||
}]
|
}]
|
||||||
});
|
});
|
||||||
$("#totalDonutCenterText").text(grandTotal.toLocaleString());
|
|
||||||
|
|
||||||
|
|
||||||
// 호출량 차트 업데이트
|
// 호출량 차트 업데이트
|
||||||
@@ -459,9 +470,6 @@
|
|||||||
for (var i = 0; i < colModel.length; i++) {
|
for (var i = 0; i < colModel.length; i++) {
|
||||||
$(this).setColProp(colModel[i].name, { sortable: false });
|
$(this).setColProp(colModel[i].name, { sortable: false });
|
||||||
}
|
}
|
||||||
// 조회(reloadGrid) 시 컬럼 폭이 고정폭(shrinkToFit:false)으로 남아
|
|
||||||
// 실제 컨테이너 폭과 어긋나 가로 스크롤이 생기는 것을 방지
|
|
||||||
$(this).jqGrid('setGridWidth', $('#content_middle').width(), true);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -508,9 +516,6 @@
|
|||||||
for (var i = 0; i < colModel.length; i++) {
|
for (var i = 0; i < colModel.length; i++) {
|
||||||
$(this).setColProp(colModel[i].name, { sortable: false });
|
$(this).setColProp(colModel[i].name, { sortable: false });
|
||||||
}
|
}
|
||||||
// 조회(reloadGrid) 시 컬럼 폭이 고정폭(shrinkToFit:false)으로 남아
|
|
||||||
// 실제 컨테이너 폭과 어긋나 가로 스크롤이 생기는 것을 방지
|
|
||||||
$(this).jqGrid('setGridWidth', $('#content_middle').width(), true);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -520,26 +525,35 @@
|
|||||||
$("input[name=searchStartDate], input[name=searchEndDate]").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=searchStartTime], input[name=searchEndTime]").inputmask("99:99", { 'autoUnmask': true });
|
||||||
|
|
||||||
// 기본값 설정 (현재 시간(분) 기준 1시간 전 ~ 현재 시간(분))
|
// 기본값 설정 (현재 시간 기준 1시간 전 정각 ~ 현재 정각)
|
||||||
var now = new Date();
|
var now = new Date();
|
||||||
var oneHourAgo = new Date(now.getTime() - 60 * 60 * 1000);
|
var endHour = now.getHours();
|
||||||
|
var startHour = endHour - 1;
|
||||||
|
var startDate, endDate;
|
||||||
|
|
||||||
function formatDate(d) {
|
if (startHour < 0) {
|
||||||
return d.getFullYear() + '-' +
|
startHour = 23;
|
||||||
String(d.getMonth() + 1).padStart(2, '0') + '-' +
|
var yesterday = new Date(now);
|
||||||
String(d.getDate()).padStart(2, '0');
|
yesterday.setDate(yesterday.getDate() - 1);
|
||||||
}
|
startDate = yesterday.getFullYear() + '-' +
|
||||||
function formatTime(d) {
|
String(yesterday.getMonth() + 1).padStart(2, '0') + '-' +
|
||||||
return String(d.getHours()).padStart(2, '0') + ':' + String(d.getMinutes()).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()) {
|
if (!$("input[name=searchStartDate]").val()) {
|
||||||
$("input[name=searchStartDate]").val(formatDate(oneHourAgo));
|
$("input[name=searchStartDate]").val(startDate);
|
||||||
$("input[name=searchStartTime]").val(formatTime(oneHourAgo));
|
$("input[name=searchStartTime]").val(String(startHour).padStart(2, '0') + ':00');
|
||||||
}
|
}
|
||||||
if (!$("input[name=searchEndDate]").val()) {
|
if (!$("input[name=searchEndDate]").val()) {
|
||||||
$("input[name=searchEndDate]").val(formatDate(now));
|
$("input[name=searchEndDate]").val(endDate);
|
||||||
$("input[name=searchEndTime]").val(formatTime(now));
|
$("input[name=searchEndTime]").val(String(endHour).padStart(2, '0') + ':00');
|
||||||
}
|
}
|
||||||
|
|
||||||
initCharts();
|
initCharts();
|
||||||
@@ -614,10 +628,7 @@
|
|||||||
</table>
|
</table>
|
||||||
<!-- 도넛 차트 (상단 50%씩) -->
|
<!-- 도넛 차트 (상단 50%씩) -->
|
||||||
<div class="chart-container">
|
<div class="chart-container">
|
||||||
<div class="chart" style="position:relative; padding:0;">
|
<div id="totalDonutChart" class="chart"></div>
|
||||||
<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="callChart" class="chart"></div>
|
<div id="callChart" class="chart"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -634,17 +645,7 @@
|
|||||||
<div id="pagerSummary"></div>
|
<div id="pagerSummary"></div>
|
||||||
</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>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@@ -66,27 +66,22 @@
|
|||||||
},
|
},
|
||||||
tooltip: {
|
tooltip: {
|
||||||
trigger: 'item',
|
trigger: 'item',
|
||||||
formatter: function (params) {
|
formatter: '{b}: {c} ({d}%)'
|
||||||
return params.name + ': ' + params.value.toLocaleString() + ' (' + params.percent + '%)';
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
legend: {
|
legend: {
|
||||||
orient: 'vertical',
|
orient: 'horizontal',
|
||||||
right: 30,
|
bottom: 10,
|
||||||
top: 'middle',
|
|
||||||
data: ['성공', 'Timeout', '시스템오류']
|
data: ['성공', 'Timeout', '시스템오류']
|
||||||
},
|
},
|
||||||
series: [{
|
series: [{
|
||||||
name: '총건수',
|
name: '총건수',
|
||||||
type: 'pie',
|
type: 'pie',
|
||||||
radius: ['38%', '65%'],
|
radius: ['40%', '70%'],
|
||||||
center: ['44%', '55%'],
|
center: ['50%', '50%'],
|
||||||
avoidLabelOverlap: true,
|
avoidLabelOverlap: true,
|
||||||
label: {
|
label: {
|
||||||
show: true,
|
show: true,
|
||||||
formatter: function (params) {
|
formatter: '{b}: {c}'
|
||||||
return params.name + ': ' + params.value.toLocaleString();
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
emphasis: {
|
emphasis: {
|
||||||
label: {
|
label: {
|
||||||
@@ -103,6 +98,18 @@
|
|||||||
{ value: 0, name: 'Timeout', itemStyle: { color: '#FAC858' } },
|
{ value: 0, name: 'Timeout', itemStyle: { color: '#FAC858' } },
|
||||||
{ value: 0, name: '시스템오류', itemStyle: { color: '#EE6666' } }
|
{ value: 0, name: '시스템오류', itemStyle: { color: '#EE6666' } }
|
||||||
]
|
]
|
||||||
|
}],
|
||||||
|
graphic: [{
|
||||||
|
type: 'text',
|
||||||
|
left: 'center',
|
||||||
|
top: 'center',
|
||||||
|
style: {
|
||||||
|
text: '0',
|
||||||
|
textAlign: 'center',
|
||||||
|
fill: '#333',
|
||||||
|
fontSize: 24,
|
||||||
|
fontWeight: 'bold'
|
||||||
|
}
|
||||||
}]
|
}]
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -112,7 +119,7 @@
|
|||||||
|
|
||||||
// 호출량 차트
|
// 호출량 차트
|
||||||
var callOption = {
|
var callOption = {
|
||||||
title: { text: '호출량 추이', left: 'center', top: 10, textStyle: { fontSize: 14 } },
|
title: { text: '호출량 추이', left: 'center', textStyle: { fontSize: 14 } },
|
||||||
tooltip: {
|
tooltip: {
|
||||||
trigger: 'axis',
|
trigger: 'axis',
|
||||||
axisPointer: { type: 'cross', label: { backgroundColor: '#6a7985' } }
|
axisPointer: { type: 'cross', label: { backgroundColor: '#6a7985' } }
|
||||||
@@ -182,9 +189,13 @@
|
|||||||
{ value: totalTimeout, name: 'Timeout' },
|
{ value: totalTimeout, name: 'Timeout' },
|
||||||
{ value: totalSystemErr, name: '시스템오류' }
|
{ value: totalSystemErr, name: '시스템오류' }
|
||||||
]
|
]
|
||||||
|
}],
|
||||||
|
graphic: [{
|
||||||
|
style: {
|
||||||
|
text: grandTotal.toLocaleString()
|
||||||
|
}
|
||||||
}]
|
}]
|
||||||
});
|
});
|
||||||
$("#totalDonutCenterText").text(grandTotal.toLocaleString());
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -449,9 +460,6 @@
|
|||||||
for (var i = 0; i < colModel.length; i++) {
|
for (var i = 0; i < colModel.length; i++) {
|
||||||
$(this).setColProp(colModel[i].name, { sortable: false });
|
$(this).setColProp(colModel[i].name, { sortable: false });
|
||||||
}
|
}
|
||||||
// 조회(reloadGrid) 시 컬럼 폭이 고정폭(shrinkToFit:false)으로 남아
|
|
||||||
// 실제 컨테이너 폭과 어긋나 가로 스크롤이 생기는 것을 방지
|
|
||||||
$(this).jqGrid('setGridWidth', $('#content_middle').width(), true);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -498,9 +506,6 @@
|
|||||||
for (var i = 0; i < colModel.length; i++) {
|
for (var i = 0; i < colModel.length; i++) {
|
||||||
$(this).setColProp(colModel[i].name, { sortable: false });
|
$(this).setColProp(colModel[i].name, { sortable: false });
|
||||||
}
|
}
|
||||||
// 조회(reloadGrid) 시 컬럼 폭이 고정폭(shrinkToFit:false)으로 남아
|
|
||||||
// 실제 컨테이너 폭과 어긋나 가로 스크롤이 생기는 것을 방지
|
|
||||||
$(this).jqGrid('setGridWidth', $('#content_middle').width(), true);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -573,19 +578,11 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="title" id="title">API 월별 통계</div>
|
<div class="title" id="title">API 월별 통계</div>
|
||||||
<table class="search_condition" cellspacing="0" style="table-layout:fixed;">
|
<table class="search_condition" cellspacing="0">
|
||||||
<colgroup>
|
|
||||||
<col style="width:150px;">
|
|
||||||
<col>
|
|
||||||
<col style="width:150px;">
|
|
||||||
<col>
|
|
||||||
<col style="width:150px;">
|
|
||||||
<col>
|
|
||||||
</colgroup>
|
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<th>조회기간</th>
|
<th style="width:100px;">조회기간</th>
|
||||||
<td colspan="5">
|
<td colspan="3">
|
||||||
<input type="text" name="searchStartDateTime" value="${param.searchStartDateTime}" style="width:100px;" placeholder="YYYY-MM">
|
<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">
|
<input type="text" name="searchEndDateTime" value="${param.searchEndDateTime}" style="width:100px;" placeholder="YYYY-MM">
|
||||||
@@ -593,29 +590,31 @@
|
|||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>API명</th>
|
<th style="width:100px;">API명</th>
|
||||||
<td>
|
<td>
|
||||||
<input type="text" name="searchApiName" value="${param.searchApiName}">
|
<input type="text" name="searchApiName" value="${param.searchApiName}">
|
||||||
</td>
|
</td>
|
||||||
<th>업무구분</th>
|
<th style="width:100px;">인스턴스</th>
|
||||||
<td>
|
|
||||||
<input type="text" name="searchBizDivCode" value="${param.searchBizDivCode}">
|
|
||||||
</td>
|
|
||||||
<th>인스턴스</th>
|
|
||||||
<td>
|
<td>
|
||||||
<input type="text" name="searchGwInstanceId" value="${param.searchGwInstanceId}">
|
<input type="text" name="searchGwInstanceId" value="${param.searchGwInstanceId}">
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<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>
|
<td>
|
||||||
<input type="text" name="searchClientId" value="${param.searchClientId}">
|
<input type="text" name="searchClientId" value="${param.searchClientId}">
|
||||||
</td>
|
</td>
|
||||||
<th>Inbound Adapter</th>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th style="width:100px;">Inbound Adapter</th>
|
||||||
<td>
|
<td>
|
||||||
<input type="text" name="searchInboundAdapter" value="${param.searchInboundAdapter}">
|
<input type="text" name="searchInboundAdapter" value="${param.searchInboundAdapter}">
|
||||||
</td>
|
</td>
|
||||||
<th>Outbound Adapter</th>
|
<th style="width:100px;">Outbound Adapter</th>
|
||||||
<td>
|
<td>
|
||||||
<input type="text" name="searchOutboundAdapter" value="${param.searchOutboundAdapter}">
|
<input type="text" name="searchOutboundAdapter" value="${param.searchOutboundAdapter}">
|
||||||
</td>
|
</td>
|
||||||
@@ -624,10 +623,7 @@
|
|||||||
</table>
|
</table>
|
||||||
<!-- 도넛 차트 (상단 50%씩) -->
|
<!-- 도넛 차트 (상단 50%씩) -->
|
||||||
<div class="chart-container">
|
<div class="chart-container">
|
||||||
<div class="chart" style="position:relative; padding:0;">
|
<div id="totalDonutChart" class="chart"></div>
|
||||||
<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="callChart" class="chart"></div>
|
<div id="callChart" class="chart"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -66,27 +66,22 @@
|
|||||||
},
|
},
|
||||||
tooltip: {
|
tooltip: {
|
||||||
trigger: 'item',
|
trigger: 'item',
|
||||||
formatter: function (params) {
|
formatter: '{b}: {c} ({d}%)'
|
||||||
return params.name + ': ' + params.value.toLocaleString() + ' (' + params.percent + '%)';
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
legend: {
|
legend: {
|
||||||
orient: 'vertical',
|
orient: 'horizontal',
|
||||||
right: 30,
|
bottom: 10,
|
||||||
top: 'middle',
|
|
||||||
data: ['성공', 'Timeout', '시스템오류']
|
data: ['성공', 'Timeout', '시스템오류']
|
||||||
},
|
},
|
||||||
series: [{
|
series: [{
|
||||||
name: '총건수',
|
name: '총건수',
|
||||||
type: 'pie',
|
type: 'pie',
|
||||||
radius: ['38%', '65%'],
|
radius: ['40%', '70%'],
|
||||||
center: ['44%', '55%'],
|
center: ['50%', '50%'],
|
||||||
avoidLabelOverlap: true,
|
avoidLabelOverlap: true,
|
||||||
label: {
|
label: {
|
||||||
show: true,
|
show: true,
|
||||||
formatter: function (params) {
|
formatter: '{b}: {c}'
|
||||||
return params.name + ': ' + params.value.toLocaleString();
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
emphasis: {
|
emphasis: {
|
||||||
label: {
|
label: {
|
||||||
@@ -103,6 +98,18 @@
|
|||||||
{ value: 0, name: 'Timeout', itemStyle: { color: '#FAC858' } },
|
{ value: 0, name: 'Timeout', itemStyle: { color: '#FAC858' } },
|
||||||
{ value: 0, name: '시스템오류', itemStyle: { color: '#EE6666' } }
|
{ value: 0, name: '시스템오류', itemStyle: { color: '#EE6666' } }
|
||||||
]
|
]
|
||||||
|
}],
|
||||||
|
graphic: [{
|
||||||
|
type: 'text',
|
||||||
|
left: 'center',
|
||||||
|
top: 'center',
|
||||||
|
style: {
|
||||||
|
text: '0',
|
||||||
|
textAlign: 'center',
|
||||||
|
fill: '#333',
|
||||||
|
fontSize: 24,
|
||||||
|
fontWeight: 'bold'
|
||||||
|
}
|
||||||
}]
|
}]
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -112,7 +119,7 @@
|
|||||||
|
|
||||||
// 호출량 차트
|
// 호출량 차트
|
||||||
var callOption = {
|
var callOption = {
|
||||||
title: { text: '호출량 추이', left: 'center', top: 10, textStyle: { fontSize: 14 } },
|
title: { text: '호출량 추이', left: 'center', textStyle: { fontSize: 14 } },
|
||||||
tooltip: {
|
tooltip: {
|
||||||
trigger: 'axis',
|
trigger: 'axis',
|
||||||
axisPointer: { type: 'cross', label: { backgroundColor: '#6a7985' } }
|
axisPointer: { type: 'cross', label: { backgroundColor: '#6a7985' } }
|
||||||
@@ -184,9 +191,13 @@
|
|||||||
{ value: totalTimeout, name: 'Timeout' },
|
{ value: totalTimeout, name: 'Timeout' },
|
||||||
{ value: totalSystemErr, name: '시스템오류' }
|
{ value: totalSystemErr, name: '시스템오류' }
|
||||||
]
|
]
|
||||||
|
}],
|
||||||
|
graphic: [{
|
||||||
|
style: {
|
||||||
|
text: grandTotal.toLocaleString()
|
||||||
|
}
|
||||||
}]
|
}]
|
||||||
});
|
});
|
||||||
$("#totalDonutCenterText").text(grandTotal.toLocaleString());
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -425,9 +436,6 @@
|
|||||||
for (var i = 0; i < colModel.length; i++) {
|
for (var i = 0; i < colModel.length; i++) {
|
||||||
$(this).setColProp(colModel[i].name, { sortable: false });
|
$(this).setColProp(colModel[i].name, { sortable: false });
|
||||||
}
|
}
|
||||||
// 조회(reloadGrid) 시 컬럼 폭이 고정폭(shrinkToFit:false)으로 남아
|
|
||||||
// 실제 컨테이너 폭과 어긋나 가로 스크롤이 생기는 것을 방지
|
|
||||||
$(this).jqGrid('setGridWidth', $('#content_middle').width(), true);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -474,9 +482,6 @@
|
|||||||
for (var i = 0; i < colModel.length; i++) {
|
for (var i = 0; i < colModel.length; i++) {
|
||||||
$(this).setColProp(colModel[i].name, { sortable: false });
|
$(this).setColProp(colModel[i].name, { sortable: false });
|
||||||
}
|
}
|
||||||
// 조회(reloadGrid) 시 컬럼 폭이 고정폭(shrinkToFit:false)으로 남아
|
|
||||||
// 실제 컨테이너 폭과 어긋나 가로 스크롤이 생기는 것을 방지
|
|
||||||
$(this).jqGrid('setGridWidth', $('#content_middle').width(), true);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -544,48 +549,42 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="title" id="title">API 연간 통계</div>
|
<div class="title" id="title">API 연간 통계</div>
|
||||||
<table class="search_condition" cellspacing="0" style="table-layout:fixed;">
|
<table class="search_condition" cellspacing="0">
|
||||||
<colgroup>
|
|
||||||
<col style="width:150px;">
|
|
||||||
<col>
|
|
||||||
<col style="width:150px;">
|
|
||||||
<col>
|
|
||||||
<col style="width:150px;">
|
|
||||||
<col>
|
|
||||||
</colgroup>
|
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<th>조회기간</th>
|
<th style="width:100px;">조회기간</th>
|
||||||
<td colspan="5">
|
<td colspan="3">
|
||||||
<input type="text" name="searchStartDateTime" value="${param.searchStartDateTime}" style="width:100px;" placeholder="YYYY">
|
<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">
|
<input type="text" name="searchEndDateTime" value="${param.searchEndDateTime}" style="width:100px;" placeholder="YYYY">
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>API명</th>
|
<th style="width:100px;">API명</th>
|
||||||
<td>
|
<td>
|
||||||
<input type="text" name="searchApiName" value="${param.searchApiName}">
|
<input type="text" name="searchApiName" value="${param.searchApiName}">
|
||||||
</td>
|
</td>
|
||||||
<th>업무구분</th>
|
<th style="width:100px;">인스턴스</th>
|
||||||
<td>
|
|
||||||
<input type="text" name="searchBizDivCode" value="${param.searchBizDivCode}">
|
|
||||||
</td>
|
|
||||||
<th>인스턴스</th>
|
|
||||||
<td>
|
<td>
|
||||||
<input type="text" name="searchGwInstanceId" value="${param.searchGwInstanceId}">
|
<input type="text" name="searchGwInstanceId" value="${param.searchGwInstanceId}">
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<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>
|
<td>
|
||||||
<input type="text" name="searchClientId" value="${param.searchClientId}">
|
<input type="text" name="searchClientId" value="${param.searchClientId}">
|
||||||
</td>
|
</td>
|
||||||
<th>Inbound Adapter</th>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th style="width:100px;">Inbound Adapter</th>
|
||||||
<td>
|
<td>
|
||||||
<input type="text" name="searchInboundAdapter" value="${param.searchInboundAdapter}">
|
<input type="text" name="searchInboundAdapter" value="${param.searchInboundAdapter}">
|
||||||
</td>
|
</td>
|
||||||
<th>Outbound Adapter</th>
|
<th style="width:100px;">Outbound Adapter</th>
|
||||||
<td>
|
<td>
|
||||||
<input type="text" name="searchOutboundAdapter" value="${param.searchOutboundAdapter}">
|
<input type="text" name="searchOutboundAdapter" value="${param.searchOutboundAdapter}">
|
||||||
</td>
|
</td>
|
||||||
@@ -594,10 +593,7 @@
|
|||||||
</table>
|
</table>
|
||||||
<!-- 도넛 차트 (상단 50%씩) -->
|
<!-- 도넛 차트 (상단 50%씩) -->
|
||||||
<div class="chart-container">
|
<div class="chart-container">
|
||||||
<div class="chart" style="position:relative; padding:0;">
|
<div id="totalDonutChart" class="chart"></div>
|
||||||
<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="callChart" class="chart"></div>
|
<div id="callChart" class="chart"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -242,10 +242,6 @@
|
|||||||
search();
|
search();
|
||||||
});
|
});
|
||||||
|
|
||||||
$("input[name=searchType]").click(function() {
|
|
||||||
search();
|
|
||||||
});
|
|
||||||
|
|
||||||
$("#btn_excel").click(function() {
|
$("#btn_excel").click(function() {
|
||||||
exportToExcel();
|
exportToExcel();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -76,15 +76,6 @@
|
|||||||
return cellvalue.substring(1, 4);
|
return cellvalue.substring(1, 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
function apiStatusFormatter(cellvalue, options, rowObject) {
|
|
||||||
switch (cellvalue) {
|
|
||||||
case 'E': return '장애';
|
|
||||||
case 'D': return '지연';
|
|
||||||
case 'C': return '점검';
|
|
||||||
default: return '정상';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function list() {
|
function list() {
|
||||||
detail()
|
detail()
|
||||||
var gridPostData = getSearchForJqgrid("cmd", "LIST"); //jqgrid에서는 object 로
|
var gridPostData = getSearchForJqgrid("cmd", "LIST"); //jqgrid에서는 object 로
|
||||||
@@ -99,7 +90,6 @@
|
|||||||
'API FULL PATH',
|
'API FULL PATH',
|
||||||
'요청',
|
'요청',
|
||||||
'응답',
|
'응답',
|
||||||
'상태',
|
|
||||||
'작성자',
|
'작성자',
|
||||||
'가상응답여부',
|
'가상응답여부',
|
||||||
'변경일시(*)',
|
'변경일시(*)',
|
||||||
@@ -109,12 +99,11 @@
|
|||||||
{name: 'eaiSvcName', align: 'left', width: '100', sortable: true},
|
{name: 'eaiSvcName', align: 'left', width: '100', sortable: true},
|
||||||
{name: 'eaiSvcDesc', align: 'left', sortable: false},
|
{name: 'eaiSvcDesc', align: 'left', sortable: false},
|
||||||
{name: 'apiFullPath', align: 'left', sortable: false},
|
{name: 'apiFullPath', align: 'left', sortable: false},
|
||||||
{name: 'fromAdapter', align: 'center', width: '30', formatter: adapterNameShortFormatter, sortable: false},
|
{name: 'fromAdapter', align: 'center', width: '40', formatter: adapterNameShortFormatter, sortable: false},
|
||||||
{name: 'toAdapter', align: 'center', width: '30', formatter: adapterNameShortFormatter, sortable: false},
|
{name: 'toAdapter', align: 'center', width: '40', formatter: adapterNameShortFormatter, sortable: false},
|
||||||
{name: 'statusCode', align: 'center', width: '30', formatter: apiStatusFormatter, sortable: false},
|
{name: 'author', align: 'center', width: '60', sortable: false},
|
||||||
{name: 'author', align: 'center', width: '40', sortable: false},
|
|
||||||
{name : 'simYn', align : 'center' , width:'40', hidden: true },
|
{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},
|
{name: 'syncAsyncType', align: 'center', width: '40', hidden: true},
|
||||||
],
|
],
|
||||||
jsonReader: {
|
jsonReader: {
|
||||||
|
|||||||
@@ -78,22 +78,6 @@
|
|||||||
vertical-align: middle;
|
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>
|
</style>
|
||||||
<script language="javascript" >
|
<script language="javascript" >
|
||||||
var isDetail = false;
|
var isDetail = false;
|
||||||
@@ -101,7 +85,6 @@
|
|||||||
const url_excel ='<c:url value="/onl/transaction/apim/apiInterfaceMan.excel" />';
|
const url_excel ='<c:url value="/onl/transaction/apim/apiInterfaceMan.excel" />';
|
||||||
var url_spec = '<c:url value="/onl/transaction/apim/apiSpecMan.view"/>';
|
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 = '<c:url value="/onl/transaction/apim/djbApiSpecMan.view"/>';
|
||||||
var url_openapi_spec_json = '<c:url value="/onl/transaction/apim/djbApiSpecMan.json"/>';
|
|
||||||
var returnUrl ;
|
var returnUrl ;
|
||||||
let headerValues = [];
|
let headerValues = [];
|
||||||
let headerNames = [];
|
let headerNames = [];
|
||||||
@@ -504,7 +487,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(data.errResponseTransform != null && data.errResponseTransform != '' && data.errTransformYn == 'Y') {
|
if(data.errResponseTransform != null && data.errResponseTransform != '' && data.errTransformYn == 'Y') {
|
||||||
$('#toggleTransformYn').bootstrapToggle('on');
|
|
||||||
$('#toggleErrTransformYn').bootstrapToggle('on');
|
$('#toggleErrTransformYn').bootstrapToggle('on');
|
||||||
$('#rowErrResponseTransform').show();
|
$('#rowErrResponseTransform').show();
|
||||||
} else {
|
} else {
|
||||||
@@ -554,8 +536,6 @@
|
|||||||
|
|
||||||
$('#inboundHttpMethod').trigger('change');
|
$('#inboundHttpMethod').trigger('change');
|
||||||
$('#outboundHttpMethod').trigger('change');
|
$('#outboundHttpMethod').trigger('change');
|
||||||
|
|
||||||
applyOpenApiSpecStatus(); // OpenAPI Spec 버튼 색상(PTL_API_SPEC_INFO)
|
|
||||||
},
|
},
|
||||||
error:function(e){
|
error:function(e){
|
||||||
alert(e.responseText);
|
alert(e.responseText);
|
||||||
@@ -1031,46 +1011,10 @@
|
|||||||
window.open(urlAddServiceType(baseUrl + '&menuId=' + encodeURIComponent(getMenuId())), '_blank');
|
window.open(urlAddServiceType(baseUrl + '&menuId=' + encodeURIComponent(getMenuId())), '_blank');
|
||||||
} else {
|
} else {
|
||||||
// 기본: 모달 + iframe. showModal 이 serviceType/menuId/pop 을 자동 부착.
|
// 기본: 모달 + iframe. showModal 이 serviceType/menuId/pop 을 자동 부착.
|
||||||
// 콘텐츠 고정폭 1600 + 다이얼로그 chrome(패딩/보더 ~40px) 때문에 iframe 내부폭이 1600 미만이면 가로스크롤 발생.
|
showModal(baseUrl, { title: 'OpenAPI Spec' }, 1600, 880);
|
||||||
// 뷰포트 내에서 최대한 크게 열어(캡 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(); });
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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() {
|
$(document).ready(function() {
|
||||||
makeValidate();
|
makeValidate();
|
||||||
// var bootstrapButton = $.fn.button.noConflict()
|
// var bootstrapButton = $.fn.button.noConflict()
|
||||||
@@ -1094,23 +1038,6 @@
|
|||||||
showOpenApiSpecPopup(e);
|
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_modify").click(modifyApi);
|
||||||
$("#btn_delete").click(function(){
|
$("#btn_delete").click(function(){
|
||||||
if(!confirm("삭제하시겠습니까?")){
|
if(!confirm("삭제하시겠습니까?")){
|
||||||
@@ -1609,9 +1536,9 @@
|
|||||||
<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_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>
|
<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 스펙 (모달) — 기능 비교용 복원 -->
|
<!-- v1: 기존 API 스펙 (모달) — 기능 비교용 복원 -->
|
||||||
<button type="button" class="cssbtn" id="btn_api_spec" level="R" status="DETAIL"><i class="material-icons">description</i> API 스펙(삭제예정)</button>
|
<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 (새 탭) -->
|
<!-- 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>
|
<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>
|
||||||
<div class="title" id="title" style="font-size:1.4em">${rmsMenuName}</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",
|
saveUrl: "<c:url value='/onl/transaction/apim/djbApiSpecMan.json'/>?cmd=SAVE&serviceType=APIGW",
|
||||||
swaggerBase: "<c:url value='/plugins/swaggerUI/'/>",
|
swaggerBase: "<c:url value='/plugins/swaggerUI/'/>",
|
||||||
gwAddress: "${gwAddress}",
|
gwAddress: "${gwAddress}",
|
||||||
useProxy: ${useProxy},
|
|
||||||
tokenUseProxy: ${tokenUseProxy},
|
|
||||||
exportEnabled: ${exportEnabled},
|
|
||||||
orgPopupUrl: "<c:url value='/onl/transaction/apim/apiSpecMan.view'/>",
|
orgPopupUrl: "<c:url value='/onl/transaction/apim/apiSpecMan.view'/>",
|
||||||
groupPopupUrl: "<c:url value='/onl/transaction/apim/djbApiSpecMan.view'/>?cmd=GROUP_POPUP",
|
groupPopupUrl: "<c:url value='/onl/transaction/apim/djbApiSpecMan.view'/>?cmd=GROUP_POPUP",
|
||||||
jsonUrl: "<c:url value='/onl/transaction/apim/djbApiSpecMan.json'/>"
|
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-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-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>
|
<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">
|
<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>
|
<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">
|
<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>
|
<button data-export="json" class="block w-full text-left px-3 py-2 text-sm hover:bg-slate-50">JSON 다운로드</button>
|
||||||
</div>
|
</div>
|
||||||
</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>
|
<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>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|||||||
+69
-69
@@ -1,7 +1,7 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id 'java-library'
|
id 'java-library'
|
||||||
id 'eclipse'
|
id 'eclipse'
|
||||||
id 'eclipse-wtp'
|
id 'eclipse-wtp'
|
||||||
id 'idea'
|
id 'idea'
|
||||||
id 'war'
|
id 'war'
|
||||||
}
|
}
|
||||||
@@ -31,9 +31,9 @@ allprojects {
|
|||||||
project.webAppDirName = 'WebContent'
|
project.webAppDirName = 'WebContent'
|
||||||
|
|
||||||
java {
|
java {
|
||||||
toolchain {
|
toolchain {
|
||||||
languageVersion = JavaLanguageVersion.of(8)
|
languageVersion = JavaLanguageVersion.of(8)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sourceSets {
|
sourceSets {
|
||||||
@@ -45,52 +45,56 @@ sourceSets {
|
|||||||
}
|
}
|
||||||
|
|
||||||
compileJava {
|
compileJava {
|
||||||
options.encoding = 'UTF-8'
|
options.encoding = 'UTF-8'
|
||||||
options.compilerArgs = ['-parameters']
|
options.compilerArgs = ['-parameters']
|
||||||
options.generatedSourceOutputDirectory = project.file(generatedJavaDir)
|
options.generatedSourceOutputDirectory = project.file(generatedJavaDir)
|
||||||
}
|
}
|
||||||
|
|
||||||
war {
|
war {
|
||||||
def profile = project.findProperty("profile") ?: "tomcat"
|
def profile = project.findProperty("profile") ?: "tomcat"
|
||||||
|
|
||||||
if (profile == "weblogic") {
|
if (profile == "weblogic") {
|
||||||
webXml = file("WebContent/WEB-INF/weblogic-web.xml")
|
webXml = file("WebContent/WEB-INF/weblogic-web.xml")ㅇ
|
||||||
exclude 'WEB-INF/web.xml'
|
exclude 'WEB-INF/web.xml'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// rootSpec.exclude '**/persistence.xml'
|
// rootSpec.exclude '**/persistence.xml'
|
||||||
exclude '**/context.xml'
|
exclude '**/context.xml'
|
||||||
exclude '**/context-*.xml'
|
exclude '**/context-*.xml'
|
||||||
archiveFileName = "eapim-admin.war"
|
archiveFileName = "eapim-admin.war"
|
||||||
duplicatesStrategy = DuplicatesStrategy.WARN
|
duplicatesStrategy = DuplicatesStrategy.WARN
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
tasks.withType(JavaCompile) {
|
tasks.withType(JavaCompile) {
|
||||||
options.encoding = 'UTF-8'
|
options.encoding = 'UTF-8'
|
||||||
options.compilerArgs += ['-parameters', "-Xlint:unchecked", "-Xlint:deprecation"]
|
options.compilerArgs += ['-parameters', "-Xlint:unchecked", "-Xlint:deprecation"]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
dependencies {
|
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 "org.projectlombok:lombok:1.18.28",
|
||||||
annotationProcessor "com.querydsl:querydsl-apt:${queryDslVersion}:jpa", "jakarta.persistence:jakarta.persistence-api:2.2.3", "jakarta.annotation:jakarta.annotation-api:1.3.5"
|
"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-core')
|
||||||
implementation project(':elink-online-transformer')
|
implementation project(':elink-online-transformer')
|
||||||
implementation project(':elink-online-common')
|
implementation project(':elink-online-common')
|
||||||
implementation project(':elink-online-emsclient')
|
implementation project(':elink-online-emsclient')
|
||||||
implementation project(':elink-portal-common')
|
implementation project(':elink-portal-common')
|
||||||
//implementation project(':kjb-safedb')
|
//implementation project(':kjb-safedb')
|
||||||
//implementation project(":eapim-admin-djb")
|
implementation project(":eapim-admin-djb")
|
||||||
|
|
||||||
/* Custom Libs (damo-manager.jar 는 Tomcat lib 가 런타임 제공 → compileOnly, WAR 미포함) */
|
/* Custom Libs (damo-manager.jar 는 Tomcat lib 가 런타임 제공 → compileOnly, WAR 미포함) */
|
||||||
implementation fileTree(dir: 'libs', include: ['*.jar'], exclude: ['damo-manager.jar'])
|
implementation fileTree(dir: 'libs', include: ['*.jar'], exclude: ['damo-manager.jar'])
|
||||||
compileOnly files('libs/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 "com.rabbitmq:amqp-client:3.6.6"
|
||||||
|
|
||||||
implementation "commons-primitives:commons-primitives:1.0"
|
implementation "commons-primitives:commons-primitives:1.0"
|
||||||
@@ -115,7 +119,7 @@ dependencies {
|
|||||||
|
|
||||||
implementation group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.13.1'
|
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 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 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.13.1'
|
||||||
|
|
||||||
implementation "javax.jms:javax.jms-api:2.0"
|
implementation "javax.jms:javax.jms-api:2.0"
|
||||||
implementation "org.snmp4j:snmp4j:1.10.1"
|
implementation "org.snmp4j:snmp4j:1.10.1"
|
||||||
@@ -183,31 +187,31 @@ dependencies {
|
|||||||
implementation 'ch.qos.logback:logback-access:1.2.10'
|
implementation 'ch.qos.logback:logback-access:1.2.10'
|
||||||
implementation 'ch.qos.logback:logback-core:1.2.10'
|
implementation 'ch.qos.logback:logback-core:1.2.10'
|
||||||
|
|
||||||
implementation files("libs/eai-bap-client.jar")
|
implementation files("libs/eai-bap-client.jar")
|
||||||
|
|
||||||
compileOnly 'javax.xml.bind:jaxb-api:2.3.1'
|
compileOnly 'javax.xml.bind:jaxb-api:2.3.1'
|
||||||
|
|
||||||
//implementation 'org.postgresql:postgresql:42.2.23'
|
//implementation 'org.postgresql:postgresql:42.2.23'
|
||||||
|
|
||||||
implementation 'backport-util-concurrent:backport-util-concurrent:3.0'
|
implementation 'backport-util-concurrent:backport-util-concurrent:3.0'
|
||||||
|
|
||||||
implementation ('software.amazon.awssdk:s3:2.20.142') {
|
implementation ('software.amazon.awssdk:s3:2.20.142') {
|
||||||
exclude group: 'org.slf4j', module: 'slf4j-api'
|
exclude group: 'org.slf4j', module: 'slf4j-api'
|
||||||
exclude group: 'org.slf4j', module: 'logback-classic'
|
exclude group: 'org.slf4j', module: 'logback-classic'
|
||||||
}
|
}
|
||||||
implementation 'software.amazon.awssdk:sso:2.20.142'
|
implementation 'software.amazon.awssdk:sso:2.20.142'
|
||||||
implementation 'software.amazon.awssdk:sts:2.20.142'
|
implementation 'software.amazon.awssdk:sts:2.20.142'
|
||||||
|
|
||||||
implementation group: 'commons-net', name: 'commons-net', version: '3.5'
|
implementation group: 'commons-net', name: 'commons-net', version: '3.5'
|
||||||
|
|
||||||
// JDK 8 의 rt.jar 에는 org.w3c.dom.ElementTraversal 이 없어 xercesImpl 가 NCDFE 를 일으킴.
|
// JDK 8 의 rt.jar 에는 org.w3c.dom.ElementTraversal 이 없어 xercesImpl 가 NCDFE 를 일으킴.
|
||||||
// xml-apis 1.4.01 에 그 클래스가 포함됨. 직접 의존성으로 묶어 WAR 에 패키징되도록 함.
|
// xml-apis 1.4.01 에 그 클래스가 포함됨. 직접 의존성으로 묶어 WAR 에 패키징되도록 함.
|
||||||
implementation 'xml-apis:xml-apis:1.4.01'
|
implementation 'xml-apis:xml-apis:1.4.01'
|
||||||
|
|
||||||
testRuntimeOnly 'com.h2database:h2:2.1.214'
|
testRuntimeOnly 'com.h2database:h2:2.1.214'
|
||||||
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1'
|
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1'
|
||||||
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1'
|
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1'
|
||||||
testImplementation 'org.springframework.boot:spring-boot-starter-test:2.6.15'
|
testImplementation 'org.springframework.boot:spring-boot-starter-test:2.6.15'
|
||||||
}
|
}
|
||||||
|
|
||||||
test {
|
test {
|
||||||
@@ -215,9 +219,9 @@ test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
configurations.all {
|
configurations.all {
|
||||||
exclude group: 'log4j', module: 'log4j'
|
exclude group: 'log4j', module: 'log4j'
|
||||||
exclude group: 'org.apache.activemq'
|
exclude group: 'org.apache.activemq'
|
||||||
exclude group: 'org.codehaus.jackson'
|
exclude group: 'org.codehaus.jackson'
|
||||||
resolutionStrategy {
|
resolutionStrategy {
|
||||||
// 10 minute cache of dynamic version navigation
|
// 10 minute cache of dynamic version navigation
|
||||||
cacheDynamicVersionsFor 10, 'minutes'
|
cacheDynamicVersionsFor 10, 'minutes'
|
||||||
@@ -241,29 +245,25 @@ task settingEclipseEncoding {
|
|||||||
}
|
}
|
||||||
|
|
||||||
task initDirs() {
|
task initDirs() {
|
||||||
file(generatedJavaDir).mkdirs()
|
file(generatedJavaDir).mkdirs()
|
||||||
}
|
}
|
||||||
|
|
||||||
eclipse {
|
eclipse {
|
||||||
wtp {
|
wtp {
|
||||||
component {
|
component {
|
||||||
contextPath = 'monitoring'
|
contextPath = 'monitoring'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
synchronizationTasks settingEclipseEncoding, initDirs
|
synchronizationTasks settingEclipseEncoding, initDirs
|
||||||
jdt {
|
jdt {
|
||||||
// apt {
|
|
||||||
// // generated 된 패스 경로를 .setting/org.eclipse.jdt.apt.core.prefs 의 org.eclipse.jdt.apt.genSrcDir에 적용한다.
|
}
|
||||||
// // project > properties > Java Compiler > Annoation Processing 화면에서 확인 가능하다.
|
project {
|
||||||
// genSrcDir = file(generatedJavaDir)
|
if (!natures.contains('org.eclipse.buildship.core.gradleprojectnature')) {
|
||||||
// }
|
natures.add('org.eclipse.buildship.core.gradleprojectnature')
|
||||||
}
|
buildCommand 'org.eclipse.buildship.core.gradleprojectbuilder'
|
||||||
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) {
|
tasks.withType(JavaCompile) {
|
||||||
|
|||||||
+2
-2
@@ -7,7 +7,7 @@ include 'elink-online-common'
|
|||||||
include 'elink-online-emsclient'
|
include 'elink-online-emsclient'
|
||||||
include 'elink-portal-common'
|
include 'elink-portal-common'
|
||||||
//include 'kjb-safedb'
|
//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-jpa').projectDir = new File(settingsDir, "../eapim-online/elink-online-core-jpa")
|
||||||
project (':elink-online-core').projectDir = new File(settingsDir, "../eapim-online/elink-online-core")
|
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-online-emsclient').projectDir = new File(settingsDir, "../eapim-online/elink-online-emsclient")
|
||||||
project (':elink-portal-common').projectDir = new File(settingsDir, "../elink-portal-common")
|
project (':elink-portal-common').projectDir = new File(settingsDir, "../elink-portal-common")
|
||||||
//project (':kjb-safedb').projectDir = new File(settingsDir, "../kjb-safedb")
|
//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)) {
|
if (isAuditableUsecase.isAuditable(userId, command, logType, systemCode)) {
|
||||||
String parameters = getParametersAsString(request);
|
String parameters = getParametersAsString(request);
|
||||||
String message = request.getParameter("auditReason");
|
|
||||||
log
|
log
|
||||||
.debug("audit log save : logType={}, cmd={}, user={}, parameters={}", logType, command,
|
.debug("audit log save : logType={}, cmd={}, user={}, parameters={}", logType, command,
|
||||||
userId, parameters);
|
userId, parameters);
|
||||||
saveAuditLogUseCase
|
saveAuditLogUseCase.log(systemCode, logType, command, remoteAddress, userId, parameters);
|
||||||
.log(systemCode, logType, command, remoteAddress, userId, parameters, message);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
-3
@@ -51,9 +51,6 @@ public class AuditLogPageRepositoryImpl implements AuditLogPageRepository {
|
|||||||
if (StringUtils.isNotBlank(command.getSearchRemoteAddress())) {
|
if (StringUtils.isNotBlank(command.getSearchRemoteAddress())) {
|
||||||
jpaQuery.where(qAuditLogEntity.remoteAddress.containsIgnoreCase(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();
|
long totalCount = jpaQuery.fetchOne();
|
||||||
List<AuditLogEntity> auditLogs = jpaQuery
|
List<AuditLogEntity> auditLogs = jpaQuery
|
||||||
|
|||||||
@@ -23,8 +23,6 @@ public class AuditLog {
|
|||||||
|
|
||||||
private String logTypeText;
|
private String logTypeText;
|
||||||
|
|
||||||
private String message;
|
|
||||||
|
|
||||||
private String parameters;
|
private String parameters;
|
||||||
|
|
||||||
private LocalDateTime lastModifiedDate;
|
private LocalDateTime lastModifiedDate;
|
||||||
|
|||||||
@@ -3,6 +3,6 @@ package com.eactive.eai.rms.common.acl.audit.port.in;
|
|||||||
public interface SaveAuditLogUseCase {
|
public interface SaveAuditLogUseCase {
|
||||||
|
|
||||||
public void log(String systemCode, String logType, String command, String remoteAddress, String userId,
|
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 searchUserId;
|
||||||
private String searchRemoteAddress;
|
private String searchRemoteAddress;
|
||||||
private String searchParameters;
|
|
||||||
|
|
||||||
@JsonFormat(pattern = "yyyyMMddHHmmss")
|
@JsonFormat(pattern = "yyyyMMddHHmmss")
|
||||||
@DateTimeFormat(pattern = "yyyyMMddHHmmss")
|
@DateTimeFormat(pattern = "yyyyMMddHHmmss")
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ class AuditLogService
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void log(String systemCode, String logType, String command, String remoteAddress, String userId,
|
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);
|
AuditLog auditLog = new AuditLog(systemCode, logType, command, userId, remoteAddress);
|
||||||
String auditPointKey = AuditPoint.generateKey(logType, systemCode, command);
|
String auditPointKey = AuditPoint.generateKey(logType, systemCode, command);
|
||||||
@@ -68,7 +68,6 @@ class AuditLogService
|
|||||||
auditLog.setLogTypeText(auditPoint.getLogTypeText());
|
auditLog.setLogTypeText(auditPoint.getLogTypeText());
|
||||||
auditLog.setLastModifiedDate(LocalDateTime.now());
|
auditLog.setLastModifiedDate(LocalDateTime.now());
|
||||||
auditLog.setParameters(parameters);
|
auditLog.setParameters(parameters);
|
||||||
auditLog.setMessage(message);
|
|
||||||
|
|
||||||
saveAuditLogPort.save(auditLog);
|
saveAuditLogPort.save(auditLog);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,23 +0,0 @@
|
|||||||
package com.eactive.eai.rms.common.acl.sitemap;
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
@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, Model model) {
|
|
||||||
model.addAttribute("sitemapTree", sitemapService.getSitemapTree(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(String serviceType);
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,96 +0,0 @@
|
|||||||
package com.eactive.eai.rms.common.acl.sitemap;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Comparator;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.context.i18n.LocaleContextHolder;
|
|
||||||
import org.springframework.stereotype.Service;
|
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
|
||||||
|
|
||||||
import com.eactive.eai.rms.common.acl.sitemap.ui.SitemapNode;
|
|
||||||
import com.eactive.eai.rms.common.base.BaseService;
|
|
||||||
import com.eactive.eai.rms.data.entity.man.menu.Menu;
|
|
||||||
import com.eactive.eai.rms.data.entity.man.menu.MenuService;
|
|
||||||
import com.eactive.eai.rms.data.entity.man.role.RoleMenuAuthService;
|
|
||||||
|
|
||||||
@Service("sitemapService")
|
|
||||||
@Transactional(transactionManager = "transactionManagerForEMS")
|
|
||||||
public class SitemapServiceImpl extends BaseService implements SitemapService {
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private MenuService menuService;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private RoleMenuAuthService roleMenuAuthService;
|
|
||||||
|
|
||||||
public List<SitemapNode> getSitemapTree(String serviceType) {
|
|
||||||
Menu root = menuService.getById("ROOT_DEV");
|
|
||||||
Menu langRoot = findLangRoot(root);
|
|
||||||
List<SitemapNode> tree = new ArrayList<>();
|
|
||||||
if (langRoot == null) {
|
|
||||||
return tree;
|
|
||||||
}
|
|
||||||
|
|
||||||
Set<String> serviceTypeMenuIds = StringUtils.isBlank(serviceType)
|
|
||||||
? null
|
|
||||||
: new HashSet<>(roleMenuAuthService.findMenuIdsByServiceType(serviceType));
|
|
||||||
|
|
||||||
for (Menu menu : sortedChildren(langRoot)) {
|
|
||||||
if (isVisible(menu) && matchesServiceType(menu, serviceTypeMenuIds)) {
|
|
||||||
tree.add(buildNode(menu, serviceTypeMenuIds));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return tree;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 엔티티의 childMenus는 menuId 기준으로 정렬되어 있어, 실제 메뉴(운영화면 GNB)와 동일하게 sortOrder 기준으로 재정렬한다.
|
|
||||||
private List<Menu> sortedChildren(Menu menu) {
|
|
||||||
List<Menu> children = new ArrayList<>(menu.getChildMenus());
|
|
||||||
children.sort(Comparator.comparing(Menu::getSortOrder, Comparator.nullsLast(Comparator.naturalOrder())));
|
|
||||||
return children;
|
|
||||||
}
|
|
||||||
|
|
||||||
// MenuService.getMenuList()의 GNB 렌더링과 동일하게 "ROOT"(한글)/"ROOT_EN"(영문) 이름의 언어별 루트를 찾는다.
|
|
||||||
private Menu findLangRoot(Menu root) {
|
|
||||||
String lang = LocaleContextHolder.getLocale().getLanguage();
|
|
||||||
String langRootName = (StringUtils.isBlank(lang) || "ko".equalsIgnoreCase(lang))
|
|
||||||
? "ROOT"
|
|
||||||
: "ROOT_" + lang.toUpperCase();
|
|
||||||
|
|
||||||
return root.getChildMenus().stream()
|
|
||||||
.filter(m -> langRootName.equalsIgnoreCase(m.getMenuName()))
|
|
||||||
.findFirst()
|
|
||||||
.orElse(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
private SitemapNode buildNode(Menu menu, Set<String> serviceTypeMenuIds) {
|
|
||||||
SitemapNode node = new SitemapNode(menu);
|
|
||||||
for (Menu child : sortedChildren(menu)) {
|
|
||||||
if (isVisible(child) && matchesServiceType(child, serviceTypeMenuIds)) {
|
|
||||||
node.getChildren().add(buildNode(child, serviceTypeMenuIds));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isVisible(Menu menu) {
|
|
||||||
return "Y".equalsIgnoreCase(menu.getDisplayYn()) && "Y".equalsIgnoreCase(menu.getUseYn());
|
|
||||||
}
|
|
||||||
|
|
||||||
// serviceType 필터가 없으면 통과. 있으면 자신이나 하위메뉴 중 하나라도 해당 serviceType 권한이 있어야 통과(상위메뉴 누락 방지).
|
|
||||||
private boolean matchesServiceType(Menu menu, Set<String> serviceTypeMenuIds) {
|
|
||||||
if (serviceTypeMenuIds == null) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (serviceTypeMenuIds.contains(menu.getMenuId())) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return menu.getChildMenus().stream().anyMatch(child -> matchesServiceType(child, serviceTypeMenuIds));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
package com.eactive.eai.rms.common.acl.sitemap.ui;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import com.eactive.eai.rms.data.entity.man.menu.Menu;
|
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
@Data
|
|
||||||
public class SitemapNode {
|
|
||||||
|
|
||||||
private String menuId;
|
|
||||||
|
|
||||||
private String menuName;
|
|
||||||
|
|
||||||
private String menuUrl;
|
|
||||||
|
|
||||||
private List<SitemapNode> children = new ArrayList<>();
|
|
||||||
|
|
||||||
public SitemapNode(Menu menu) {
|
|
||||||
this.menuId = menu.getMenuId();
|
|
||||||
this.menuName = menu.getMenuName();
|
|
||||||
this.menuUrl = menu.getMenuUrl();
|
|
||||||
}
|
|
||||||
|
|
||||||
public SitemapNode() {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -42,8 +42,6 @@ import lombok.RequiredArgsConstructor;
|
|||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class UserManService extends BaseService {
|
public class UserManService extends BaseService {
|
||||||
|
|
||||||
private static final String USER_STATUS_NORMAL = "1";
|
|
||||||
|
|
||||||
private final UserInfoService userInfoService;
|
private final UserInfoService userInfoService;
|
||||||
private final UserRoleService userRoleService;
|
private final UserRoleService userRoleService;
|
||||||
private final UserBusinessService userBusinessService;
|
private final UserBusinessService userBusinessService;
|
||||||
@@ -100,12 +98,9 @@ public class UserManService extends BaseService {
|
|||||||
|
|
||||||
UserInfo userInfo = userUIMapper.toEntity(userUI);
|
UserInfo userInfo = userUIMapper.toEntity(userUI);
|
||||||
userInfo.setRoleidnfiname(CommonConstants.DEPT_DEVELOPER);
|
userInfo.setRoleidnfiname(CommonConstants.DEPT_DEVELOPER);
|
||||||
userInfo.setStatus(USER_STATUS_NORMAL);
|
|
||||||
|
|
||||||
String subfix = monitoringContext.getStringProperty(MonitoringContext.RMS_PASSWORD_INIT_SUBFIX, "@!");
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
userInfo.setPassword(DamoManager.getInstance().hash(DamoManager.SHA256,userUI.getUserId()+subfix));
|
userInfo.setPassword(DamoManager.getInstance().hash(DamoManager.SHA256,userUI.getUserId()));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new RuntimeException("Error encrypting password", e);
|
throw new RuntimeException("Error encrypting password", e);
|
||||||
}
|
}
|
||||||
@@ -134,12 +129,6 @@ public class UserManService extends BaseService {
|
|||||||
public void update(UserUI userUI) {
|
public void update(UserUI userUI) {
|
||||||
UserInfo userInfo = userInfoService.getById(userUI.getUserId());
|
UserInfo userInfo = userInfoService.getById(userUI.getUserId());
|
||||||
userUIMapper.updateToEntity(userUI, userInfo);
|
userUIMapper.updateToEntity(userUI, userInfo);
|
||||||
|
|
||||||
// 계정상태를 정상으로 저장하면 로그인 실패횟수 초기화
|
|
||||||
if (USER_STATUS_NORMAL.equals(userInfo.getStatus())) {
|
|
||||||
userInfo.setLoginfailcount(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
userInfoService.save(userInfo);
|
userInfoService.save(userInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -151,15 +140,9 @@ public class UserManService extends BaseService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void updatePassword(UserUI userUI) throws Exception {
|
public void updatePassword(UserUI userUI) throws Exception {
|
||||||
String subfix = monitoringContext.getStringProperty(MonitoringContext.RMS_PASSWORD_INIT_SUBFIX, "@!");
|
|
||||||
UserInfo userInfo = userInfoService.getById(userUI.getUserId());
|
UserInfo userInfo = userInfoService.getById(userUI.getUserId());
|
||||||
userInfo.setPassword(DamoManager.getInstance().hash(DamoManager.SHA256, userUI.getUserId()+subfix));
|
userInfo.setPassword(DamoManager.getInstance().hash(DamoManager.SHA256, userUI.getUserId()));
|
||||||
userUIMapper.updateToEntity(userUI, userInfo);
|
userUIMapper.updateToEntity(userUI, userInfo);
|
||||||
|
|
||||||
// 비밀번호 초기화 시 계정상태를 정상으로 변경하고 로그인 실패횟수 초기화
|
|
||||||
userInfo.setLoginfailcount(0);
|
|
||||||
userInfo.setStatus(USER_STATUS_NORMAL);
|
|
||||||
|
|
||||||
userInfoService.save(userInfo);
|
userInfoService.save(userInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,8 +24,6 @@ public interface UserUIMapper extends GenericMapper<UserUI, UserInfo> {
|
|||||||
@Mapping(source = "secondmentstdt", target = "SECONDMENTSTDT")
|
@Mapping(source = "secondmentstdt", target = "SECONDMENTSTDT")
|
||||||
@Mapping(source = "secondmentendt", target = "SECONDMENTENDT")
|
@Mapping(source = "secondmentendt", target = "SECONDMENTENDT")
|
||||||
@Mapping(source = "allowip", target = "allowIp")
|
@Mapping(source = "allowip", target = "allowIp")
|
||||||
@Mapping(source = "lastloginyms", target = "LASTLOGINYMS")
|
|
||||||
@Mapping(source = "regdyms", target = "REGDYMS")
|
|
||||||
UserUI toVo(UserInfo entity);
|
UserUI toVo(UserInfo entity);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -82,14 +82,4 @@ public class UserUI {
|
|||||||
|
|
||||||
@JsonProperty("STATUS")
|
@JsonProperty("STATUS")
|
||||||
private String status; // 사용자계정상태
|
private String status; // 사용자계정상태
|
||||||
|
|
||||||
@JsonProperty("LASTLOGINYMS")
|
|
||||||
@JsonFormat(pattern = LocalDateTimeFormatters.FORMAT_YYYYMMDDHHMMSS_14)
|
|
||||||
@DateTimeFormat(pattern = "yyyyMMddHHmmss")
|
|
||||||
private LocalDateTime LASTLOGINYMS; // 최종로그인일시
|
|
||||||
|
|
||||||
@JsonProperty("REGDYMS")
|
|
||||||
@JsonFormat(pattern = LocalDateTimeFormatters.FORMAT_YYYYMMDDHHMMSS_14)
|
|
||||||
@DateTimeFormat(pattern = "yyyyMMddHHmmss")
|
|
||||||
private LocalDateTime REGDYMS; // 등록일시
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -255,21 +255,12 @@ public interface MonitoringContext {
|
|||||||
// 이중 로그인 허용여부
|
// 이중 로그인 허용여부
|
||||||
public static final String RMS_DUAL_LOGIN_ENABLED = "rms.DUAL_LOGIN_ENABLED";
|
public static final String RMS_DUAL_LOGIN_ENABLED = "rms.DUAL_LOGIN_ENABLED";
|
||||||
|
|
||||||
// 자동 로그아웃 타임아웃 (단위: 분, 기본값: 60)
|
// 자동 로그아웃 타임아웃 (단위: 분, 기본값: 10)
|
||||||
public static final String RMS_AUTO_LOGOUT_TIMEOUT = "rms.auto.logout.timeout";
|
public static final String RMS_AUTO_LOGOUT_TIMEOUT = "rms.auto.logout.timeout";
|
||||||
|
|
||||||
// 웹훅
|
// 웹훅 재전송 설정
|
||||||
public static final String RMS_WEBHOOK_REVERSE_PROXY_URL = "rms.webhook.reverse_proxy.url";
|
public static final String API_WEBHOOK_RETRY_COUNT = "api.webhook.retry_count";
|
||||||
public static final String RMS_WEBHOOK_RETRY_COUNT = "rms.webhook.retry_count";
|
public static final String API_WEBHOOK_RETRY_TIME = "api.webhook.retry_time";
|
||||||
public static final String RMS_WEBHOOK_RETRY_TIME = "rms.webhook.retry_time";
|
|
||||||
|
|
||||||
//비밀번호 초기화 접미사
|
|
||||||
public static final String RMS_PASSWORD_INIT_SUBFIX = "rms.password.init.subfix";
|
|
||||||
|
|
||||||
// 최대 로그인 실패 허용횟수 (0이면 계정 잠금 미적용)
|
|
||||||
public static final String RMS_PASSWORD_FAIL_COUNT = "rms.password.fail.count";
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -21,8 +21,8 @@ import com.eactive.eai.rms.common.util.ContainerUtil;
|
|||||||
import com.eactive.eai.rms.data.entity.man.monitoringProperty.MonitoringPropertyId;
|
import com.eactive.eai.rms.data.entity.man.monitoringProperty.MonitoringPropertyId;
|
||||||
import com.eactive.eai.rms.data.entity.man.monitoringProperty.service.MonitoringPropertyGroupService;
|
import com.eactive.eai.rms.data.entity.man.monitoringProperty.service.MonitoringPropertyGroupService;
|
||||||
import com.eactive.eai.rms.data.entity.man.monitoringProperty.service.MonitoringPropertyService;
|
import com.eactive.eai.rms.data.entity.man.monitoringProperty.service.MonitoringPropertyService;
|
||||||
import com.eactive.eai.rms.ext.djb.common.DjbPropertyHolder;
|
import com.eactive.ext.kjb.common.KjbPropertyHolder;
|
||||||
import com.eactive.eai.rms.ext.djb.common.DjbPropertyInjector;
|
import com.eactive.ext.kjb.util.KjbPropertyInjector;
|
||||||
|
|
||||||
@Service("monitoringContext")
|
@Service("monitoringContext")
|
||||||
@Transactional(transactionManager = "transactionManagerForEMS")
|
@Transactional(transactionManager = "transactionManagerForEMS")
|
||||||
@@ -36,7 +36,7 @@ class MonitoringContextImpl implements MonitoringContext {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private MonitoringContextDAO monitoringContextDAO;
|
private MonitoringContextDAO monitoringContextDAO;
|
||||||
|
|
||||||
private Properties properties = new Properties();
|
private Properties properties = null;
|
||||||
|
|
||||||
private String instancePropertyGroupName;
|
private String instancePropertyGroupName;
|
||||||
|
|
||||||
@@ -45,7 +45,7 @@ class MonitoringContextImpl implements MonitoringContext {
|
|||||||
|
|
||||||
public void refresh() {
|
public void refresh() {
|
||||||
init();
|
init();
|
||||||
DjbPropertyHolder.reload();
|
KjbPropertyHolder.reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void refresh(String propName) {
|
public void refresh(String propName) {
|
||||||
@@ -73,10 +73,7 @@ class MonitoringContextImpl implements MonitoringContext {
|
|||||||
String hostIp = InetAddress.getLocalHost().getHostAddress();
|
String hostIp = InetAddress.getLocalHost().getHostAddress();
|
||||||
this.instancePropertyGroupName = String
|
this.instancePropertyGroupName = String
|
||||||
.format(MonitoringPropertyGroupService.MONITORING_PROPERTY_KEY_FORMAT, hostIp);
|
.format(MonitoringPropertyGroupService.MONITORING_PROPERTY_KEY_FORMAT, hostIp);
|
||||||
Properties loaded = monitoringContextDAO.getProperties(instancePropertyGroupName);
|
this.properties = monitoringContextDAO.getProperties(instancePropertyGroupName);
|
||||||
if (loaded != null) {
|
|
||||||
this.properties = loaded;
|
|
||||||
}
|
|
||||||
|
|
||||||
String eaiAgentId = System.getProperty(EAI_AGENTID);
|
String eaiAgentId = System.getProperty(EAI_AGENTID);
|
||||||
if (StringUtils.isBlank(eaiAgentId)) {
|
if (StringUtils.isBlank(eaiAgentId)) {
|
||||||
@@ -88,9 +85,9 @@ class MonitoringContextImpl implements MonitoringContext {
|
|||||||
|
|
||||||
setPortMapper(monitoringContextDAO.getPortMap());
|
setPortMapper(monitoringContextDAO.getPortMap());
|
||||||
|
|
||||||
// DjbPropertyHolder 초기화
|
// KjbPropertyHolder 초기화
|
||||||
DjbPropertyInjector injector = new DjbPropertyInjector(monitoringPropertyService, "Monitoring");
|
KjbPropertyInjector injector = new KjbPropertyInjector(monitoringPropertyService, "Monitoring");
|
||||||
DjbPropertyHolder.initialize(injector::inject);
|
KjbPropertyHolder.initialize(injector::inject);
|
||||||
|
|
||||||
logger.info("SSO AgentId : " + System.getProperty(EAI_AGENTID));
|
logger.info("SSO AgentId : " + System.getProperty(EAI_AGENTID));
|
||||||
|
|
||||||
|
|||||||
@@ -45,9 +45,4 @@ public class LoginResponseDto {
|
|||||||
* 에러 메시지 (로그인 실패 시)
|
* 에러 메시지 (로그인 실패 시)
|
||||||
*/
|
*/
|
||||||
private String errorMessage;
|
private String errorMessage;
|
||||||
|
|
||||||
/**
|
|
||||||
* 초기 비밀번호 변경 필요 여부
|
|
||||||
*/
|
|
||||||
private boolean changePassword;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ import org.springframework.web.bind.annotation.ResponseBody;
|
|||||||
import org.springframework.web.servlet.View;
|
import org.springframework.web.servlet.View;
|
||||||
|
|
||||||
import com.eactive.eai.common.seed.Seed;
|
import com.eactive.eai.common.seed.Seed;
|
||||||
|
import com.eactive.eai.rms.ext.kjb.smsauth.SmsAuthService;
|
||||||
import com.eactive.ext.djb.DamoManager;
|
import com.eactive.ext.djb.DamoManager;
|
||||||
import com.eactive.eai.data.converter.LocalDateTimeFormatters;
|
import com.eactive.eai.data.converter.LocalDateTimeFormatters;
|
||||||
import com.eactive.eai.rms.common.context.MonitoringContext;
|
import com.eactive.eai.rms.common.context.MonitoringContext;
|
||||||
@@ -50,7 +51,6 @@ import com.eactive.eai.rms.data.entity.man.user.UserLoginHistoryService;
|
|||||||
import com.eactive.eai.rms.data.entity.man.user.UserRole;
|
import com.eactive.eai.rms.data.entity.man.user.UserRole;
|
||||||
import com.eactive.eai.rms.data.entity.man.user.UserRoleService;
|
import com.eactive.eai.rms.data.entity.man.user.UserRoleService;
|
||||||
import com.eactive.eai.rms.data.entity.man.user.UserServiceTypeService;
|
import com.eactive.eai.rms.data.entity.man.user.UserServiceTypeService;
|
||||||
import com.eactive.eai.rms.ext.djb.smsauth.SmsAuthService;
|
|
||||||
|
|
||||||
@Controller
|
@Controller
|
||||||
public class MainController implements InterceptorSkipController {
|
public class MainController implements InterceptorSkipController {
|
||||||
@@ -64,9 +64,6 @@ public class MainController implements InterceptorSkipController {
|
|||||||
private static final String REDIRECT_LOGIN_URL = "redirect:/emergency.jsp";
|
private static final String REDIRECT_LOGIN_URL = "redirect:/emergency.jsp";
|
||||||
private static final String REDIRECT_CHANGE_PASSWORD_URL = "redirect:/emergency.jsp";
|
private static final String REDIRECT_CHANGE_PASSWORD_URL = "redirect:/emergency.jsp";
|
||||||
|
|
||||||
private static final String USER_STATUS_LOCKED = "2";
|
|
||||||
private static final int DEFAULT_MAX_LOGIN_FAIL_COUNT = 5;
|
|
||||||
|
|
||||||
private final LocaleMessage localeMessage;
|
private final LocaleMessage localeMessage;
|
||||||
private final MonitoringContext monitoringContext;
|
private final MonitoringContext monitoringContext;
|
||||||
|
|
||||||
@@ -163,7 +160,7 @@ public class MainController implements InterceptorSkipController {
|
|||||||
session.removeAttribute(RESULT_MSG);
|
session.removeAttribute(RESULT_MSG);
|
||||||
return LoginResponseDto.builder()
|
return LoginResponseDto.builder()
|
||||||
.success(false)
|
.success(false)
|
||||||
.errorMessage(toAlertMessage(errorMsg != null ? errorMsg : "로그인에 실패했습니다."))
|
.errorMessage(errorMsg != null ? errorMsg : "로그인에 실패했습니다.")
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -195,19 +192,6 @@ public class MainController implements InterceptorSkipController {
|
|||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// 2.1 초기 비밀번호인 경우, 비밀번호 변경해야 함
|
|
||||||
String subfix = monitoringContext.getStringProperty(MonitoringContext.RMS_PASSWORD_INIT_SUBFIX, "@!");
|
|
||||||
if (userInfo.getPassword().equals(DamoManager.getInstance().hash(DamoManager.SHA256, userInfo.getUserid()+subfix))
|
|
||||||
|| userInfo.getPassword().equals(DamoManager.getInstance().hash(DamoManager.SHA256, userInfo.getUserid()))) {
|
|
||||||
return LoginResponseDto.builder()
|
|
||||||
.success(false)
|
|
||||||
.changePassword(true)
|
|
||||||
.errorMessage("비밀번호 변경후 사용가능합니다.")
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// 3. SMS 인증 필요 여부 체크
|
// 3. SMS 인증 필요 여부 체크
|
||||||
SmsAuthService smsAuthService = getSmsAuthService();
|
SmsAuthService smsAuthService = getSmsAuthService();
|
||||||
if (smsAuthService != null && smsAuthService.isEnabled()) {
|
if (smsAuthService != null && smsAuthService.isEnabled()) {
|
||||||
@@ -348,19 +332,9 @@ public class MainController implements InterceptorSkipController {
|
|||||||
|
|
||||||
//logger.info("사용자 계정 상태: " + userInfo.getStatus());
|
//logger.info("사용자 계정 상태: " + userInfo.getStatus());
|
||||||
|
|
||||||
if ( USER_STATUS_LOCKED.equals(userInfo.getStatus()) ) {
|
if ( userInfo.getStatus() != null && "2".equals(userInfo.getStatus()) ) {
|
||||||
int maxLoginFailCount = getMaxLoginFailCount();
|
setLoginFailure(request, session, "login.checkstatus1",
|
||||||
boolean lockedByFailCount = maxLoginFailCount > 0
|
"login.checkstatus2");
|
||||||
&& userInfo.getLoginfailcount() != null
|
|
||||||
&& userInfo.getLoginfailcount() >= maxLoginFailCount;
|
|
||||||
|
|
||||||
if (lockedByFailCount) {
|
|
||||||
setLoginFailure(request, session, "login.accountlocked1",
|
|
||||||
"login.accountlocked2", String.valueOf(maxLoginFailCount));
|
|
||||||
} else {
|
|
||||||
setLoginFailure(request, session, "login.checkstatus1",
|
|
||||||
"login.checkstatus2");
|
|
||||||
}
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -387,9 +361,8 @@ public class MainController implements InterceptorSkipController {
|
|||||||
/* 기존로직 - LDAP 사용 x */
|
/* 기존로직 - LDAP 사용 x */
|
||||||
if (!userInfo.getPassword().equals(DamoManager.getInstance().hash(DamoManager.SHA256, dto.getPassword()))) {
|
if (!userInfo.getPassword().equals(DamoManager.getInstance().hash(DamoManager.SHA256, dto.getPassword()))) {
|
||||||
// password 다름.
|
// password 다름.
|
||||||
int failCount = increaseLoginFailCount(userInfo);
|
|
||||||
setLoginFailure(request, session, "login.loginfail1",
|
setLoginFailure(request, session, "login.loginfail1",
|
||||||
"login.loginfail2", failCount);
|
"login.loginfail2");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -403,9 +376,8 @@ public class MainController implements InterceptorSkipController {
|
|||||||
//if (!authenticate(userInfo.getUserid(), dto.getPassword())) {
|
//if (!authenticate(userInfo.getUserid(), dto.getPassword())) {
|
||||||
if (!authenticate(userInfo.getUserid(), password)) {
|
if (!authenticate(userInfo.getUserid(), password)) {
|
||||||
// password 다름.
|
// password 다름.
|
||||||
int failCount = increaseLoginFailCount(userInfo);
|
|
||||||
setLoginFailure(request, session, "login.loginfail1",
|
setLoginFailure(request, session, "login.loginfail1",
|
||||||
"login.loginfail2", failCount);
|
"login.loginfail2");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -447,27 +419,6 @@ public class MainController implements InterceptorSkipController {
|
|||||||
return userInfo;
|
return userInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 최대 로그인 실패 허용횟수 조회 (rms.password.fail.count, 0이면 계정 잠금 미적용)
|
|
||||||
private int getMaxLoginFailCount() {
|
|
||||||
return monitoringContext.getIntProperty(
|
|
||||||
MonitoringContext.RMS_PASSWORD_FAIL_COUNT, DEFAULT_MAX_LOGIN_FAIL_COUNT);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 로그인 실패 횟수 증가, 임계치 도달 시 계정 잠금
|
|
||||||
private int increaseLoginFailCount(UserInfo userInfo) {
|
|
||||||
int failCount = (userInfo.getLoginfailcount() == null ? 0 : userInfo.getLoginfailcount()) + 1;
|
|
||||||
userInfo.setLoginfailcount(failCount);
|
|
||||||
|
|
||||||
int maxLoginFailCount = getMaxLoginFailCount();
|
|
||||||
if (maxLoginFailCount > 0 && failCount >= maxLoginFailCount) {
|
|
||||||
userInfo.setStatus(USER_STATUS_LOCKED);
|
|
||||||
logger.warn("로그인 " + maxLoginFailCount + "회 이상 실패로 계정 잠금 처리: " + userInfo.getUserid());
|
|
||||||
}
|
|
||||||
|
|
||||||
userInfoService.save(userInfo);
|
|
||||||
return failCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
// LDAP
|
// LDAP
|
||||||
public boolean authenticate(String userId, String password) {
|
public boolean authenticate(String userId, String password) {
|
||||||
String LDAP_URL = monitoringPropertyService.getPrpty2ValById("Monitoring", "ldap.domain");
|
String LDAP_URL = monitoringPropertyService.getPrpty2ValById("Monitoring", "ldap.domain");
|
||||||
@@ -532,7 +483,6 @@ public class MainController implements InterceptorSkipController {
|
|||||||
String lastLoginIp = IpUtil.getClientIp(request);
|
String lastLoginIp = IpUtil.getClientIp(request);
|
||||||
userInfo.setLastloginyms(LocalDateTime.now());
|
userInfo.setLastloginyms(LocalDateTime.now());
|
||||||
userInfo.setLastloginip(lastLoginIp);
|
userInfo.setLastloginip(lastLoginIp);
|
||||||
userInfo.setLoginfailcount(0);
|
|
||||||
userInfoService.save(userInfo);
|
userInfoService.save(userInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -593,32 +543,12 @@ public class MainController implements InterceptorSkipController {
|
|||||||
return "redirect:/common/errors/errorLogon.jsp";
|
return "redirect:/common/errors/errorLogon.jsp";
|
||||||
}
|
}
|
||||||
|
|
||||||
// HTML 개행 태그(<BR/> 등)를 JS alert용 개행문자로 변환
|
|
||||||
private String toAlertMessage(String message) {
|
|
||||||
return message.replaceAll("(?i)<br\\s*/?>", "\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
// 로그인 실패 시 로그 기록, 세션에 실패 메시지 설정
|
// 로그인 실패 시 로그 기록, 세션에 실패 메시지 설정
|
||||||
private void setLoginFailure(HttpServletRequest request,
|
private void setLoginFailure(HttpServletRequest request,
|
||||||
HttpSession session, String logKey, String msgKey,
|
HttpSession session, String logKey, String msgKey) {
|
||||||
String... msgArgs) {
|
|
||||||
UserAccessLogger.log(request, LOG_CATEGORY_LOGIN, "F",
|
UserAccessLogger.log(request, LOG_CATEGORY_LOGIN, "F",
|
||||||
localeMessage.getString(logKey));
|
localeMessage.getString(logKey));
|
||||||
session.setAttribute(RESULT_MSG, localeMessage.getString(msgKey, msgArgs));
|
session.setAttribute(RESULT_MSG, localeMessage.getString(msgKey));
|
||||||
}
|
|
||||||
|
|
||||||
// 로그인 실패 시 로그 기록, 세션에 실패 메시지(실패 횟수 포함) 설정
|
|
||||||
private void setLoginFailure(HttpServletRequest request,
|
|
||||||
HttpSession session, String logKey, String msgKey,
|
|
||||||
int failCount) {
|
|
||||||
UserAccessLogger.log(request, LOG_CATEGORY_LOGIN, "F",
|
|
||||||
localeMessage.getString(logKey));
|
|
||||||
String msg = localeMessage.getString(msgKey);
|
|
||||||
int maxLoginFailCount = getMaxLoginFailCount();
|
|
||||||
if (maxLoginFailCount > 0) {
|
|
||||||
msg += " (" + failCount + "/" + maxLoginFailCount + ")";
|
|
||||||
}
|
|
||||||
session.setAttribute(RESULT_MSG, msg);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 서비스 타입 검증
|
// 서비스 타입 검증
|
||||||
@@ -684,7 +614,7 @@ public class MainController implements InterceptorSkipController {
|
|||||||
model.addAttribute("mainPage", mainPage);
|
model.addAttribute("mainPage", mainPage);
|
||||||
model.addAttribute("menuId", menuId);
|
model.addAttribute("menuId", menuId);
|
||||||
model.addAttribute("autoLogoutTimeout",
|
model.addAttribute("autoLogoutTimeout",
|
||||||
monitoringContext.getIntProperty(MonitoringContext.RMS_AUTO_LOGOUT_TIMEOUT, 60));
|
monitoringContext.getIntProperty(MonitoringContext.RMS_AUTO_LOGOUT_TIMEOUT, 10));
|
||||||
|
|
||||||
if (logger.isDebugEnabled()) {
|
if (logger.isDebugEnabled()) {
|
||||||
logger.debug("CURRENT SERVICE TYPE : " + service);
|
logger.debug("CURRENT SERVICE TYPE : " + service);
|
||||||
@@ -828,9 +758,6 @@ public class MainController implements InterceptorSkipController {
|
|||||||
String changePassword, String confirmPassword,
|
String changePassword, String confirmPassword,
|
||||||
String resetUserId, String resetPassword) {
|
String resetUserId, String resetPassword) {
|
||||||
HttpSession session = request.getSession();
|
HttpSession session = request.getSession();
|
||||||
// AJAX 로그인 인증 후 초기비밀번호 변경 경로에서 세션에 부분 로그인 상태가 남아
|
|
||||||
// root emergency.jsp가 main.do로 잘못 리다이렉트하는 것을 방지
|
|
||||||
session.removeAttribute(CommonConstants.LOGIN);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
@@ -888,8 +815,6 @@ public class MainController implements InterceptorSkipController {
|
|||||||
.getString("login.pwdchanged"));
|
.getString("login.pwdchanged"));
|
||||||
session.setAttribute(RESULT_MSG, localeMessage
|
session.setAttribute(RESULT_MSG, localeMessage
|
||||||
.getString("login.pwdchanged"));
|
.getString("login.pwdchanged"));
|
||||||
logger.debug("\n\n\n\n===================" + localeMessage
|
|
||||||
.getString("login.pwdchanged"));
|
|
||||||
return REDIRECT_LOGIN_URL;
|
return REDIRECT_LOGIN_URL;
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
|||||||
+1
-1
@@ -106,7 +106,7 @@ public class MonitoringPropertyManController extends BaseAnnotationController {
|
|||||||
monitoringContext.refresh();
|
monitoringContext.refresh();
|
||||||
|
|
||||||
String url = "/monitoring/refresh.do";
|
String url = "/monitoring/refresh.do";
|
||||||
Map<String, String> ret = monitoringSyncAgent.sync(url, paramMap);
|
Map<String, String> ret = monitoringSyncAgent.targetSync(url, paramMap);
|
||||||
return ret.get("result");
|
return ret.get("result");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -51,17 +51,4 @@ public class RoleMenuAuthService extends AbstractEMSDataSerivce<RoleMenuAuth, Ro
|
|||||||
.fetch();
|
.fetch();
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<String> findMenuIdsByServiceType(String serviceType) {
|
|
||||||
|
|
||||||
QRoleMenuAuth qRoleMenuAuth = QRoleMenuAuth.roleMenuAuth;
|
|
||||||
|
|
||||||
return getJPAQueryFactory()
|
|
||||||
.select(qRoleMenuAuth.id.menuId)
|
|
||||||
.distinct()
|
|
||||||
.from(qRoleMenuAuth)
|
|
||||||
.where(qRoleMenuAuth.id.serviceType.eq(serviceType))
|
|
||||||
.setHint(QueryHints.CACHEABLE, true)
|
|
||||||
.fetch();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
-12
@@ -14,8 +14,6 @@ import org.springframework.data.domain.Sort;
|
|||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
import java.time.LocalDate;
|
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
@Transactional(transactionManager = "transactionManagerForEMS")
|
@Transactional(transactionManager = "transactionManagerForEMS")
|
||||||
public class MessageRequestService
|
public class MessageRequestService
|
||||||
@@ -55,16 +53,6 @@ public class MessageRequestService
|
|||||||
return repository.findAll(predicate, fixed);
|
return repository.findAll(predicate, fixed);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 특정 일자에 SMS/KAKAO 로 발송 요청된 건수 (UMS 일련번호 채번용). */
|
|
||||||
public long countSmsKakaoRequestsOnDate(LocalDate date) {
|
|
||||||
QMessageRequest q = QMessageRequest.messageRequest;
|
|
||||||
BooleanBuilder predicate = new BooleanBuilder();
|
|
||||||
predicate.and(q.messageType.in("SMS", "KAKAO"));
|
|
||||||
predicate.and(q.requestDate.goe(date.atStartOfDay()));
|
|
||||||
predicate.and(q.requestDate.loe(date.atTime(23, 59, 59)));
|
|
||||||
return repository.count(predicate);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** PENDING 상태인 건만 FAILED 로 전환한다. */
|
/** PENDING 상태인 건만 FAILED 로 전환한다. */
|
||||||
public void updateStatusToFailedIfPending(String id) {
|
public void updateStatusToFailedIfPending(String id) {
|
||||||
MessageRequest entity = getById(id);
|
MessageRequest entity = getById(id);
|
||||||
|
|||||||
@@ -1,9 +0,0 @@
|
|||||||
package com.eactive.eai.rms.data.entity.onl.djb.apistatus;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* EAI 서비스명 조회 결과 (TSEAIHE01)
|
|
||||||
*/
|
|
||||||
public interface ApiNameRow {
|
|
||||||
String getEaisvcname();
|
|
||||||
String getEaisvcdesc();
|
|
||||||
}
|
|
||||||
@@ -26,12 +26,9 @@ public class WebhookSendLog {
|
|||||||
)
|
)
|
||||||
private Long id;
|
private Long id;
|
||||||
|
|
||||||
@Column(name = "TARGET_URL", length = 500)
|
@Column(name = "TARGET_URL", nullable = false, length = 500)
|
||||||
private String targetUrl;
|
private String targetUrl;
|
||||||
|
|
||||||
@Column(name = "PROXY_URL", length = 500)
|
|
||||||
private String proxyUrl;
|
|
||||||
|
|
||||||
@Column(name = "EVENT_TYPE", length = 100)
|
@Column(name = "EVENT_TYPE", length = 100)
|
||||||
private String eventType;
|
private String eventType;
|
||||||
|
|
||||||
@@ -69,9 +66,6 @@ public class WebhookSendLog {
|
|||||||
@Column(name = "SENT_AT")
|
@Column(name = "SENT_AT")
|
||||||
private LocalDateTime sentAt;
|
private LocalDateTime sentAt;
|
||||||
|
|
||||||
@Column(name = "SEND_BY", length = 100)
|
|
||||||
private String sendBy;
|
|
||||||
|
|
||||||
/* Boolean 편의 메서드 */
|
/* Boolean 편의 메서드 */
|
||||||
public void setSuccess(Boolean success) {
|
public void setSuccess(Boolean success) {
|
||||||
this.success = (success != null && success) ? "Y" : "N";
|
this.success = (success != null && success) ? "Y" : "N";
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ import com.eactive.eai.data.entity.onl.message.QServiceMessageEntity;
|
|||||||
import com.eactive.eai.data.entity.onl.routing.QRouting;
|
import com.eactive.eai.data.entity.onl.routing.QRouting;
|
||||||
import com.eactive.eai.data.entity.onl.stdmessage.QStandardMessageInfo;
|
import com.eactive.eai.data.entity.onl.stdmessage.QStandardMessageInfo;
|
||||||
import com.eactive.eai.data.jpa.AbstractDataService;
|
import com.eactive.eai.data.jpa.AbstractDataService;
|
||||||
import com.eactive.eai.rms.data.entity.onl.djb.apistatus.QApiStatus;
|
|
||||||
import com.eactive.eai.rms.data.entity.onl.authserver.QScopeInfo;
|
import com.eactive.eai.rms.data.entity.onl.authserver.QScopeInfo;
|
||||||
import com.eactive.eai.rms.data.entity.onl.bzwkdstcd.UserBusinessService;
|
import com.eactive.eai.rms.data.entity.onl.bzwkdstcd.UserBusinessService;
|
||||||
import com.querydsl.core.BooleanBuilder;
|
import com.querydsl.core.BooleanBuilder;
|
||||||
@@ -46,7 +45,6 @@ public class EAIMessageService extends AbstractDataService<EAIMessageEntity, Str
|
|||||||
QServiceMessageEntity qServiceMessageEntity = QServiceMessageEntity.serviceMessageEntity;
|
QServiceMessageEntity qServiceMessageEntity = QServiceMessageEntity.serviceMessageEntity;
|
||||||
QRouting qRouting = QRouting.routing;
|
QRouting qRouting = QRouting.routing;
|
||||||
QStandardMessageInfo qStdInfo = QStandardMessageInfo.standardMessageInfo;
|
QStandardMessageInfo qStdInfo = QStandardMessageInfo.standardMessageInfo;
|
||||||
QApiStatus qApiStatus = QApiStatus.apiStatus;
|
|
||||||
|
|
||||||
JPAQuery<Tuple> jpaQuery = createBaseQuery(eaiMessageUISearch);
|
JPAQuery<Tuple> jpaQuery = createBaseQuery(eaiMessageUISearch);
|
||||||
|
|
||||||
@@ -62,7 +60,6 @@ public class EAIMessageService extends AbstractDataService<EAIMessageEntity, Str
|
|||||||
, qRouting.nonmotivrouturiname
|
, qRouting.nonmotivrouturiname
|
||||||
, qStdInfo.apifullpath
|
, qStdInfo.apifullpath
|
||||||
, qStdInfo.bzwksvckeyname
|
, qStdInfo.bzwksvckeyname
|
||||||
, qApiStatus.statusCode
|
|
||||||
|
|
||||||
)
|
)
|
||||||
.limit(pageable.getPageSize())
|
.limit(pageable.getPageSize())
|
||||||
@@ -108,7 +105,6 @@ public class EAIMessageService extends AbstractDataService<EAIMessageEntity, Str
|
|||||||
QServiceMessageEntity qServiceMessageEntity = QServiceMessageEntity.serviceMessageEntity;
|
QServiceMessageEntity qServiceMessageEntity = QServiceMessageEntity.serviceMessageEntity;
|
||||||
QRouting qRouting = QRouting.routing;
|
QRouting qRouting = QRouting.routing;
|
||||||
QStandardMessageInfo qStdInfo = QStandardMessageInfo.standardMessageInfo;
|
QStandardMessageInfo qStdInfo = QStandardMessageInfo.standardMessageInfo;
|
||||||
QApiStatus qApiStatus = QApiStatus.apiStatus;
|
|
||||||
|
|
||||||
BooleanBuilder predicate = createSelectListPredicate(eaiMessageUISearch);
|
BooleanBuilder predicate = createSelectListPredicate(eaiMessageUISearch);
|
||||||
|
|
||||||
@@ -118,7 +114,6 @@ public class EAIMessageService extends AbstractDataService<EAIMessageEntity, Str
|
|||||||
, qRouting.nonmotivrouturiname
|
, qRouting.nonmotivrouturiname
|
||||||
, qStdInfo.apifullpath
|
, qStdInfo.apifullpath
|
||||||
, qStdInfo.bzwksvckeyname
|
, qStdInfo.bzwksvckeyname
|
||||||
, qApiStatus.statusCode
|
|
||||||
)
|
)
|
||||||
.from(qeaiMessageEntity)
|
.from(qeaiMessageEntity)
|
||||||
.join(qRouting)
|
.join(qRouting)
|
||||||
@@ -127,8 +122,6 @@ public class EAIMessageService extends AbstractDataService<EAIMessageEntity, Str
|
|||||||
.on(qServiceMessageEntity.id.svcprcssno.eq(1))
|
.on(qServiceMessageEntity.id.svcprcssno.eq(1))
|
||||||
.leftJoin(qStdInfo)
|
.leftJoin(qStdInfo)
|
||||||
.on(qeaiMessageEntity.eaisvcname.eq(qStdInfo.eaisvcname))
|
.on(qeaiMessageEntity.eaisvcname.eq(qStdInfo.eaisvcname))
|
||||||
.leftJoin(qApiStatus)
|
|
||||||
.on(qeaiMessageEntity.eaisvcname.eq(qApiStatus.eaisvcname))
|
|
||||||
.where(predicate);
|
.where(predicate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
-11
@@ -1,6 +1,5 @@
|
|||||||
package com.eactive.eai.rms.data.entity.onl.inflow;
|
package com.eactive.eai.rms.data.entity.onl.inflow;
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.data.domain.Page;
|
import org.springframework.data.domain.Page;
|
||||||
import org.springframework.data.domain.Pageable;
|
import org.springframework.data.domain.Pageable;
|
||||||
@@ -64,19 +63,11 @@ public class InflowControlLogService
|
|||||||
|
|
||||||
public Iterable<InflowControlLog> findAll(InflowControlHistoryManUISearch uiSearch) {
|
public Iterable<InflowControlLog> findAll(InflowControlHistoryManUISearch uiSearch) {
|
||||||
QInflowControlLog qInflowConfrolLog = QInflowControlLog.inflowControlLog;
|
QInflowControlLog qInflowConfrolLog = QInflowControlLog.inflowControlLog;
|
||||||
String startTime = StringUtils.defaultIfEmpty(uiSearch.getSearchStartTime(), "000000");
|
|
||||||
String endTime = StringUtils.defaultIfEmpty(uiSearch.getSearchEndTime(), "235959");
|
|
||||||
BooleanBuilder booleanBuilder = new BooleanBuilder();
|
BooleanBuilder booleanBuilder = new BooleanBuilder();
|
||||||
booleanBuilder
|
booleanBuilder
|
||||||
.and(qInflowConfrolLog.id.msgdpstyms
|
.and(qInflowConfrolLog.id.msgdpstyms
|
||||||
.between(uiSearch.getSearchStartDate() + startTime + "000",
|
.between(uiSearch.getSearchStartDate() + "000000000",
|
||||||
uiSearch.getSearchEndDate() + endTime + "999"));
|
uiSearch.getSearchEndDate() + "235959999"));
|
||||||
if (!StringUtils.isEmpty(uiSearch.getSearchInstanceName()))
|
|
||||||
booleanBuilder.and(qInflowConfrolLog.eaisevrinstncname.contains(uiSearch.getSearchInstanceName()));
|
|
||||||
if (!StringUtils.isEmpty(uiSearch.getSearchApiName()))
|
|
||||||
booleanBuilder.and(qInflowConfrolLog.eaisvcname.contains(uiSearch.getSearchApiName()));
|
|
||||||
if (!StringUtils.isEmpty(uiSearch.getSearchAdapterGroupName()))
|
|
||||||
booleanBuilder.and(qInflowConfrolLog.adptrbzwkgroupname.contains(uiSearch.getSearchAdapterGroupName()));
|
|
||||||
return super.repository.findAll(booleanBuilder);
|
return super.repository.findAll(booleanBuilder);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,8 +27,6 @@ import com.eactive.eai.rms.onl.manage.inflow.history.InflowControlHistoryManUISe
|
|||||||
import com.eactive.eai.rms.onl.manage.inflow.inflow.InflowControlManMapper;
|
import com.eactive.eai.rms.onl.manage.inflow.inflow.InflowControlManMapper;
|
||||||
import com.querydsl.core.BooleanBuilder;
|
import com.querydsl.core.BooleanBuilder;
|
||||||
import com.querydsl.core.Tuple;
|
import com.querydsl.core.Tuple;
|
||||||
import com.querydsl.core.types.dsl.BooleanExpression;
|
|
||||||
import com.querydsl.core.types.dsl.StringPath;
|
|
||||||
import com.querydsl.jpa.impl.JPAQuery;
|
import com.querydsl.jpa.impl.JPAQuery;
|
||||||
import com.querydsl.jpa.impl.JPAQueryFactory;
|
import com.querydsl.jpa.impl.JPAQueryFactory;
|
||||||
|
|
||||||
@@ -48,12 +46,7 @@ public class InflowControlService extends AbstractDataService<InflowControl, Inf
|
|||||||
@Autowired
|
@Autowired
|
||||||
InflowControlManMapper mapper;
|
InflowControlManMapper mapper;
|
||||||
|
|
||||||
// 유량제어 미설정(useyn null)은 "사용안함" 검색 시 함께 조회되도록 처리
|
public Page<InflowControlServiceDto> findAllForAdapter(Pageable pageable, String searchName) {
|
||||||
private static BooleanExpression useYnCondition(StringPath useYnPath, String searchUseYn) {
|
|
||||||
return "0".equals(searchUseYn) ? useYnPath.eq(searchUseYn).or(useYnPath.isNull()) : useYnPath.eq(searchUseYn);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Page<InflowControlServiceDto> findAllForAdapter(Pageable pageable, String searchName, String searchUseYn) {
|
|
||||||
JPAQueryFactory jpaQueryFactory = new JPAQueryFactory(entityManager);
|
JPAQueryFactory jpaQueryFactory = new JPAQueryFactory(entityManager);
|
||||||
QAdapterGroup qAdapterGroup = QAdapterGroup.adapterGroup;
|
QAdapterGroup qAdapterGroup = QAdapterGroup.adapterGroup;
|
||||||
QInflowControl qInflowControl = QInflowControl.inflowControl;
|
QInflowControl qInflowControl = QInflowControl.inflowControl;
|
||||||
@@ -61,8 +54,6 @@ public class InflowControlService extends AbstractDataService<InflowControl, Inf
|
|||||||
BooleanBuilder boolBuilder = new BooleanBuilder();
|
BooleanBuilder boolBuilder = new BooleanBuilder();
|
||||||
if (!StringUtils.isEmpty(searchName))
|
if (!StringUtils.isEmpty(searchName))
|
||||||
boolBuilder.and(qAdapterGroup.adptrbzwkgroupname.contains(searchName));
|
boolBuilder.and(qAdapterGroup.adptrbzwkgroupname.contains(searchName));
|
||||||
if (!StringUtils.isEmpty(searchUseYn))
|
|
||||||
boolBuilder.and(useYnCondition(qInflowControl.useyn, searchUseYn));
|
|
||||||
|
|
||||||
BooleanBuilder adapterWherePredicate = new BooleanBuilder(qAdapterGroup.adptriodstcd
|
BooleanBuilder adapterWherePredicate = new BooleanBuilder(qAdapterGroup.adptriodstcd
|
||||||
.eq("I")
|
.eq("I")
|
||||||
@@ -93,10 +84,6 @@ public class InflowControlService extends AbstractDataService<InflowControl, Inf
|
|||||||
long totalCount = jpaQueryFactory
|
long totalCount = jpaQueryFactory
|
||||||
.select(qAdapterGroup.adptrbzwkgroupname.count())
|
.select(qAdapterGroup.adptrbzwkgroupname.count())
|
||||||
.from(qAdapterGroup)
|
.from(qAdapterGroup)
|
||||||
.leftJoin(qInflowControl)
|
|
||||||
.on(qAdapterGroup.adptrbzwkgroupname
|
|
||||||
.eq(qInflowControl.id.name)
|
|
||||||
.and(qInflowControl.id.type.eq(ADAPTER_TYPE_INFLOW)))
|
|
||||||
.where(boolBuilder)
|
.where(boolBuilder)
|
||||||
.fetchOne();
|
.fetchOne();
|
||||||
|
|
||||||
@@ -134,22 +121,17 @@ public class InflowControlService extends AbstractDataService<InflowControl, Inf
|
|||||||
return toDto(qAdapterGroup, qInflowControl, tuple);
|
return toDto(qAdapterGroup, qInflowControl, tuple);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Page<InflowControlServiceDto> findAllForInterface(Pageable pageable, String searchName, String searchUseYn) {
|
public Page<InflowControlServiceDto> findAllForInterface(Pageable pageable, String searchName) {
|
||||||
JPAQueryFactory jpaQueryFactory = new JPAQueryFactory(entityManager);
|
JPAQueryFactory jpaQueryFactory = new JPAQueryFactory(entityManager);
|
||||||
QInflowControl qInflowControl = QInflowControl.inflowControl;
|
QInflowControl qInflowControl = QInflowControl.inflowControl;
|
||||||
QEAIMessageEntity qEAIMessageEntity = QEAIMessageEntity.eAIMessageEntity;
|
QEAIMessageEntity qEAIMessageEntity = QEAIMessageEntity.eAIMessageEntity;
|
||||||
|
|
||||||
BooleanBuilder boolBuilder = new BooleanBuilder();
|
|
||||||
boolBuilder.and(qEAIMessageEntity.eaisvcname.contains(searchName));
|
|
||||||
if (!StringUtils.isEmpty(searchUseYn))
|
|
||||||
boolBuilder.and(useYnCondition(qInflowControl.useyn, searchUseYn));
|
|
||||||
|
|
||||||
List<Tuple> tupleList = jpaQueryFactory
|
List<Tuple> tupleList = jpaQueryFactory
|
||||||
.select(qInflowControl, qEAIMessageEntity.eaisvcname, qEAIMessageEntity.eaisvcdesc)
|
.select(qInflowControl, qEAIMessageEntity.eaisvcname, qEAIMessageEntity.eaisvcdesc)
|
||||||
.from(qEAIMessageEntity)
|
.from(qEAIMessageEntity)
|
||||||
.leftJoin(qInflowControl)
|
.leftJoin(qInflowControl)
|
||||||
.on(qInflowControl.id.type.eq(INTERFACE_TYPE_INFLOW).and(qInflowControl.id.name.eq(qEAIMessageEntity.eaisvcname)))
|
.on(qInflowControl.id.type.eq(INTERFACE_TYPE_INFLOW).and(qInflowControl.id.name.eq(qEAIMessageEntity.eaisvcname)))
|
||||||
.where(boolBuilder)
|
.where(qEAIMessageEntity.eaisvcname.contains(searchName))
|
||||||
.orderBy(qEAIMessageEntity.eaisvcname.asc())
|
.orderBy(qEAIMessageEntity.eaisvcname.asc())
|
||||||
.offset(pageable.getOffset())
|
.offset(pageable.getOffset())
|
||||||
.limit(pageable.getPageSize())
|
.limit(pageable.getPageSize())
|
||||||
@@ -163,9 +145,7 @@ public class InflowControlService extends AbstractDataService<InflowControl, Inf
|
|||||||
long totalCount = jpaQueryFactory
|
long totalCount = jpaQueryFactory
|
||||||
.select(qEAIMessageEntity.eaisvcname.count())
|
.select(qEAIMessageEntity.eaisvcname.count())
|
||||||
.from(qEAIMessageEntity)
|
.from(qEAIMessageEntity)
|
||||||
.leftJoin(qInflowControl)
|
.where(qEAIMessageEntity.eaisvcname.contains(searchName))
|
||||||
.on(qInflowControl.id.type.eq(INTERFACE_TYPE_INFLOW).and(qInflowControl.id.name.eq(qEAIMessageEntity.eaisvcname)))
|
|
||||||
.where(boolBuilder)
|
|
||||||
.fetchOne();
|
.fetchOne();
|
||||||
return new PageImpl<>(dtoList, pageable, totalCount);
|
return new PageImpl<>(dtoList, pageable, totalCount);
|
||||||
}
|
}
|
||||||
@@ -219,24 +199,19 @@ public class InflowControlService extends AbstractDataService<InflowControl, Inf
|
|||||||
return toDto(qClientEntity, qInflowControl, tuple);
|
return toDto(qClientEntity, qInflowControl, tuple);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Page<InflowControlServiceDto> findAllForClient(Pageable pageable, String searchName, String searchUseYn) {
|
public Page<InflowControlServiceDto> findAllForClient(Pageable pageable, String searchName) {
|
||||||
|
|
||||||
JPAQueryFactory jpaQueryFactory = new JPAQueryFactory(entityManager);
|
JPAQueryFactory jpaQueryFactory = new JPAQueryFactory(entityManager);
|
||||||
QInflowControl qInflowControl = QInflowControl.inflowControl;
|
QInflowControl qInflowControl = QInflowControl.inflowControl;
|
||||||
QClientEntity qClientEntity = QClientEntity.clientEntity;
|
QClientEntity qClientEntity = QClientEntity.clientEntity;
|
||||||
|
|
||||||
BooleanBuilder boolBuilder = new BooleanBuilder();
|
|
||||||
boolBuilder.and(qClientEntity.clientname.contains(searchName));
|
|
||||||
if (!StringUtils.isEmpty(searchUseYn))
|
|
||||||
boolBuilder.and(useYnCondition(qInflowControl.useyn, searchUseYn));
|
|
||||||
|
|
||||||
List<Tuple> tupleList = jpaQueryFactory
|
List<Tuple> tupleList = jpaQueryFactory
|
||||||
.select(qInflowControl, qClientEntity.clientid, qClientEntity.clientname)
|
.select(qInflowControl, qClientEntity.clientid, qClientEntity.clientname)
|
||||||
.from(qClientEntity)
|
.from(qClientEntity)
|
||||||
.leftJoin(qInflowControl)
|
.leftJoin(qInflowControl)
|
||||||
.on(qInflowControl.id.type.eq(CLIENT_TYPE_INFLOW)
|
.on(qInflowControl.id.type.eq(CLIENT_TYPE_INFLOW)
|
||||||
.and(qInflowControl.id.name.eq(qClientEntity.clientid)))
|
.and(qInflowControl.id.name.eq(qClientEntity.clientid)))
|
||||||
.where(boolBuilder)
|
.where(qClientEntity.clientname.contains(searchName))
|
||||||
.orderBy(qClientEntity.clientname.asc())
|
.orderBy(qClientEntity.clientname.asc())
|
||||||
.offset(pageable.getOffset())
|
.offset(pageable.getOffset())
|
||||||
.limit(pageable.getPageSize())
|
.limit(pageable.getPageSize())
|
||||||
@@ -250,10 +225,7 @@ public class InflowControlService extends AbstractDataService<InflowControl, Inf
|
|||||||
long totalCount = jpaQueryFactory
|
long totalCount = jpaQueryFactory
|
||||||
.select(qClientEntity.clientname.count())
|
.select(qClientEntity.clientname.count())
|
||||||
.from(qClientEntity)
|
.from(qClientEntity)
|
||||||
.leftJoin(qInflowControl)
|
.where(qClientEntity.clientname.contains(searchName))
|
||||||
.on(qInflowControl.id.type.eq(CLIENT_TYPE_INFLOW)
|
|
||||||
.and(qInflowControl.id.name.eq(qClientEntity.clientid)))
|
|
||||||
.where(boolBuilder)
|
|
||||||
.fetchOne();
|
.fetchOne();
|
||||||
return new PageImpl<>(dtoList, pageable, totalCount);
|
return new PageImpl<>(dtoList, pageable, totalCount);
|
||||||
}
|
}
|
||||||
@@ -294,20 +266,11 @@ public class InflowControlService extends AbstractDataService<InflowControl, Inf
|
|||||||
QInflowControlLog qInflowConfrolLog = QInflowControlLog.inflowControlLog;
|
QInflowControlLog qInflowConfrolLog = QInflowControlLog.inflowControlLog;
|
||||||
QInflowControlGroup qinflowControlGroup = QInflowControlGroup.inflowControlGroup;
|
QInflowControlGroup qinflowControlGroup = QInflowControlGroup.inflowControlGroup;
|
||||||
|
|
||||||
String startTime = StringUtils.defaultIfEmpty(uiSearch.getSearchStartTime(), "000000");
|
|
||||||
String endTime = StringUtils.defaultIfEmpty(uiSearch.getSearchEndTime(), "235959");
|
|
||||||
|
|
||||||
BooleanBuilder predicate = new BooleanBuilder();
|
BooleanBuilder predicate = new BooleanBuilder();
|
||||||
predicate
|
predicate
|
||||||
.and(qInflowConfrolLog.id.msgdpstyms
|
.and(qInflowConfrolLog.id.msgdpstyms
|
||||||
.between(uiSearch.getSearchStartDate() + startTime + "000",
|
.between(uiSearch.getSearchStartDate() + "000000000",
|
||||||
uiSearch.getSearchEndDate() + endTime + "999"));
|
uiSearch.getSearchEndDate() + "235959999"));
|
||||||
if (!StringUtils.isEmpty(uiSearch.getSearchInstanceName()))
|
|
||||||
predicate.and(qInflowConfrolLog.eaisevrinstncname.contains(uiSearch.getSearchInstanceName()));
|
|
||||||
if (!StringUtils.isEmpty(uiSearch.getSearchApiName()))
|
|
||||||
predicate.and(qInflowConfrolLog.eaisvcname.contains(uiSearch.getSearchApiName()));
|
|
||||||
if (!StringUtils.isEmpty(uiSearch.getSearchAdapterGroupName()))
|
|
||||||
predicate.and(qInflowConfrolLog.adptrbzwkgroupname.contains(uiSearch.getSearchAdapterGroupName()));
|
|
||||||
|
|
||||||
return getJPAQueryFactory()
|
return getJPAQueryFactory()
|
||||||
.select(
|
.select(
|
||||||
|
|||||||
@@ -126,15 +126,6 @@ public class EAILogRepositoryImpl implements EAILogRepository {
|
|||||||
query.where(qeaiLog.eaisvcname.containsIgnoreCase(eaiLogSearch.getSearchEaiSvcName()));
|
query.where(qeaiLog.eaisvcname.containsIgnoreCase(eaiLogSearch.getSearchEaiSvcName()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (StringUtils.isNotBlank(eaiLogSearch.getSearchEaiSvcDesc())) {
|
|
||||||
QEAIMessageEntity qeaiMessageEntity = QEAIMessageEntity.eAIMessageEntity;
|
|
||||||
JPAQuery<String> subQuery = new JPAQueryFactory(em)
|
|
||||||
.select(qeaiMessageEntity.eaisvcname)
|
|
||||||
.from(qeaiMessageEntity)
|
|
||||||
.where(qeaiMessageEntity.eaisvcdesc.containsIgnoreCase(eaiLogSearch.getSearchEaiSvcDesc()));
|
|
||||||
query.where(qeaiLog.eaisvcname.in(subQuery));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (StringUtils.isNotBlank(eaiLogSearch.getSearchKeyMgtMsgCtnt())) {
|
if (StringUtils.isNotBlank(eaiLogSearch.getSearchKeyMgtMsgCtnt())) {
|
||||||
query.where(qeaiLog.keymgtmsgctnt.contains(eaiLogSearch.getSearchKeyMgtMsgCtnt()));
|
query.where(qeaiLog.keymgtmsgctnt.contains(eaiLogSearch.getSearchKeyMgtMsgCtnt()));
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-375
@@ -1,90 +1,18 @@
|
|||||||
package com.eactive.eai.rms.ext.djb.apistatus;
|
package com.eactive.eai.rms.ext.djb.apistatus;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
|
||||||
import java.time.ZoneId;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.dao.DataIntegrityViolationException;
|
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
import com.eactive.apim.portal.common.entity.Auditable;
|
|
||||||
import com.eactive.apim.portal.djb.apistatus.incident.entity.DjbApistatusIncident;
|
|
||||||
import com.eactive.apim.portal.djb.apistatus.incident.entity.DjbApistatusIncidentApi;
|
|
||||||
import com.eactive.apim.portal.djb.apistatus.incident.entity.DjbApistatusIncidentTimeline;
|
|
||||||
import com.eactive.apim.portal.djb.apistatus.incident.entity.IncidentKind;
|
|
||||||
import com.eactive.apim.portal.djb.apistatus.incident.entity.IncidentState;
|
|
||||||
import com.eactive.apim.portal.djb.apistatus.incident.repository.DjbApistatusIncidentApiRepository;
|
|
||||||
import com.eactive.apim.portal.djb.apistatus.incident.repository.DjbApistatusIncidentRepository;
|
|
||||||
import com.eactive.apim.portal.djb.apistatus.incident.repository.DjbApistatusIncidentTimelineRepository;
|
|
||||||
import com.eactive.apim.portal.portalNotice.entity.PortalNotice;
|
|
||||||
import com.eactive.eai.rms.data.entity.onl.apim.portalnotice.PortalNoticeService;
|
|
||||||
import com.eactive.eai.rms.data.entity.onl.djb.apistatus.ApiNameRow;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* API 상태 탐지 결과를 개발자포탈 API Status 화면용 장애 데이터로 반영한다.
|
|
||||||
*
|
|
||||||
* <p>탐지 자체는 {@link ApiStatusService} 가 수행하며, 본 서비스는 그 결과를
|
|
||||||
* {@code DJB_APISTATUS_INCIDENT} / {@code _API} / {@code _TIMELINE} + {@code PTL_NOTICE} 초안으로 기록한다.</p>
|
|
||||||
*
|
|
||||||
* <p>점검(CONTROL_*) 은 관리자가 공지사항으로 등록한 점검 일정이 원천이다
|
|
||||||
* ({@code ApiStatusRepository.findApiStatusEvents} 가 INCIDENT 기간을 읽어 CTRL_YN 을 판정).
|
|
||||||
* 따라서 CONTROL 이벤트를 다시 기록하면 순환이 되므로 기록하지 않는다.</p>
|
|
||||||
*/
|
|
||||||
@Service
|
@Service
|
||||||
@Transactional(transactionManager = "transactionManagerForEMS", rollbackFor = Exception.class)
|
@Transactional
|
||||||
public class ApiStatusDetectionService {
|
public class ApiStatusDetectionService {
|
||||||
|
|
||||||
private static final Logger log = LoggerFactory.getLogger(ApiStatusDetectionService.class);
|
private static final Logger log = LoggerFactory.getLogger(ApiStatusDetectionService.class);
|
||||||
|
|
||||||
public static final String EVENT_CONTROL_START = "CONTROL_START";
|
|
||||||
public static final String EVENT_CONTROL_END = "CONTROL_END";
|
|
||||||
public static final String EVENT_ERROR_START = "ERROR_START";
|
|
||||||
public static final String EVENT_ERROR_END = "ERROR_END";
|
|
||||||
public static final String EVENT_DELAY_START = "DELAY_START";
|
|
||||||
public static final String EVENT_DELAY_END = "DELAY_END";
|
|
||||||
|
|
||||||
/** INTERFACE_ID 멱등 윈도우 (분) */
|
|
||||||
private static final int IDEMPOTENT_WINDOW_MINUTES = 5;
|
|
||||||
|
|
||||||
/** 종결 상태 - 신규 탐지 병합 대상에서 제외 */
|
|
||||||
private static final List<IncidentState> CLOSED_STATES =
|
|
||||||
Collections.unmodifiableList(Arrays.asList(IncidentState.RESOLVED, IncidentState.CANCELED));
|
|
||||||
|
|
||||||
private static final String AUTHOR_SYSTEM = "SYSTEM";
|
|
||||||
private static final String DETECTED_BY_AUTO = "AUTO";
|
|
||||||
private static final String NOTICE_TYPE_INCIDENT = "3";
|
|
||||||
private static final int NOTICE_SUBJECT_MAX_LENGTH = 255;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private DjbApistatusIncidentRepository incidentRepository;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private DjbApistatusIncidentApiRepository incidentApiRepository;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private DjbApistatusIncidentTimelineRepository incidentTimelineRepository;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private PortalNoticeService portalNoticeService;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private ApiStatusRepository apiStatusRepository;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 이벤트 감지
|
* 이벤트 감지
|
||||||
* @param event "CONTROL_START" : 점검시작
|
* @param event "CONTROL_START" : 점검시작
|
||||||
@@ -97,313 +25,17 @@ public class ApiStatusDetectionService {
|
|||||||
*/
|
*/
|
||||||
public void detect(String event, List<String> apiIds) {
|
public void detect(String event, List<String> apiIds) {
|
||||||
log.debug("이벤트 감지: {} {}", event, apiIds);
|
log.debug("이벤트 감지: {} {}", event, apiIds);
|
||||||
|
|
||||||
if (StringUtils.isBlank(event) || apiIds == null || apiIds.isEmpty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<String> targets = apiIds.stream()
|
|
||||||
.filter(StringUtils::isNotBlank)
|
|
||||||
.distinct()
|
|
||||||
.sorted()
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
if (targets.isEmpty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (event) {
|
|
||||||
case EVENT_ERROR_START:
|
|
||||||
openIncident(event, targets, "에러율 임계 초과", "장애");
|
|
||||||
break;
|
|
||||||
case EVENT_DELAY_START:
|
|
||||||
openIncident(event, targets, "응답시간 임계 초과", "응답지연");
|
|
||||||
break;
|
|
||||||
case EVENT_ERROR_END:
|
|
||||||
case EVENT_DELAY_END:
|
|
||||||
markRecovered(event, targets);
|
|
||||||
break;
|
|
||||||
case EVENT_CONTROL_START:
|
|
||||||
case EVENT_CONTROL_END:
|
|
||||||
// 점검은 관리자 등록 공지가 원천 - 재기록하지 않음
|
|
||||||
log.debug("점검 이벤트는 장애 데이터로 기록하지 않음: {} {}", event, targets);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
log.warn("알 수 없는 이벤트: {}", event);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// API FSM 내 아직 장애로 남아있는 API 목록
|
||||||
* 미종결 장애로 남아있는 API 목록 (탐지 Job 폴링용)
|
public void remainDownApiIds() {
|
||||||
*/
|
|
||||||
@Transactional(transactionManager = "transactionManagerForEMS", readOnly = true)
|
|
||||||
public List<String> remainDownApiIds() {
|
|
||||||
List<DjbApistatusIncident> openIncidents =
|
|
||||||
incidentRepository.findByKindAndStateNotInOrderByStartedAtDesc(IncidentKind.INCIDENT, CLOSED_STATES);
|
|
||||||
if (openIncidents.isEmpty()) {
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
|
||||||
|
|
||||||
List<Long> incidentIds = openIncidents.stream()
|
|
||||||
.map(DjbApistatusIncident::getIncidentId)
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
|
|
||||||
return incidentApiRepository.findByIncidentIdInOrderByIncidentIdAscApiIdAsc(incidentIds).stream()
|
|
||||||
.filter(api -> api.getRecoveredAt() == null)
|
|
||||||
.map(DjbApistatusIncidentApi::getApiId)
|
|
||||||
.distinct()
|
|
||||||
.sorted()
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ────────────── 장애 발생 ──────────────
|
|
||||||
|
|
||||||
private void openIncident(String event, List<String> apiIds, String summary, String titleKeyword) {
|
// 정상 동작 - detect()에서 일괄 처리 (복구 = 점검종료, 장애종료, 지연종료)
|
||||||
LocalDateTime now = LocalDateTime.now();
|
// public void recovered(String event, String[] apiIds) {
|
||||||
String interfaceId = makeInterfaceId(event, apiIds, now);
|
//
|
||||||
|
// }
|
||||||
|
|
||||||
// 1) 동일 윈도우 재탐지 - 타임라인만 남기고 종료
|
|
||||||
Optional<DjbApistatusIncident> duplicated = incidentRepository.findByInterfaceId(interfaceId);
|
|
||||||
if (duplicated.isPresent()) {
|
|
||||||
appendTimeline(duplicated.get().getIncidentId(), null,
|
|
||||||
"재탐지 신호 수신 (" + event + ")", now);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2) 이미 열려있는 자동 등록 장애가 해당 API 를 포함하면 영향 API 만 병합
|
|
||||||
// (관리자가 직접 작성한 장애 공지는 건드리지 않는다)
|
|
||||||
List<DjbApistatusIncident> openIncidents = incidentRepository
|
|
||||||
.findOpenByApiIds(IncidentKind.INCIDENT, DETECTED_BY_AUTO, CLOSED_STATES, apiIds);
|
|
||||||
if (!openIncidents.isEmpty()) {
|
|
||||||
mergeIntoOpenIncident(openIncidents.get(0), event, apiIds, now);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3) 신규 장애 + 공지 초안 생성
|
|
||||||
Map<String, String> apiNames = resolveApiNames(apiIds);
|
|
||||||
String title = buildTitle(titleKeyword, apiIds, apiNames);
|
|
||||||
|
|
||||||
PortalNotice notice = createDraftNotice(title, summary, apiIds, apiNames, now);
|
|
||||||
|
|
||||||
DjbApistatusIncident incident = new DjbApistatusIncident();
|
|
||||||
incident.setKind(IncidentKind.INCIDENT);
|
|
||||||
incident.setState(IncidentState.INVESTIGATING);
|
|
||||||
incident.setTitle(title);
|
|
||||||
incident.setSummary(summary);
|
|
||||||
incident.setStartedAt(now);
|
|
||||||
incident.setDetectedBy(DETECTED_BY_AUTO);
|
|
||||||
incident.setInterfaceId(interfaceId);
|
|
||||||
incident.setNoticeId(notice.getId());
|
|
||||||
incident.setDraftYn("Y");
|
|
||||||
incident.setFixYn("N");
|
|
||||||
stampAudit(incident, now);
|
|
||||||
|
|
||||||
DjbApistatusIncident saved;
|
|
||||||
try {
|
|
||||||
saved = incidentRepository.saveAndFlush(incident);
|
|
||||||
} catch (DataIntegrityViolationException e) {
|
|
||||||
// INTERFACE_ID UNIQUE 충돌 - 동시 탐지. 공지 초안 정리 후 타임라인만 남긴다
|
|
||||||
log.info("장애 중복 감지 (interfaceId={}) - 기존 장애에 타임라인 추가", interfaceId, e);
|
|
||||||
portalNoticeService.deleteById(notice.getId());
|
|
||||||
incidentRepository.findByInterfaceId(interfaceId).ifPresent(existing ->
|
|
||||||
appendTimeline(existing.getIncidentId(), null,
|
|
||||||
"재탐지 신호 수신 (" + event + ")", now));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
saveIncidentApis(saved.getIncidentId(), apiIds, apiNames, now);
|
|
||||||
appendTimeline(saved.getIncidentId(), IncidentState.INVESTIGATING,
|
|
||||||
summary + " 자동 감지 (" + event + ")\n영향 API: " + String.join(", ", apiIds), now);
|
|
||||||
|
|
||||||
log.info("자동 장애 등록: incidentId={}, noticeId={}, apis={}",
|
|
||||||
saved.getIncidentId(), notice.getId(), apiIds);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void mergeIntoOpenIncident(DjbApistatusIncident incident, String event,
|
|
||||||
List<String> apiIds, LocalDateTime now) {
|
|
||||||
Long incidentId = incident.getIncidentId();
|
|
||||||
|
|
||||||
Set<String> mapped = incidentApiRepository.findByIncidentIdOrderByApiId(incidentId).stream()
|
|
||||||
.map(DjbApistatusIncidentApi::getApiId)
|
|
||||||
.collect(Collectors.toCollection(HashSet::new));
|
|
||||||
|
|
||||||
List<String> added = apiIds.stream()
|
|
||||||
.filter(apiId -> !mapped.contains(apiId))
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
|
|
||||||
if (added.isEmpty()) {
|
|
||||||
appendTimeline(incidentId, null, "재탐지 신호 수신 (" + event + ")", now);
|
|
||||||
log.debug("기존 장애에 이미 포함된 API - 타임라인만 추가: incidentId={}, apis={}", incidentId, apiIds);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
saveIncidentApis(incidentId, added, resolveApiNames(added), now);
|
|
||||||
appendTimeline(incidentId, null,
|
|
||||||
"영향 API 추가 감지 (" + event + ")\n" + String.join(", ", added), now);
|
|
||||||
log.info("기존 장애에 영향 API 병합: incidentId={}, addedApis={}", incidentId, added);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ────────────── 복구 ──────────────
|
|
||||||
|
|
||||||
private void markRecovered(String event, List<String> apiIds) {
|
|
||||||
LocalDateTime now = LocalDateTime.now();
|
|
||||||
|
|
||||||
List<DjbApistatusIncidentApi> candidates =
|
|
||||||
incidentApiRepository.findByApiIdInAndRecoveredAtIsNull(apiIds);
|
|
||||||
if (candidates.isEmpty()) {
|
|
||||||
log.debug("복구 대상 장애 없음: {} {}", event, apiIds);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<Long, DjbApistatusIncident> openIncidents = incidentRepository
|
|
||||||
.findByKindAndStateNotInOrderByStartedAtDesc(IncidentKind.INCIDENT, CLOSED_STATES).stream()
|
|
||||||
.collect(Collectors.toMap(DjbApistatusIncident::getIncidentId, incident -> incident));
|
|
||||||
|
|
||||||
Map<Long, List<String>> recoveredByIncident = new HashMap<>();
|
|
||||||
for (DjbApistatusIncidentApi api : candidates) {
|
|
||||||
if (!openIncidents.containsKey(api.getIncidentId())) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
api.setRecoveredAt(now);
|
|
||||||
api.setLastModifiedBy(AUTHOR_SYSTEM);
|
|
||||||
api.setLastModifiedDate(now);
|
|
||||||
incidentApiRepository.save(api);
|
|
||||||
|
|
||||||
recoveredByIncident.computeIfAbsent(api.getIncidentId(), key -> new ArrayList<>())
|
|
||||||
.add(api.getApiId());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (recoveredByIncident.isEmpty()) {
|
|
||||||
log.debug("복구 대상 장애 없음 (미종결 장애 미포함): {} {}", event, apiIds);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Map.Entry<Long, List<String>> entry : recoveredByIncident.entrySet()) {
|
|
||||||
Long incidentId = entry.getKey();
|
|
||||||
List<String> recoveredApis = entry.getValue();
|
|
||||||
DjbApistatusIncident incident = openIncidents.get(incidentId);
|
|
||||||
|
|
||||||
boolean allRecovered = incidentApiRepository.countByIncidentIdAndRecoveredAtIsNull(incidentId) == 0;
|
|
||||||
IncidentState nextState = allRecovered ? IncidentState.RESOLVED : IncidentState.MONITORING;
|
|
||||||
|
|
||||||
if (incident.getState() != nextState) {
|
|
||||||
incident.setPreviousState(incident.getState());
|
|
||||||
incident.setState(nextState);
|
|
||||||
}
|
|
||||||
if (allRecovered) {
|
|
||||||
incident.setEndAt(now);
|
|
||||||
}
|
|
||||||
incident.setLastModifiedBy(AUTHOR_SYSTEM);
|
|
||||||
incident.setLastModifiedDate(now);
|
|
||||||
incidentRepository.save(incident);
|
|
||||||
|
|
||||||
String body = (allRecovered ? "전체 복구 확인 (" : "일부 복구 확인 (") + event + ")\n"
|
|
||||||
+ String.join(", ", recoveredApis);
|
|
||||||
appendTimeline(incidentId, nextState, body, now);
|
|
||||||
|
|
||||||
log.info("자동 복구 처리: incidentId={}, state={}, apis={}", incidentId, nextState, recoveredApis);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ────────────── 내부 헬퍼 ──────────────
|
|
||||||
|
|
||||||
private String makeInterfaceId(String event, List<String> sortedApiIds, LocalDateTime now) {
|
|
||||||
long windowSeconds = IDEMPOTENT_WINDOW_MINUTES * 60L;
|
|
||||||
long flooredEpoch = now.atZone(ZoneId.systemDefault()).toEpochSecond()
|
|
||||||
/ windowSeconds * windowSeconds;
|
|
||||||
String joined = String.join(",", sortedApiIds);
|
|
||||||
String interfaceId = event + ":" + joined + ":" + flooredEpoch;
|
|
||||||
|
|
||||||
// INTERFACE_ID 는 VARCHAR2(200) - 초과 시 API 목록을 해시로 축약
|
|
||||||
if (interfaceId.length() > 200) {
|
|
||||||
interfaceId = event + ":#" + Integer.toHexString(joined.hashCode())
|
|
||||||
+ ":" + sortedApiIds.size() + ":" + flooredEpoch;
|
|
||||||
}
|
|
||||||
return interfaceId;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Map<String, String> resolveApiNames(List<String> apiIds) {
|
|
||||||
Map<String, String> names = new HashMap<>();
|
|
||||||
try {
|
|
||||||
for (ApiNameRow row : apiStatusRepository.findApiNames(apiIds)) {
|
|
||||||
names.put(row.getEaisvcname(), row.getEaisvcdesc());
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.warn("API 명 조회 실패 - API ID 를 그대로 사용", e);
|
|
||||||
}
|
|
||||||
return names;
|
|
||||||
}
|
|
||||||
|
|
||||||
private String apiLabel(String apiId, Map<String, String> apiNames) {
|
|
||||||
return StringUtils.defaultIfBlank(apiNames.get(apiId), apiId);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String buildTitle(String titleKeyword, List<String> apiIds, Map<String, String> apiNames) {
|
|
||||||
String first = apiLabel(apiIds.get(0), apiNames);
|
|
||||||
if (apiIds.size() > 1) {
|
|
||||||
return "[자동감지] " + first + " 외 " + (apiIds.size() - 1) + "종 API " + titleKeyword;
|
|
||||||
}
|
|
||||||
return "[자동감지] " + first + " API " + titleKeyword;
|
|
||||||
}
|
|
||||||
|
|
||||||
private PortalNotice createDraftNotice(String title, String summary, List<String> apiIds,
|
|
||||||
Map<String, String> apiNames, LocalDateTime now) {
|
|
||||||
StringBuilder detail = new StringBuilder();
|
|
||||||
detail.append("<p>").append(summary).append(" 로 자동 감지된 장애입니다. 관리자 검수 후 정식 게시됩니다.</p>");
|
|
||||||
detail.append("<p>영향 API</p><ul>");
|
|
||||||
for (String apiId : apiIds) {
|
|
||||||
detail.append("<li>").append(apiLabel(apiId, apiNames)).append(" (").append(apiId).append(")</li>");
|
|
||||||
}
|
|
||||||
detail.append("</ul>");
|
|
||||||
|
|
||||||
PortalNotice notice = new PortalNotice();
|
|
||||||
notice.setId(null);
|
|
||||||
notice.setNoticeSubject(StringUtils.abbreviate(title, NOTICE_SUBJECT_MAX_LENGTH));
|
|
||||||
notice.setNoticeDetail(detail.toString());
|
|
||||||
notice.setNoticeType(NOTICE_TYPE_INCIDENT);
|
|
||||||
notice.setUseYn("N");
|
|
||||||
notice.setFixYn("N");
|
|
||||||
notice.setReadCount(0L);
|
|
||||||
notice.setInquirerName(AUTHOR_SYSTEM);
|
|
||||||
notice.setCreatedBy(AUTHOR_SYSTEM);
|
|
||||||
notice.setCreatedDate(now);
|
|
||||||
|
|
||||||
portalNoticeService.save(notice);
|
|
||||||
return notice;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void saveIncidentApis(Long incidentId, Collection<String> apiIds,
|
|
||||||
Map<String, String> apiNames, LocalDateTime now) {
|
|
||||||
List<DjbApistatusIncidentApi> rows = new ArrayList<>();
|
|
||||||
for (String apiId : apiIds) {
|
|
||||||
DjbApistatusIncidentApi api = new DjbApistatusIncidentApi();
|
|
||||||
api.setIncidentId(incidentId);
|
|
||||||
api.setApiId(apiId);
|
|
||||||
api.setApiName(apiNames.get(apiId));
|
|
||||||
stampAudit(api, now);
|
|
||||||
rows.add(api);
|
|
||||||
}
|
|
||||||
if (!rows.isEmpty()) {
|
|
||||||
incidentApiRepository.saveAll(rows);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void appendTimeline(Long incidentId, IncidentState stateAfter, String body, LocalDateTime now) {
|
|
||||||
DjbApistatusIncidentTimeline timeline = new DjbApistatusIncidentTimeline();
|
|
||||||
timeline.setIncidentId(incidentId);
|
|
||||||
timeline.setEventAt(now);
|
|
||||||
timeline.setStateAfter(stateAfter);
|
|
||||||
timeline.setBody(StringUtils.abbreviate(body, 4000));
|
|
||||||
timeline.setAuthorType(AUTHOR_SYSTEM);
|
|
||||||
timeline.setVisibleYn("Y");
|
|
||||||
stampAudit(timeline, now);
|
|
||||||
incidentTimelineRepository.save(timeline);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 스케줄러 스레드는 로그인 세션이 없어 AuditorAware 가 비어있다. CREATED_BY 는 NOT NULL 이므로 직접 채운다.
|
|
||||||
*/
|
|
||||||
private void stampAudit(Auditable entity, LocalDateTime now) {
|
|
||||||
entity.setCreatedBy(AUTHOR_SYSTEM);
|
|
||||||
entity.setCreatedDate(now);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -4,10 +4,7 @@ import java.util.List;
|
|||||||
import org.springframework.data.jpa.repository.Query;
|
import org.springframework.data.jpa.repository.Query;
|
||||||
import org.springframework.data.repository.query.Param;
|
import org.springframework.data.repository.query.Param;
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
|
|
||||||
import com.eactive.eai.data.jpa.BaseRepository;
|
import com.eactive.eai.data.jpa.BaseRepository;
|
||||||
import com.eactive.eai.rms.data.entity.onl.djb.apistatus.ApiNameRow;
|
|
||||||
import com.eactive.eai.rms.data.entity.onl.djb.apistatus.ApiStatus;
|
import com.eactive.eai.rms.data.entity.onl.djb.apistatus.ApiStatus;
|
||||||
import com.eactive.eai.rms.data.entity.onl.djb.apistatus.ApiStatusEvent;
|
import com.eactive.eai.rms.data.entity.onl.djb.apistatus.ApiStatusEvent;
|
||||||
|
|
||||||
@@ -49,7 +46,7 @@ public interface ApiStatusRepository extends BaseRepository<ApiStatus, String> {
|
|||||||
+ " , (SELECT CASE WHEN TOTAL = 0 THEN 'X'"
|
+ " , (SELECT CASE WHEN TOTAL = 0 THEN 'X'"
|
||||||
+ " WHEN (TOTAL - SUCCESS) * 100 / TOTAL > :errorRate THEN 'Y'"
|
+ " WHEN (TOTAL - SUCCESS) * 100 / TOTAL > :errorRate THEN 'Y'"
|
||||||
+ " ELSE 'N' END"
|
+ " ELSE 'N' END"
|
||||||
+ " FROM (SELECT NVL(SUM(SUCCESS_CNT + TIMEOUT_CNT + SYSTEM_ERR_CNT + BIZ_ERR_CNT),0) AS TOTAL"
|
+ " FROM (SELECT SUM(SUCCESS_CNT + TIMEOUT_CNT + SYSTEM_ERR_CNT + BIZ_ERR_CNT) AS TOTAL"
|
||||||
+ " , SUM(SUCCESS_CNT) AS SUCCESS"
|
+ " , SUM(SUCCESS_CNT) AS SUCCESS"
|
||||||
+ " FROM API_STATS_MINUTE"
|
+ " FROM API_STATS_MINUTE"
|
||||||
+ " WHERE A.EAISVCNAME = API_NAME"
|
+ " WHERE A.EAISVCNAME = API_NAME"
|
||||||
@@ -59,7 +56,7 @@ public interface ApiStatusRepository extends BaseRepository<ApiStatus, String> {
|
|||||||
+ " , (SELECT CASE WHEN TOTAL = 0 THEN 'X'"
|
+ " , (SELECT CASE WHEN TOTAL = 0 THEN 'X'"
|
||||||
+ " WHEN RESP_SUM / TOTAL > :delayAvgRespTime THEN 'Y'"
|
+ " WHEN RESP_SUM / TOTAL > :delayAvgRespTime THEN 'Y'"
|
||||||
+ " ELSE 'N' END"
|
+ " ELSE 'N' END"
|
||||||
+ " FROM (SELECT NVL(SUM(SUCCESS_CNT + TIMEOUT_CNT + SYSTEM_ERR_CNT + BIZ_ERR_CNT),0) AS TOTAL"
|
+ " FROM (SELECT SUM(SUCCESS_CNT + TIMEOUT_CNT + SYSTEM_ERR_CNT + BIZ_ERR_CNT) AS TOTAL"
|
||||||
+ " , SUM(SUCCESS_CNT) AS SUCCESS"
|
+ " , SUM(SUCCESS_CNT) AS SUCCESS"
|
||||||
+ " , SUM((SUCCESS_CNT + TIMEOUT_CNT + SYSTEM_ERR_CNT + BIZ_ERR_CNT) * AVG_RESP_TIME) AS RESP_SUM"
|
+ " , SUM((SUCCESS_CNT + TIMEOUT_CNT + SYSTEM_ERR_CNT + BIZ_ERR_CNT) * AVG_RESP_TIME) AS RESP_SUM"
|
||||||
+ " FROM API_STATS_MINUTE"
|
+ " FROM API_STATS_MINUTE"
|
||||||
@@ -76,11 +73,4 @@ public interface ApiStatusRepository extends BaseRepository<ApiStatus, String> {
|
|||||||
@Param("delayRangeMinute") int delayRangeMinute,
|
@Param("delayRangeMinute") int delayRangeMinute,
|
||||||
@Param("delayAvgRespTime") int delayAvgRespTime
|
@Param("delayAvgRespTime") int delayAvgRespTime
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
|
||||||
* EAI 서비스명 조회. 장애/점검 영향 API 의 API_NAME 캐시용.
|
|
||||||
*/
|
|
||||||
@Query(nativeQuery = true, value =
|
|
||||||
" SELECT EAISVCNAME, EAISVCDESC FROM TSEAIHE01 WHERE EAISVCNAME IN (:apiIds)")
|
|
||||||
List<ApiNameRow> findApiNames(@Param("apiIds") Collection<String> apiIds);
|
|
||||||
}
|
}
|
||||||
@@ -1,115 +0,0 @@
|
|||||||
package com.eactive.eai.rms.ext.djb.common;
|
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
import org.hibernate.validator.constraints.NotEmpty;
|
|
||||||
|
|
||||||
import com.google.gson.Gson;
|
|
||||||
import com.google.gson.GsonBuilder;
|
|
||||||
|
|
||||||
import javax.validation.constraints.Max;
|
|
||||||
import javax.validation.constraints.Min;
|
|
||||||
import javax.validation.constraints.Pattern;
|
|
||||||
|
|
||||||
@Data
|
|
||||||
public class DjbProperty {
|
|
||||||
|
|
||||||
// SMS 2차 인증
|
|
||||||
|
|
||||||
@DjbPropertyValue(
|
|
||||||
key = "djb.sms_auth.enabled",
|
|
||||||
defaultValue = "true",
|
|
||||||
description = "SMS 2차 인증 사용 여부. true 시 휴대폰번호 없는 사용자는 ID/PW 로그인 불가")
|
|
||||||
private boolean smsAuthEnabled = false;
|
|
||||||
|
|
||||||
@DjbPropertyValue(
|
|
||||||
key = "djb.sms_auth.mode",
|
|
||||||
defaultValue = "real",
|
|
||||||
description = "인증번호 생성 모드: real(랜덤), hhmm00(현재시각+00), fixed(고정값)")
|
|
||||||
@Pattern(regexp = "^(real|hhmm00|fixed)$", message = "djb.sms_auth.mode는 real, hhmm00, fixed 중 하나여야 합니다.")
|
|
||||||
private String smsAuthMode = "real";
|
|
||||||
|
|
||||||
@DjbPropertyValue(
|
|
||||||
key = "djb.sms_auth.fixed_value",
|
|
||||||
defaultValue = "654321",
|
|
||||||
description = "mode가 fixed일 때 사용할 고정 인증번호 (6자리 숫자)")
|
|
||||||
@Pattern(regexp = "^\\d{6}$", message = "djb.sms_auth.fixed_value는 6자리 숫자여야 합니다.")
|
|
||||||
private String smsAuthFixedValue = "654321";
|
|
||||||
|
|
||||||
|
|
||||||
@DjbPropertyValue(
|
|
||||||
key = "djb.api.allow_ip_list",
|
|
||||||
description = "API 접근 허용 IP 목록 (콤마(,)로 구분된 IP들, 예: 192.168.0.1, 192.168.1.*,127.*.*.*",
|
|
||||||
defaultValue = "10.15.106.*, 10.14.8.*, 10.15.8.*, 192.168.240.*, 127.0.0.1, 192.168.132.*, 192.168.131.*"
|
|
||||||
)
|
|
||||||
private String apiAllowIpList = "10.15.106.*, 10.14.8.*, 10.15.8.*, 192.168.240.*, 127.0.0.1, 192.168.132.*, 192.168.131.*";
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public String toJsonString(boolean isPretty) {
|
|
||||||
return isPretty
|
|
||||||
? new GsonBuilder().setPrettyPrinting().create().toJson(this)
|
|
||||||
: new Gson().toJson(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// 기본 프로퍼티 목록을 콘솔로 출력 (가이드용)
|
|
||||||
public static void main(String[] args) {
|
|
||||||
System.out.println("| Key | Default Value | Description |");
|
|
||||||
System.out.println("|-----|---------------|-------------|");
|
|
||||||
|
|
||||||
for (java.lang.reflect.Field field : DjbProperty.class.getDeclaredFields()) {
|
|
||||||
DjbPropertyValue annotation = field.getAnnotation(DjbPropertyValue.class);
|
|
||||||
if (annotation != null) {
|
|
||||||
String key = annotation.key();
|
|
||||||
String defaultValue = annotation.defaultValue();
|
|
||||||
String description = annotation.description();
|
|
||||||
System.out.printf("| %s | %s | %s |%n", key, defaultValue, description);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,92 +0,0 @@
|
|||||||
package com.eactive.eai.rms.ext.djb.common;
|
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
|
|
||||||
import java.util.function.Function;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* DjbProperty 싱글톤 홀더.
|
|
||||||
*
|
|
||||||
* <p>모든 곳에서 {@code DjbPropertyHolder.get()}으로 현재 DjbProperty에 접근합니다.
|
|
||||||
* eapim-admin에서 초기화 시 {@code initialize()}를 호출하고,
|
|
||||||
* MonitoringContext.refresh() 시 {@code reload()}를 호출하여 값을 갱신합니다.</p>
|
|
||||||
*
|
|
||||||
* <p>사용 예시:</p>
|
|
||||||
* <pre>
|
|
||||||
* // 값 접근
|
|
||||||
* String url = DjbPropertyHolder.get().getUmsHostUrl();
|
|
||||||
*
|
|
||||||
* // 초기화 (eapim-admin에서)
|
|
||||||
* DjbPropertyInjector injector = new DjbPropertyInjector(service, "Monitoring");
|
|
||||||
* DjbPropertyHolder.initialize(injector::inject);
|
|
||||||
*
|
|
||||||
* // reload (MonitoringContext.refresh() 시)
|
|
||||||
* DjbPropertyHolder.reload();
|
|
||||||
*
|
|
||||||
* // 테스트용
|
|
||||||
* DjbPropertyHolder.setForTest(mockProperty);
|
|
||||||
* </pre>
|
|
||||||
*/
|
|
||||||
@Slf4j
|
|
||||||
public class DjbPropertyHolder {
|
|
||||||
|
|
||||||
private static volatile DjbProperty instance = new DjbProperty();
|
|
||||||
private static volatile Function<DjbProperty, DjbProperty> injector;
|
|
||||||
|
|
||||||
private DjbPropertyHolder() {
|
|
||||||
// 유틸리티 클래스
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 현재 DjbProperty 인스턴스를 반환합니다.
|
|
||||||
*/
|
|
||||||
public static DjbProperty get() {
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 초기화 함수를 등록하고 최초 로딩을 수행합니다.
|
|
||||||
* eapim-admin 부팅 시 MonitoringContextImpl.init()에서 호출됩니다.
|
|
||||||
*
|
|
||||||
* @param injectorFn DjbProperty를 DB에서 로딩하는 함수 (DjbPropertyInjector::inject)
|
|
||||||
*/
|
|
||||||
public static void initialize(Function<DjbProperty, DjbProperty> injectorFn) {
|
|
||||||
injector = injectorFn;
|
|
||||||
reload();
|
|
||||||
log.info("DjbPropertyHolder 초기화 완료");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* DB에서 프로퍼티를 다시 로딩합니다.
|
|
||||||
* MonitoringContext.refresh() 호출 시 함께 호출됩니다.
|
|
||||||
*/
|
|
||||||
public static void reload() {
|
|
||||||
if (injector != null) {
|
|
||||||
try {
|
|
||||||
instance = injector.apply(new DjbProperty());
|
|
||||||
log.info("DjbProperty reload 완료");
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.error("DjbProperty reload 실패: {}", e.getMessage(), e);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
log.debug("DjbPropertyHolder: injector가 설정되지 않음 (테스트 환경일 수 있음)");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 테스트용 - 직접 인스턴스를 설정합니다.
|
|
||||||
*/
|
|
||||||
public static void setForTest(DjbProperty testProperty) {
|
|
||||||
instance = testProperty;
|
|
||||||
log.debug("DjbProperty 테스트 인스턴스 설정");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 테스트용 - 기본값으로 초기화합니다.
|
|
||||||
*/
|
|
||||||
public static void resetForTest() {
|
|
||||||
instance = new DjbProperty();
|
|
||||||
injector = null;
|
|
||||||
log.debug("DjbPropertyHolder 테스트 초기화");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
package com.eactive.eai.rms.ext.djb.common;
|
|
||||||
|
|
||||||
import java.lang.annotation.ElementType;
|
|
||||||
import java.lang.annotation.Retention;
|
|
||||||
import java.lang.annotation.RetentionPolicy;
|
|
||||||
import java.lang.annotation.Target;
|
|
||||||
|
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
|
||||||
@Target(ElementType.FIELD)
|
|
||||||
public @interface DjbPropertyValue {
|
|
||||||
String key();
|
|
||||||
String defaultValue() default "";
|
|
||||||
String description() default "";
|
|
||||||
}
|
|
||||||
@@ -1,9 +1,8 @@
|
|||||||
package com.eactive.eai.rms.ext.djb.job;
|
package com.eactive.eai.rms.ext.djb.job;
|
||||||
|
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalTime;
|
||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
import java.time.temporal.ChronoUnit;
|
|
||||||
|
|
||||||
import javax.persistence.EntityManager;
|
import javax.persistence.EntityManager;
|
||||||
import javax.persistence.PersistenceContext;
|
import javax.persistence.PersistenceContext;
|
||||||
@@ -29,8 +28,7 @@ import com.eactive.eai.rms.data.entity.onl.kjb.statistics.QApiStatsHour;
|
|||||||
/**
|
/**
|
||||||
* API 로그 테이블을 시간별 통계로 집계하는 배치 작업
|
* API 로그 테이블을 시간별 통계로 집계하는 배치 작업
|
||||||
* TSEAILGXX → API_STATS_HOUR
|
* TSEAILGXX → API_STATS_HOUR
|
||||||
* 실행 주기: 매시 00:10, 직전 1시간(예: 14:10 실행 시 13:00~13:59:59) 데이터만 집계
|
* 실행 주기: 매시 00:10 (당일 데이터 집계)
|
||||||
* (0시대 실행 시 전날 23:00~23:59:59 집계)
|
|
||||||
*/
|
*/
|
||||||
@Component
|
@Component
|
||||||
public class ApiStatsHourlyAggregationJob implements Job {
|
public class ApiStatsHourlyAggregationJob implements Job {
|
||||||
@@ -61,9 +59,11 @@ public class ApiStatsHourlyAggregationJob implements Job {
|
|||||||
DataSourceType dataType = DataSourceTypeManager.getDataSourceType(DataSourceTypeManager.APIGW);
|
DataSourceType dataType = DataSourceTypeManager.getDataSourceType(DataSourceTypeManager.APIGW);
|
||||||
DataSourceContextHolder.setDataSourceType(dataType);
|
DataSourceContextHolder.setDataSourceType(dataType);
|
||||||
try {
|
try {
|
||||||
// 직전 1시간(예: 14:10 실행 -> 13:00~13:59:59). 0시대 실행 시 자동으로 전날 23시로 롤오버됨
|
LocalDate targetDate = LocalDate.now();
|
||||||
LocalDateTime targetHour = LocalDateTime.now().minusHours(1).truncatedTo(ChronoUnit.HOURS);
|
if (LocalTime.now().isBefore(LocalTime.of(1, 0))) {
|
||||||
selfJob.executeManual(targetHour);
|
targetDate = targetDate.minusDays(1);
|
||||||
|
}
|
||||||
|
selfJob.executeManual(targetDate);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new JobExecutionException(e);
|
throw new JobExecutionException(e);
|
||||||
} finally {
|
} finally {
|
||||||
@@ -72,38 +72,22 @@ public class ApiStatsHourlyAggregationJob implements Job {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 수동 실행 메서드 (날짜 지정, 하루 전체 재집계 - 관리자 화면 백필용)
|
* 수동 실행 메서드 (날짜 지정)
|
||||||
*/
|
*/
|
||||||
@Transactional
|
@Transactional
|
||||||
public int executeManual(LocalDate targetDate) {
|
public int executeManual(LocalDate targetDate) {
|
||||||
LocalDateTime rangeStart = targetDate.atStartOfDay();
|
|
||||||
return aggregate(rangeStart, rangeStart.plusDays(1));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 수동/스케줄 실행 메서드 (시각 지정, 해당 시각이 속한 1시간만 집계)
|
|
||||||
*/
|
|
||||||
@Transactional
|
|
||||||
public int executeManual(LocalDateTime targetHour) {
|
|
||||||
LocalDateTime rangeStart = targetHour.truncatedTo(ChronoUnit.HOURS);
|
|
||||||
return aggregate(rangeStart, rangeStart.plusHours(1));
|
|
||||||
}
|
|
||||||
|
|
||||||
private int aggregate(LocalDateTime rangeStart, LocalDateTime rangeEnd) {
|
|
||||||
long startTime = System.currentTimeMillis();
|
long startTime = System.currentTimeMillis();
|
||||||
log.info("=== 시간별 통계 집계 작업 시작 === 대상: {} ~ {}", rangeStart, rangeEnd);
|
log.info("=== 시간별 통계 집계 작업 시작 === 대상: {}", targetDate);
|
||||||
|
|
||||||
DateTimeFormatter tsFormat = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
|
String searchDate = targetDate.format(DateTimeFormatter.ofPattern("yyyyMMdd"));
|
||||||
String logTableName = CommonUtil.getLogTable(rangeStart.format(DateTimeFormatter.ofPattern("yyyyMMdd")), true);
|
String logTableName = CommonUtil.getLogTable(searchDate, true);
|
||||||
String rangeStartStr = rangeStart.format(tsFormat) + "000";
|
|
||||||
String rangeEndStr = rangeEnd.format(tsFormat) + "000";
|
|
||||||
|
|
||||||
// 1. 대상 구간(rangeStart ~ rangeEnd)의 기존 데이터만 삭제 (다른 시간대 데이터는 보존)
|
// 1. 기존 데이터 삭제
|
||||||
QApiStatsHour q = QApiStatsHour.apiStatsHour;
|
QApiStatsHour q = QApiStatsHour.apiStatsHour;
|
||||||
long deletedCount = apiStatsHourService.getJPAQueryFactory()
|
long deletedCount = apiStatsHourService.getJPAQueryFactory()
|
||||||
.delete(q)
|
.delete(q)
|
||||||
.where(q.statTime.goe(rangeStart)
|
.where(q.statTime.goe(targetDate.atStartOfDay())
|
||||||
.and(q.statTime.lt(rangeEnd)))
|
.and(q.statTime.lt(targetDate.plusDays(1).atStartOfDay())))
|
||||||
.execute();
|
.execute();
|
||||||
log.info("기존 데이터 삭제: {} 건", deletedCount);
|
log.info("기존 데이터 삭제: {} 건", deletedCount);
|
||||||
|
|
||||||
@@ -138,23 +122,18 @@ public class ApiStatsHourlyAggregationJob implements Job {
|
|||||||
" , MAX(CASE WHEN LOGPRCSSSERNO = '100' THEN GSTATSYSADPTRBZWKGROUPNAME ELSE '' END) AS INBOUND_ADAPTER" +
|
" , MAX(CASE WHEN LOGPRCSSSERNO = '100' THEN GSTATSYSADPTRBZWKGROUPNAME ELSE '' END) AS INBOUND_ADAPTER" +
|
||||||
" , MAX(CASE WHEN LOGPRCSSSERNO = '100' THEN PSVSYSADPTRBZWKGROUPNAME ELSE '' END) AS OUTBOUND_ADAPTER" +
|
" , MAX(CASE WHEN LOGPRCSSSERNO = '100' THEN PSVSYSADPTRBZWKGROUPNAME ELSE '' END) AS OUTBOUND_ADAPTER" +
|
||||||
" , MIN(MSGDPSTYMS) AS DT" +
|
" , MIN(MSGDPSTYMS) AS DT" +
|
||||||
" , ( (TO_DATE(SUBSTR(MAX(MSGPRCSSYMS),1,14),'YYYYMMDDHH24MISS')" +
|
" , MAX(MSGPRCSSYMS) - MIN(MSGDPSTYMS) AS RESP_TIME" +
|
||||||
" - TO_DATE(SUBSTR(MIN(MSGPRCSSYMS),1,14),'YYYYMMDDHH24MISS')) * 86400000" +
|
|
||||||
" + TO_NUMBER(SUBSTR(MAX(MSGPRCSSYMS),15,3))" +
|
|
||||||
" - TO_NUMBER(SUBSTR(MIN(MSGPRCSSYMS),15,3))" +
|
|
||||||
" ) AS RESP_TIME" +
|
|
||||||
" , MAX(CASE WHEN LOGPRCSSSERNO = '400' THEN LOGPRCSSSERNO ELSE '' END) AS E400" +
|
" , MAX(CASE WHEN LOGPRCSSSERNO = '400' THEN LOGPRCSSSERNO ELSE '' END) AS E400" +
|
||||||
" , MAX(EAIERRCD) AS ERROR_CODE" +
|
" , MAX(EAIERRCD) AS ERROR_CODE" +
|
||||||
" FROM " + logTableName +
|
" FROM " + logTableName +
|
||||||
" WHERE MSGDPSTYMS >= :rangeStart AND MSGDPSTYMS < :rangeEnd" +
|
" WHERE MSGDPSTYMS LIKE :searchDate || '%'" +
|
||||||
" GROUP BY EAISVCSERNO" +
|
" GROUP BY EAISVCSERNO" +
|
||||||
" ) A LEFT OUTER JOIN TSEAICM20 B ON A.ERROR_CODE = B.CODE AND B.CODEGROUP = 'ERRCODE_TIMEOUT' AND B.USEYN = 'Y' " +
|
" ) A LEFT OUTER JOIN TSEAICM20 B ON A.ERROR_CODE = B.CODE AND B.CODEGROUP = 'ERRCODE_TIMEOUT' AND B.USEYN = 'Y' " +
|
||||||
" GROUP BY SUBSTR(DT,1,10), API_NAME, GW_INSTANCE_ID, BIZ_DIV_CODE, NVL(A.CLIENT_ID, 'NONE')" +
|
" GROUP BY SUBSTR(DT,1,10), API_NAME, GW_INSTANCE_ID, BIZ_DIV_CODE, NVL(A.CLIENT_ID, 'NONE')" +
|
||||||
" , INBOUND_ADAPTER, OUTBOUND_ADAPTER";
|
" , INBOUND_ADAPTER, OUTBOUND_ADAPTER";
|
||||||
|
|
||||||
int savedCount = entityManager.createNativeQuery(sql)
|
int savedCount = entityManager.createNativeQuery(sql)
|
||||||
.setParameter("rangeStart", rangeStartStr)
|
.setParameter("searchDate", searchDate)
|
||||||
.setParameter("rangeEnd", rangeEndStr)
|
|
||||||
.executeUpdate();
|
.executeUpdate();
|
||||||
|
|
||||||
long elapsedTime = System.currentTimeMillis() - startTime;
|
long elapsedTime = System.currentTimeMillis() - startTime;
|
||||||
|
|||||||
@@ -61,12 +61,13 @@ public class ApiUseStatsService
|
|||||||
|
|
||||||
|
|
||||||
private Query getDataQuery(ApiStatsSearch search) {
|
private Query getDataQuery(ApiStatsSearch search) {
|
||||||
|
|
||||||
StringBuilder where = buildNativeWhere(search);
|
StringBuilder where = buildNativeWhere(search);
|
||||||
|
|
||||||
String dataSql = "";
|
String dataSql = "";
|
||||||
if ("ORG".equals(search.getSearchType())) {
|
if ("ORG".equals(search.getSearchType())) {
|
||||||
dataSql = "SELECT COUNT(*) OVER () AS RECORDS "
|
dataSql = "SELECT COUNT(*) OVER () AS RECORDS "
|
||||||
+ ", NVL(B.ORGNAME, D.ORG_NAME) AS ORGNAME"
|
+ ", B.ORGNAME"
|
||||||
+ ", SUM(A.TOTAL_CNT)"
|
+ ", SUM(A.TOTAL_CNT)"
|
||||||
+ ", SUM(A.SUCCESS_CNT)"
|
+ ", SUM(A.SUCCESS_CNT)"
|
||||||
+ ", CASE WHEN SUM(A.TOTAL_CNT) > 0 THEN SUM(A.SUCCESS_CNT) * 100 / SUM(A.TOTAL_CNT) ELSE NULL END"
|
+ ", CASE WHEN SUM(A.TOTAL_CNT) > 0 THEN SUM(A.SUCCESS_CNT) * 100 / SUM(A.TOTAL_CNT) ELSE NULL END"
|
||||||
@@ -76,41 +77,13 @@ public class ApiUseStatsService
|
|||||||
+ ", CASE WHEN SUM(A.TOTAL_CNT) > 0 THEN TRUNC(SUM(A.TOTAL_CNT * A.AVG_RESP_TIME) / SUM(A.TOTAL_CNT)) ELSE NULL END"
|
+ ", CASE WHEN SUM(A.TOTAL_CNT) > 0 THEN TRUNC(SUM(A.TOTAL_CNT * A.AVG_RESP_TIME) / SUM(A.TOTAL_CNT)) ELSE NULL END"
|
||||||
+ ", MIN(A.MIN_RESP_TIME)"
|
+ ", MIN(A.MIN_RESP_TIME)"
|
||||||
+ ", MAX(A.MAX_RESP_TIME)"
|
+ ", MAX(A.MAX_RESP_TIME)"
|
||||||
+ " FROM ("
|
+ " FROM API_STATS_DAY A"
|
||||||
+ " SELECT A.STAT_TIME "
|
|
||||||
+ " , A.API_NAME"
|
|
||||||
+ " , A.CLIENT_ID "
|
|
||||||
+ " , A.TOTAL_CNT"
|
|
||||||
+ " , A.SUCCESS_CNT"
|
|
||||||
+ " , A.AVG_RESP_TIME"
|
|
||||||
+ " , A.TIMEOUT_CNT"
|
|
||||||
+ " , A.SYSTEM_ERR_CNT"
|
|
||||||
+ " , A.MIN_RESP_TIME"
|
|
||||||
+ " , A.MAX_RESP_TIME"
|
|
||||||
+ " , CASE WHEN API_NAME LIKE '%1' THEN SUBSTR(INBOUND_ADAPTER,2,3) ELSE SUBSTR(OUTBOUND_ADAPTER,2,3) END AS ORG_CODE"
|
|
||||||
+ " FROM API_STATS_DAY A "
|
|
||||||
+ " WHERE A.STAT_TIME >= :searchStartDate "
|
|
||||||
+ " AND A.STAT_TIME <= :searchEndDate ";
|
|
||||||
|
|
||||||
|
|
||||||
if (StringUtils.isNotBlank(search.getSearchApiName())) {
|
|
||||||
dataSql += " AND A.API_NAME LIKE :searchApiName ";
|
|
||||||
}
|
|
||||||
|
|
||||||
dataSql += " ) A"
|
|
||||||
+ " LEFT OUTER JOIN TSEAIAU01 B ON A.CLIENT_ID = B.CLIENTID"
|
+ " LEFT OUTER JOIN TSEAIAU01 B ON A.CLIENT_ID = B.CLIENTID"
|
||||||
+ " LEFT OUTER JOIN EMSAPP.PTL_ORG D ON A.ORG_CODE = D.ORG_CODE"
|
+ " LEFT OUTER JOIN TSEAIHE01 C ON A.API_NAME = C.EAISVCNAME "
|
||||||
+ " WHERE 1=1 ";
|
+ where
|
||||||
|
+ " GROUP BY B.ORGNAME"
|
||||||
if (StringUtils.isNotBlank(search.getSearchOrgName())) {
|
+ " ORDER BY ORGNAME";
|
||||||
dataSql += " AND UPPER(NVL(B.ORGNAME, D.ORG_NAME)) LIKE :searchOrgName ";
|
} else if ("API".equals(search.getSearchType())) {
|
||||||
}
|
|
||||||
|
|
||||||
dataSql += " GROUP BY NVL(B.ORGNAME, D.ORG_NAME)"
|
|
||||||
+ " ORDER BY ORGNAME";
|
|
||||||
|
|
||||||
}
|
|
||||||
else if ("API".equals(search.getSearchType())) {
|
|
||||||
dataSql = "SELECT COUNT(*) OVER () AS RECORDS "
|
dataSql = "SELECT COUNT(*) OVER () AS RECORDS "
|
||||||
+ ", A.API_NAME"
|
+ ", A.API_NAME"
|
||||||
+ ", SUM(A.TOTAL_CNT)"
|
+ ", SUM(A.TOTAL_CNT)"
|
||||||
@@ -124,11 +97,11 @@ public class ApiUseStatsService
|
|||||||
+ ", MAX(A.MAX_RESP_TIME)"
|
+ ", MAX(A.MAX_RESP_TIME)"
|
||||||
+ " FROM API_STATS_DAY A "
|
+ " FROM API_STATS_DAY A "
|
||||||
+ " LEFT OUTER JOIN TSEAIAU01 B ON A.CLIENT_ID = B.CLIENTID"
|
+ " LEFT OUTER JOIN TSEAIAU01 B ON A.CLIENT_ID = B.CLIENTID"
|
||||||
|
+ " LEFT OUTER JOIN TSEAIHE01 C ON A.API_NAME = C.EAISVCNAME "
|
||||||
+ where
|
+ where
|
||||||
+ " GROUP BY A.API_NAME"
|
+ " GROUP BY A.API_NAME"
|
||||||
+ " ORDER BY API_NAME";
|
+ " ORDER BY API_NAME";
|
||||||
}
|
} else if ("DATE".equals(search.getSearchType())) {
|
||||||
else if ("DATE".equals(search.getSearchType())) {
|
|
||||||
dataSql = "SELECT COUNT(*) OVER () AS RECORDS "
|
dataSql = "SELECT COUNT(*) OVER () AS RECORDS "
|
||||||
+ ", TO_CHAR(STAT_TIME,'YYYY-MM-DD') AS STAT_TIME "
|
+ ", TO_CHAR(STAT_TIME,'YYYY-MM-DD') AS STAT_TIME "
|
||||||
+ ", SUM(A.TOTAL_CNT)"
|
+ ", SUM(A.TOTAL_CNT)"
|
||||||
@@ -142,24 +115,23 @@ public class ApiUseStatsService
|
|||||||
+ ", MAX(A.MAX_RESP_TIME)"
|
+ ", MAX(A.MAX_RESP_TIME)"
|
||||||
+ " FROM API_STATS_DAY A "
|
+ " FROM API_STATS_DAY A "
|
||||||
+ " LEFT OUTER JOIN TSEAIAU01 B ON A.CLIENT_ID = B.CLIENTID"
|
+ " LEFT OUTER JOIN TSEAIAU01 B ON A.CLIENT_ID = B.CLIENTID"
|
||||||
|
+ " LEFT OUTER JOIN TSEAIHE01 C ON A.API_NAME = C.EAISVCNAME "
|
||||||
+ where
|
+ where
|
||||||
+ " GROUP BY TO_CHAR(STAT_TIME,'YYYY-MM-DD')"
|
+ " GROUP BY TO_CHAR(STAT_TIME,'YYYY-MM-DD')"
|
||||||
+ " ORDER BY STAT_TIME";
|
+ " ORDER BY STAT_TIME";
|
||||||
}
|
}
|
||||||
|
|
||||||
Query dataQuery = entityManager.createNativeQuery(dataSql);
|
Query dataQuery = entityManager.createNativeQuery(dataSql);
|
||||||
|
if (search.getParsedStartDate() != null) {
|
||||||
|
dataQuery.setParameter("searchStartDate", search.getParsedStartDate());
|
||||||
dataQuery.setParameter("searchStartDate", search.getParsedStartDate());
|
dataQuery.setParameter("searchEndDate", search.getParsedEndDate());
|
||||||
dataQuery.setParameter("searchEndDate", search.getParsedEndDate());
|
|
||||||
|
|
||||||
if (StringUtils.isNotBlank(search.getSearchApiName())) {
|
|
||||||
dataQuery.setParameter("searchApiName", "%" + search.getSearchApiName().toUpperCase() + "%");
|
|
||||||
}
|
}
|
||||||
if (StringUtils.isNotBlank(search.getSearchOrgName())) {
|
if (StringUtils.isNotBlank(search.getSearchOrgName())) {
|
||||||
dataQuery.setParameter("searchOrgName", "%" + search.getSearchOrgName().toUpperCase() + "%");
|
dataQuery.setParameter("searchOrgName", "%" + search.getSearchOrgName().toUpperCase() + "%");
|
||||||
}
|
}
|
||||||
|
if (StringUtils.isNotBlank(search.getSearchApiName())) {
|
||||||
|
dataQuery.setParameter("searchApiName", "%" + search.getSearchApiName().toUpperCase() + "%");
|
||||||
|
}
|
||||||
return dataQuery;
|
return dataQuery;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -191,17 +163,19 @@ public class ApiUseStatsService
|
|||||||
|
|
||||||
calculateDateRange(search);
|
calculateDateRange(search);
|
||||||
|
|
||||||
where.append(" AND A.STAT_TIME >= :searchStartDate ");
|
if (search.getParsedStartDate() != null) {
|
||||||
where.append(" AND A.STAT_TIME <= :searchEndDate ");
|
where.append(" AND A.STAT_TIME >= :searchStartDate ");
|
||||||
|
where.append(" AND A.STAT_TIME <= :searchEndDate ");
|
||||||
if (StringUtils.isNotBlank(search.getSearchApiName())) {
|
|
||||||
where.append(" AND A.API_NAME LIKE :searchApiName ");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (StringUtils.isNotBlank(search.getSearchOrgName())) {
|
if (StringUtils.isNotBlank(search.getSearchOrgName())) {
|
||||||
where.append(" AND UPPER(B.ORGNAME) LIKE :searchOrgName ");
|
where.append(" AND UPPER(B.ORGNAME) LIKE :searchOrgName ");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (StringUtils.isNotBlank(search.getSearchApiName())) {
|
||||||
|
where.append(" AND UPPER(C.EAISVCDESC) LIKE :searchApiName ");
|
||||||
|
}
|
||||||
|
|
||||||
return where;
|
return where;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import java.net.URL;
|
|||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.time.LocalDate;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -19,10 +18,8 @@ import org.springframework.stereotype.Service;
|
|||||||
import com.eactive.apim.portal.common.util.PhoneNumberUtil;
|
import com.eactive.apim.portal.common.util.PhoneNumberUtil;
|
||||||
import com.eactive.apim.portal.portalproperty.service.PortalPropertyService;
|
import com.eactive.apim.portal.portalproperty.service.PortalPropertyService;
|
||||||
import com.eactive.apim.portal.template.entity.MessageRequest;
|
import com.eactive.apim.portal.template.entity.MessageRequest;
|
||||||
import com.eactive.eai.common.util.StringUtil;
|
|
||||||
import com.eactive.eai.common.util.SystemUtil;
|
import com.eactive.eai.common.util.SystemUtil;
|
||||||
import com.eactive.eai.rms.data.entity.man.monitoringProperty.service.MonitoringPropertyService;
|
import com.eactive.eai.rms.data.entity.man.monitoringProperty.service.MonitoringPropertyService;
|
||||||
import com.eactive.eai.rms.data.entity.onl.apim.messagerequest.MessageRequestService;
|
|
||||||
import com.eactive.eai.rms.ext.djb.ums.vo.EmailDataVO;
|
import com.eactive.eai.rms.ext.djb.ums.vo.EmailDataVO;
|
||||||
import com.eactive.eai.rms.ext.djb.ums.vo.SmsDataVO;
|
import com.eactive.eai.rms.ext.djb.ums.vo.SmsDataVO;
|
||||||
import com.eactive.eai.rms.ext.djb.ums.vo.StdHeadVO;
|
import com.eactive.eai.rms.ext.djb.ums.vo.StdHeadVO;
|
||||||
@@ -42,15 +39,12 @@ public class UmsService {
|
|||||||
private static final String SYS_DVCD = "API"; //시스템코드
|
private static final String SYS_DVCD = "API"; //시스템코드
|
||||||
private static final String BZWK_DVCD = "99"; //업무구분코드
|
private static final String BZWK_DVCD = "99"; //업무구분코드
|
||||||
private static final String SUB_BZWK_CD = "9019"; //세부구분코드
|
private static final String SUB_BZWK_CD = "9019"; //세부구분코드
|
||||||
private static final String TEAM_DVCD = "00"; //팀구분코드 TODO: 변경해야함
|
|
||||||
|
|
||||||
|
|
||||||
private final MonitoringPropertyService propertyService;
|
private final MonitoringPropertyService propertyService;
|
||||||
private final MessageRequestService messageRequestService;
|
|
||||||
|
|
||||||
public UmsService(MonitoringPropertyService propertyService, MessageRequestService messageRequestService) {
|
public UmsService(MonitoringPropertyService propertyService) {
|
||||||
this.propertyService = propertyService;
|
this.propertyService = propertyService;
|
||||||
this.messageRequestService = messageRequestService;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean send(MessageRequest messageRequest) throws Exception {
|
public boolean send(MessageRequest messageRequest) throws Exception {
|
||||||
@@ -174,16 +168,17 @@ public class UmsService {
|
|||||||
|
|
||||||
// 스윙챗 알림 데이터
|
// 스윙챗 알림 데이터
|
||||||
SwingDataVO swiDataVO = new SwingDataVO();
|
SwingDataVO swiDataVO = new SwingDataVO();
|
||||||
swiDataVO.setNotiCode("CNO_FEP_APIM_0002"); //0001:긴급, 0002:중요
|
swiDataVO.setNotiCode("CNO_FEP_HMP_0002");
|
||||||
swiDataVO.setNotiTitle("알림");
|
swiDataVO.setNotiTitle("알림");
|
||||||
swiDataVO.setNotiContent(messageRequest.getMessage());
|
swiDataVO.setNotiContent(messageRequest.getMessage());
|
||||||
|
|
||||||
// 스윙챗 알림 데이터
|
// 스윙챗 알림 데이터
|
||||||
List<SwingReceiverDataVO> swiReDataVOList = new ArrayList<SwingReceiverDataVO>();
|
List<SwingReceiverDataVO> swiReDataVOList = new ArrayList<SwingReceiverDataVO>();
|
||||||
SwingReceiverDataVO receiver = new SwingReceiverDataVO();
|
SwingReceiverDataVO receiver = new SwingReceiverDataVO();
|
||||||
receiver.setType("PSN");
|
receiver.setType("");
|
||||||
receiver.setCompanyCode("CB");
|
receiver.setCompanyCode("CB");
|
||||||
receiver.setCode(messageRequest.getMessengerId());
|
receiver.setCode(messageRequest.getMessengerId());
|
||||||
|
receiver.setPrdNm("");
|
||||||
swiReDataVOList.add(receiver);
|
swiReDataVOList.add(receiver);
|
||||||
|
|
||||||
swiDataVO.setReceiverInfo(swiReDataVOList);
|
swiDataVO.setReceiverInfo(swiReDataVOList);
|
||||||
@@ -205,27 +200,22 @@ public class UmsService {
|
|||||||
// Sms 데이터
|
// Sms 데이터
|
||||||
SmsDataVO dataVO = new SmsDataVO();
|
SmsDataVO dataVO = new SmsDataVO();
|
||||||
dataVO.setSnd_dman_dt(jsonData[0]);
|
dataVO.setSnd_dman_dt(jsonData[0]);
|
||||||
dataVO.setSnd_dman_id(this.getSndDmanId(messageRequest.getMessageType()));
|
dataVO.setSnd_dman_id("");
|
||||||
dataVO.setSnd_dvcd("EAI");
|
dataVO.setSnd_dvcd("EAI");
|
||||||
dataVO.setMsg_titl(StringUtil.chunkString(messageRequest.getSubject(), 40, false)); // MS949 40byte 초과시 절단
|
dataVO.setMsg_titl("");
|
||||||
dataVO.setMsg_ctnt(messageRequest.getMessage());
|
dataVO.setMsg_ctnt(messageRequest.getMessage());
|
||||||
dataVO.setRecvr_no(PhoneNumberUtil.digitsOnly(messageRequest.getPhone()));
|
dataVO.setRecvr_no(PhoneNumberUtil.digitsOnly(messageRequest.getPhone()));
|
||||||
dataVO.setSndn_no("15880079"); //발신번호
|
dataVO.setSndn_no("15880079"); //발신번호
|
||||||
dataVO.setSnd_empno("APIM"); //요청자
|
dataVO.setSnd_empno(""); //요청자
|
||||||
dataVO.setSnd_emp_dprm_cd("132"); //요청부서코드
|
dataVO.setSnd_emp_dprm_cd(""); //요청부서코드
|
||||||
if ("SMS".equals(messageRequest.getMessageType())) {
|
if ("SMS".equals(messageRequest.getMessageType())) {
|
||||||
dataVO.setLtrs_snd_tycd("1"); // 1:SMS 2:LMS/MMS
|
dataVO.setLtrs_snd_tycd("1"); // 1:SMS 2:LMS/MMS
|
||||||
dataVO.setMsg_snd_tycd("0000"); // 알림톡 메세지 종류 0000:SMS, 1000:LMS, 1008:알림톡, ....
|
dataVO.setMsg_snd_tycd("0000"); // 알림톡 메세지 종류 0000:SMS, 1000:LMS, 1008:알림톡, ....
|
||||||
} else if ("KAKAO".equals(messageRequest.getMessageType())) {
|
} else if ("KAKAO".equals(messageRequest.getMessageType())) {
|
||||||
dataVO.setLtrs_snd_tycd("2"); // 1:SMS 2:LMS/MMS
|
dataVO.setLtrs_snd_tycd("2"); // 1:SMS 2:LMS/MMS
|
||||||
dataVO.setMsg_snd_tycd("1008"); // 알림톡 메세지 종류 0000:SMS, 1000:LMS, 1008:알림톡, ....
|
dataVO.setMsg_snd_tycd("1008"); // 알림톡 메세지 종류 0000:SMS, 1000:LMS, 1008:알림톡, ....
|
||||||
//ADMIN으로 시작하면 제주은행 임직원, 나머지는 제주은행 고객용
|
dataVO.setAlmtk_snd_prfl_key(""); // 플러스친구아이디
|
||||||
if (messageRequest.getMessageCode().name().startsWith("ADMIN")) {
|
dataVO.setAlmtk_tmplt_cd(""); // 템플릿코드
|
||||||
dataVO.setAlmtk_snd_prfl_key(propertyService.getPropertyValue("Monitoring", "djb.ums.sms.almtk_snd_prtl_key.intra", "")); // 제주은행 임직원 플러스친구아이디
|
|
||||||
} else {
|
|
||||||
dataVO.setAlmtk_snd_prfl_key(propertyService.getPropertyValue("Monitoring", "djb.ums.sms.almtk_snd_prtl_key.public", "")); // 제주은행 플러스친구아이디
|
|
||||||
}
|
|
||||||
dataVO.setAlmtk_tmplt_cd(BZWK_DVCD + TEAM_DVCD + SUB_BZWK_CD + "001"); // 템플릿코드
|
|
||||||
dataVO.setAlmtk_err_ltrs_snd_yn("Y"); // 오류자문자발송여부
|
dataVO.setAlmtk_err_ltrs_snd_yn("Y"); // 오류자문자발송여부
|
||||||
dataVO.setAlmtk_btn_trnm_tycd("2"); // 카카오버튼전송방식 1-format string 2-JSON 3-XML
|
dataVO.setAlmtk_btn_trnm_tycd("2"); // 카카오버튼전송방식 1-format string 2-JSON 3-XML
|
||||||
}
|
}
|
||||||
@@ -253,26 +243,26 @@ public class UmsService {
|
|||||||
|
|
||||||
// 이메일 데이터
|
// 이메일 데이터
|
||||||
EmailDataVO dataVO = new EmailDataVO();
|
EmailDataVO dataVO = new EmailDataVO();
|
||||||
dataVO.setUms_evnt_id(""); // TODO: UMS이벤트ID(필수)
|
dataVO.setUms_evnt_id(""); // UMS이벤트ID(필수)
|
||||||
dataVO.setSnd_dman_dt(jsonData[0]);
|
dataVO.setSnd_dman_dt(jsonData[0]);
|
||||||
dataVO.setSnd_dman_id("");
|
dataVO.setSnd_dman_id("");
|
||||||
dataVO.setSnd_dman_sno("1");
|
dataVO.setSnd_dman_sno("1");
|
||||||
dataVO.setEmail_titl(messageRequest.getSubject()); // 메일제목
|
dataVO.setEmail_titl(messageRequest.getSubject()); // 메일제목
|
||||||
dataVO.setCstno(""); // TODO: 고객번호(필수)
|
dataVO.setCstno(""); // 고객번호(필수)
|
||||||
dataVO.setSendr_emaddr(""); // TODO: 발신자 이메일
|
dataVO.setSendr_emaddr(""); // 발신자 이메일
|
||||||
dataVO.setSendr_nm(""); // TODO: 발신자 이름
|
dataVO.setSendr_nm(""); // 발신자 이름
|
||||||
dataVO.setSndbk_emaddr(""); // TODO: 리턴 이메일
|
dataVO.setSndbk_emaddr(""); // 리턴 이메일
|
||||||
dataVO.setRecvr_emaddr(messageRequest.getEmail()); // 수신자 이메일(필수)
|
dataVO.setRecvr_emaddr(messageRequest.getEmail()); // 수신자 이메일(필수)
|
||||||
dataVO.setRecvr_nm(messageRequest.getUsername()); // 수신자명(필수)
|
dataVO.setRecvr_nm(messageRequest.getUsername()); // 수신자명(필수)
|
||||||
dataVO.setTmplt_mpng_info(messageRequest.getMessage()); // 탬플릿매핑정보
|
dataVO.setTmplt_mpng_info(messageRequest.getMessage()); // 탬플릿매핑정보
|
||||||
dataVO.setTmplt_mpng_rptt_info(""); // 탬플릿매핑반복정보
|
dataVO.setTmplt_mpng_rptt_info(""); // 탬플릿매핑반복정보
|
||||||
dataVO.setSnd_empno("APIM"); // 발신자ID(필수)
|
dataVO.setSnd_empno(""); // 발신자ID(필수)
|
||||||
dataVO.setSnd_emp_dprm_cd("132"); // 발신자부서코드(필수)
|
dataVO.setSnd_emp_dprm_cd(""); // 발신자부서코드(필수)
|
||||||
dataVO.setBzwk_cd(BZWK_DVCD); // 업무구분코드
|
dataVO.setBzwk_cd(BZWK_DVCD); // 업무구분코드
|
||||||
dataVO.setSub_bzwk_cd(SUB_BZWK_CD); // 업무세부코드
|
dataVO.setSub_bzwk_cd(SUB_BZWK_CD); // 업무세부코드
|
||||||
dataVO.setSys_dvcd(SYS_DVCD);
|
dataVO.setSys_dvcd(SYS_DVCD);
|
||||||
dataVO.setTx_dt(jsonData[0]); // 거래일자
|
dataVO.setTx_dt(jsonData[0]); //거래일자
|
||||||
dataVO.setTx_tktm(jsonData[1].substring(0,6)); // 거래시간
|
dataVO.setTx_tktm(jsonData[1].substring(0,6)); //거래시간
|
||||||
|
|
||||||
List<EmailDataVO> dataList = new ArrayList<EmailDataVO>();
|
List<EmailDataVO> dataList = new ArrayList<EmailDataVO>();
|
||||||
dataList.add(dataVO);
|
dataList.add(dataVO);
|
||||||
@@ -344,24 +334,4 @@ public class UmsService {
|
|||||||
}
|
}
|
||||||
return buffer.toString();
|
return buffer.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getSndDmanId(String messageType) {
|
|
||||||
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
|
|
||||||
String yyyymmdd = sdf.format(new Date());
|
|
||||||
|
|
||||||
String uuid = SYS_DVCD + BZWK_DVCD + SUB_BZWK_CD + yyyymmdd + "O" ;
|
|
||||||
|
|
||||||
if ("SMS".equals(messageType)) {
|
|
||||||
uuid += "S";
|
|
||||||
} else if ("LMS".equals(messageType)) {
|
|
||||||
uuid += "L";
|
|
||||||
} else if ("KAKAO".equals(messageType)) {
|
|
||||||
uuid += "K";
|
|
||||||
}
|
|
||||||
|
|
||||||
long seq = messageRequestService.countSmsKakaoRequestsOnDate(LocalDate.now()) + 1;
|
|
||||||
uuid += String.format("%014d", seq);
|
|
||||||
|
|
||||||
return uuid;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
-65
@@ -1,65 +0,0 @@
|
|||||||
package com.eactive.eai.rms.ext.djb.webhook.controller;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import org.springframework.data.domain.Page;
|
|
||||||
import org.springframework.data.domain.Pageable;
|
|
||||||
import org.springframework.data.domain.Sort;
|
|
||||||
import org.springframework.data.web.SortDefault;
|
|
||||||
import org.springframework.http.ResponseEntity;
|
|
||||||
import org.springframework.stereotype.Controller;
|
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
|
||||||
|
|
||||||
import com.eactive.eai.rms.common.base.BaseAnnotationController;
|
|
||||||
import com.eactive.eai.rms.common.combo.ComboService;
|
|
||||||
import com.eactive.eai.rms.common.vo.GridResponse;
|
|
||||||
import com.eactive.eai.rms.ext.djb.webhook.dto.WebhookSendLogUI;
|
|
||||||
import com.eactive.eai.rms.ext.djb.webhook.dto.WebhookSendLogUISearch;
|
|
||||||
import com.eactive.eai.rms.ext.djb.webhook.service.WebhookSendLogManService;
|
|
||||||
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 웹훅 발송 로그(PTL_WEBHOOK_SEND_LOG) 조회 화면 컨트롤러.
|
|
||||||
*
|
|
||||||
* <p>조회 전용 (등록/수정/삭제 없음)</p>
|
|
||||||
*/
|
|
||||||
@Controller
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
public class WebhookSendLogManController extends BaseAnnotationController {
|
|
||||||
|
|
||||||
private final WebhookSendLogManService webhookSendLogManService;
|
|
||||||
private final ComboService comboService;
|
|
||||||
|
|
||||||
@GetMapping(value = "/onl/apim/webhook/webhookSendLogMan.view")
|
|
||||||
public void view() {
|
|
||||||
// 목록 view
|
|
||||||
}
|
|
||||||
|
|
||||||
@GetMapping(value = "/onl/apim/webhook/webhookSendLogMan.view", params = "cmd=DETAIL")
|
|
||||||
public String detailView() {
|
|
||||||
return "/onl/apim/webhook/webhookSendLogManDetail";
|
|
||||||
}
|
|
||||||
|
|
||||||
@PostMapping(value = "/onl/apim/webhook/webhookSendLogMan.json", params = "cmd=LIST")
|
|
||||||
public ResponseEntity<GridResponse<WebhookSendLogUI>> selectList(
|
|
||||||
@SortDefault(sort = "createdAt", direction = Sort.Direction.DESC) Pageable pageable,
|
|
||||||
WebhookSendLogUISearch search) {
|
|
||||||
Page<WebhookSendLogUI> page = webhookSendLogManService.selectList(pageable, search);
|
|
||||||
return ResponseEntity.ok(new GridResponse<>(page));
|
|
||||||
}
|
|
||||||
|
|
||||||
@PostMapping(value = "/onl/apim/webhook/webhookSendLogMan.json", params = "cmd=LIST_INIT_COMBO")
|
|
||||||
public ResponseEntity<Map<String, Object>> initCombo() {
|
|
||||||
Map<String, Object> resultMap = new HashMap<>();
|
|
||||||
resultMap.put("eventTypeList", comboService.getMonitoringCodeSortedBySeq(WebhookSendLogManService.CODE_GROUP_EVENT_TYPE));
|
|
||||||
return ResponseEntity.ok(resultMap);
|
|
||||||
}
|
|
||||||
|
|
||||||
@PostMapping(value = "/onl/apim/webhook/webhookSendLogMan.json", params = "cmd=DETAIL")
|
|
||||||
public ResponseEntity<WebhookSendLogUI> selectDetail(Long id) {
|
|
||||||
return ResponseEntity.ok(webhookSendLogManService.selectDetail(id));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
package com.eactive.eai.rms.ext.djb.webhook.dto;
|
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 웹훅 발송 로그(PTL_WEBHOOK_SEND_LOG) 화면 응답 DTO.
|
|
||||||
*
|
|
||||||
* <p>목록에서는 일부 항목만 사용하고, 상세에서는 전체 항목을 사용한다.
|
|
||||||
* 날짜는 yyyyMMdd HH:mm:ss 형식 문자열로 제공한다.</p>
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
public class WebhookSendLogUI {
|
|
||||||
|
|
||||||
private Long id;
|
|
||||||
|
|
||||||
private String orgId;
|
|
||||||
private String orgName;
|
|
||||||
|
|
||||||
private String eventType;
|
|
||||||
/** 모니터링 공통코드(EVENT_TYPE) 코드명. 상세 조회에서만 채워진다(목록은 화면에서 콤보로 변환). */
|
|
||||||
private String eventTypeName;
|
|
||||||
private String targetUrl;
|
|
||||||
private String proxyUrl;
|
|
||||||
|
|
||||||
private String payload;
|
|
||||||
private String signature;
|
|
||||||
|
|
||||||
private Integer statusCode;
|
|
||||||
private String responseBody;
|
|
||||||
|
|
||||||
private Boolean success;
|
|
||||||
private String sendBy;
|
|
||||||
private String errorMessage;
|
|
||||||
private Integer retryCount;
|
|
||||||
|
|
||||||
/* 등록/발송 시간 — yyyyMMdd HH:mm:ss */
|
|
||||||
private String createdAt;
|
|
||||||
private String sentAt;
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
package com.eactive.eai.rms.ext.djb.webhook.dto;
|
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
@Data
|
|
||||||
public class WebhookSendLogUISearch {
|
|
||||||
|
|
||||||
/* 기관명 (ptl_org.org_name, LIKE 검색) */
|
|
||||||
private String searchOrgName;
|
|
||||||
|
|
||||||
/* 이벤트 유형 (모니터링 공통코드 EVENT_TYPE, 콤보 선택 값과 정확히 일치) */
|
|
||||||
private String searchEventType;
|
|
||||||
|
|
||||||
/* 수신 URL (LIKE 검색) */
|
|
||||||
private String searchTargetUrl;
|
|
||||||
|
|
||||||
/* 성공여부 (Y/N, 공백=전체) */
|
|
||||||
private String searchSuccess;
|
|
||||||
|
|
||||||
/* 발송일시 기간 검색 (yyyyMMdd, 8자리 — userLoginHistoryMan.jsp 기간검색 스타일) */
|
|
||||||
private String searchStartDate;
|
|
||||||
private String searchEndDate;
|
|
||||||
}
|
|
||||||
@@ -8,9 +8,7 @@ import lombok.Setter;
|
|||||||
public class WebhookSendRequest {
|
public class WebhookSendRequest {
|
||||||
private String orgId;
|
private String orgId;
|
||||||
private String targetUrl;
|
private String targetUrl;
|
||||||
private String proxyUrl;
|
|
||||||
private String eventType;
|
private String eventType;
|
||||||
private String secret;
|
private String secret;
|
||||||
private String reverseProxyPath;
|
|
||||||
private Object data;
|
private Object data;
|
||||||
}
|
}
|
||||||
+2
-2
@@ -4,17 +4,17 @@ package com.eactive.eai.rms.ext.djb.webhook.repository;
|
|||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
import org.springframework.data.jpa.repository.Query;
|
import org.springframework.data.jpa.repository.Query;
|
||||||
import org.springframework.data.repository.query.Param;
|
import org.springframework.data.repository.query.Param;
|
||||||
import org.springframework.stereotype.Repository;
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
import com.eactive.eai.data.jpa.BaseRepository;
|
|
||||||
import com.eactive.eai.rms.data.EMSDataSource;
|
import com.eactive.eai.rms.data.EMSDataSource;
|
||||||
import com.eactive.eai.rms.data.entity.onl.djb.webhook.WebhookSendLog;
|
import com.eactive.eai.rms.data.entity.onl.djb.webhook.WebhookSendLog;
|
||||||
|
|
||||||
@Repository
|
@Repository
|
||||||
@EMSDataSource
|
@EMSDataSource
|
||||||
public interface WebhookSendLogRepository extends BaseRepository<WebhookSendLog, Long> {
|
public interface WebhookSendLogRepository extends JpaRepository<WebhookSendLog, Long> {
|
||||||
|
|
||||||
// 오라클 CHAR(1) Y/N 기준 조회
|
// 오라클 CHAR(1) Y/N 기준 조회
|
||||||
@Query("SELECT l FROM WebhookSendLog l " +
|
@Query("SELECT l FROM WebhookSendLog l " +
|
||||||
|
|||||||
+18
-17
@@ -85,14 +85,14 @@ public class WebhookReceiveService {
|
|||||||
log.info("[Webhook] 이벤트 처리 시작 - eventType: {}", eventType);
|
log.info("[Webhook] 이벤트 처리 시작 - eventType: {}", eventType);
|
||||||
|
|
||||||
switch (eventType) {
|
switch (eventType) {
|
||||||
case "CONTROL_START":
|
case "ORDER_CREATED":
|
||||||
handleControlStart(data);
|
handleOrderCreated(data);
|
||||||
break;
|
break;
|
||||||
case "CONTROL_END":
|
case "ORDER_CANCELLED":
|
||||||
handleControlEnd(data);
|
handleOrderCancelled(data);
|
||||||
break;
|
break;
|
||||||
case "ERROR_START":
|
case "PAYMENT_COMPLETED":
|
||||||
handleErrorStart(data);
|
handlePaymentCompleted(data);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
log.warn("[Webhook] 처리되지 않은 이벤트 - eventType: {}", eventType);
|
log.warn("[Webhook] 처리되지 않은 이벤트 - eventType: {}", eventType);
|
||||||
@@ -106,19 +106,20 @@ public class WebhookReceiveService {
|
|||||||
/* 이벤트별 핸들러 (비즈니스 로직 구현) */
|
/* 이벤트별 핸들러 (비즈니스 로직 구현) */
|
||||||
/* ------------------------------------------------------------------ */
|
/* ------------------------------------------------------------------ */
|
||||||
|
|
||||||
private void handleControlStart(JsonNode data) {
|
private void handleOrderCreated(JsonNode data) {
|
||||||
log.info("[Webhook] CONTROL_START 처리 - data: {}", data);
|
log.info("[Webhook] ORDER_CREATED 처리 - data: {}", data);
|
||||||
// TODO: 점검시작 처리 로직
|
// TODO: 주문 생성 처리 로직
|
||||||
}
|
|
||||||
private void handleControlEnd(JsonNode data) {
|
|
||||||
log.info("[Webhook] CONTROL_END 처리 - data: {}", data);
|
|
||||||
// TODO: 점검종료 처리 로직
|
|
||||||
}
|
|
||||||
private void handleErrorStart(JsonNode data) {
|
|
||||||
log.info("[Webhook] ERROR_START 처리 - data: {}", data);
|
|
||||||
// TODO: 장애시작 처리 로직
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void handleOrderCancelled(JsonNode data) {
|
||||||
|
log.info("[Webhook] ORDER_CANCELLED 처리 - data: {}", data);
|
||||||
|
// TODO: 주문 취소 처리 로직
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handlePaymentCompleted(JsonNode data) {
|
||||||
|
log.info("[Webhook] PAYMENT_COMPLETED 처리 - data: {}", data);
|
||||||
|
// TODO: 결제 완료 처리 로직
|
||||||
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------------ */
|
/* ------------------------------------------------------------------ */
|
||||||
/* 공통 유틸 */
|
/* 공통 유틸 */
|
||||||
|
|||||||
-174
@@ -1,174 +0,0 @@
|
|||||||
package com.eactive.eai.rms.ext.djb.webhook.service;
|
|
||||||
|
|
||||||
import java.time.LocalDate;
|
|
||||||
import java.time.LocalDateTime;
|
|
||||||
import java.time.format.DateTimeFormatter;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import javax.persistence.EntityManager;
|
|
||||||
import javax.persistence.PersistenceContext;
|
|
||||||
|
|
||||||
import org.springframework.data.domain.Page;
|
|
||||||
import org.springframework.data.domain.Pageable;
|
|
||||||
import org.springframework.stereotype.Service;
|
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
|
||||||
|
|
||||||
import com.eactive.apim.portal.monitoringcode.entity.MonitoringCode;
|
|
||||||
import com.eactive.apim.portal.monitoringcode.entity.MonitoringCodeId;
|
|
||||||
import com.eactive.apim.portal.portalorg.entity.QPortalOrg;
|
|
||||||
import com.eactive.eai.rms.common.base.BaseService;
|
|
||||||
import com.eactive.eai.rms.common.util.StringUtils;
|
|
||||||
import com.eactive.eai.rms.data.entity.man.monitoringCode.repository.MonitoringCodeRepository;
|
|
||||||
import com.eactive.eai.rms.data.entity.onl.djb.webhook.QWebhookSendLog;
|
|
||||||
import com.eactive.eai.rms.data.entity.onl.djb.webhook.WebhookSendLog;
|
|
||||||
import com.eactive.eai.rms.ext.djb.webhook.dto.WebhookSendLogUI;
|
|
||||||
import com.eactive.eai.rms.ext.djb.webhook.dto.WebhookSendLogUISearch;
|
|
||||||
import com.eactive.eai.rms.ext.djb.webhook.repository.WebhookSendLogRepository;
|
|
||||||
import com.eactive.eai.rms.onl.common.exception.BizException;
|
|
||||||
import com.querydsl.core.BooleanBuilder;
|
|
||||||
import com.querydsl.jpa.impl.JPAQueryFactory;
|
|
||||||
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 웹훅 발송 로그(PTL_WEBHOOK_SEND_LOG) 조회 화면 서비스.
|
|
||||||
*
|
|
||||||
* <p>조회 전용. orgId 는 엔티티 연관관계가 아닌 단순 컬럼이므로,
|
|
||||||
* 기관명은 QueryDSL 로 ptl_org 를 별도 조회하여 매핑한다.</p>
|
|
||||||
*/
|
|
||||||
@Service
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
@Transactional(transactionManager = "transactionManagerForEMS")
|
|
||||||
public class WebhookSendLogManService extends BaseService {
|
|
||||||
|
|
||||||
/** 모니터링 공통코드(tseairm28) 상의 웹훅 이벤트유형 코드 그룹. */
|
|
||||||
public static final String CODE_GROUP_EVENT_TYPE = "EVENT_TYPE";
|
|
||||||
|
|
||||||
private static final DateTimeFormatter FMT_DATETIME = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
|
||||||
private static final DateTimeFormatter FMT_YYYYMMDD = DateTimeFormatter.ofPattern("yyyyMMdd");
|
|
||||||
|
|
||||||
private final WebhookSendLogRepository webhookSendLogRepository;
|
|
||||||
private final MonitoringCodeRepository monitoringCodeRepository;
|
|
||||||
|
|
||||||
@PersistenceContext(unitName = "entityManagerFactoryForEMS")
|
|
||||||
private EntityManager entityManager;
|
|
||||||
|
|
||||||
public Page<WebhookSendLogUI> selectList(Pageable pageable, WebhookSendLogUISearch search) {
|
|
||||||
QWebhookSendLog log = QWebhookSendLog.webhookSendLog;
|
|
||||||
BooleanBuilder predicate = new BooleanBuilder();
|
|
||||||
|
|
||||||
if (StringUtils.isNotBlank(search.getSearchOrgName())) {
|
|
||||||
List<String> orgIds = findOrgIdsByName(search.getSearchOrgName());
|
|
||||||
if (orgIds.isEmpty()) {
|
|
||||||
// 일치하는 기관이 없으면 쿼리를 실행할 필요 없이 빈 결과를 바로 반환한다.
|
|
||||||
// (QueryDSL의 where false 는 이 프로젝트의 Hibernate HQL 파서에서 지원하지 않음)
|
|
||||||
return Page.empty(pageable);
|
|
||||||
}
|
|
||||||
predicate.and(log.orgId.in(orgIds));
|
|
||||||
}
|
|
||||||
if (StringUtils.isNotBlank(search.getSearchEventType())) {
|
|
||||||
predicate.and(log.eventType.eq(search.getSearchEventType()));
|
|
||||||
}
|
|
||||||
if (StringUtils.isNotBlank(search.getSearchTargetUrl())) {
|
|
||||||
predicate.and(log.targetUrl.containsIgnoreCase(search.getSearchTargetUrl()));
|
|
||||||
}
|
|
||||||
if (StringUtils.isNotBlank(search.getSearchSuccess())) {
|
|
||||||
predicate.and(log.success.eq(search.getSearchSuccess()));
|
|
||||||
}
|
|
||||||
if (StringUtils.isNotBlank(search.getSearchStartDate())) {
|
|
||||||
predicate.and(log.sentAt.goe(parseYYYYMMDD(search.getSearchStartDate()).atStartOfDay()));
|
|
||||||
}
|
|
||||||
if (StringUtils.isNotBlank(search.getSearchEndDate())) {
|
|
||||||
predicate.and(log.sentAt.loe(parseYYYYMMDD(search.getSearchEndDate()).atTime(23, 59, 59)));
|
|
||||||
}
|
|
||||||
|
|
||||||
Page<WebhookSendLog> page = webhookSendLogRepository.findAll(predicate, pageable);
|
|
||||||
|
|
||||||
Map<String, String> orgNameMap = findOrgNames(page.getContent());
|
|
||||||
// 목록의 이벤트유형 코드명은 화면에서 콤보(LIST_INIT_COMBO) 기준으로 변환하므로 서버에서는 조회하지 않는다(N+1 방지).
|
|
||||||
return page.map(e -> convertToUI(e, orgNameMap.get(e.getOrgId()), null));
|
|
||||||
}
|
|
||||||
|
|
||||||
public WebhookSendLogUI selectDetail(Long id) {
|
|
||||||
WebhookSendLog entity = webhookSendLogRepository.findById(id)
|
|
||||||
.orElseThrow(() -> new BizException("존재하지 않는 웹훅 발송 로그입니다."));
|
|
||||||
|
|
||||||
Map<String, String> orgNameMap = findOrgNames(Collections.singletonList(entity));
|
|
||||||
String eventTypeName = resolveEventTypeName(entity.getEventType());
|
|
||||||
return convertToUI(entity, orgNameMap.get(entity.getOrgId()), eventTypeName);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static LocalDate parseYYYYMMDD(String yyyyMMdd) {
|
|
||||||
return LocalDate.parse(yyyyMMdd, FMT_YYYYMMDD);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 모니터링 공통코드(EVENT_TYPE)에서 코드명을 조회한다. 코드가 없으면 원본 코드를 그대로 반환한다. */
|
|
||||||
private String resolveEventTypeName(String eventType) {
|
|
||||||
if (StringUtils.isBlank(eventType)) {
|
|
||||||
return eventType;
|
|
||||||
}
|
|
||||||
return monitoringCodeRepository.findById(new MonitoringCodeId(CODE_GROUP_EVENT_TYPE, eventType))
|
|
||||||
.map(MonitoringCode::getCodeName)
|
|
||||||
.orElse(eventType);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 기관명 LIKE 검색으로 매칭되는 orgId 목록. */
|
|
||||||
private List<String> findOrgIdsByName(String orgName) {
|
|
||||||
QPortalOrg org = QPortalOrg.portalOrg;
|
|
||||||
return new JPAQueryFactory(entityManager)
|
|
||||||
.select(org.id)
|
|
||||||
.from(org)
|
|
||||||
.where(org.orgName.containsIgnoreCase(orgName))
|
|
||||||
.fetch();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 목록/상세에 필요한 orgId → orgName 매핑 (일괄 조회로 N+1 방지). */
|
|
||||||
private Map<String, String> findOrgNames(List<WebhookSendLog> logs) {
|
|
||||||
Set<String> orgIds = logs.stream()
|
|
||||||
.map(WebhookSendLog::getOrgId)
|
|
||||||
.filter(StringUtils::isNotBlank)
|
|
||||||
.collect(Collectors.toSet());
|
|
||||||
if (orgIds.isEmpty()) {
|
|
||||||
return Collections.emptyMap();
|
|
||||||
}
|
|
||||||
|
|
||||||
QPortalOrg org = QPortalOrg.portalOrg;
|
|
||||||
return new JPAQueryFactory(entityManager)
|
|
||||||
.select(org.id, org.orgName)
|
|
||||||
.from(org)
|
|
||||||
.where(org.id.in(orgIds))
|
|
||||||
.fetch()
|
|
||||||
.stream()
|
|
||||||
.collect(Collectors.toMap(t -> t.get(org.id), t -> t.get(org.orgName)));
|
|
||||||
}
|
|
||||||
|
|
||||||
private WebhookSendLogUI convertToUI(WebhookSendLog e, String orgName, String eventTypeName) {
|
|
||||||
WebhookSendLogUI ui = new WebhookSendLogUI();
|
|
||||||
ui.setId(e.getId());
|
|
||||||
ui.setOrgId(e.getOrgId());
|
|
||||||
ui.setOrgName(orgName);
|
|
||||||
ui.setEventType(e.getEventType());
|
|
||||||
ui.setEventTypeName(eventTypeName);
|
|
||||||
ui.setTargetUrl(e.getTargetUrl());
|
|
||||||
ui.setProxyUrl(e.getProxyUrl());
|
|
||||||
ui.setPayload(e.getPayload());
|
|
||||||
ui.setSignature(e.getSignature());
|
|
||||||
ui.setStatusCode(e.getStatusCode());
|
|
||||||
ui.setResponseBody(e.getResponseBody());
|
|
||||||
ui.setSuccess(e.getSuccess());
|
|
||||||
ui.setSendBy(e.getSendBy());
|
|
||||||
ui.setErrorMessage(e.getErrorMessage());
|
|
||||||
ui.setRetryCount(e.getRetryCount());
|
|
||||||
ui.setCreatedAt(fmt(e.getCreatedAt(), FMT_DATETIME));
|
|
||||||
ui.setSentAt(fmt(e.getSentAt(), FMT_DATETIME));
|
|
||||||
return ui;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String fmt(LocalDateTime t, DateTimeFormatter f) {
|
|
||||||
return t == null ? "" : t.format(f);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -22,9 +22,7 @@ import org.springframework.web.client.HttpClientErrorException;
|
|||||||
import org.springframework.web.client.HttpServerErrorException;
|
import org.springframework.web.client.HttpServerErrorException;
|
||||||
import org.springframework.web.client.RestTemplate;
|
import org.springframework.web.client.RestTemplate;
|
||||||
|
|
||||||
import com.eactive.apim.portal.portalorg.entity.QPortalOrg;
|
|
||||||
import com.eactive.eai.rms.common.context.MonitoringContext;
|
import com.eactive.eai.rms.common.context.MonitoringContext;
|
||||||
import com.eactive.eai.rms.common.util.ContainerUtil;
|
|
||||||
import com.eactive.eai.rms.data.entity.onl.djb.webhook.QWebhookReq;
|
import com.eactive.eai.rms.data.entity.onl.djb.webhook.QWebhookReq;
|
||||||
import com.eactive.eai.rms.data.entity.onl.djb.webhook.QWebhookReqApi;
|
import com.eactive.eai.rms.data.entity.onl.djb.webhook.QWebhookReqApi;
|
||||||
import com.eactive.eai.rms.data.entity.onl.djb.webhook.QWebhookReqEvent;
|
import com.eactive.eai.rms.data.entity.onl.djb.webhook.QWebhookReqEvent;
|
||||||
@@ -62,21 +60,17 @@ public class WebhookService {
|
|||||||
QWebhookReq req = QWebhookReq.webhookReq;
|
QWebhookReq req = QWebhookReq.webhookReq;
|
||||||
QWebhookReqEvent evt = QWebhookReqEvent.webhookReqEvent;
|
QWebhookReqEvent evt = QWebhookReqEvent.webhookReqEvent;
|
||||||
QWebhookReqApi api = QWebhookReqApi.webhookReqApi;
|
QWebhookReqApi api = QWebhookReqApi.webhookReqApi;
|
||||||
QPortalOrg org = QPortalOrg.portalOrg;
|
|
||||||
|
|
||||||
String reverseProxyUrl = monitoringContext.getProperty(MonitoringContext.RMS_WEBHOOK_REVERSE_PROXY_URL);
|
|
||||||
|
|
||||||
List<Tuple> tuples = new JPAQueryFactory(entityManager)
|
List<Tuple> tuples = new JPAQueryFactory(entityManager)
|
||||||
.select(req.orgId, req.targetUrl, req.secret, org.reverseProxyPath, api.id.apiId)
|
.select(req.orgId, req.targetUrl, req.secret, api.id.apiId)
|
||||||
.from(req)
|
.from(req)
|
||||||
.join(evt).on(req.id.eq(evt.id.webhookReqId))
|
.join(evt).on(req.id.eq(evt.id.webhookReqId))
|
||||||
.join(api).on(req.id.eq(api.id.webhookReqId))
|
.join(api).on(req.id.eq(api.id.webhookReqId))
|
||||||
.join(org).on(req.orgId.eq(org.id))
|
|
||||||
.where(evt.id.eventType.eq(event)
|
.where(evt.id.eventType.eq(event)
|
||||||
.and(api.id.apiId.in(apiIds)))
|
.and(api.id.apiId.in(apiIds)))
|
||||||
.fetch();
|
.fetch();
|
||||||
|
|
||||||
// orgId + targetUrl + secret 기준으로 그룹핑, apiIds를 data(List)로 집계
|
// orgId + targetUrl + secret 기준으로 그룹핑, apiId를 data(List)로 집계
|
||||||
Map<String, WebhookSendRequest> resultMap = new LinkedHashMap<>();
|
Map<String, WebhookSendRequest> resultMap = new LinkedHashMap<>();
|
||||||
for (Tuple t : tuples) {
|
for (Tuple t : tuples) {
|
||||||
String key = t.get(req.orgId) + "|" + t.get(req.targetUrl);
|
String key = t.get(req.orgId) + "|" + t.get(req.targetUrl);
|
||||||
@@ -84,7 +78,6 @@ public class WebhookService {
|
|||||||
WebhookSendRequest wr = new WebhookSendRequest();
|
WebhookSendRequest wr = new WebhookSendRequest();
|
||||||
wr.setOrgId(t.get(req.orgId));
|
wr.setOrgId(t.get(req.orgId));
|
||||||
wr.setTargetUrl(t.get(req.targetUrl));
|
wr.setTargetUrl(t.get(req.targetUrl));
|
||||||
wr.setProxyUrl(this.getProxyUrl(reverseProxyUrl, t.get(org.reverseProxyPath), t.get(req.targetUrl)));
|
|
||||||
wr.setSecret(t.get(req.secret));
|
wr.setSecret(t.get(req.secret));
|
||||||
wr.setEventType(event);
|
wr.setEventType(event);
|
||||||
wr.setData(new ArrayList<String>());
|
wr.setData(new ArrayList<String>());
|
||||||
@@ -96,46 +89,6 @@ public class WebhookService {
|
|||||||
return new ArrayList<>(resultMap.values());
|
return new ArrayList<>(resultMap.values());
|
||||||
}
|
}
|
||||||
|
|
||||||
//reverse proxy 서버로 발송하기 위한 url 만든다
|
|
||||||
private String getProxyUrl(String proxyUrl, String path, String targetUrl) {
|
|
||||||
String base = stripTrailingSlash(proxyUrl);
|
|
||||||
String normalizedPath = stripSlashes(path);
|
|
||||||
// scheme://host[:port] 부분만 제거하고 이후 path/query/fragment는 그대로 유지
|
|
||||||
String targetPath = targetUrl.replaceFirst("^[a-zA-Z][a-zA-Z0-9+.-]*://[^/]+", "");
|
|
||||||
|
|
||||||
StringBuilder url = new StringBuilder(base);
|
|
||||||
if (!normalizedPath.isEmpty()) {
|
|
||||||
url.append('/').append(normalizedPath);
|
|
||||||
}
|
|
||||||
url.append(targetPath);
|
|
||||||
return url.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
private String stripTrailingSlash(String value) {
|
|
||||||
if (value == null) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
int end = value.length();
|
|
||||||
while (end > 0 && value.charAt(end - 1) == '/') {
|
|
||||||
end--;
|
|
||||||
}
|
|
||||||
return value.substring(0, end);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String stripSlashes(String value) {
|
|
||||||
if (value == null) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
String result = value.trim();
|
|
||||||
while (result.startsWith("/")) {
|
|
||||||
result = result.substring(1);
|
|
||||||
}
|
|
||||||
while (result.endsWith("/")) {
|
|
||||||
result = result.substring(0, result.length() - 1);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 웹훅 발송 메인 메서드
|
* 웹훅 발송 메인 메서드
|
||||||
*/
|
*/
|
||||||
@@ -143,7 +96,6 @@ public class WebhookService {
|
|||||||
public void send(WebhookSendRequest req) {
|
public void send(WebhookSendRequest req) {
|
||||||
|
|
||||||
String targetUrl = req.getTargetUrl();
|
String targetUrl = req.getTargetUrl();
|
||||||
String proxyUrl = req.getProxyUrl();
|
|
||||||
String secret = req.getSecret();
|
String secret = req.getSecret();
|
||||||
String eventType = req.getEventType();
|
String eventType = req.getEventType();
|
||||||
Object data = req.getData();
|
Object data = req.getData();
|
||||||
@@ -151,9 +103,7 @@ public class WebhookService {
|
|||||||
WebhookSendLog sendLog = new WebhookSendLog();
|
WebhookSendLog sendLog = new WebhookSendLog();
|
||||||
sendLog.setOrgId(req.getOrgId());
|
sendLog.setOrgId(req.getOrgId());
|
||||||
sendLog.setTargetUrl(targetUrl);
|
sendLog.setTargetUrl(targetUrl);
|
||||||
sendLog.setProxyUrl(proxyUrl);
|
|
||||||
sendLog.setEventType(eventType);
|
sendLog.setEventType(eventType);
|
||||||
sendLog.setSendBy(ContainerUtil.getInstanceName());
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 1. Payload 생성
|
// 1. Payload 생성
|
||||||
@@ -173,11 +123,11 @@ public class WebhookService {
|
|||||||
headers.set("X-Webhook-Timestamp", String.valueOf(webhookPayload.getTimestamp()));
|
headers.set("X-Webhook-Timestamp", String.valueOf(webhookPayload.getTimestamp()));
|
||||||
|
|
||||||
// 4. 발송 (재시도 포함)
|
// 4. 발송 (재시도 포함)
|
||||||
int retryCount = monitoringContext.getIntProperty(MonitoringContext.RMS_WEBHOOK_RETRY_COUNT, DEFAULT_RETRY_COUNT);
|
int retryCount = monitoringContext.getIntProperty(MonitoringContext.API_WEBHOOK_RETRY_COUNT, DEFAULT_RETRY_COUNT);
|
||||||
int retryTime = monitoringContext.getIntProperty(MonitoringContext.RMS_WEBHOOK_RETRY_TIME, DEFAULT_RETRY_TIME);
|
int retryTime = monitoringContext.getIntProperty(MonitoringContext.API_WEBHOOK_RETRY_TIME, DEFAULT_RETRY_TIME);
|
||||||
sendLog.setSentAt(LocalDateTime.now());
|
sendLog.setSentAt(LocalDateTime.now());
|
||||||
HttpEntity<String> httpRequest = new HttpEntity<>(payloadJson, headers);
|
HttpEntity<String> httpRequest = new HttpEntity<>(payloadJson, headers);
|
||||||
ResponseEntity<String> response = sendWithRetry(proxyUrl, httpRequest, sendLog, retryCount, retryTime);
|
ResponseEntity<String> response = sendWithRetry(targetUrl, httpRequest, sendLog, retryCount, retryTime);
|
||||||
|
|
||||||
// 5. 성공 로그
|
// 5. 성공 로그
|
||||||
sendLog.setStatusCode(response.getStatusCode().value());
|
sendLog.setStatusCode(response.getStatusCode().value());
|
||||||
@@ -185,7 +135,7 @@ public class WebhookService {
|
|||||||
sendLog.setSuccess(response.getStatusCode().is2xxSuccessful());
|
sendLog.setSuccess(response.getStatusCode().is2xxSuccessful());
|
||||||
|
|
||||||
log.info("[Webhook] 발송 성공 - eventType: {}, url: {}, status: {}, retryCount: {}",
|
log.info("[Webhook] 발송 성공 - eventType: {}, url: {}, status: {}, retryCount: {}",
|
||||||
eventType, proxyUrl, response.getStatusCode(), sendLog.getRetryCount());
|
eventType, targetUrl, response.getStatusCode(), sendLog.getRetryCount());
|
||||||
|
|
||||||
} catch (HttpClientErrorException | HttpServerErrorException e) {
|
} catch (HttpClientErrorException | HttpServerErrorException e) {
|
||||||
sendLog.setStatusCode(e.getStatusCode().value());
|
sendLog.setStatusCode(e.getStatusCode().value());
|
||||||
@@ -193,13 +143,13 @@ public class WebhookService {
|
|||||||
sendLog.setSuccess(false);
|
sendLog.setSuccess(false);
|
||||||
sendLog.setErrorMessage(e.getMessage());
|
sendLog.setErrorMessage(e.getMessage());
|
||||||
log.error("[Webhook] HTTP 오류 - eventType: {}, url: {}, status: {}",
|
log.error("[Webhook] HTTP 오류 - eventType: {}, url: {}, status: {}",
|
||||||
eventType, proxyUrl, e.getStatusCode());
|
eventType, targetUrl, e.getStatusCode());
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
sendLog.setSuccess(false);
|
sendLog.setSuccess(false);
|
||||||
sendLog.setErrorMessage(e.getMessage());
|
sendLog.setErrorMessage(e.getMessage());
|
||||||
log.error("[Webhook] 발송 실패 - eventType: {}, url: {}, error: {}",
|
log.error("[Webhook] 발송 실패 - eventType: {}, url: {}, error: {}",
|
||||||
eventType, proxyUrl, e.getMessage());
|
eventType, targetUrl, e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
sendLogRepository.save(sendLog);
|
sendLogRepository.save(sendLog);
|
||||||
@@ -211,25 +161,24 @@ public class WebhookService {
|
|||||||
/**
|
/**
|
||||||
* 재시도 포함 HTTP 발송
|
* 재시도 포함 HTTP 발송
|
||||||
* - 4xx: 클라이언트 오류이므로 즉시 throw (재시도 불필요)
|
* - 4xx: 클라이언트 오류이므로 즉시 throw (재시도 불필요)
|
||||||
* - 5xx / 네트워크 오류: 최대 RETRY_COUNT회까지 선형 증가 backoff 재시도 (1s → 2s → 3s → 4s → 5s)
|
* - 5xx / 네트워크 오류: 최대 RETRY_COUNT회까지 exponential backoff 재시도 (1s → 2s → 4s)
|
||||||
*/
|
*/
|
||||||
private ResponseEntity<String> sendWithRetry(String proxyUrl, HttpEntity<String> request, WebhookSendLog sendLog, int retryCount, int retryTime) throws Exception {
|
private ResponseEntity<String> sendWithRetry(String targetUrl, HttpEntity<String> request, WebhookSendLog sendLog, int retryCount, int retryTime) throws Exception {
|
||||||
Exception lastException = null;
|
Exception lastException = null;
|
||||||
for (int attempt = 0; attempt <= retryCount; attempt++) {
|
for (int attempt = 0; attempt <= retryCount; attempt++) {
|
||||||
try {
|
try {
|
||||||
if (attempt > 0) {
|
if (attempt > 0) {
|
||||||
long delayMs = (long) retryTime * attempt;
|
long delayMs = retryTime * (1L << (attempt - 1));
|
||||||
log.info("[Webhook] 재시도 {}/{} - url: {}, delay: {}ms", attempt, retryCount, proxyUrl, delayMs);
|
log.info("[Webhook] 재시도 {}/{} - url: {}, delay: {}ms", attempt, retryCount, targetUrl, delayMs);
|
||||||
sendLog.setRetryCount(attempt);
|
|
||||||
Thread.sleep(delayMs);
|
Thread.sleep(delayMs);
|
||||||
|
sendLog.setRetryCount(attempt);
|
||||||
}
|
}
|
||||||
return restTemplate.postForEntity(proxyUrl, request, String.class);
|
return restTemplate.postForEntity(targetUrl, request, String.class);
|
||||||
} catch (HttpClientErrorException e) {
|
} catch (HttpClientErrorException e) {
|
||||||
//throw e; // 4xx는 재시도 불필요
|
throw e; // 4xx는 재시도 불필요
|
||||||
lastException = e; //TODO: 재시도 테스트
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
lastException = e;
|
lastException = e;
|
||||||
log.warn("[Webhook] 발송 실패 (attempt {}/{}) - url: {}, error: {}", attempt, retryCount, proxyUrl, e.getMessage());
|
log.warn("[Webhook] 발송 실패 (attempt {}/{}) - url: {}, error: {}", attempt, retryCount, targetUrl, e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw lastException;
|
throw lastException;
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package com.eactive.eai.rms.ext.djb.smsauth;
|
package com.eactive.eai.rms.ext.kjb.smsauth;
|
||||||
|
|
||||||
import com.eactive.apim.portal.user.entity.UserInfo;
|
import com.eactive.apim.portal.user.entity.UserInfo;
|
||||||
import com.eactive.eai.rms.common.interceptor.InterceptorSkipController;
|
import com.eactive.eai.rms.common.interceptor.InterceptorSkipController;
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user