Compare commits
42 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8a884e53e3 | |||
| 7de00ecad2 | |||
| df5bbb162a | |||
| 3eec267769 | |||
| 6ab7e1fe16 | |||
| 58fe176715 | |||
| 6b6e2863cd | |||
| cfafccc34d | |||
| fe98ac45c4 | |||
| 5f9de93d35 | |||
| 48dfcf7714 | |||
| 6e3906878a | |||
| fafe7e45cb | |||
| eee7f46aa7 | |||
| b5eb63448a | |||
| 54291e5cbb | |||
| a0743465ab | |||
| d3e05256b7 | |||
| 4d5572f880 | |||
| e21014f992 | |||
| 43fe63964f | |||
| ac0d081a9a | |||
| 164ab92919 | |||
| a6e7d1adb3 | |||
| e51331986d | |||
| 1aa40f2c61 | |||
| c8caeeda8c | |||
| 06ad1230b2 | |||
| 8109f91aa2 | |||
| 62ccebd773 | |||
| ec8d6bfd7b | |||
| 480d7dac5a | |||
| feb4f845a8 | |||
| 7857f5b45c | |||
| 68c8e18e8d | |||
| 35e5564252 | |||
| 0d7d006f92 | |||
| 737b493f7c | |||
| af0a878a39 | |||
| b639e29039 | |||
| 350297aaa2 | |||
| 7677e3b26e |
@@ -0,0 +1,140 @@
|
|||||||
|
name: eapim-admin CI
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
- develop
|
||||||
|
- stage
|
||||||
|
- feats/ci-test
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: [self-hosted, linux, eapim-admin]
|
||||||
|
env:
|
||||||
|
JAVA_HOME: /apps/opts/jdk8
|
||||||
|
JAVA_HOME_TOMCAT: /apps/opts/jdk17
|
||||||
|
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"
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
working-directory: eapim-admin
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout eapim-admin (trigger SHA)
|
||||||
|
uses: http://172.30.1.50:3000/actions/checkout@v4
|
||||||
|
with:
|
||||||
|
path: eapim-admin
|
||||||
|
|
||||||
|
- name: Checkout sibling modules (master)
|
||||||
|
working-directory: ${{ github.workspace }}
|
||||||
|
run: |
|
||||||
|
set -e
|
||||||
|
mkdir -p eapim-online
|
||||||
|
|
||||||
|
# eapim-online/* — 5 modules referenced from settings.gradle
|
||||||
|
for REPO in elink-online-core elink-online-core-jpa elink-online-transformer elink-online-common elink-online-emsclient; do
|
||||||
|
rm -rf "eapim-online/$REPO"
|
||||||
|
git clone --depth=1 --branch master \
|
||||||
|
http://oauth2:${{ secrets.CI_TOKEN }}@172.30.1.50:3000/djb-eapim/$REPO.git \
|
||||||
|
"eapim-online/$REPO"
|
||||||
|
done
|
||||||
|
|
||||||
|
# standalone siblings
|
||||||
|
for REPO in elink-portal-common eapim-admin-djb; do
|
||||||
|
rm -rf "$REPO"
|
||||||
|
git clone --depth=1 --branch master \
|
||||||
|
http://oauth2:${{ secrets.CI_TOKEN }}@172.30.1.50:3000/djb-eapim/$REPO.git \
|
||||||
|
"$REPO"
|
||||||
|
done
|
||||||
|
|
||||||
|
- name: Verify toolchain
|
||||||
|
run: |
|
||||||
|
java -version
|
||||||
|
gradle --version
|
||||||
|
|
||||||
|
- name: Gradle build (no tests)
|
||||||
|
run: gradle clean build -x test --no-daemon
|
||||||
|
|
||||||
|
- name: Upload WAR artifact
|
||||||
|
uses: http://172.30.1.50:3000/actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: eapim-admin-war-${{ github.sha }}
|
||||||
|
path: eapim-admin/build/libs/eapim-admin.war
|
||||||
|
if-no-files-found: error
|
||||||
|
|
||||||
|
- name: Stop tomcat (pre-clean)
|
||||||
|
working-directory: ${{ github.workspace }}
|
||||||
|
run: |
|
||||||
|
# 1) systemd 관리 인스턴스 정지 (이번 워크플로가 띄운 것)
|
||||||
|
systemctl --user stop eapim-admin 2>/dev/null || true
|
||||||
|
|
||||||
|
# 2) systemd 외부 detached 인스턴스 정리
|
||||||
|
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"
|
||||||
|
|
||||||
|
- name: Deploy war as /monitoring
|
||||||
|
working-directory: ${{ github.workspace }}
|
||||||
|
run: |
|
||||||
|
DEPLOY_DIR="$CATALINA_BASE/webapps"
|
||||||
|
rm -rf "$DEPLOY_DIR/monitoring" "$DEPLOY_DIR/monitoring.war"
|
||||||
|
cp eapim-admin/build/libs/eapim-admin.war "$DEPLOY_DIR/monitoring.war"
|
||||||
|
ls -la "$DEPLOY_DIR"
|
||||||
|
|
||||||
|
- name: Start tomcat and readiness probe (timeout 300s)
|
||||||
|
working-directory: ${{ github.workspace }}
|
||||||
|
run: |
|
||||||
|
LOG="$CATALINA_BASE/logs/catalina.$(date +%Y-%m-%d).log"
|
||||||
|
BEFORE=0
|
||||||
|
[ -f "$LOG" ] && BEFORE=$(stat -c %s "$LOG")
|
||||||
|
|
||||||
|
# systemd 가 Tomcat 을 새 cgroup 으로 띄움 — runner step 종료와 무관하게 살아남음
|
||||||
|
systemctl --user start eapim-admin
|
||||||
|
|
||||||
|
# /loginForm.do 는 GET 매핑(MainController#loginForm) → tomcat·webapp 양쪽 살아있으면 200
|
||||||
|
# /login.do 는 POST 처리 — GET 으로 probe 시 405/잘못된 응답 가능성 있어 회피
|
||||||
|
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 "::error::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 "::error::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 미점유)"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
echo "--- key boot log lines ---"
|
||||||
|
tail -c +$((BEFORE+1)) "$LOG" | grep -E "Server startup|Deployment of web" | head -10 || true
|
||||||
@@ -25,31 +25,31 @@
|
|||||||
class="com.eactive.eai.rms.common.datasource.DataSourceType"
|
class="com.eactive.eai.rms.common.datasource.DataSourceType"
|
||||||
p:name="APIGW"
|
p:name="APIGW"
|
||||||
p:text="APIGW"
|
p:text="APIGW"
|
||||||
p:schema="AGWADM"
|
p:schema="AGWAPP"
|
||||||
p:dynamic="true"
|
p:dynamic="true"
|
||||||
p:online="true"
|
p:online="true"
|
||||||
p:jndiName="jdbc/dsOBP_AGW"
|
p:jndiName="jdbc/dsOBP_AGW"
|
||||||
p:render="ONL,COM"
|
p:render="ONL,COM"
|
||||||
/>
|
/>
|
||||||
<!-- BAP 일괄전송FTP -->
|
<!-- BAP 일괄전송FTP -->
|
||||||
<!-- <bean
|
<bean
|
||||||
id="BAP"
|
id="BAP"
|
||||||
class="com.eactive.eai.rms.common.datasource.DataSourceType"
|
class="com.eactive.eai.rms.common.datasource.DataSourceType"
|
||||||
p:name="BAP"
|
p:name="BAP"
|
||||||
p:text="BAP"
|
p:text="BAP"
|
||||||
p:schema="BAPADM"
|
p:schema="BAPAPP"
|
||||||
p:dynamic="true"
|
p:dynamic="true"
|
||||||
p:online="true"
|
p:online="true"
|
||||||
p:jndiName="jdbc/dsOBP_BAP"
|
p:jndiName="jdbc/dsOBP_BAP"
|
||||||
p:render="BAP,COM"
|
p:render="BAP,COM"
|
||||||
/> -->
|
/>
|
||||||
<!-- RMS_DEFAULT -->
|
<!-- RMS_DEFAULT -->
|
||||||
<bean
|
<bean
|
||||||
id="MONITORING"
|
id="MONITORING"
|
||||||
class="com.eactive.eai.rms.common.datasource.DataSourceType"
|
class="com.eactive.eai.rms.common.datasource.DataSourceType"
|
||||||
p:name="MONITORING"
|
p:name="MONITORING"
|
||||||
p:text="MONITORING"
|
p:text="MONITORING"
|
||||||
p:schema="EMSADM"
|
p:schema="EMSAPP"
|
||||||
p:dynamic="false"
|
p:dynamic="false"
|
||||||
p:online="false"
|
p:online="false"
|
||||||
p:jndiName="jdbc/dsOBP_EMS"
|
p:jndiName="jdbc/dsOBP_EMS"
|
||||||
|
|||||||
@@ -25,31 +25,31 @@
|
|||||||
class="com.eactive.eai.rms.common.datasource.DataSourceType"
|
class="com.eactive.eai.rms.common.datasource.DataSourceType"
|
||||||
p:name="APIGW"
|
p:name="APIGW"
|
||||||
p:text="APIGW"
|
p:text="APIGW"
|
||||||
p:schema="AGWADM"
|
p:schema="AGWAPP"
|
||||||
p:dynamic="true"
|
p:dynamic="true"
|
||||||
p:online="true"
|
p:online="true"
|
||||||
p:jndiName="jdbc/dsOBP_AGW"
|
p:jndiName="jdbc/dsOBP_AGW"
|
||||||
p:render="ONL,COM"
|
p:render="ONL,COM"
|
||||||
/>
|
/>
|
||||||
<!-- BAP 일괄전송FTP -->
|
<!-- BAP 일괄전송FTP -->
|
||||||
<!-- <bean-->
|
<bean
|
||||||
<!-- id="BAP"-->
|
id="BAP"
|
||||||
<!-- class="com.eactive.eai.rms.common.datasource.DataSourceType"-->
|
class="com.eactive.eai.rms.common.datasource.DataSourceType"
|
||||||
<!-- p:name="BAP"-->
|
p:name="BAP"
|
||||||
<!-- p:text="BAP"-->
|
p:text="BAP"
|
||||||
<!-- p:schema="BAPADM"-->
|
p:schema="BAPAPP"
|
||||||
<!-- p:dynamic="true"-->
|
p:dynamic="true"
|
||||||
<!-- p:online="true"-->
|
p:online="true"
|
||||||
<!-- p:jndiName="jdbc/dsOBP_BAP"-->
|
p:jndiName="jdbc/dsOBP_BAP"
|
||||||
<!-- p:render="BAP,COM"-->
|
p:render="BAP,COM"
|
||||||
<!-- />-->
|
/>
|
||||||
<!-- RMS_DEFAULT -->
|
<!-- RMS_DEFAULT -->
|
||||||
<bean
|
<bean
|
||||||
id="MONITORING"
|
id="MONITORING"
|
||||||
class="com.eactive.eai.rms.common.datasource.DataSourceType"
|
class="com.eactive.eai.rms.common.datasource.DataSourceType"
|
||||||
p:name="MONITORING"
|
p:name="MONITORING"
|
||||||
p:text="MONITORING"
|
p:text="MONITORING"
|
||||||
p:schema="EMSADM"
|
p:schema="EMSAPP"
|
||||||
p:dynamic="false"
|
p:dynamic="false"
|
||||||
p:online="false"
|
p:online="false"
|
||||||
p:jndiName="jdbc/dsOBP_EMS"
|
p:jndiName="jdbc/dsOBP_EMS"
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
class="com.eactive.eai.rms.common.datasource.DataSourceType"
|
class="com.eactive.eai.rms.common.datasource.DataSourceType"
|
||||||
p:name="APIGW"
|
p:name="APIGW"
|
||||||
p:text="APIGW"
|
p:text="APIGW"
|
||||||
p:schema="AGWADM"
|
p:schema="AGWAPP"
|
||||||
p:dynamic="true"
|
p:dynamic="true"
|
||||||
p:online="true"
|
p:online="true"
|
||||||
p:jndiName="jdbc/dsOBP_AGW"
|
p:jndiName="jdbc/dsOBP_AGW"
|
||||||
@@ -37,7 +37,7 @@
|
|||||||
class="com.eactive.eai.rms.common.datasource.DataSourceType"
|
class="com.eactive.eai.rms.common.datasource.DataSourceType"
|
||||||
p:name="BAP"
|
p:name="BAP"
|
||||||
p:text="BAP"
|
p:text="BAP"
|
||||||
p:schema="BAPADM"
|
p:schema="BAPAPP"
|
||||||
p:dynamic="true"
|
p:dynamic="true"
|
||||||
p:online="true"
|
p:online="true"
|
||||||
p:jndiName="jdbc/dsOBP_BAP"
|
p:jndiName="jdbc/dsOBP_BAP"
|
||||||
@@ -49,7 +49,7 @@
|
|||||||
class="com.eactive.eai.rms.common.datasource.DataSourceType"
|
class="com.eactive.eai.rms.common.datasource.DataSourceType"
|
||||||
p:name="MONITORING"
|
p:name="MONITORING"
|
||||||
p:text="MONITORING"
|
p:text="MONITORING"
|
||||||
p:schema="EMSADM"
|
p:schema="EMSAPP"
|
||||||
p:dynamic="false"
|
p:dynamic="false"
|
||||||
p:online="false"
|
p:online="false"
|
||||||
p:jndiName="jdbc/dsOBP_EMS"
|
p:jndiName="jdbc/dsOBP_EMS"
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
class="com.eactive.eai.rms.common.datasource.DataSourceType"
|
class="com.eactive.eai.rms.common.datasource.DataSourceType"
|
||||||
p:name="APIGW"
|
p:name="APIGW"
|
||||||
p:text="APIGW"
|
p:text="APIGW"
|
||||||
p:schema="AGWADM"
|
p:schema="AGWAPP"
|
||||||
p:dynamic="true"
|
p:dynamic="true"
|
||||||
p:online="true"
|
p:online="true"
|
||||||
p:jndiName="jdbc/dsOBP_AGW"
|
p:jndiName="jdbc/dsOBP_AGW"
|
||||||
@@ -37,7 +37,7 @@
|
|||||||
class="com.eactive.eai.rms.common.datasource.DataSourceType"
|
class="com.eactive.eai.rms.common.datasource.DataSourceType"
|
||||||
p:name="BAP"
|
p:name="BAP"
|
||||||
p:text="BAP"
|
p:text="BAP"
|
||||||
p:schema="BAPADM"
|
p:schema="BAPAPP"
|
||||||
p:dynamic="true"
|
p:dynamic="true"
|
||||||
p:online="true"
|
p:online="true"
|
||||||
p:jndiName="jdbc/dsOBP_BAP"
|
p:jndiName="jdbc/dsOBP_BAP"
|
||||||
@@ -49,7 +49,7 @@
|
|||||||
class="com.eactive.eai.rms.common.datasource.DataSourceType"
|
class="com.eactive.eai.rms.common.datasource.DataSourceType"
|
||||||
p:name="MONITORING"
|
p:name="MONITORING"
|
||||||
p:text="MONITORING"
|
p:text="MONITORING"
|
||||||
p:schema="EMSADM"
|
p:schema="EMSAPP"
|
||||||
p:dynamic="false"
|
p:dynamic="false"
|
||||||
p:online="false"
|
p:online="false"
|
||||||
p:jndiName="jdbc/dsOBP_EMS"
|
p:jndiName="jdbc/dsOBP_EMS"
|
||||||
|
|||||||
@@ -30,9 +30,9 @@
|
|||||||
<entry
|
<entry
|
||||||
key="APIGW"
|
key="APIGW"
|
||||||
value-ref="APIGW" />
|
value-ref="APIGW" />
|
||||||
<!-- <entry
|
<entry
|
||||||
key="BAP"
|
key="BAP"
|
||||||
value-ref="BAP" /> -->
|
value-ref="BAP" />
|
||||||
</map>
|
</map>
|
||||||
</property>
|
</property>
|
||||||
<property
|
<property
|
||||||
@@ -91,7 +91,7 @@
|
|||||||
<property name="configLocations">
|
<property name="configLocations">
|
||||||
<list>
|
<list>
|
||||||
<value>/WEB-INF/sqlmap-config/${db.vendor}/sqlMapConfigOfOnl.xml</value>
|
<value>/WEB-INF/sqlmap-config/${db.vendor}/sqlMapConfigOfOnl.xml</value>
|
||||||
<!-- <value>/WEB-INF/sqlmap-config/${db.vendor}/sqlMapConfigOfBap.xml</value> -->
|
<value>/WEB-INF/sqlmap-config/${db.vendor}/sqlMapConfigOfBap.xml</value>
|
||||||
<!-- <value>/WEB-INF/sqlmap-config/${db.vendor}/sqlMapConfigOfBat.xml</value>
|
<!-- <value>/WEB-INF/sqlmap-config/${db.vendor}/sqlMapConfigOfBat.xml</value>
|
||||||
<value>/WEB-INF/sqlmap-config/${db.vendor}/sqlMapConfigOfService.xml</value> -->
|
<value>/WEB-INF/sqlmap-config/${db.vendor}/sqlMapConfigOfService.xml</value> -->
|
||||||
</list>
|
</list>
|
||||||
|
|||||||
@@ -30,9 +30,9 @@
|
|||||||
<entry
|
<entry
|
||||||
key="APIGW"
|
key="APIGW"
|
||||||
value-ref="APIGW" />
|
value-ref="APIGW" />
|
||||||
<!-- <entry-->
|
<entry
|
||||||
<!-- key="BAP"-->
|
key="BAP"
|
||||||
<!-- value-ref="BAP" />-->
|
value-ref="BAP" />
|
||||||
</map>
|
</map>
|
||||||
</property>
|
</property>
|
||||||
<property
|
<property
|
||||||
@@ -91,7 +91,7 @@
|
|||||||
<property name="configLocations">
|
<property name="configLocations">
|
||||||
<list>
|
<list>
|
||||||
<value>/WEB-INF/sqlmap-config/${db.vendor}/sqlMapConfigOfOnl.xml</value>
|
<value>/WEB-INF/sqlmap-config/${db.vendor}/sqlMapConfigOfOnl.xml</value>
|
||||||
<!-- <value>/WEB-INF/sqlmap-config/${db.vendor}/sqlMapConfigOfBap.xml</value>-->
|
<value>/WEB-INF/sqlmap-config/${db.vendor}/sqlMapConfigOfBap.xml</value>
|
||||||
<!-- <value>/WEB-INF/sqlmap-config/${db.vendor}/sqlMapConfigOfBat.xml</value>
|
<!-- <value>/WEB-INF/sqlmap-config/${db.vendor}/sqlMapConfigOfBat.xml</value>
|
||||||
<value>/WEB-INF/sqlmap-config/${db.vendor}/sqlMapConfigOfService.xml</value> -->
|
<value>/WEB-INF/sqlmap-config/${db.vendor}/sqlMapConfigOfService.xml</value> -->
|
||||||
</list>
|
</list>
|
||||||
|
|||||||
@@ -69,6 +69,7 @@
|
|||||||
<context:component-scan base-package="com.eactive.apim.portal" />
|
<context:component-scan base-package="com.eactive.apim.portal" />
|
||||||
|
|
||||||
<context:component-scan base-package="com.eactive.ext.kjb.statistics" />
|
<context:component-scan base-package="com.eactive.ext.kjb.statistics" />
|
||||||
|
<context:component-scan base-package="com.eactive.ext.djb" />
|
||||||
|
|
||||||
|
|
||||||
<bean id="cacheManager"
|
<bean id="cacheManager"
|
||||||
|
|||||||
@@ -115,7 +115,7 @@
|
|||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
<bean id="ioAcceptor" class="com.eactive.eai.rms.onl.server.UdpServer" init-method="init" destroy-method="destory">
|
<bean id="ioAcceptor" class="com.eactive.eai.rms.onl.server.UdpServer" init-method="init" destroy-method="destory">
|
||||||
<property name="port" value="10800" />
|
<property name="port" value="39126" />
|
||||||
<property name="handler" ref="handler"/>
|
<property name="handler" ref="handler"/>
|
||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ hibernate.dialect=org.hibernate.dialect.Oracle12cDialect
|
|||||||
|
|
||||||
# Priority of the was -D option
|
# Priority of the was -D option
|
||||||
inst.Name=emsSvr11
|
inst.Name=emsSvr11
|
||||||
eai.tableowner=EMSADM
|
eai.tableowner=EMSAPP
|
||||||
# P/T/D/S
|
# P/T/D/S
|
||||||
eai.systemmode=D
|
eai.systemmode=D
|
||||||
eai.drmode=N
|
eai.drmode=N
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ hibernate.dialect=org.hibernate.dialect.Oracle12cDialect
|
|||||||
|
|
||||||
# Priority of the was -D option
|
# Priority of the was -D option
|
||||||
inst.Name=emsSvr96
|
inst.Name=emsSvr96
|
||||||
eai.tableowner=EMSADM
|
eai.tableowner=EMSAPP
|
||||||
# P/T/D/S
|
# P/T/D/S
|
||||||
eai.systemmode=L
|
eai.systemmode=L
|
||||||
eai.drmode=N
|
eai.drmode=N
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ hibernate.dialect=org.hibernate.dialect.Oracle12cDialect
|
|||||||
|
|
||||||
# Priority of the was -D option
|
# Priority of the was -D option
|
||||||
inst.Name=emsSvr11
|
inst.Name=emsSvr11
|
||||||
eai.tableowner=EMSADM
|
eai.tableowner=EMSAPP
|
||||||
# P/T/D/S
|
# P/T/D/S
|
||||||
eai.systemmode=P
|
eai.systemmode=P
|
||||||
eai.drmode=N
|
eai.drmode=N
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ hibernate.dialect=org.hibernate.dialect.Oracle12cDialect
|
|||||||
|
|
||||||
# Priority of the was -D option
|
# Priority of the was -D option
|
||||||
inst.Name=emsSvr11
|
inst.Name=emsSvr11
|
||||||
eai.tableowner=EMSADM
|
eai.tableowner=EMSAPP
|
||||||
# P/T/D/S
|
# P/T/D/S
|
||||||
eai.systemmode=T
|
eai.systemmode=T
|
||||||
eai.drmode=N
|
eai.drmode=N
|
||||||
|
|||||||
@@ -36,6 +36,14 @@
|
|||||||
// save the returnValue in options so that it is available in the callback function
|
// save the returnValue in options so that it is available in the callback function
|
||||||
optns.returnValue = $frame[0].contentWindow.window.returnValue;
|
optns.returnValue = $frame[0].contentWindow.window.returnValue;
|
||||||
optns.onClose();
|
optns.onClose();
|
||||||
|
// Remove iframe from DOM so window.frames doesn't accumulate stale entries;
|
||||||
|
// without this, a second showModal call leaves frames[0] pointing to the first
|
||||||
|
// (closed) iframe, causing the previous returnValue to bleed into the next callback.
|
||||||
|
var $dialogEl = $frame.closest('.ui-dialog');
|
||||||
|
setTimeout(function() {
|
||||||
|
$frame.remove();
|
||||||
|
if ($dialogEl.length) $dialogEl.remove();
|
||||||
|
}, 0);
|
||||||
|
|
||||||
if(optns.option && optns.option.fullScreenPopup){
|
if(optns.option && optns.option.fullScreenPopup){
|
||||||
$('iframe[name=topFrame]',window.parent.document).css('display', 'block');
|
$('iframe[name=topFrame]',window.parent.document).css('display', 'block');
|
||||||
|
|||||||
@@ -30,6 +30,10 @@
|
|||||||
<script language="javascript" src="<c:url value="/js/jquery.contextMenu.js"/>"></script>
|
<script language="javascript" src="<c:url value="/js/jquery.contextMenu.js"/>"></script>
|
||||||
<script language="javascript" src="<c:url value="/js/custom.js"/>"></script>
|
<script language="javascript" src="<c:url value="/js/custom.js"/>"></script>
|
||||||
<script language="javascript" src="<c:url value="/js/common-${locale}.js"/>"></script>
|
<script language="javascript" src="<c:url value="/js/common-${locale}.js"/>"></script>
|
||||||
|
<script type="text/javascript">
|
||||||
|
/* 자동 로그아웃: iframe 내 활동을 sessionStorage에 기록 (main.jsp 타이머와 공유) */
|
||||||
|
(function() { var m = function() { sessionStorage.setItem('lastActivity', String(Date.now())); }; ['mousemove','keydown','click','touchstart'].forEach(function(e){ document.addEventListener(e, m, true); }); })();
|
||||||
|
</script>
|
||||||
<script language="javascript" src="<c:url value="/js/jquery.inputmask.bundle.min.js"/>"></script>
|
<script language="javascript" src="<c:url value="/js/jquery.inputmask.bundle.min.js"/>"></script>
|
||||||
<script language="javascript" src="<c:url value="/js/jquery.searchabledropdown-1.0.8.min.js"/>"></script>
|
<script language="javascript" src="<c:url value="/js/jquery.searchabledropdown-1.0.8.min.js"/>"></script>
|
||||||
<%-- <script language="javascript" src="<c:url value="/js/jquery.searchable-1.1.0.min.js"/>"></script> --%>
|
<%-- <script language="javascript" src="<c:url value="/js/jquery.searchable-1.1.0.min.js"/>"></script> --%>
|
||||||
|
|||||||
@@ -353,10 +353,10 @@
|
|||||||
</div>
|
</div>
|
||||||
<!-- SSO 로그인 버튼 (비상모드 시 숨김) -->
|
<!-- SSO 로그인 버튼 (비상모드 시 숨김) -->
|
||||||
<c:if test="${not emergencyMode}">
|
<c:if test="${not emergencyMode}">
|
||||||
<div class="form-group">
|
<!-- <div class="form-group">
|
||||||
<input type="button" id="btn_sso_login" class="form-control btn btn-success rounded submit px-3"
|
<input type="button" id="btn_sso_login" class="form-control btn btn-success rounded submit px-3"
|
||||||
value="SSO Login">
|
value="SSO Login">
|
||||||
</div>
|
</div> -->
|
||||||
</c:if>
|
</c:if>
|
||||||
<p style="color:red;"><%=resultMsg %></p>
|
<p style="color:red;"><%=resultMsg %></p>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -99,6 +99,93 @@
|
|||||||
<iframe class="topMenu" src="" id="topFrame" name="topFrame" scrolling="no" noresize="noresize"></iframe>
|
<iframe class="topMenu" src="" id="topFrame" name="topFrame" scrolling="no" noresize="noresize"></iframe>
|
||||||
<iframe class="leftMenu" src="" name="leftFrame" scrolling="no" noresize="noresize"></iframe>
|
<iframe class="leftMenu" src="" name="leftFrame" scrolling="no" noresize="noresize"></iframe>
|
||||||
<iframe class="rightCon" src="" name="mainFrame" scrolling="auto"></iframe>
|
<iframe class="rightCon" src="" name="mainFrame" scrolling="auto"></iframe>
|
||||||
|
|
||||||
|
<%-- 자동 로그아웃 경고 모달 --%>
|
||||||
|
<div id="autoLogoutModal" style="display:none; position:fixed; top:0; left:0; width:100%; height:100%; background:rgba(0,0,0,0.5); z-index:99999;">
|
||||||
|
<div style="position:absolute; top:50%; left:50%; transform:translate(-50%,-50%); background:#fff; padding:30px 40px; border-radius:4px; text-align:center; min-width:320px; box-shadow:0 4px 20px rgba(0,0,0,0.3);">
|
||||||
|
<h3 style="margin:0 0 12px; color:#333; font-size:16px;">자동 로그아웃 안내</h3>
|
||||||
|
<p style="margin:0 0 22px; color:#666; font-size:14px;">
|
||||||
|
장시간 사용하지 않아 <strong><span id="autoLogoutSeconds">60</span>초</strong> 후 자동 로그아웃됩니다.
|
||||||
|
</p>
|
||||||
|
<button id="autoLogoutContinue" style="margin-right:8px; padding:8px 22px; background:#337ab7; color:#fff; border:none; border-radius:3px; cursor:pointer; font-size:13px;">계속 사용</button>
|
||||||
|
<button id="autoLogoutNow" style="padding:8px 22px; background:#d9534f; color:#fff; border:none; border-radius:3px; cursor:pointer; font-size:13px;">로그아웃</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
(function () {
|
||||||
|
var IDLE_MS = ${autoLogoutTimeout} * 60 * 1000; // 설정값(분) → ms
|
||||||
|
var WARN_MS = 60 * 1000; // 로그아웃 1분 전 경고
|
||||||
|
var CHECK_INTERVAL = 5000; // 5초 간격 체크
|
||||||
|
var countdownTimer = null;
|
||||||
|
|
||||||
|
/* sessionStorage: iframe 포함 모든 동일-origin 프레임과 공유 */
|
||||||
|
function getLastActivity() {
|
||||||
|
var t = sessionStorage.getItem('lastActivity');
|
||||||
|
return t ? parseInt(t, 10) : Date.now();
|
||||||
|
}
|
||||||
|
function markActivity() {
|
||||||
|
sessionStorage.setItem('lastActivity', String(Date.now()));
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetTimer() {
|
||||||
|
markActivity();
|
||||||
|
if ($('#autoLogoutModal').is(':visible')) {
|
||||||
|
$('#autoLogoutModal').hide();
|
||||||
|
clearInterval(countdownTimer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function showWarning(remainMs) {
|
||||||
|
var sec = Math.ceil(remainMs / 1000);
|
||||||
|
$('#autoLogoutSeconds').text(sec);
|
||||||
|
$('#autoLogoutModal').show();
|
||||||
|
clearInterval(countdownTimer);
|
||||||
|
countdownTimer = setInterval(function () {
|
||||||
|
sec--;
|
||||||
|
if (sec <= 0) {
|
||||||
|
clearInterval(countdownTimer);
|
||||||
|
doLogout();
|
||||||
|
} else {
|
||||||
|
$('#autoLogoutSeconds').text(sec);
|
||||||
|
}
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
function doLogout() {
|
||||||
|
location.href = '<%=request.getContextPath()%>/rms/logout.do';
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 상위 프레임(main.jsp) 활동 감지 */
|
||||||
|
['mousemove', 'keydown', 'click', 'touchstart'].forEach(function (e) {
|
||||||
|
document.addEventListener(e, markActivity, true);
|
||||||
|
});
|
||||||
|
|
||||||
|
/* 주기적 유휴 시간 체크 */
|
||||||
|
setInterval(function () {
|
||||||
|
var idle = Date.now() - getLastActivity();
|
||||||
|
var remain = IDLE_MS - idle;
|
||||||
|
if (remain <= 0) {
|
||||||
|
clearInterval(countdownTimer);
|
||||||
|
doLogout();
|
||||||
|
} else if (remain <= WARN_MS && !$('#autoLogoutModal').is(':visible')) {
|
||||||
|
showWarning(remain);
|
||||||
|
}
|
||||||
|
}, CHECK_INTERVAL);
|
||||||
|
|
||||||
|
/* 버튼 이벤트 */
|
||||||
|
$(document).on('click', '#autoLogoutContinue', function () {
|
||||||
|
resetTimer();
|
||||||
|
$.get('<%=request.getContextPath()%>/checkSession.do'); // 서버 세션도 갱신
|
||||||
|
});
|
||||||
|
$(document).on('click', '#autoLogoutNow', function () {
|
||||||
|
doLogout();
|
||||||
|
});
|
||||||
|
|
||||||
|
/* 페이지 로드 시 초기화 */
|
||||||
|
markActivity();
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
||||||
|
|||||||
@@ -553,7 +553,7 @@
|
|||||||
<ul>
|
<ul>
|
||||||
<li style="width:240px;" id="newDashboardShow">
|
<li style="width:240px;" id="newDashboardShow">
|
||||||
<a style="width:240px;"
|
<a style="width:240px;"
|
||||||
href="#"><span><%=SessionManager.getUserName(request) %>(<%=SessionManager.getUserId(request) %>)</span>
|
href="#"><span><%=SessionManager.getUserName(request) %>(<%=SessionManager.getUserId(request) %>-<%=System.getProperty("inst.Name") %>)</span>
|
||||||
<span onClick="javascript:openColorPopup();"><%=localeMessage.getString("screen.customer") %></span>
|
<span onClick="javascript:openColorPopup();"><%=localeMessage.getString("screen.customer") %></span>
|
||||||
<% if (!"".equals(LastLoginYms.trim())) { %>
|
<% if (!"".equals(LastLoginYms.trim())) { %>
|
||||||
<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>
|
||||||
|
|||||||
@@ -36,6 +36,14 @@
|
|||||||
// save the returnValue in options so that it is available in the callback function
|
// save the returnValue in options so that it is available in the callback function
|
||||||
optns.returnValue = $frame[0].contentWindow.window.returnValue;
|
optns.returnValue = $frame[0].contentWindow.window.returnValue;
|
||||||
optns.onClose();
|
optns.onClose();
|
||||||
|
// Remove iframe from DOM so window.frames doesn't accumulate stale entries;
|
||||||
|
// without this, a second showModal call leaves frames[0] pointing to the first
|
||||||
|
// (closed) iframe, causing the previous returnValue to bleed into the next callback.
|
||||||
|
var $dialogEl = $frame.closest('.ui-dialog');
|
||||||
|
setTimeout(function() {
|
||||||
|
$frame.remove();
|
||||||
|
if ($dialogEl.length) $dialogEl.remove();
|
||||||
|
}, 0);
|
||||||
|
|
||||||
if(optns.option && optns.option.fullScreenPopup){
|
if(optns.option && optns.option.fullScreenPopup){
|
||||||
$('iframe[name=topFrame]',window.parent.document).css('display', 'block');
|
$('iframe[name=topFrame]',window.parent.document).css('display', 'block');
|
||||||
|
|||||||
@@ -16,173 +16,486 @@
|
|||||||
<jsp:include page="/jsp/common/include/css.jsp"/>
|
<jsp:include page="/jsp/common/include/css.jsp"/>
|
||||||
<jsp:include page="/jsp/common/include/script.jsp"/>
|
<jsp:include page="/jsp/common/include/script.jsp"/>
|
||||||
|
|
||||||
<script language="javascript" >
|
<!-- 페이지 커스텀 스타일 (fragment) -->
|
||||||
|
<jsp:include page="fragments/inflowGroupDetailStyle.jsp"/>
|
||||||
|
|
||||||
|
<script language="javascript">
|
||||||
var url ='<c:url value="/onl/admin/inflow/inflowAdapterControlMan.json" />';
|
var url ='<c:url value="/onl/admin/inflow/inflowAdapterControlMan.json" />';
|
||||||
var url_view ='<c:url value="/onl/admin/inflow/inflowAdapterControlMan.view" />';
|
var url_view ='<c:url value="/onl/admin/inflow/inflowAdapterControlMan.view" />';
|
||||||
var isDetail = false;
|
var isDetail = false;
|
||||||
function init(url,key,callback){
|
|
||||||
|
function updateThresholdLabel() {
|
||||||
|
var timeUnit = $('select[name=thresholdTimeUnit]').val();
|
||||||
|
var $input = $('#thresholdInput');
|
||||||
|
var $unit = $('#thresholdUnit');
|
||||||
|
|
||||||
|
var unitLabels = {
|
||||||
|
'': '',
|
||||||
|
'SEC': 'req/sec',
|
||||||
|
'MIN': 'req/min',
|
||||||
|
'HOU': 'req/hour',
|
||||||
|
'DAY': 'req/day',
|
||||||
|
'MON': 'req/month'
|
||||||
|
};
|
||||||
|
|
||||||
|
$unit.text(unitLabels[timeUnit] || 'requests');
|
||||||
|
|
||||||
|
if (timeUnit === '') {
|
||||||
|
$input.prop('disabled', true).val('');
|
||||||
|
$unit.css('visibility', 'hidden');
|
||||||
|
} else {
|
||||||
|
$input.prop('disabled', false);
|
||||||
|
$unit.css('visibility', 'visible');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function init(url, key, callback) {
|
||||||
$.ajax({
|
$.ajax({
|
||||||
type : "POST",
|
type: "POST",
|
||||||
url:url,
|
url: url,
|
||||||
dataType:"json",
|
dataType: "json",
|
||||||
data:{cmd: 'LIST_INIT_COMBO'},
|
data: {cmd: 'LIST_INIT_COMBO'},
|
||||||
success:function(json){
|
success: function(json) {
|
||||||
new makeOptions("CODE","NAME").setObj($("select[name=useYn]")).setData(json.useYnRows).setFormat(codeName3OptionFormat).rendering();
|
new makeOptions("CODE","NAME").setObj($("select[name=useYn]")).setData(json.useYnRows).setFormat(codeName3OptionFormat).rendering();
|
||||||
new makeOptions("CODE","NAME").setObj($("select[name=thresholdTimeUnit]")).setNoValueInclude(true).setData(json.timeUnitRows).setFormat(codeName3OptionFormat).rendering();
|
new makeOptions("CODE","NAME").setObj($("select[name=thresholdTimeUnit]")).setNoValueInclude(true).setData(json.timeUnitRows).setFormat(codeName3OptionFormat).rendering();
|
||||||
|
updateThresholdLabel();
|
||||||
|
|
||||||
if(typeof callback === 'function') {
|
if (typeof callback === 'function') {
|
||||||
callback(url,key);
|
callback(url, key);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
error:function(e){
|
error: function(e) {
|
||||||
alert(e.responseText);
|
alert(e.responseText);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
function detail(url,key){
|
|
||||||
if (!isDetail)return;
|
|
||||||
$.ajax({
|
|
||||||
type : "POST",
|
|
||||||
url:url,
|
|
||||||
dataType:"json",
|
|
||||||
data:{cmd: 'DETAIL', name : key},
|
|
||||||
success:function(json){
|
|
||||||
var data = json;
|
|
||||||
$("input[name=name]").attr('readonly',true);
|
|
||||||
$("input[name=desc]").attr('readonly',true);
|
|
||||||
|
|
||||||
$("#ajaxForm input[type!=radio],#ajaxForm select,#ajaxForm textarea").each(function(){
|
function detail(url, key) {
|
||||||
|
if (!isDetail) return;
|
||||||
|
$.ajax({
|
||||||
|
type: "POST",
|
||||||
|
url: url,
|
||||||
|
dataType: "json",
|
||||||
|
data: {cmd: 'DETAIL', name: key},
|
||||||
|
success: function(json) {
|
||||||
|
var data = json;
|
||||||
|
$("input[name=name]").attr('readonly', true);
|
||||||
|
$("input[name=desc]").attr('readonly', true);
|
||||||
|
|
||||||
|
$("#ajaxForm input[type!=radio],#ajaxForm select,#ajaxForm textarea").each(function() {
|
||||||
var name = $(this).attr("name");
|
var name = $(this).attr("name");
|
||||||
var tag = $(this).prop("tagName").toLowerCase();
|
var tag = $(this).prop("tagName").toLowerCase();
|
||||||
$(tag+"[name="+name+"]").val(data[name.toUpperCase()]);
|
$(tag+"[name="+name+"]").val(data[name.toUpperCase()]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
updateThresholdLabel();
|
||||||
|
|
||||||
|
var modifiedAt = data.MODIFIEDAT || data.modifiedAt;
|
||||||
|
if (modifiedAt) {
|
||||||
|
$('#modifiedAtText').text(modifiedAt);
|
||||||
|
$('#modifiedAtInfo').show();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
error:function(e){
|
error: function(e) {
|
||||||
alert(e.responseText);
|
alert(e.responseText);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function switchTab(tabName) {
|
||||||
|
$('.detail-tab').removeClass('active');
|
||||||
|
$('.detail-tab[data-tab="' + tabName + '"]').addClass('active');
|
||||||
|
$('.tab-content').removeClass('active');
|
||||||
|
$('#tab' + tabName.charAt(0).toUpperCase() + tabName.slice(1) + 'Content').addClass('active');
|
||||||
|
|
||||||
|
if (tabName === 'status' && isDetail) {
|
||||||
|
loadBucketStatus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadBucketStatus() {
|
||||||
|
$('#bucketServerList').html('<div class="bucket-empty-msg"><i class="material-icons">hourglass_empty</i>조회 중...</div>');
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
type: 'POST',
|
||||||
|
url: url,
|
||||||
|
data: { cmd: 'LIST_BUCKET_STATUS', adapterId: "${param.name}" },
|
||||||
|
dataType: 'json',
|
||||||
|
success: function(result) {
|
||||||
|
renderBucketStatus(result.servers || []);
|
||||||
|
$('#lastRefreshTime').text(formatDateTime(new Date()));
|
||||||
|
},
|
||||||
|
error: function(e) {
|
||||||
|
$('#bucketServerList').html('<div class="bucket-empty-msg bucket-error-msg"><i class="material-icons">error_outline</i>조회 실패: ' + e.statusText + '</div>');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatDateTime(date) {
|
||||||
|
var h = date.getHours();
|
||||||
|
var m = date.getMinutes();
|
||||||
|
var s = date.getSeconds();
|
||||||
|
return (h < 10 ? '0' + h : h) + ':' + (m < 10 ? '0' + m : m) + ':' + (s < 10 ? '0' + s : s);
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderBucketStatus(servers) {
|
||||||
|
var container = $('#bucketServerList');
|
||||||
|
container.empty();
|
||||||
|
|
||||||
|
if (servers.length === 0) {
|
||||||
|
container.html('<div class="bucket-empty-msg"><i class="material-icons">dns</i>등록된 서버가 없습니다</div>');
|
||||||
|
$('#bucketSummary').hide();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var onlineCount = 0;
|
||||||
|
var perSecondCapacity = 0;
|
||||||
|
var thresholdCapacity = 0;
|
||||||
|
var thresholdTimeUnit = '';
|
||||||
|
|
||||||
|
$.each(servers, function(i, server) {
|
||||||
|
if (server.status === 'online' && server.data && server.data.buckets) {
|
||||||
|
onlineCount++;
|
||||||
|
$.each(server.data.buckets, function(j, bucket) {
|
||||||
|
if (bucket.type === 'perSecond') {
|
||||||
|
perSecondCapacity += bucket.capacity || 0;
|
||||||
|
} else if (bucket.type === 'threshold') {
|
||||||
|
thresholdCapacity += bucket.capacity || 0;
|
||||||
|
if (!thresholdTimeUnit) thresholdTimeUnit = bucket.timeUnit;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (onlineCount > 0) {
|
||||||
|
$('#summaryInstanceCount').html(onlineCount + '<span class="unit">개</span>');
|
||||||
|
|
||||||
|
var configPerSecond = parseInt($('input[name=thresholdPerSecond]').val()) || 0;
|
||||||
|
var configThreshold = parseInt($('input[name=threshold]').val()) || 0;
|
||||||
|
|
||||||
|
if (configPerSecond > 0) {
|
||||||
|
$('#summaryPerSecond').html(formatNumber(perSecondCapacity) + '<span class="unit">req/sec</span>');
|
||||||
|
$('#summaryPerSecondCalc').text(formatNumber(configPerSecond) + ' × ' + onlineCount + ' 인스턴스');
|
||||||
|
$('#summaryPerSecondWrap').show();
|
||||||
|
} else {
|
||||||
|
$('#summaryPerSecondWrap').hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (configThreshold > 0) {
|
||||||
|
var timeUnitLabel = getTimeUnitText(thresholdTimeUnit);
|
||||||
|
$('#summaryThreshold').html(formatNumber(thresholdCapacity) + '<span class="unit">req/' + timeUnitLabel + '</span>');
|
||||||
|
$('#summaryThresholdCalc').text(formatNumber(configThreshold) + ' × ' + onlineCount + ' 인스턴스');
|
||||||
|
$('#summaryThresholdWrap').show();
|
||||||
|
} else {
|
||||||
|
$('#summaryThresholdWrap').hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#bucketSummary').show();
|
||||||
|
} else {
|
||||||
|
$('#bucketSummary').hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
$.each(servers, function(i, server) {
|
||||||
|
var statusClass = server.status || 'offline';
|
||||||
|
var statusText = { online: '정상', offline: '오프라인', no_data: '데이터 없음' };
|
||||||
|
|
||||||
|
var card = '<div class="bucket-server-card">' +
|
||||||
|
'<div class="bucket-server-header">' +
|
||||||
|
'<div>' +
|
||||||
|
'<span class="bucket-server-name">' + server.serverName + '</span> ' +
|
||||||
|
'<span class="bucket-server-ip">' + server.serverIp + ':' + server.serverPort + '</span>' +
|
||||||
|
'</div>' +
|
||||||
|
'<span class="bucket-server-status ' + statusClass + '">' + (statusText[statusClass] || statusClass) + '</span>' +
|
||||||
|
'</div>' +
|
||||||
|
'<div class="bucket-server-body">' + renderBucketBody(server) + '</div>' +
|
||||||
|
'</div>';
|
||||||
|
container.append(card);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderBucketBody(server) {
|
||||||
|
if (server.status === 'offline') {
|
||||||
|
return '<div class="bucket-error-msg">' + (server.message || '서버 연결 실패') + '</div>';
|
||||||
|
}
|
||||||
|
if (server.status === 'no_data') {
|
||||||
|
return '<div class="bucket-empty-msg" style="padding:10px;"><i class="material-icons" style="font-size:18px;">info_outline</i> ' + (server.message || '어댑터가 로드되지 않음') + '</div>';
|
||||||
|
}
|
||||||
|
|
||||||
|
var data = server.data;
|
||||||
|
if (!data || !data.buckets || data.buckets.length === 0) {
|
||||||
|
return '<div class="bucket-empty-msg" style="padding:10px;">버킷 정보 없음</div>';
|
||||||
|
}
|
||||||
|
|
||||||
|
var html = '<div class="bucket-info-grid">';
|
||||||
|
$.each(data.buckets, function(j, bucket) {
|
||||||
|
var typeLabel = bucket.type === 'perSecond' ? '초당 임계치' : '추가 임계치';
|
||||||
|
var timeUnitText = getTimeUnitText(bucket.timeUnit);
|
||||||
|
var usagePercent = bucket.usagePercent || 0;
|
||||||
|
var progressClass = usagePercent < 50 ? 'low' : (usagePercent < 80 ? 'medium' : 'high');
|
||||||
|
|
||||||
|
html += '<div class="bucket-card">' +
|
||||||
|
'<div class="bucket-card-header">' +
|
||||||
|
'<span class="bucket-type">' + typeLabel + '</span>' +
|
||||||
|
'<span class="bucket-type-badge">' + timeUnitText + '</span>' +
|
||||||
|
'</div>' +
|
||||||
|
'<div class="bucket-progress-wrap">' +
|
||||||
|
'<div class="bucket-progress-bar">' +
|
||||||
|
'<div class="bucket-progress-fill ' + progressClass + '" style="width:' + usagePercent + '%"></div>' +
|
||||||
|
'</div>' +
|
||||||
|
'</div>' +
|
||||||
|
'<div class="bucket-stats">' +
|
||||||
|
'<span>사용량: <span class="bucket-stats-value">' + usagePercent.toFixed(1) + '%</span></span>' +
|
||||||
|
'<span>잔여: <span class="bucket-stats-value">' + formatNumber(bucket.availableTokens) + '</span> / ' + formatNumber(bucket.capacity) + '</span>' +
|
||||||
|
'</div>' +
|
||||||
|
'</div>';
|
||||||
|
});
|
||||||
|
html += '</div>';
|
||||||
|
return html;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTimeUnitText(timeUnit) {
|
||||||
|
var units = {SEC: '초', MIN: '분', HOU: '시간', DAY: '일', MON: '월'};
|
||||||
|
return units[timeUnit] || timeUnit || '-';
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatNumber(num) {
|
||||||
|
if (num === undefined || num === null) return '-';
|
||||||
|
return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
|
||||||
|
}
|
||||||
|
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
var returnUrl = getReturnUrlForReturn();
|
var returnUrl = getReturnUrlForReturn();
|
||||||
var key ="${param.name}";
|
var key = "${param.name}";
|
||||||
if (key != "" && key !="null"){
|
if (key != "" && key != "null") {
|
||||||
isDetail = true;
|
isDetail = true;
|
||||||
}
|
}
|
||||||
init(url,key,detail);
|
init(url, key, detail);
|
||||||
|
|
||||||
|
$("select[name=thresholdTimeUnit]").on('change', function() {
|
||||||
|
updateThresholdLabel();
|
||||||
|
});
|
||||||
|
|
||||||
$("#btn_modify").click(function(){
|
$("#btn_modify").click(function() {
|
||||||
var postData = $('#ajaxForm').serializeArray();
|
$(this).blur();
|
||||||
if (isDetail){
|
var confirmMsg = isDetail ? '변경사항을 저장하시겠습니까?' : '새 어댑터를 등록하시겠습니까?';
|
||||||
postData.push({ name: "cmd" , value:"UPDATE"});
|
showConfirm(confirmMsg, {
|
||||||
}else{
|
title: '저장 확인',
|
||||||
postData.push({ name: "cmd" , value:"INSERT"});
|
confirmText: '저장',
|
||||||
}
|
onConfirm: function() {
|
||||||
$.ajax({
|
var postData = $('#ajaxForm').serializeArray();
|
||||||
type : "POST",
|
if (isDetail) {
|
||||||
url:url,
|
postData.push({name: "cmd", value: "UPDATE"});
|
||||||
data:postData,
|
} else {
|
||||||
success:function(args){
|
postData.push({name: "cmd", value: "INSERT"});
|
||||||
alert("<%= localeMessage.getString("common.saveMsg") %>");
|
}
|
||||||
goNav(returnUrl);//LIST로 이동
|
var isInsert = !isDetail;
|
||||||
},
|
$.ajax({
|
||||||
error:function(e){
|
type: "POST",
|
||||||
alert(e.responseText);
|
url: url,
|
||||||
|
data: postData,
|
||||||
|
success: function(args) {
|
||||||
|
showAlert("<%= localeMessage.getString("common.saveMsg") %>", {
|
||||||
|
type: 'success',
|
||||||
|
title: '저장 완료',
|
||||||
|
onClose: isInsert ? function() { goNav(returnUrl); } : undefined
|
||||||
|
});
|
||||||
|
},
|
||||||
|
error: function(e) {
|
||||||
|
showAlert(e.responseText, {type: 'error'});
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
$("#btn_delete").click(function(){
|
|
||||||
|
|
||||||
if ( confirm( '<%=localeMessage.getString("common.confirmMsg")%>' ) != true ) return;
|
$("#btn_delete").click(function() {
|
||||||
|
$(this).blur();
|
||||||
var postData = $('#ajaxForm').serializeArray();
|
var adapterName = $('input[name=desc]').val() || $('input[name=name]').val();
|
||||||
postData.push({ name: "cmd" , value:"DELETE"});
|
showConfirm('<strong>[' + adapterName + ']</strong> 어댑터를 삭제하시겠습니까?', {
|
||||||
$.ajax({
|
type: 'error',
|
||||||
type : "POST",
|
title: '삭제 확인',
|
||||||
url:url,
|
confirmText: '삭제',
|
||||||
data:postData,
|
onConfirm: function() {
|
||||||
success:function(args){
|
var postData = $('#ajaxForm').serializeArray();
|
||||||
alert("<%= localeMessage.getString("common.deleteMsg") %>");
|
postData.push({name: "cmd", value: "DELETE"});
|
||||||
goNav(returnUrl);//LIST로 이동
|
$.ajax({
|
||||||
|
type: "POST",
|
||||||
},
|
url: url,
|
||||||
error:function(e){
|
data: postData,
|
||||||
alert(e.responseText);
|
success: function(args) {
|
||||||
|
showAlert("<%= localeMessage.getString("common.deleteMsg") %>", {
|
||||||
|
type: 'success',
|
||||||
|
title: '삭제 완료',
|
||||||
|
onClose: function() { goNav(returnUrl); }
|
||||||
|
});
|
||||||
|
},
|
||||||
|
error: function(e) {
|
||||||
|
showAlert(e.responseText, {type: 'error'});
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
$("#btn_previous").click(function(){
|
|
||||||
goNav(returnUrl);//LIST로 이동
|
$("#btn_previous").click(function() {
|
||||||
|
goNav(returnUrl);
|
||||||
});
|
});
|
||||||
|
|
||||||
buttonControl(isDetail);
|
buttonControl(isDetail);
|
||||||
titleControl(isDetail);
|
titleControl(isDetail);
|
||||||
});
|
|
||||||
|
|
||||||
|
$('.detail-tab').click(function() {
|
||||||
|
switchTab($(this).data('tab'));
|
||||||
|
});
|
||||||
|
|
||||||
|
if (isDetail) {
|
||||||
|
$('#tabStatus').show();
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#btn_refresh_bucket').click(function() {
|
||||||
|
loadBucketStatus();
|
||||||
|
});
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="right_box">
|
<div class="right_box">
|
||||||
<div class="content_top">
|
<div class="content_top">
|
||||||
<ul class="path">
|
<ul class="path">
|
||||||
<li><a href="#">${rmsMenuPath}</a></li>
|
<li><a href="#">${rmsMenuPath}</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div><!-- end content_top -->
|
</div><!-- end content_top -->
|
||||||
<div class="content_middle">
|
<div class="content_middle" id="content_middle">
|
||||||
<div class="search_wrap">
|
<div class="search_wrap">
|
||||||
<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="W" status="DETAIL,NEW"><i class="material-icons">save</i> <%= localeMessage.getString("button.modify") %></button>
|
<button type="button" class="cssbtn" id="btn_modify" level="W" status="DETAIL,NEW"><i class="material-icons">save</i> <%= localeMessage.getString("button.save") %></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>
|
||||||
<%-- <img src="<c:url value="/img/btn_delete.png"/>" alt="" id="btn_delete" level="W" status="DETAIL"/> --%>
|
</div>
|
||||||
<%-- <img src="<c:url value="/img/btn_modify.png"/>" alt="" id="btn_modify" level="W" status="DETAIL,NEW" /> --%>
|
|
||||||
<%-- <img src="<c:url value="/img/btn_previous.png"/>" alt="" id="btn_previous" level="R" status="DETAIL,NEW"/> --%>
|
|
||||||
</div>
|
|
||||||
<div class="title"><%= localeMessage.getString("infAdpConMan.title") %><span class="tooltip" ><%= localeMessage.getString("infAdpConMan.title") %></span></div>
|
|
||||||
|
|
||||||
<table id="grid" ></table>
|
<div class="title-wrap">
|
||||||
<div id="pager"></div>
|
<div class="title"><%= localeMessage.getString("infAdpConMan.title") %><span class="tooltip"><%= localeMessage.getString("infAdpConMan.title") %></span></div>
|
||||||
|
<span id="modifiedAtInfo" class="modified-badge" style="display:none;">
|
||||||
|
<i class="material-icons">update</i><span id="modifiedAtText">-</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- detail -->
|
<!-- 탭 네비게이션 -->
|
||||||
<form id="ajaxForm">
|
<div class="detail-tabs">
|
||||||
<table class="table_row" cellspacing="0">
|
<button type="button" class="detail-tab active" data-tab="config">
|
||||||
<tr>
|
<i class="material-icons">settings</i> 설정
|
||||||
<th style="width:20%;"><%= localeMessage.getString("infAdpConMan.adaNm") %></th>
|
</button>
|
||||||
<td><input type="text" name="name" /></td>
|
<button type="button" class="detail-tab" data-tab="status" id="tabStatus" style="display:none;">
|
||||||
</tr>
|
<i class="material-icons">analytics</i> 현황
|
||||||
<tr>
|
</button>
|
||||||
<th style="width:20%;"><%= localeMessage.getString("infAdpConMan.adaDes") %></th>
|
</div>
|
||||||
<td><input type="text" name="desc" /></td>
|
|
||||||
</tr>
|
<!-- 설정 탭 -->
|
||||||
<tr>
|
<div class="tab-content active" id="tabConfigContent">
|
||||||
<th style="width:20%;"><%= localeMessage.getString("infAdpConMan.thrPerSecond") %></th>
|
<form id="ajaxForm">
|
||||||
<td><input type="text" name="thresholdPerSecond" /></td>
|
<span class="section-label">기본 정보</span>
|
||||||
</tr>
|
<table class="form-table" cellspacing="0">
|
||||||
<tr>
|
<colgroup>
|
||||||
<th style="width:20%;"><%= localeMessage.getString("infAdpConMan.thr") %></th>
|
<col style="width:150px" />
|
||||||
<td><input type="text" name="threshold" /></td>
|
<col />
|
||||||
</tr>
|
</colgroup>
|
||||||
<tr>
|
<tbody>
|
||||||
<th style="width:20%;"><%= localeMessage.getString("infAdpConMan.thrTimeUnit") %></th>
|
<tr>
|
||||||
<td>
|
<th><%= localeMessage.getString("infAdpConMan.adaNm") %></th>
|
||||||
<div class="select-style">
|
<td><input type="text" name="name" style="width:300px; background-color: #ebebec;" /></td>
|
||||||
<select name="thresholdTimeUnit" />
|
</tr>
|
||||||
</div>
|
<tr>
|
||||||
</td>
|
<th><%= localeMessage.getString("infAdpConMan.adaDes") %></th>
|
||||||
</tr>
|
<td><input type="text" name="desc" style="width:300px; background-color: #ebebec;" /></td>
|
||||||
<tr>
|
</tr>
|
||||||
<th style="width:20%;"><%= localeMessage.getString("infAdpConMan.useYn") %></th>
|
<tr>
|
||||||
<td>
|
<th><%= localeMessage.getString("infAdpConMan.useYn") %></th>
|
||||||
<div class="select-style">
|
<td>
|
||||||
<select name="useYn" />
|
<select name="useYn" class="styled-select"></select>
|
||||||
</div>
|
</td>
|
||||||
</td>
|
</tr>
|
||||||
</tr>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</form>
|
|
||||||
|
|
||||||
</div><!-- end content_middle -->
|
<span class="section-label">유량제어 설정</span>
|
||||||
</div><!-- end right_box -->
|
<table class="form-table" cellspacing="0">
|
||||||
</body>
|
<colgroup>
|
||||||
|
<col style="width:150px" />
|
||||||
|
<col />
|
||||||
|
</colgroup>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<th>
|
||||||
|
<%= localeMessage.getString("infAdpConMan.thrPerSecond") %>
|
||||||
|
<span class="hint-icon"><i class="material-icons">help_outline</i>
|
||||||
|
<span class="hint-bubble">1초 단위의 순간 트래픽 제한입니다. 매 초마다 설정한 건수만큼 토큰이 리필되며, 초과 시 요청이 거부됩니다.</span>
|
||||||
|
</span>
|
||||||
|
</th>
|
||||||
|
<td>
|
||||||
|
<div class="threshold-group">
|
||||||
|
<input type="number" name="thresholdPerSecond" placeholder="0" />
|
||||||
|
<span class="unit">req/sec</span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>
|
||||||
|
<%= localeMessage.getString("infAdpConMan.thr") %>
|
||||||
|
<span class="hint-icon"><i class="material-icons">help_outline</i>
|
||||||
|
<span class="hint-bubble">장시간 누적 트래픽 제한입니다. 시간단위를 선택하면 해당 주기마다 토큰이 리필됩니다.</span>
|
||||||
|
</span>
|
||||||
|
</th>
|
||||||
|
<td>
|
||||||
|
<div class="threshold-group">
|
||||||
|
<select name="thresholdTimeUnit" class="styled-select"></select>
|
||||||
|
<input type="number" name="threshold" id="thresholdInput" placeholder="0" />
|
||||||
|
<span class="unit" id="thresholdUnit">requests</span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</form>
|
||||||
|
</div><!-- /tabConfigContent -->
|
||||||
|
|
||||||
|
<!-- 현황 탭 -->
|
||||||
|
<div class="tab-content" id="tabStatusContent">
|
||||||
|
<div class="bucket-status-wrap">
|
||||||
|
<div class="bucket-refresh-bar">
|
||||||
|
<span class="last-refresh">마지막 갱신: <span id="lastRefreshTime">-</span></span>
|
||||||
|
<button type="button" class="cssbtn" id="btn_refresh_bucket"><i class="material-icons">refresh</i> 새로고침</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 전체 합산 요약 -->
|
||||||
|
<div class="bucket-summary" id="bucketSummary" style="display:none;">
|
||||||
|
<div class="bucket-summary-title"><i class="material-icons">functions</i> 전체 인스턴스 합산</div>
|
||||||
|
<div class="bucket-summary-grid">
|
||||||
|
<div class="bucket-summary-item">
|
||||||
|
<span class="bucket-summary-label">활성 인스턴스</span>
|
||||||
|
<span class="bucket-summary-value" id="summaryInstanceCount">0<span class="unit">개</span></span>
|
||||||
|
</div>
|
||||||
|
<div class="bucket-summary-item" id="summaryPerSecondWrap">
|
||||||
|
<span class="bucket-summary-label">초당 임계치 (전체)</span>
|
||||||
|
<span class="bucket-summary-value" id="summaryPerSecond">0<span class="unit">req/sec</span></span>
|
||||||
|
<span class="bucket-summary-sub" id="summaryPerSecondCalc"></span>
|
||||||
|
</div>
|
||||||
|
<div class="bucket-summary-item" id="summaryThresholdWrap">
|
||||||
|
<span class="bucket-summary-label">추가 임계치 (전체)</span>
|
||||||
|
<span class="bucket-summary-value" id="summaryThreshold">0<span class="unit">req</span></span>
|
||||||
|
<span class="bucket-summary-sub" id="summaryThresholdCalc"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="bucket-server-list" id="bucketServerList">
|
||||||
|
<div class="bucket-empty-msg">
|
||||||
|
<i class="material-icons">hourglass_empty</i>
|
||||||
|
버킷 현황을 조회하려면 새로고침 버튼을 클릭하세요
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div><!-- /tabStatusContent -->
|
||||||
|
|
||||||
|
</div><!-- end content_middle -->
|
||||||
|
</div><!-- end right_box -->
|
||||||
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
||||||
|
|||||||
@@ -16,170 +16,501 @@
|
|||||||
<jsp:include page="/jsp/common/include/css.jsp"/>
|
<jsp:include page="/jsp/common/include/css.jsp"/>
|
||||||
<jsp:include page="/jsp/common/include/script.jsp"/>
|
<jsp:include page="/jsp/common/include/script.jsp"/>
|
||||||
|
|
||||||
<script language="javascript" >
|
<!-- 페이지 커스텀 스타일 (fragment) -->
|
||||||
|
<jsp:include page="fragments/inflowGroupDetailStyle.jsp"/>
|
||||||
|
|
||||||
|
<script language="javascript">
|
||||||
var url ='<c:url value="/onl/admin/inflow/inflowClientControlMan.json" />';
|
var url ='<c:url value="/onl/admin/inflow/inflowClientControlMan.json" />';
|
||||||
var url_view ='<c:url value="/onl/admin/inflow/inflowClientControlMan.view" />';
|
var url_view ='<c:url value="/onl/admin/inflow/inflowClientControlMan.view" />';
|
||||||
var isDetail = false;
|
var isDetail = false;
|
||||||
function init(url,key,callback){
|
|
||||||
|
function updateThresholdLabel() {
|
||||||
|
var timeUnit = $('select[name=thresholdTimeUnit]').val();
|
||||||
|
var $input = $('#thresholdInput');
|
||||||
|
var $unit = $('#thresholdUnit');
|
||||||
|
|
||||||
|
var unitLabels = {
|
||||||
|
'': '',
|
||||||
|
'SEC': 'req/sec',
|
||||||
|
'MIN': 'req/min',
|
||||||
|
'HOU': 'req/hour',
|
||||||
|
'DAY': 'req/day',
|
||||||
|
'MON': 'req/month'
|
||||||
|
};
|
||||||
|
|
||||||
|
$unit.text(unitLabels[timeUnit] || 'requests');
|
||||||
|
|
||||||
|
if (timeUnit === '') {
|
||||||
|
$input.prop('disabled', true).val('');
|
||||||
|
$unit.css('visibility', 'hidden');
|
||||||
|
} else {
|
||||||
|
$input.prop('disabled', false);
|
||||||
|
$unit.css('visibility', 'visible');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function init(url, key, callback) {
|
||||||
$.ajax({
|
$.ajax({
|
||||||
type : "POST",
|
type: "POST",
|
||||||
url:url,
|
url: url,
|
||||||
dataType:"json",
|
dataType: "json",
|
||||||
data:{cmd: 'LIST_INIT_COMBO'},
|
data: {cmd: 'LIST_INIT_COMBO'},
|
||||||
success:function(json){
|
success: function(json) {
|
||||||
new makeOptions("CODE","NAME").setObj($("select[name=useYn]")).setData(json.useYnRows).setFormat(codeName3OptionFormat).rendering();
|
new makeOptions("CODE","NAME").setObj($("select[name=useYn]")).setData(json.useYnRows).setFormat(codeName3OptionFormat).rendering();
|
||||||
new makeOptions("CODE","NAME").setObj($("select[name=thresholdTimeUnit]")).setNoValueInclude(true).setData(json.timeUnitRows).setFormat(codeName3OptionFormat).rendering();
|
new makeOptions("CODE","NAME").setObj($("select[name=thresholdTimeUnit]")).setNoValueInclude(true).setData(json.timeUnitRows).setFormat(codeName3OptionFormat).rendering();
|
||||||
|
updateThresholdLabel();
|
||||||
|
|
||||||
if(typeof callback === 'function') {
|
if (typeof callback === 'function') {
|
||||||
callback(url,key);
|
callback(url, key);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
error:function(e){
|
error: function(e) {
|
||||||
alert(e.responseText);
|
alert(e.responseText);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
function detail(url,key){
|
|
||||||
if (!isDetail)return;
|
|
||||||
$.ajax({
|
|
||||||
type : "POST",
|
|
||||||
url:url,
|
|
||||||
dataType:"json",
|
|
||||||
data:{cmd: 'DETAIL', name : key},
|
|
||||||
success:function(json){
|
|
||||||
var data = json;
|
|
||||||
$("input[name=name]").attr('readonly',true);
|
|
||||||
$("input[name=desc]").attr('readonly',true);
|
|
||||||
|
|
||||||
$("#ajaxForm input[type!=radio],#ajaxForm select,#ajaxForm textarea").each(function(){
|
function detail(url, key) {
|
||||||
|
if (!isDetail) return;
|
||||||
|
$.ajax({
|
||||||
|
type: "POST",
|
||||||
|
url: url,
|
||||||
|
dataType: "json",
|
||||||
|
data: {cmd: 'DETAIL', name: key},
|
||||||
|
success: function(json) {
|
||||||
|
var data = json;
|
||||||
|
$("input[name=name]").attr('readonly', true);
|
||||||
|
$("input[name=desc]").attr('readonly', true);
|
||||||
|
|
||||||
|
$("#ajaxForm input[type!=radio],#ajaxForm select,#ajaxForm textarea").each(function() {
|
||||||
var name = $(this).attr("name");
|
var name = $(this).attr("name");
|
||||||
var tag = $(this).prop("tagName").toLowerCase();
|
var tag = $(this).prop("tagName").toLowerCase();
|
||||||
$(tag+"[name="+name+"]").val(data[name.toUpperCase()]);
|
$(tag+"[name="+name+"]").val(data[name.toUpperCase()]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
updateThresholdLabel();
|
||||||
|
|
||||||
|
var modifiedAt = data.MODIFIEDAT || data.modifiedAt;
|
||||||
|
if (modifiedAt) {
|
||||||
|
$('#modifiedAtText').text(modifiedAt);
|
||||||
|
$('#modifiedAtInfo').show();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
error:function(e){
|
error: function(e) {
|
||||||
alert(e.responseText);
|
alert(e.responseText);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function switchTab(tabName) {
|
||||||
|
$('.detail-tab').removeClass('active');
|
||||||
|
$('.detail-tab[data-tab="' + tabName + '"]').addClass('active');
|
||||||
|
$('.tab-content').removeClass('active');
|
||||||
|
$('#tab' + tabName.charAt(0).toUpperCase() + tabName.slice(1) + 'Content').addClass('active');
|
||||||
|
|
||||||
|
if (tabName === 'status' && isDetail) {
|
||||||
|
loadBucketStatus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//버킷 현황 조회
|
||||||
|
function loadBucketStatus() {
|
||||||
|
|
||||||
|
$('#bucketServerList').html('<div class="bucket-empty-msg"><i class="material-icons">hourglass_empty</i>조회 중...</div>');
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
type: 'POST',
|
||||||
|
url: url,
|
||||||
|
data: { cmd: 'LIST_BUCKET_STATUS', clientId: "${param.name}" },
|
||||||
|
dataType: 'json',
|
||||||
|
success: function(result) {
|
||||||
|
renderBucketStatus(result.servers || []);
|
||||||
|
$('#lastRefreshTime').text(formatDateTime(new Date()));
|
||||||
|
},
|
||||||
|
error: function(e) {
|
||||||
|
$('#bucketServerList').html('<div class="bucket-empty-msg bucket-error-msg"><i class="material-icons">error_outline</i>조회 실패: ' + e.statusText + '</div>');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatDateTime(date) {
|
||||||
|
var h = date.getHours();
|
||||||
|
var m = date.getMinutes();
|
||||||
|
var s = date.getSeconds();
|
||||||
|
return (h < 10 ? '0' + h : h) + ':' + (m < 10 ? '0' + m : m) + ':' + (s < 10 ? '0' + s : s);
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderBucketStatus(servers) {
|
||||||
|
var container = $('#bucketServerList');
|
||||||
|
container.empty();
|
||||||
|
|
||||||
|
if (servers.length === 0) {
|
||||||
|
container.html('<div class="bucket-empty-msg"><i class="material-icons">dns</i>등록된 서버가 없습니다</div>');
|
||||||
|
$('#bucketSummary').hide();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 전체 합산 계산
|
||||||
|
var onlineCount = 0;
|
||||||
|
var perSecondCapacity = 0;
|
||||||
|
var thresholdCapacity = 0;
|
||||||
|
var thresholdTimeUnit = '';
|
||||||
|
|
||||||
|
$.each(servers, function(i, server) {
|
||||||
|
if (server.status === 'online' && server.data && server.data.buckets) {
|
||||||
|
onlineCount++;
|
||||||
|
$.each(server.data.buckets, function(j, bucket) {
|
||||||
|
if (bucket.type === 'perSecond') {
|
||||||
|
perSecondCapacity += bucket.capacity || 0;
|
||||||
|
} else if (bucket.type === 'threshold') {
|
||||||
|
thresholdCapacity += bucket.capacity || 0;
|
||||||
|
if (!thresholdTimeUnit) thresholdTimeUnit = bucket.timeUnit;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 요약 영역 표시
|
||||||
|
if (onlineCount > 0) {
|
||||||
|
$('#summaryInstanceCount').html(onlineCount + '<span class="unit">개</span>');
|
||||||
|
|
||||||
|
var configPerSecond = parseInt($('input[name=thresholdPerSecond]').val()) || 0;
|
||||||
|
var configThreshold = parseInt($('input[name=threshold]').val()) || 0;
|
||||||
|
|
||||||
|
if (configPerSecond > 0) {
|
||||||
|
$('#summaryPerSecond').html(formatNumber(perSecondCapacity) + '<span class="unit">req/sec</span>');
|
||||||
|
$('#summaryPerSecondCalc').text(formatNumber(configPerSecond) + ' × ' + onlineCount + ' 인스턴스');
|
||||||
|
$('#summaryPerSecondWrap').show();
|
||||||
|
} else {
|
||||||
|
$('#summaryPerSecondWrap').hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (configThreshold > 0) {
|
||||||
|
var timeUnitLabel = getTimeUnitText(thresholdTimeUnit);
|
||||||
|
$('#summaryThreshold').html(formatNumber(thresholdCapacity) + '<span class="unit">req/' + timeUnitLabel + '</span>');
|
||||||
|
$('#summaryThresholdCalc').text(formatNumber(configThreshold) + ' × ' + onlineCount + ' 인스턴스');
|
||||||
|
$('#summaryThresholdWrap').show();
|
||||||
|
} else {
|
||||||
|
$('#summaryThresholdWrap').hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#bucketSummary').show();
|
||||||
|
} else {
|
||||||
|
$('#bucketSummary').hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
$.each(servers, function(i, server) {
|
||||||
|
var statusClass = server.status || 'offline';
|
||||||
|
var statusText = { online: '정상', offline: '오프라인', no_data: '데이터 없음' };
|
||||||
|
|
||||||
|
var card = '<div class="bucket-server-card">' +
|
||||||
|
'<div class="bucket-server-header">' +
|
||||||
|
'<div>' +
|
||||||
|
'<span class="bucket-server-name">' + server.serverName + '</span> ' +
|
||||||
|
'<span class="bucket-server-ip">' + server.serverIp + ':' + server.serverPort + '</span>' +
|
||||||
|
'</div>' +
|
||||||
|
'<span class="bucket-server-status ' + statusClass + '">' + (statusText[statusClass] || statusClass) + '</span>' +
|
||||||
|
'</div>' +
|
||||||
|
'<div class="bucket-server-body">' + renderBucketBody(server) + '</div>' +
|
||||||
|
'</div>';
|
||||||
|
container.append(card);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderBucketBody(server) {
|
||||||
|
if (server.status === 'offline') {
|
||||||
|
return '<div class="bucket-error-msg">' + (server.message || '서버 연결 실패') + '</div>';
|
||||||
|
}
|
||||||
|
if (server.status === 'no_data') {
|
||||||
|
return '<div class="bucket-empty-msg" style="padding:10px;"><i class="material-icons" style="font-size:18px;">info_outline</i> ' + (server.message || '그룹이 로드되지 않음') + '</div>';
|
||||||
|
}
|
||||||
|
|
||||||
|
var data = server.data;
|
||||||
|
if (!data || !data.buckets || data.buckets.length === 0) {
|
||||||
|
return '<div class="bucket-empty-msg" style="padding:10px;">버킷 정보 없음</div>';
|
||||||
|
}
|
||||||
|
|
||||||
|
var html = '<div class="bucket-info-grid">';
|
||||||
|
$.each(data.buckets, function(j, bucket) {
|
||||||
|
var typeLabel = bucket.type === 'perSecond' ? '초당 임계치' : '추가 임계치';
|
||||||
|
var timeUnitText = getTimeUnitText(bucket.timeUnit);
|
||||||
|
var usagePercent = bucket.usagePercent || 0;
|
||||||
|
var progressClass = usagePercent < 50 ? 'low' : (usagePercent < 80 ? 'medium' : 'high');
|
||||||
|
|
||||||
|
html += '<div class="bucket-card">' +
|
||||||
|
'<div class="bucket-card-header">' +
|
||||||
|
'<span class="bucket-type">' + typeLabel + '</span>' +
|
||||||
|
'<span class="bucket-type-badge">' + timeUnitText + '</span>' +
|
||||||
|
'</div>' +
|
||||||
|
'<div class="bucket-progress-wrap">' +
|
||||||
|
'<div class="bucket-progress-bar">' +
|
||||||
|
'<div class="bucket-progress-fill ' + progressClass + '" style="width:' + usagePercent + '%"></div>' +
|
||||||
|
'</div>' +
|
||||||
|
'</div>' +
|
||||||
|
'<div class="bucket-stats">' +
|
||||||
|
'<span>사용량: <span class="bucket-stats-value">' + usagePercent.toFixed(1) + '%</span></span>' +
|
||||||
|
'<span>잔여: <span class="bucket-stats-value">' + formatNumber(bucket.availableTokens) + '</span> / ' + formatNumber(bucket.capacity) + '</span>' +
|
||||||
|
'</div>' +
|
||||||
|
'</div>';
|
||||||
|
});
|
||||||
|
html += '</div>';
|
||||||
|
return html;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTimeUnitText(timeUnit) {
|
||||||
|
var units = {SEC: '초', MIN: '분', HOU: '시간', DAY: '일', MON: '월'};
|
||||||
|
return units[timeUnit] || timeUnit || '-';
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatNumber(num) {
|
||||||
|
if (num === undefined || num === null) return '-';
|
||||||
|
return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
|
||||||
|
}
|
||||||
|
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
var returnUrl = getReturnUrlForReturn();
|
var returnUrl = getReturnUrlForReturn();
|
||||||
var key ="${param.name}";
|
var key = "${param.name}";
|
||||||
if (key != "" && key !="null"){
|
if (key != "" && key != "null") {
|
||||||
isDetail = true;
|
isDetail = true;
|
||||||
}
|
}
|
||||||
init(url,key,detail);
|
init(url, key, detail);
|
||||||
|
|
||||||
|
$("select[name=thresholdTimeUnit]").on('change', function() {
|
||||||
|
updateThresholdLabel();
|
||||||
|
});
|
||||||
|
|
||||||
$("#btn_modify").click(function(){
|
$("#btn_modify").click(function() {
|
||||||
var postData = $('#ajaxForm').serializeArray();
|
$(this).blur();
|
||||||
if (isDetail){
|
var confirmMsg = isDetail ? '변경사항을 저장하시겠습니까?' : '새 클라이언트를 등록하시겠습니까?';
|
||||||
postData.push({ name: "cmd" , value:"UPDATE"});
|
showConfirm(confirmMsg, {
|
||||||
}else{
|
title: '저장 확인',
|
||||||
postData.push({ name: "cmd" , value:"INSERT"});
|
confirmText: '저장',
|
||||||
}
|
onConfirm: function() {
|
||||||
$.ajax({
|
var postData = $('#ajaxForm').serializeArray();
|
||||||
type : "POST",
|
if (isDetail) {
|
||||||
url:url,
|
postData.push({name: "cmd", value: "UPDATE"});
|
||||||
data:postData,
|
} else {
|
||||||
success:function(args){
|
postData.push({name: "cmd", value: "INSERT"});
|
||||||
alert("<%= localeMessage.getString("common.saveMsg") %>");
|
}
|
||||||
goNav(returnUrl);//LIST로 이동
|
var isInsert = !isDetail;
|
||||||
},
|
$.ajax({
|
||||||
error:function(e){
|
type: "POST",
|
||||||
alert(e.responseText);
|
url: url,
|
||||||
|
data: postData,
|
||||||
|
success: function(args) {
|
||||||
|
showAlert("<%= localeMessage.getString("common.saveMsg") %>", {
|
||||||
|
type: 'success',
|
||||||
|
title: '저장 완료',
|
||||||
|
onClose: isInsert ? function() { goNav(returnUrl); } : undefined
|
||||||
|
});
|
||||||
|
},
|
||||||
|
error: function(e) {
|
||||||
|
showAlert(e.responseText, {type: 'error'});
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
$("#btn_delete").click(function(){
|
|
||||||
|
|
||||||
if ( confirm( '<%=localeMessage.getString("common.confirmMsg")%>' ) != true ) return;
|
$("#btn_delete").click(function() {
|
||||||
|
$(this).blur();
|
||||||
var postData = $('#ajaxForm').serializeArray();
|
var clientName = $('input[name=desc]').val() || $('input[name=name]').val();
|
||||||
postData.push({ name: "cmd" , value:"DELETE"});
|
showConfirm('<strong>[' + clientName + ']</strong> 클라이언트를 삭제하시겠습니까?', {
|
||||||
$.ajax({
|
type: 'error',
|
||||||
type : "POST",
|
title: '삭제 확인',
|
||||||
url:url,
|
confirmText: '삭제',
|
||||||
data:postData,
|
onConfirm: function() {
|
||||||
success:function(args){
|
var postData = $('#ajaxForm').serializeArray();
|
||||||
alert("<%= localeMessage.getString("common.deleteMsg") %>");
|
postData.push({name: "cmd", value: "DELETE"});
|
||||||
goNav(returnUrl);//LIST로 이동
|
$.ajax({
|
||||||
|
type: "POST",
|
||||||
},
|
url: url,
|
||||||
error:function(e){
|
data: postData,
|
||||||
alert(e.responseText);
|
success: function(args) {
|
||||||
|
showAlert("<%= localeMessage.getString("common.deleteMsg") %>", {
|
||||||
|
type: 'success',
|
||||||
|
title: '삭제 완료',
|
||||||
|
onClose: function() { goNav(returnUrl); }
|
||||||
|
});
|
||||||
|
},
|
||||||
|
error: function(e) {
|
||||||
|
showAlert(e.responseText, {type: 'error'});
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
$("#btn_previous").click(function(){
|
|
||||||
goNav(returnUrl);//LIST로 이동
|
$("#btn_previous").click(function() {
|
||||||
|
goNav(returnUrl);
|
||||||
});
|
});
|
||||||
|
|
||||||
buttonControl(isDetail);
|
buttonControl(isDetail);
|
||||||
titleControl(isDetail);
|
titleControl(isDetail);
|
||||||
});
|
|
||||||
|
|
||||||
|
$('.detail-tab').click(function() {
|
||||||
|
switchTab($(this).data('tab'));
|
||||||
|
});
|
||||||
|
|
||||||
|
if (isDetail) {
|
||||||
|
$('#tabStatus').show();
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#btn_refresh_bucket').click(function() {
|
||||||
|
loadBucketStatus();
|
||||||
|
});
|
||||||
|
|
||||||
|
$(document).keydown(function(e) {
|
||||||
|
if (e.keyCode === 27) {
|
||||||
|
// ESC 키 처리 (필요시 모달 닫기 등)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="right_box">
|
<div class="right_box">
|
||||||
<div class="content_top">
|
<div class="content_top">
|
||||||
<ul class="path">
|
<ul class="path">
|
||||||
<li><a href="#">${rmsMenuPath}</a></li>
|
<li><a href="#">${rmsMenuPath}</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div><!-- end content_top -->
|
</div><!-- end content_top -->
|
||||||
<div class="content_middle">
|
<div class="content_middle" id="content_middle">
|
||||||
<div class="search_wrap">
|
<div class="search_wrap">
|
||||||
<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="W" status="DETAIL,NEW"><i class="material-icons">save</i> <%= localeMessage.getString("button.modify") %></button>
|
<button type="button" class="cssbtn" id="btn_modify" level="W" status="DETAIL,NEW"><i class="material-icons">save</i> <%= localeMessage.getString("button.save") %></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">클라이언트 유량제어 <span class="tooltip" >클라이언트 유량제어 </span></div>
|
|
||||||
|
|
||||||
<table id="grid" ></table>
|
<div class="title-wrap">
|
||||||
<div id="pager"></div>
|
<div class="title">클라이언트 유량제어<span class="tooltip">클라이언트 유량제어</span></div>
|
||||||
|
<span id="modifiedAtInfo" class="modified-badge" style="display:none;">
|
||||||
|
<i class="material-icons">update</i><span id="modifiedAtText">-</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- detail -->
|
<!-- 탭 네비게이션 -->
|
||||||
<form id="ajaxForm">
|
<div class="detail-tabs">
|
||||||
<table class="table_row" cellspacing="0">
|
<button type="button" class="detail-tab active" data-tab="config">
|
||||||
<tr>
|
<i class="material-icons">settings</i> 설정
|
||||||
<th style="width:20%;">클라이언트 ID</th>
|
</button>
|
||||||
<td><input type="text" name="name" /></td>
|
<button type="button" class="detail-tab" data-tab="status" id="tabStatus" style="display:none;">
|
||||||
</tr>
|
<i class="material-icons">analytics</i> 현황
|
||||||
<tr>
|
</button>
|
||||||
<th style="width:20%;">클라이언트명</th>
|
</div>
|
||||||
<td><input type="text" name="desc" /></td>
|
|
||||||
</tr>
|
<!-- 설정 탭 -->
|
||||||
<tr>
|
<div class="tab-content active" id="tabConfigContent">
|
||||||
<th style="width:20%;"><%= localeMessage.getString("infAdpConMan.thrPerSecond") %></th>
|
<form id="ajaxForm">
|
||||||
<td><input type="text" name="thresholdPerSecond" /></td>
|
<span class="section-label">기본 정보</span>
|
||||||
</tr>
|
<table class="form-table" cellspacing="0">
|
||||||
<tr>
|
<colgroup>
|
||||||
<th style="width:20%;"><%= localeMessage.getString("infAdpConMan.thr") %></th>
|
<col style="width:150px" />
|
||||||
<td><input type="text" name="threshold" /></td>
|
<col />
|
||||||
</tr>
|
</colgroup>
|
||||||
<tr>
|
<tbody>
|
||||||
<th style="width:20%;"><%= localeMessage.getString("infAdpConMan.thrTimeUnit") %></th>
|
<tr>
|
||||||
<td>
|
<th>클라이언트 ID</th>
|
||||||
<div class="select-style">
|
<td><input type="text" name="name" style="width:300px; background-color: #ebebec;" /></td>
|
||||||
<select name="thresholdTimeUnit" />
|
</tr>
|
||||||
</div>
|
<tr>
|
||||||
</td>
|
<th>클라이언트명</th>
|
||||||
</tr>
|
<td><input type="text" name="desc" style="width:300px; background-color: #ebebec;" /></td>
|
||||||
<tr>
|
</tr>
|
||||||
<th style="width:20%;"><%= localeMessage.getString("infAdpConMan.useYn") %></th>
|
<tr>
|
||||||
<td>
|
<th><%= localeMessage.getString("infAdpConMan.useYn") %></th>
|
||||||
<div class="select-style">
|
<td>
|
||||||
<select name="useYn" />
|
<select name="useYn" class="styled-select"></select>
|
||||||
</div>
|
</td>
|
||||||
</td>
|
</tr>
|
||||||
</tr>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</form>
|
|
||||||
|
|
||||||
</div><!-- end content_middle -->
|
<span class="section-label">유량제어 설정</span>
|
||||||
</div><!-- end right_box -->
|
<table class="form-table" cellspacing="0">
|
||||||
</body>
|
<colgroup>
|
||||||
|
<col style="width:150px" />
|
||||||
|
<col />
|
||||||
|
</colgroup>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<th>
|
||||||
|
<%= localeMessage.getString("infAdpConMan.thrPerSecond") %>
|
||||||
|
<span class="hint-icon"><i class="material-icons">help_outline</i>
|
||||||
|
<span class="hint-bubble">1초 단위의 순간 트래픽 제한입니다. 매 초마다 설정한 건수만큼 토큰이 리필되며, 초과 시 요청이 거부됩니다.</span>
|
||||||
|
</span>
|
||||||
|
</th>
|
||||||
|
<td>
|
||||||
|
<div class="threshold-group">
|
||||||
|
<input type="number" name="thresholdPerSecond" placeholder="0" />
|
||||||
|
<span class="unit">req/sec</span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>
|
||||||
|
<%= localeMessage.getString("infAdpConMan.thr") %>
|
||||||
|
<span class="hint-icon"><i class="material-icons">help_outline</i>
|
||||||
|
<span class="hint-bubble">장시간 누적 트래픽 제한입니다. 시간단위를 선택하면 해당 주기마다 토큰이 리필됩니다.</span>
|
||||||
|
</span>
|
||||||
|
</th>
|
||||||
|
<td>
|
||||||
|
<div class="threshold-group">
|
||||||
|
<select name="thresholdTimeUnit" class="styled-select"></select>
|
||||||
|
<input type="number" name="threshold" id="thresholdInput" placeholder="0" />
|
||||||
|
<span class="unit" id="thresholdUnit">requests</span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</form>
|
||||||
|
</div><!-- /tabConfigContent -->
|
||||||
|
|
||||||
|
<!-- 현황 탭 -->
|
||||||
|
<div class="tab-content" id="tabStatusContent">
|
||||||
|
<div class="bucket-status-wrap">
|
||||||
|
<div class="bucket-refresh-bar">
|
||||||
|
<span class="last-refresh">마지막 갱신: <span id="lastRefreshTime">-</span></span>
|
||||||
|
<button type="button" class="cssbtn" id="btn_refresh_bucket"><i class="material-icons">refresh</i> 새로고침</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 전체 합산 요약 -->
|
||||||
|
<div class="bucket-summary" id="bucketSummary" style="display:none;">
|
||||||
|
<div class="bucket-summary-title"><i class="material-icons">functions</i> 전체 인스턴스 합산</div>
|
||||||
|
<div class="bucket-summary-grid">
|
||||||
|
<div class="bucket-summary-item">
|
||||||
|
<span class="bucket-summary-label">활성 인스턴스</span>
|
||||||
|
<span class="bucket-summary-value" id="summaryInstanceCount">0<span class="unit">개</span></span>
|
||||||
|
</div>
|
||||||
|
<div class="bucket-summary-item" id="summaryPerSecondWrap">
|
||||||
|
<span class="bucket-summary-label">초당 임계치 (전체)</span>
|
||||||
|
<span class="bucket-summary-value" id="summaryPerSecond">0<span class="unit">req/sec</span></span>
|
||||||
|
<span class="bucket-summary-sub" id="summaryPerSecondCalc"></span>
|
||||||
|
</div>
|
||||||
|
<div class="bucket-summary-item" id="summaryThresholdWrap">
|
||||||
|
<span class="bucket-summary-label">추가 임계치 (전체)</span>
|
||||||
|
<span class="bucket-summary-value" id="summaryThreshold">0<span class="unit">req</span></span>
|
||||||
|
<span class="bucket-summary-sub" id="summaryThresholdCalc"></span>
|
||||||
|
</div>
|
||||||
|
<div class="bucket-summary-item">
|
||||||
|
<span class="bucket-summary-label">적용 API</span>
|
||||||
|
<span class="bucket-summary-value" id="summaryActiveApi">0<span class="unit">개</span></span>
|
||||||
|
<span class="bucket-summary-sub" id="summaryActiveApiList"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="bucket-server-list" id="bucketServerList">
|
||||||
|
<div class="bucket-empty-msg">
|
||||||
|
<i class="material-icons">hourglass_empty</i>
|
||||||
|
버킷 현황을 조회하려면 새로고침 버튼을 클릭하세요
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div><!-- /tabStatusContent -->
|
||||||
|
|
||||||
|
</div><!-- end content_middle -->
|
||||||
|
</div><!-- end right_box -->
|
||||||
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
||||||
|
|||||||
@@ -16,173 +16,486 @@
|
|||||||
<jsp:include page="/jsp/common/include/css.jsp"/>
|
<jsp:include page="/jsp/common/include/css.jsp"/>
|
||||||
<jsp:include page="/jsp/common/include/script.jsp"/>
|
<jsp:include page="/jsp/common/include/script.jsp"/>
|
||||||
|
|
||||||
<script language="javascript" >
|
<!-- 페이지 커스텀 스타일 (fragment) -->
|
||||||
|
<jsp:include page="fragments/inflowGroupDetailStyle.jsp"/>
|
||||||
|
|
||||||
|
<script language="javascript">
|
||||||
var url ='<c:url value="/onl/admin/inflow/inflowInterfaceControlMan.json" />';
|
var url ='<c:url value="/onl/admin/inflow/inflowInterfaceControlMan.json" />';
|
||||||
var url_view ='<c:url value="/onl/admin/inflow/inflowInterfaceControlMan.view" />';
|
var url_view ='<c:url value="/onl/admin/inflow/inflowInterfaceControlMan.view" />';
|
||||||
var isDetail = false;
|
var isDetail = false;
|
||||||
function init(url,key,callback){
|
|
||||||
|
function updateThresholdLabel() {
|
||||||
|
var timeUnit = $('select[name=thresholdTimeUnit]').val();
|
||||||
|
var $input = $('#thresholdInput');
|
||||||
|
var $unit = $('#thresholdUnit');
|
||||||
|
|
||||||
|
var unitLabels = {
|
||||||
|
'': '',
|
||||||
|
'SEC': 'req/sec',
|
||||||
|
'MIN': 'req/min',
|
||||||
|
'HOU': 'req/hour',
|
||||||
|
'DAY': 'req/day',
|
||||||
|
'MON': 'req/month'
|
||||||
|
};
|
||||||
|
|
||||||
|
$unit.text(unitLabels[timeUnit] || 'requests');
|
||||||
|
|
||||||
|
if (timeUnit === '') {
|
||||||
|
$input.prop('disabled', true).val('');
|
||||||
|
$unit.css('visibility', 'hidden');
|
||||||
|
} else {
|
||||||
|
$input.prop('disabled', false);
|
||||||
|
$unit.css('visibility', 'visible');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function init(url, key, callback) {
|
||||||
$.ajax({
|
$.ajax({
|
||||||
type : "POST",
|
type: "POST",
|
||||||
url:url,
|
url: url,
|
||||||
dataType:"json",
|
dataType: "json",
|
||||||
data:{cmd: 'LIST_INIT_COMBO'},
|
data: {cmd: 'LIST_INIT_COMBO'},
|
||||||
success:function(json){
|
success: function(json) {
|
||||||
new makeOptions("CODE","NAME").setObj($("select[name=useYn]")).setData(json.useYnRows).setFormat(codeName3OptionFormat).rendering();
|
new makeOptions("CODE","NAME").setObj($("select[name=useYn]")).setData(json.useYnRows).setFormat(codeName3OptionFormat).rendering();
|
||||||
new makeOptions("CODE","NAME").setObj($("select[name=thresholdTimeUnit]")).setNoValueInclude(true).setData(json.timeUnitRows).setFormat(codeName3OptionFormat).rendering();
|
new makeOptions("CODE","NAME").setObj($("select[name=thresholdTimeUnit]")).setNoValueInclude(true).setData(json.timeUnitRows).setFormat(codeName3OptionFormat).rendering();
|
||||||
|
updateThresholdLabel();
|
||||||
|
|
||||||
if(typeof callback === 'function') {
|
if (typeof callback === 'function') {
|
||||||
callback(url,key);
|
callback(url, key);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
error:function(e){
|
error: function(e) {
|
||||||
alert(e.responseText);
|
alert(e.responseText);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
function detail(url,key){
|
|
||||||
if (!isDetail)return;
|
|
||||||
$.ajax({
|
|
||||||
type : "POST",
|
|
||||||
url:url,
|
|
||||||
dataType:"json",
|
|
||||||
data:{cmd: 'DETAIL', name : key},
|
|
||||||
success:function(json){
|
|
||||||
var data = json;
|
|
||||||
$("input[name=name]").attr('readonly',true);
|
|
||||||
$("input[name=desc]").attr('readonly',true);
|
|
||||||
|
|
||||||
$("#ajaxForm input[type!=radio],#ajaxForm select,#ajaxForm textarea").each(function(){
|
function detail(url, key) {
|
||||||
|
if (!isDetail) return;
|
||||||
|
$.ajax({
|
||||||
|
type: "POST",
|
||||||
|
url: url,
|
||||||
|
dataType: "json",
|
||||||
|
data: {cmd: 'DETAIL', name: key},
|
||||||
|
success: function(json) {
|
||||||
|
var data = json;
|
||||||
|
$("input[name=name]").attr('readonly', true);
|
||||||
|
$("input[name=desc]").attr('readonly', true);
|
||||||
|
|
||||||
|
$("#ajaxForm input[type!=radio],#ajaxForm select,#ajaxForm textarea").each(function() {
|
||||||
var name = $(this).attr("name");
|
var name = $(this).attr("name");
|
||||||
var tag = $(this).prop("tagName").toLowerCase();
|
var tag = $(this).prop("tagName").toLowerCase();
|
||||||
$(tag+"[name="+name+"]").val(data[name.toUpperCase()]);
|
$(tag+"[name="+name+"]").val(data[name.toUpperCase()]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
updateThresholdLabel();
|
||||||
|
|
||||||
|
var modifiedAt = data.MODIFIEDAT || data.modifiedAt;
|
||||||
|
if (modifiedAt) {
|
||||||
|
$('#modifiedAtText').text(modifiedAt);
|
||||||
|
$('#modifiedAtInfo').show();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
error:function(e){
|
error: function(e) {
|
||||||
alert(e.responseText);
|
alert(e.responseText);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function switchTab(tabName) {
|
||||||
|
$('.detail-tab').removeClass('active');
|
||||||
|
$('.detail-tab[data-tab="' + tabName + '"]').addClass('active');
|
||||||
|
$('.tab-content').removeClass('active');
|
||||||
|
$('#tab' + tabName.charAt(0).toUpperCase() + tabName.slice(1) + 'Content').addClass('active');
|
||||||
|
|
||||||
|
if (tabName === 'status' && isDetail) {
|
||||||
|
loadBucketStatus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadBucketStatus() {
|
||||||
|
$('#bucketServerList').html('<div class="bucket-empty-msg"><i class="material-icons">hourglass_empty</i>조회 중...</div>');
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
type: 'POST',
|
||||||
|
url: url,
|
||||||
|
data: { cmd: 'LIST_BUCKET_STATUS', interfaceId: "${param.name}" },
|
||||||
|
dataType: 'json',
|
||||||
|
success: function(result) {
|
||||||
|
renderBucketStatus(result.servers || []);
|
||||||
|
$('#lastRefreshTime').text(formatDateTime(new Date()));
|
||||||
|
},
|
||||||
|
error: function(e) {
|
||||||
|
$('#bucketServerList').html('<div class="bucket-empty-msg bucket-error-msg"><i class="material-icons">error_outline</i>조회 실패: ' + e.statusText + '</div>');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatDateTime(date) {
|
||||||
|
var h = date.getHours();
|
||||||
|
var m = date.getMinutes();
|
||||||
|
var s = date.getSeconds();
|
||||||
|
return (h < 10 ? '0' + h : h) + ':' + (m < 10 ? '0' + m : m) + ':' + (s < 10 ? '0' + s : s);
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderBucketStatus(servers) {
|
||||||
|
var container = $('#bucketServerList');
|
||||||
|
container.empty();
|
||||||
|
|
||||||
|
if (servers.length === 0) {
|
||||||
|
container.html('<div class="bucket-empty-msg"><i class="material-icons">dns</i>등록된 서버가 없습니다</div>');
|
||||||
|
$('#bucketSummary').hide();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var onlineCount = 0;
|
||||||
|
var perSecondCapacity = 0;
|
||||||
|
var thresholdCapacity = 0;
|
||||||
|
var thresholdTimeUnit = '';
|
||||||
|
|
||||||
|
$.each(servers, function(i, server) {
|
||||||
|
if (server.status === 'online' && server.data && server.data.buckets) {
|
||||||
|
onlineCount++;
|
||||||
|
$.each(server.data.buckets, function(j, bucket) {
|
||||||
|
if (bucket.type === 'perSecond') {
|
||||||
|
perSecondCapacity += bucket.capacity || 0;
|
||||||
|
} else if (bucket.type === 'threshold') {
|
||||||
|
thresholdCapacity += bucket.capacity || 0;
|
||||||
|
if (!thresholdTimeUnit) thresholdTimeUnit = bucket.timeUnit;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (onlineCount > 0) {
|
||||||
|
$('#summaryInstanceCount').html(onlineCount + '<span class="unit">개</span>');
|
||||||
|
|
||||||
|
var configPerSecond = parseInt($('input[name=thresholdPerSecond]').val()) || 0;
|
||||||
|
var configThreshold = parseInt($('input[name=threshold]').val()) || 0;
|
||||||
|
|
||||||
|
if (configPerSecond > 0) {
|
||||||
|
$('#summaryPerSecond').html(formatNumber(perSecondCapacity) + '<span class="unit">req/sec</span>');
|
||||||
|
$('#summaryPerSecondCalc').text(formatNumber(configPerSecond) + ' × ' + onlineCount + ' 인스턴스');
|
||||||
|
$('#summaryPerSecondWrap').show();
|
||||||
|
} else {
|
||||||
|
$('#summaryPerSecondWrap').hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (configThreshold > 0) {
|
||||||
|
var timeUnitLabel = getTimeUnitText(thresholdTimeUnit);
|
||||||
|
$('#summaryThreshold').html(formatNumber(thresholdCapacity) + '<span class="unit">req/' + timeUnitLabel + '</span>');
|
||||||
|
$('#summaryThresholdCalc').text(formatNumber(configThreshold) + ' × ' + onlineCount + ' 인스턴스');
|
||||||
|
$('#summaryThresholdWrap').show();
|
||||||
|
} else {
|
||||||
|
$('#summaryThresholdWrap').hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#bucketSummary').show();
|
||||||
|
} else {
|
||||||
|
$('#bucketSummary').hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
$.each(servers, function(i, server) {
|
||||||
|
var statusClass = server.status || 'offline';
|
||||||
|
var statusText = { online: '정상', offline: '오프라인', no_data: '데이터 없음' };
|
||||||
|
|
||||||
|
var card = '<div class="bucket-server-card">' +
|
||||||
|
'<div class="bucket-server-header">' +
|
||||||
|
'<div>' +
|
||||||
|
'<span class="bucket-server-name">' + server.serverName + '</span> ' +
|
||||||
|
'<span class="bucket-server-ip">' + server.serverIp + ':' + server.serverPort + '</span>' +
|
||||||
|
'</div>' +
|
||||||
|
'<span class="bucket-server-status ' + statusClass + '">' + (statusText[statusClass] || statusClass) + '</span>' +
|
||||||
|
'</div>' +
|
||||||
|
'<div class="bucket-server-body">' + renderBucketBody(server) + '</div>' +
|
||||||
|
'</div>';
|
||||||
|
container.append(card);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderBucketBody(server) {
|
||||||
|
if (server.status === 'offline') {
|
||||||
|
return '<div class="bucket-error-msg">' + (server.message || '서버 연결 실패') + '</div>';
|
||||||
|
}
|
||||||
|
if (server.status === 'no_data') {
|
||||||
|
return '<div class="bucket-empty-msg" style="padding:10px;"><i class="material-icons" style="font-size:18px;">info_outline</i> ' + (server.message || '인터페이스가 로드되지 않음') + '</div>';
|
||||||
|
}
|
||||||
|
|
||||||
|
var data = server.data;
|
||||||
|
if (!data || !data.buckets || data.buckets.length === 0) {
|
||||||
|
return '<div class="bucket-empty-msg" style="padding:10px;">버킷 정보 없음</div>';
|
||||||
|
}
|
||||||
|
|
||||||
|
var html = '<div class="bucket-info-grid">';
|
||||||
|
$.each(data.buckets, function(j, bucket) {
|
||||||
|
var typeLabel = bucket.type === 'perSecond' ? '초당 임계치' : '추가 임계치';
|
||||||
|
var timeUnitText = getTimeUnitText(bucket.timeUnit);
|
||||||
|
var usagePercent = bucket.usagePercent || 0;
|
||||||
|
var progressClass = usagePercent < 50 ? 'low' : (usagePercent < 80 ? 'medium' : 'high');
|
||||||
|
|
||||||
|
html += '<div class="bucket-card">' +
|
||||||
|
'<div class="bucket-card-header">' +
|
||||||
|
'<span class="bucket-type">' + typeLabel + '</span>' +
|
||||||
|
'<span class="bucket-type-badge">' + timeUnitText + '</span>' +
|
||||||
|
'</div>' +
|
||||||
|
'<div class="bucket-progress-wrap">' +
|
||||||
|
'<div class="bucket-progress-bar">' +
|
||||||
|
'<div class="bucket-progress-fill ' + progressClass + '" style="width:' + usagePercent + '%"></div>' +
|
||||||
|
'</div>' +
|
||||||
|
'</div>' +
|
||||||
|
'<div class="bucket-stats">' +
|
||||||
|
'<span>사용량: <span class="bucket-stats-value">' + usagePercent.toFixed(1) + '%</span></span>' +
|
||||||
|
'<span>잔여: <span class="bucket-stats-value">' + formatNumber(bucket.availableTokens) + '</span> / ' + formatNumber(bucket.capacity) + '</span>' +
|
||||||
|
'</div>' +
|
||||||
|
'</div>';
|
||||||
|
});
|
||||||
|
html += '</div>';
|
||||||
|
return html;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTimeUnitText(timeUnit) {
|
||||||
|
var units = {SEC: '초', MIN: '분', HOU: '시간', DAY: '일', MON: '월'};
|
||||||
|
return units[timeUnit] || timeUnit || '-';
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatNumber(num) {
|
||||||
|
if (num === undefined || num === null) return '-';
|
||||||
|
return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
|
||||||
|
}
|
||||||
|
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
var returnUrl = getReturnUrlForReturn();
|
var returnUrl = getReturnUrlForReturn();
|
||||||
var key ="${param.name}";
|
var key = "${param.name}";
|
||||||
if (key != "" && key !="null"){
|
if (key != "" && key != "null") {
|
||||||
isDetail = true;
|
isDetail = true;
|
||||||
}
|
}
|
||||||
init(url,key,detail);
|
init(url, key, detail);
|
||||||
|
|
||||||
|
$("select[name=thresholdTimeUnit]").on('change', function() {
|
||||||
|
updateThresholdLabel();
|
||||||
|
});
|
||||||
|
|
||||||
$("#btn_modify").click(function(){
|
$("#btn_modify").click(function() {
|
||||||
var postData = $('#ajaxForm').serializeArray();
|
$(this).blur();
|
||||||
if (isDetail){
|
var confirmMsg = isDetail ? '변경사항을 저장하시겠습니까?' : '새 인터페이스를 등록하시겠습니까?';
|
||||||
postData.push({ name: "cmd" , value:"UPDATE"});
|
showConfirm(confirmMsg, {
|
||||||
}else{
|
title: '저장 확인',
|
||||||
postData.push({ name: "cmd" , value:"INSERT"});
|
confirmText: '저장',
|
||||||
}
|
onConfirm: function() {
|
||||||
$.ajax({
|
var postData = $('#ajaxForm').serializeArray();
|
||||||
type : "POST",
|
if (isDetail) {
|
||||||
url:url,
|
postData.push({name: "cmd", value: "UPDATE"});
|
||||||
data:postData,
|
} else {
|
||||||
success:function(args){
|
postData.push({name: "cmd", value: "INSERT"});
|
||||||
alert("<%= localeMessage.getString("common.saveMsg") %>");
|
}
|
||||||
goNav(returnUrl);//LIST로 이동
|
var isInsert = !isDetail;
|
||||||
},
|
$.ajax({
|
||||||
error:function(e){
|
type: "POST",
|
||||||
alert(e.responseText);
|
url: url,
|
||||||
|
data: postData,
|
||||||
|
success: function(args) {
|
||||||
|
showAlert("<%= localeMessage.getString("common.saveMsg") %>", {
|
||||||
|
type: 'success',
|
||||||
|
title: '저장 완료',
|
||||||
|
onClose: isInsert ? function() { goNav(returnUrl); } : undefined
|
||||||
|
});
|
||||||
|
},
|
||||||
|
error: function(e) {
|
||||||
|
showAlert(e.responseText, {type: 'error'});
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
$("#btn_delete").click(function(){
|
|
||||||
|
|
||||||
if ( confirm( '<%=localeMessage.getString("common.confirmMsg")%>' ) != true ) return;
|
$("#btn_delete").click(function() {
|
||||||
|
$(this).blur();
|
||||||
var postData = $('#ajaxForm').serializeArray();
|
var ifName = $('input[name=desc]').val() || $('input[name=name]').val();
|
||||||
postData.push({ name: "cmd" , value:"DELETE"});
|
showConfirm('<strong>[' + ifName + ']</strong> 인터페이스를 삭제하시겠습니까?', {
|
||||||
$.ajax({
|
type: 'error',
|
||||||
type : "POST",
|
title: '삭제 확인',
|
||||||
url:url,
|
confirmText: '삭제',
|
||||||
data:postData,
|
onConfirm: function() {
|
||||||
success:function(args){
|
var postData = $('#ajaxForm').serializeArray();
|
||||||
alert("<%= localeMessage.getString("common.deleteMsg") %>");
|
postData.push({name: "cmd", value: "DELETE"});
|
||||||
goNav(returnUrl);//LIST로 이동
|
$.ajax({
|
||||||
|
type: "POST",
|
||||||
},
|
url: url,
|
||||||
error:function(e){
|
data: postData,
|
||||||
alert(e.responseText);
|
success: function(args) {
|
||||||
|
showAlert("<%= localeMessage.getString("common.deleteMsg") %>", {
|
||||||
|
type: 'success',
|
||||||
|
title: '삭제 완료',
|
||||||
|
onClose: function() { goNav(returnUrl); }
|
||||||
|
});
|
||||||
|
},
|
||||||
|
error: function(e) {
|
||||||
|
showAlert(e.responseText, {type: 'error'});
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
$("#btn_previous").click(function(){
|
|
||||||
goNav(returnUrl);//LIST로 이동
|
$("#btn_previous").click(function() {
|
||||||
|
goNav(returnUrl);
|
||||||
});
|
});
|
||||||
|
|
||||||
buttonControl(isDetail);
|
buttonControl(isDetail);
|
||||||
titleControl(isDetail);
|
titleControl(isDetail);
|
||||||
});
|
|
||||||
|
|
||||||
|
$('.detail-tab').click(function() {
|
||||||
|
switchTab($(this).data('tab'));
|
||||||
|
});
|
||||||
|
|
||||||
|
if (isDetail) {
|
||||||
|
$('#tabStatus').show();
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#btn_refresh_bucket').click(function() {
|
||||||
|
loadBucketStatus();
|
||||||
|
});
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="right_box">
|
<div class="right_box">
|
||||||
<div class="content_top">
|
<div class="content_top">
|
||||||
<ul class="path">
|
<ul class="path">
|
||||||
<li><a href="#">${rmsMenuPath}</a></li>
|
<li><a href="#">${rmsMenuPath}</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div><!-- end content_top -->
|
</div><!-- end content_top -->
|
||||||
<div class="content_middle">
|
<div class="content_middle" id="content_middle">
|
||||||
<div class="search_wrap">
|
<div class="search_wrap">
|
||||||
<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="W" status="DETAIL,NEW"><i class="material-icons">save</i> <%= localeMessage.getString("button.modify") %></button>
|
<button type="button" class="cssbtn" id="btn_modify" level="W" status="DETAIL,NEW"><i class="material-icons">save</i> <%= localeMessage.getString("button.save") %></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>
|
||||||
<%-- <img src="<c:url value="/img/btn_delete.png"/>" alt="" id="btn_delete" level="W" status="DETAIL"/> --%>
|
</div>
|
||||||
<%-- <img src="<c:url value="/img/btn_modify.png"/>" alt="" id="btn_modify" level="W" status="DETAIL,NEW" /> --%>
|
|
||||||
<%-- <img src="<c:url value="/img/btn_previous.png"/>" alt="" id="btn_previous" level="R" status="DETAIL,NEW"/> --%>
|
|
||||||
</div>
|
|
||||||
<div class="title"><%= localeMessage.getString("infIfConMan.title") %><span class="tooltip" ><%= localeMessage.getString("infIfConMan.title") %></span></div>
|
|
||||||
|
|
||||||
<table id="grid" ></table>
|
<div class="title-wrap">
|
||||||
<div id="pager"></div>
|
<div class="title"><%= localeMessage.getString("infIfConMan.title") %><span class="tooltip"><%= localeMessage.getString("infIfConMan.title") %></span></div>
|
||||||
|
<span id="modifiedAtInfo" class="modified-badge" style="display:none;">
|
||||||
|
<i class="material-icons">update</i><span id="modifiedAtText">-</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- detail -->
|
<!-- 탭 네비게이션 -->
|
||||||
<form id="ajaxForm">
|
<div class="detail-tabs">
|
||||||
<table class="table_row" cellspacing="0">
|
<button type="button" class="detail-tab active" data-tab="config">
|
||||||
<tr>
|
<i class="material-icons">settings</i> 설정
|
||||||
<th style="width:20%;"><%= localeMessage.getString("infIfConMan.ifNm") %></th>
|
</button>
|
||||||
<td><input type="text" name="name" /></td>
|
<button type="button" class="detail-tab" data-tab="status" id="tabStatus" style="display:none;">
|
||||||
</tr>
|
<i class="material-icons">analytics</i> 현황
|
||||||
<tr>
|
</button>
|
||||||
<th style="width:20%;"><%= localeMessage.getString("infIfConMan.ifDesc") %></th>
|
</div>
|
||||||
<td><input type="text" name="desc" /></td>
|
|
||||||
</tr>
|
<!-- 설정 탭 -->
|
||||||
<tr>
|
<div class="tab-content active" id="tabConfigContent">
|
||||||
<th style="width:20%;"><%= localeMessage.getString("infAdpConMan.thrPerSecond") %></th>
|
<form id="ajaxForm">
|
||||||
<td><input type="text" name="thresholdPerSecond" /></td>
|
<span class="section-label">기본 정보</span>
|
||||||
</tr>
|
<table class="form-table" cellspacing="0">
|
||||||
<tr>
|
<colgroup>
|
||||||
<th style="width:20%;"><%= localeMessage.getString("infAdpConMan.thr") %></th>
|
<col style="width:150px" />
|
||||||
<td><input type="text" name="threshold" /></td>
|
<col />
|
||||||
</tr>
|
</colgroup>
|
||||||
<tr>
|
<tbody>
|
||||||
<th style="width:20%;"><%= localeMessage.getString("infAdpConMan.thrTimeUnit") %></th>
|
<tr>
|
||||||
<td>
|
<th><%= localeMessage.getString("infIfConMan.ifNm") %></th>
|
||||||
<div class="select-style">
|
<td><input type="text" name="name" style="width:300px;background-color: #ebebec;" /></td>
|
||||||
<select name="thresholdTimeUnit" />
|
</tr>
|
||||||
</div>
|
<tr>
|
||||||
</td>
|
<th><%= localeMessage.getString("infIfConMan.ifDesc") %></th>
|
||||||
</tr>
|
<td><input type="text" name="desc" style="width:300px;background-color: #ebebec;" /></td>
|
||||||
<tr>
|
</tr>
|
||||||
<th style="width:20%;"><%= localeMessage.getString("infAdpConMan.useYn") %></th>
|
<tr>
|
||||||
<td>
|
<th><%= localeMessage.getString("infAdpConMan.useYn") %></th>
|
||||||
<div class="select-style">
|
<td>
|
||||||
<select name="useYn" />
|
<select name="useYn" class="styled-select"></select>
|
||||||
</div>
|
</td>
|
||||||
</td>
|
</tr>
|
||||||
</tr>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</form>
|
|
||||||
|
|
||||||
</div><!-- end content_middle -->
|
<span class="section-label">유량제어 설정</span>
|
||||||
</div><!-- end right_box -->
|
<table class="form-table" cellspacing="0">
|
||||||
</body>
|
<colgroup>
|
||||||
|
<col style="width:150px" />
|
||||||
|
<col />
|
||||||
|
</colgroup>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<th>
|
||||||
|
<%= localeMessage.getString("infAdpConMan.thrPerSecond") %>
|
||||||
|
<span class="hint-icon"><i class="material-icons">help_outline</i>
|
||||||
|
<span class="hint-bubble">1초 단위의 순간 트래픽 제한입니다. 매 초마다 설정한 건수만큼 토큰이 리필되며, 초과 시 요청이 거부됩니다.</span>
|
||||||
|
</span>
|
||||||
|
</th>
|
||||||
|
<td>
|
||||||
|
<div class="threshold-group">
|
||||||
|
<input type="number" name="thresholdPerSecond" placeholder="0" />
|
||||||
|
<span class="unit">req/sec</span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>
|
||||||
|
<%= localeMessage.getString("infAdpConMan.thr") %>
|
||||||
|
<span class="hint-icon"><i class="material-icons">help_outline</i>
|
||||||
|
<span class="hint-bubble">장시간 누적 트래픽 제한입니다. 시간단위를 선택하면 해당 주기마다 토큰이 리필됩니다.</span>
|
||||||
|
</span>
|
||||||
|
</th>
|
||||||
|
<td>
|
||||||
|
<div class="threshold-group">
|
||||||
|
<select name="thresholdTimeUnit" class="styled-select"></select>
|
||||||
|
<input type="number" name="threshold" id="thresholdInput" placeholder="0" />
|
||||||
|
<span class="unit" id="thresholdUnit">requests</span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</form>
|
||||||
|
</div><!-- /tabConfigContent -->
|
||||||
|
|
||||||
|
<!-- 현황 탭 -->
|
||||||
|
<div class="tab-content" id="tabStatusContent">
|
||||||
|
<div class="bucket-status-wrap">
|
||||||
|
<div class="bucket-refresh-bar">
|
||||||
|
<span class="last-refresh">마지막 갱신: <span id="lastRefreshTime">-</span></span>
|
||||||
|
<button type="button" class="cssbtn" id="btn_refresh_bucket"><i class="material-icons">refresh</i> 새로고침</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 전체 합산 요약 -->
|
||||||
|
<div class="bucket-summary" id="bucketSummary" style="display:none;">
|
||||||
|
<div class="bucket-summary-title"><i class="material-icons">functions</i> 전체 인스턴스 합산</div>
|
||||||
|
<div class="bucket-summary-grid">
|
||||||
|
<div class="bucket-summary-item">
|
||||||
|
<span class="bucket-summary-label">활성 인스턴스</span>
|
||||||
|
<span class="bucket-summary-value" id="summaryInstanceCount">0<span class="unit">개</span></span>
|
||||||
|
</div>
|
||||||
|
<div class="bucket-summary-item" id="summaryPerSecondWrap">
|
||||||
|
<span class="bucket-summary-label">초당 임계치 (전체)</span>
|
||||||
|
<span class="bucket-summary-value" id="summaryPerSecond">0<span class="unit">req/sec</span></span>
|
||||||
|
<span class="bucket-summary-sub" id="summaryPerSecondCalc"></span>
|
||||||
|
</div>
|
||||||
|
<div class="bucket-summary-item" id="summaryThresholdWrap">
|
||||||
|
<span class="bucket-summary-label">추가 임계치 (전체)</span>
|
||||||
|
<span class="bucket-summary-value" id="summaryThreshold">0<span class="unit">req</span></span>
|
||||||
|
<span class="bucket-summary-sub" id="summaryThresholdCalc"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="bucket-server-list" id="bucketServerList">
|
||||||
|
<div class="bucket-empty-msg">
|
||||||
|
<i class="material-icons">hourglass_empty</i>
|
||||||
|
버킷 현황을 조회하려면 새로고침 버튼을 클릭하세요
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div><!-- /tabStatusContent -->
|
||||||
|
|
||||||
|
</div><!-- end content_middle -->
|
||||||
|
</div><!-- end right_box -->
|
||||||
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
||||||
|
|||||||
@@ -34,7 +34,7 @@
|
|||||||
, {id:'LLLVAR',name:'[ISO8583] LLLVAR'}, {id:'LLLLVAR',name:'[ISO8583] LLLLVAR'}, {id:'BINARY',name:'[ISO8583] BINARY'}, {id:'LLBIN',name:'[ISO8583] LLBIN'}
|
, {id:'LLLVAR',name:'[ISO8583] LLLVAR'}, {id:'LLLLVAR',name:'[ISO8583] LLLLVAR'}, {id:'BINARY',name:'[ISO8583] BINARY'}, {id:'LLBIN',name:'[ISO8583] LLBIN'}
|
||||||
, {id:'LLLBIN',name:'[ISO8583] LLLBIN'}];
|
, {id:'LLLBIN',name:'[ISO8583] LLLBIN'}];
|
||||||
|
|
||||||
const basicDataTypes = [{id:'BigDecimal',name:'BigDecimal'},{id:'String',name:'String'}];
|
const basicDataTypes = [{id:'BigDecimal',name:'BigDecimal'},{id:'String',name:'String'},{id:'Boolean',name:'Boolean'}];
|
||||||
|
|
||||||
function gridRenderingInit() {
|
function gridRenderingInit() {
|
||||||
$('#effectGrid').jqGrid({
|
$('#effectGrid').jqGrid({
|
||||||
|
|||||||
@@ -44,14 +44,16 @@
|
|||||||
'<%=localeMessage.getString("stdDefaultMan.columDesc")%>',
|
'<%=localeMessage.getString("stdDefaultMan.columDesc")%>',
|
||||||
'<%=localeMessage.getString("stdDefaultMan.columLen")%>',
|
'<%=localeMessage.getString("stdDefaultMan.columLen")%>',
|
||||||
'<%=localeMessage.getString("stdDefaultMan.keyOr")%>',
|
'<%=localeMessage.getString("stdDefaultMan.keyOr")%>',
|
||||||
'<%=localeMessage.getString("stdDefaultMan.ifOr")%>'
|
'<%=localeMessage.getString("stdDefaultMan.ifOr")%>',
|
||||||
|
'<%=localeMessage.getString("stdDefaultMan.orderSeq")%>'
|
||||||
],
|
],
|
||||||
colModel:[
|
colModel:[
|
||||||
{ name : 'COLUMNNAME' , align:'left' , sortable:false },
|
{ name : 'COLUMNNAME' , align:'left' , width: '200px', sortable:false },
|
||||||
{ name : 'COLUMNDESC' , align:'left' },
|
{ name : 'COLUMNDESC' , align:'left' , width: '200px' },
|
||||||
{ name : 'COLUMNLENGTH' , align:'right' },
|
{ name : 'COLUMNLENGTH' , align:'center' , width: '100px' },
|
||||||
{ name : 'ISKEY' , align:'center' },
|
{ name : 'ISKEY' , align:'center', width: '100px' },
|
||||||
{ name : 'ISINTERFACE' , align:'center' }
|
{ name : 'ISINTERFACE' , align:'center', width: '100px' },
|
||||||
|
{ name : 'ORDERSEQ' , align:'center', width: '100px' }
|
||||||
],
|
],
|
||||||
jsonReader: {
|
jsonReader: {
|
||||||
repeatitems:false
|
repeatitems:false
|
||||||
|
|||||||
@@ -39,12 +39,22 @@
|
|||||||
var url = '<c:url value="/onl/apim/approval/portalApprovalMan.json" />';
|
var url = '<c:url value="/onl/apim/approval/portalApprovalMan.json" />';
|
||||||
var url_view = '<c:url value="/onl/apim/approval/portalApprovalMan.view" />';
|
var url_view = '<c:url value="/onl/apim/approval/portalApprovalMan.view" />';
|
||||||
|
|
||||||
|
function dateFormat(cellvalue) {
|
||||||
|
if ( cellvalue == null || cellvalue.length < 8 )
|
||||||
|
return '';
|
||||||
|
return cellvalue.substr(0 ,4) + '-' +
|
||||||
|
cellvalue.substr(4, 2) + '-' +
|
||||||
|
cellvalue.substr(6, 2) ;
|
||||||
|
}
|
||||||
|
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
var key = "${param.id}";
|
var key = "${param.id}";
|
||||||
if (key) {
|
if (key) {
|
||||||
detail(key);
|
detail(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$("#expectEndDate").datepicker().inputmask("yyyy-mm-dd", {'autoUnmask': true});
|
||||||
|
|
||||||
$("#btn_app_approve").click(function () {
|
$("#btn_app_approve").click(function () {
|
||||||
if (confirm("승인하시겠습니까?")) {
|
if (confirm("승인하시겠습니까?")) {
|
||||||
$.ajax({
|
$.ajax({
|
||||||
@@ -113,6 +123,28 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$("#btn_modify").click(function () {
|
||||||
|
if (confirm("수정하시겠습니까?")) {
|
||||||
|
$.ajax({
|
||||||
|
type: "POST",
|
||||||
|
url: url,
|
||||||
|
data: {cmd: "UPDATE", id: key, expectEndDate: $("#expectEndDate").val().replaceAll('-','')},
|
||||||
|
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_close").click(function () {
|
$("#btn_close").click(function () {
|
||||||
window.close();
|
window.close();
|
||||||
});
|
});
|
||||||
@@ -126,12 +158,25 @@
|
|||||||
data: {cmd: 'DETAIL', id: key},
|
data: {cmd: 'DETAIL', id: key},
|
||||||
success: function (json) {
|
success: function (json) {
|
||||||
var data = json;
|
var data = json;
|
||||||
|
|
||||||
|
if (data.approvalStatus.code == 'END') {
|
||||||
|
$('#btn_modify').hide();
|
||||||
|
$('#expectEndDate').hide();
|
||||||
|
$('#spExpectEndDate').show();
|
||||||
|
$('#spExpectEndDate').text(dateFormat(data.expectEndDate));
|
||||||
|
} else {
|
||||||
|
$('#btn_modify').show();
|
||||||
|
$('#expectEndDate').show();
|
||||||
|
$('#spExpectEndDate').hide();
|
||||||
|
}
|
||||||
|
|
||||||
$("#approvalType").text(data.approvalType === 'USER' ? "법인사용자" : "API 사용");
|
$("#approvalType").text(data.approvalType === 'USER' ? "법인사용자" : "API 사용");
|
||||||
$("#approvalStatus").text(data.approvalStatus.description);
|
$("#approvalStatus").text(data.approvalStatus.description);
|
||||||
$("#requesterName").text(data.requester.userName);
|
$("#requesterName").text(data.requester.userName);
|
||||||
$("#approvalSubject").text(data.approvalSubject);
|
$("#approvalSubject").text(data.approvalSubject);
|
||||||
$("#approvalDetail").html(data.approvalDetail);
|
$("#approvalDetail").html(data.approvalDetail);
|
||||||
$("#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);
|
||||||
|
|
||||||
if (data.approvers.some(approver =>
|
if (data.approvers.some(approver =>
|
||||||
approver.approverStatus === 'CURRENT' &&
|
approver.approverStatus === 'CURRENT' &&
|
||||||
@@ -144,7 +189,7 @@
|
|||||||
document.getElementById("btn_app_reject").style.display = "none";
|
document.getElementById("btn_app_reject").style.display = "none";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.approvalStatus.description === '배포실패') {
|
if (data.approvalStatus.code === 'FAILED') {
|
||||||
document.getElementById("btn_app_redeploy").style.display = "";
|
document.getElementById("btn_app_redeploy").style.display = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -242,9 +287,14 @@
|
|||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>상세 내용</th>
|
<th>상세 내용</th>
|
||||||
<td colspan="3">
|
<td>
|
||||||
<div id="approvalDetail" style="white-space: pre-wrap;"></div>
|
<div id="approvalDetail" style="white-space: pre-wrap;"></div>
|
||||||
</td>
|
</td>
|
||||||
|
<th>예상완료일</th>
|
||||||
|
<td>
|
||||||
|
<input type="text" id="expectEndDate" style="width: 120px; text-align:center"/>
|
||||||
|
<span id="spExpectEndDate"></span>
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
@@ -282,6 +332,9 @@
|
|||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
<div class="popup_button">
|
<div class="popup_button">
|
||||||
|
<button type="button" class="cssbtn" id="btn_modify" level="W"><i
|
||||||
|
class="material-icons">save</i>수정
|
||||||
|
</button>
|
||||||
<button type="button" class="cssbtn" style="display: none;" id="btn_app_approve" level="W"><i
|
<button type="button" class="cssbtn" style="display: none;" id="btn_app_approve" level="W"><i
|
||||||
class="material-icons">check</i>승인
|
class="material-icons">check</i>승인
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -23,6 +23,14 @@
|
|||||||
'APP': 'API 사용'
|
'APP': 'API 사용'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function dateFormat(cellvalue, options, rowObject) {
|
||||||
|
if ( cellvalue == null || cellvalue.length < 8 )
|
||||||
|
return '';
|
||||||
|
return cellvalue.substr(0 ,4) + '-' +
|
||||||
|
cellvalue.substr(4, 2) + '-' +
|
||||||
|
cellvalue.substr(6, 2) ;
|
||||||
|
}
|
||||||
|
|
||||||
function deleteSelectedApprovals() {
|
function deleteSelectedApprovals() {
|
||||||
var selectedIds = $("#grid").jqGrid('getGridParam', 'selarrrow');
|
var selectedIds = $("#grid").jqGrid('getGridParam', 'selarrrow');
|
||||||
|
|
||||||
@@ -74,7 +82,7 @@
|
|||||||
searchApprovalStatus: $('select[name=searchApprovalStatus]').val(),
|
searchApprovalStatus: $('select[name=searchApprovalStatus]').val(),
|
||||||
searchApprovalSubject: $('input[name=searchApprovalSubject]').val()
|
searchApprovalSubject: $('input[name=searchApprovalSubject]').val()
|
||||||
},
|
},
|
||||||
colNames: ['No.', 'id', '제목', '승인 유형', '승인 유형', '승인 상태','요청자', '생성일'],
|
colNames: ['No.', 'id', '제목', '승인 유형', '승인 유형', '승인 상태','요청자', '생성일', '예상완료일'],
|
||||||
colModel: [
|
colModel: [
|
||||||
{ name: 'rowNum', align: 'center', width: '30', sortable: false},
|
{ name: 'rowNum', align: 'center', width: '30', sortable: false},
|
||||||
{ name: 'id', align: 'center', key: true, hidden: true },
|
{ name: 'id', align: 'center', key: true, hidden: true },
|
||||||
@@ -83,7 +91,8 @@
|
|||||||
{ name: 'approvalType', align: 'center', width: 80, hidden: true},
|
{ name: 'approvalType', align: 'center', width: 80, hidden: true},
|
||||||
{ name: 'approvalStatus.description', align: 'center', width: 80 },
|
{ name: 'approvalStatus.description', align: 'center', width: 80 },
|
||||||
{ name: 'requester.userName', align: 'center', width: 100 },
|
{ name: 'requester.userName', align: 'center', width: 100 },
|
||||||
{ name: 'createdDate', align: 'center', width: 120, formatter: 'date', formatoptions: { srcformat: 'ISO8601Long', newformat: 'Y-m-d H:i:s' } }
|
{ name: 'createdDate', align: 'center', width: 120, formatter: 'date', formatoptions: { srcformat: 'ISO8601Long', newformat: 'Y-m-d H:i:s' } },
|
||||||
|
{ name: 'expectEndDate', align: 'center', width: 100, formatter: dateFormat},
|
||||||
],
|
],
|
||||||
jsonReader: {
|
jsonReader: {
|
||||||
repeatitems: false
|
repeatitems: false
|
||||||
@@ -189,8 +198,8 @@
|
|||||||
<div class="select-style">
|
<div class="select-style">
|
||||||
<select name="searchApprovalStatus">
|
<select name="searchApprovalStatus">
|
||||||
<option value="">전체</option>
|
<option value="">전체</option>
|
||||||
<option value="CREATED">생성됨</option>
|
|
||||||
<option value="REQUESTED">요청됨</option>
|
<option value="REQUESTED">요청됨</option>
|
||||||
|
<option value="PROCESSING">진행중</option>
|
||||||
<option value="DENIED">반려됨</option>
|
<option value="DENIED">반려됨</option>
|
||||||
<option value="END">승인됨</option>
|
<option value="END">승인됨</option>
|
||||||
</select>
|
</select>
|
||||||
|
|||||||
@@ -20,15 +20,22 @@
|
|||||||
var select_orgId = "";
|
var select_orgId = "";
|
||||||
|
|
||||||
function formatInquiryStatus(cellvalue, options, rowObject) {
|
function formatInquiryStatus(cellvalue, options, rowObject) {
|
||||||
return cellvalue === 'PENDING'
|
if (cellvalue == 'PENDING') {
|
||||||
? '<span style="color: red;">문의중</span>'
|
return '<span style="color: red;">문의중</span>';
|
||||||
: '<span>답변완료</span>';
|
} else if (cellvalue == 'RESPONDED') {
|
||||||
|
return '<span >답변완료</span>'
|
||||||
|
} else if (cellvalue == 'CLOSED') {
|
||||||
|
return '<span >답변종료</span>'
|
||||||
|
} else {
|
||||||
|
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;">' : '';
|
||||||
return cellvalue + icon;
|
var commentCount = rowObject.commentCount > 0 ? ' <span style="color: #337ab7;">[' + rowObject.commentCount + ']</span>' : '';
|
||||||
|
return cellvalue + icon + commentCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
function deleteSelectedRows() {
|
function deleteSelectedRows() {
|
||||||
|
|||||||
@@ -37,6 +37,13 @@
|
|||||||
border-bottom: 2px solid #e0e0e0; /* 제목 아래 줄 긋기 */
|
border-bottom: 2px solid #e0e0e0; /* 제목 아래 줄 긋기 */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.comment-section {
|
||||||
|
margin-bottom: 30px;
|
||||||
|
border: 1px solid #e0e0e0; /* 동일한 박스 스타일 */
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
.info-row {
|
.info-row {
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -58,6 +65,15 @@
|
|||||||
min-height: 200px;
|
min-height: 200px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#commentDetail {
|
||||||
|
min-height: 100px;
|
||||||
|
border: 1px solid #00000032;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 10px;
|
||||||
|
flex: 1;
|
||||||
|
font-family: '맑은고딕'
|
||||||
|
}
|
||||||
|
|
||||||
.required-mark {
|
.required-mark {
|
||||||
color: red;
|
color: red;
|
||||||
margin-left: 2px;
|
margin-left: 2px;
|
||||||
@@ -68,6 +84,7 @@
|
|||||||
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';
|
||||||
|
|
||||||
function decodeHTMLEntities(text) {
|
function decodeHTMLEntities(text) {
|
||||||
var textArea = document.createElement('textarea');
|
var textArea = document.createElement('textarea');
|
||||||
@@ -82,10 +99,21 @@
|
|||||||
dataType: "json",
|
dataType: "json",
|
||||||
data: {cmd: 'DETAIL', id: key},
|
data: {cmd: 'DETAIL', id: key},
|
||||||
success: function (data) {
|
success: function (data) {
|
||||||
|
|
||||||
|
|
||||||
$("#id").val(key);
|
$("#id").val(key);
|
||||||
|
|
||||||
$("#inquirySubject").text(data.inquirySubject);
|
$("#inquirySubject").text(data.inquirySubject);
|
||||||
$("#inquiryDetail").html(data.inquiryDetail);
|
$("#inquiryDetail").html(data.inquiryDetail);
|
||||||
$("#inquiryStatus").text(data.inquiryStatus === 'PENDING' ? '문의중' : '답변완료');
|
|
||||||
|
inquiryStatus = data.inquiryStatus;
|
||||||
|
if (data.inquiryStatus == 'PENDING') {
|
||||||
|
$("#inquiryStatus").text('문의중');
|
||||||
|
} else if (data.inquiryStatus == 'RESPONDED') {
|
||||||
|
$("#inquiryStatus").text('답변완료');
|
||||||
|
} else if (data.inquiryStatus == 'CLOSED') {
|
||||||
|
$("#inquiryStatus").text('답변종료');
|
||||||
|
}
|
||||||
$("#inquirer").text(data.inquirer.userName);
|
$("#inquirer").text(data.inquirer.userName);
|
||||||
$("#createdDate").text(timeStampFormat(data.createdDate));
|
$("#createdDate").text(timeStampFormat(data.createdDate));
|
||||||
$("#responder").text(data.responderName || '');
|
$("#responder").text(data.responderName || '');
|
||||||
@@ -94,6 +122,8 @@
|
|||||||
var decodedContent = decodeHTMLEntities(data.responseDetail);
|
var decodedContent = decodeHTMLEntities(data.responseDetail);
|
||||||
$("#responseDetail").summernote('code', decodedContent || '');
|
$("#responseDetail").summernote('code', decodedContent || '');
|
||||||
|
|
||||||
|
// 댓글 표시
|
||||||
|
listComment(key);
|
||||||
|
|
||||||
// 첨부 파일 표시
|
// 첨부 파일 표시
|
||||||
displayAttachFiles(data.fileInfo);
|
displayAttachFiles(data.fileInfo);
|
||||||
@@ -105,6 +135,59 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function listComment(inquiryId) {
|
||||||
|
$.ajax({
|
||||||
|
type: "POST",
|
||||||
|
url: url,
|
||||||
|
dataType: "json",
|
||||||
|
data: {cmd: 'LIST_COMMENT', id: inquiryId},
|
||||||
|
success: function (data) {
|
||||||
|
var $commentList = $('#commentList');
|
||||||
|
$commentList.empty();
|
||||||
|
if (!data || data.length === 0) return;
|
||||||
|
$.each(data, function (i, comment) {
|
||||||
|
var date = comment.createdDate ? timeStampFormat(comment.createdDate) : '';
|
||||||
|
var row = $('<div>').addClass('info-row');
|
||||||
|
$('<div>').addClass('info-label').css('color', '#555').text(comment.writerName || '').appendTo(row);
|
||||||
|
var content = $('<div>').addClass('info-content');
|
||||||
|
$('<div>').css('white-space', 'pre-wrap').text(comment.commentDetail).appendTo(content);
|
||||||
|
var meta = $('<div>').css({'font-size': '0.9em', 'color': '#999', 'margin-top': '2px'});
|
||||||
|
$('<span>').text(date).appendTo(meta);
|
||||||
|
$('<button>').attr('type', 'button').addClass('cssbtn smallBtn2').css({'margin-left': '8px', 'font-size': '0.85em', 'padding': '1px 6px'})
|
||||||
|
.text('delete').on('click', (function(id, inquiryId) {
|
||||||
|
return function() { deleteComment(id, inquiryId); };
|
||||||
|
})(comment.id, comment.inquiryId))
|
||||||
|
.appendTo(meta);
|
||||||
|
meta.appendTo(content);
|
||||||
|
content.appendTo(row);
|
||||||
|
row.appendTo($commentList);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
error: function (e) {
|
||||||
|
alert(e.responseText);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function deleteComment(commentId, inquiryId) {
|
||||||
|
if (inquiryStatus == 'CLOSED') {
|
||||||
|
alert('답변이 종료되어 삭제할 수 없습니다');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!confirm("댓글을 삭제하시겠습니까?")) return;
|
||||||
|
$.ajax({
|
||||||
|
type: "POST",
|
||||||
|
url: url,
|
||||||
|
data: {cmd: 'DELETE_COMMENT', id: commentId},
|
||||||
|
success: function () {
|
||||||
|
listComment(inquiryId);
|
||||||
|
},
|
||||||
|
error: function (e) {
|
||||||
|
alert(e.responseText);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
var returnUrl = getReturnUrlForReturn();
|
var returnUrl = getReturnUrlForReturn();
|
||||||
var key = "${param.id}";
|
var key = "${param.id}";
|
||||||
@@ -115,11 +198,16 @@
|
|||||||
height: 200
|
height: 200
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
if (key) {
|
if (key) {
|
||||||
detail(key);
|
detail(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
$("#btn_modify").click(function () {
|
$("#btn_modify").click(function () {
|
||||||
|
if (inquiryStatus == 'CLOSED') {
|
||||||
|
alert('답변이 종료되어 수정할 수 없습니다');
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (!checkRequired("ajaxForm")) return;
|
if (!checkRequired("ajaxForm")) return;
|
||||||
if (!confirm("<%= localeMessage.getString("common.checkSave")%>")) return;
|
if (!confirm("<%= localeMessage.getString("common.checkSave")%>")) return;
|
||||||
|
|
||||||
@@ -140,6 +228,32 @@
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$("#btn_comment").click(function () {
|
||||||
|
if (inquiryStatus == 'CLOSED') {
|
||||||
|
alert('답변이 종료되어 댓글을 등록할 수 없습니다');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!confirm("<%= localeMessage.getString("common.checkSave")%>")) return;
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
type: "POST",
|
||||||
|
url: url,
|
||||||
|
data: {
|
||||||
|
cmd: "INSERT_COMMENT",
|
||||||
|
inquiryId: $('#id').val(),
|
||||||
|
commentDetail: $('#commentDetail').val(),
|
||||||
|
},
|
||||||
|
success: function (json) {
|
||||||
|
alert("<%= localeMessage.getString("common.saveMsg") %>");
|
||||||
|
$('#commentDetail').val('');
|
||||||
|
listComment(key);
|
||||||
|
},
|
||||||
|
error: function (e) {
|
||||||
|
alert(e.responseText);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
$("#btn_previous").click(function () {
|
$("#btn_previous").click(function () {
|
||||||
goNav(returnUrl);
|
goNav(returnUrl);
|
||||||
});
|
});
|
||||||
@@ -206,6 +320,10 @@
|
|||||||
|
|
||||||
<!-- 답변 작성 부분 -->
|
<!-- 답변 작성 부분 -->
|
||||||
<div class="response-section">
|
<div class="response-section">
|
||||||
|
<div class="info-row">
|
||||||
|
<div class="info-label">상태</div>
|
||||||
|
<div id="inquiryStatus" class="info-content"></div>
|
||||||
|
</div>
|
||||||
<div class="info-row">
|
<div class="info-row">
|
||||||
<div class="info-label">답변자</div>
|
<div class="info-label">답변자</div>
|
||||||
<div id="responder" class="info-content"></div>
|
<div id="responder" class="info-content"></div>
|
||||||
@@ -221,6 +339,19 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- 댓글 작성 부분 -->
|
||||||
|
<div class="comment-section">
|
||||||
|
<div id="commentList"></div>
|
||||||
|
<div class="info-row">
|
||||||
|
<div class="info-label">${sessionScope.login.userName}</div>
|
||||||
|
<div style="flex:1; display: flex; gap: 8px; align-items: stretch;">
|
||||||
|
<textarea id="commentDetail" name="commentDetail" data-required data-warning="댓글 내용을 입력하여 주십시오."></textarea>
|
||||||
|
<button type="button" class="cssbtn" id="btn_comment" level="W" style="width:100px; height: 100px; text-align:center; ">댓글등록</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<input type="hidden" name="id" id="id">
|
<input type="hidden" name="id" id="id">
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -19,10 +19,23 @@
|
|||||||
<script language="javascript" >
|
<script language="javascript" >
|
||||||
var url = '<c:url value="/onl/apim/portalnotice/portalNoticeMan.json" />';
|
var url = '<c:url value="/onl/apim/portalnotice/portalNoticeMan.json" />';
|
||||||
var url_view = '<c:url value="/onl/apim/portalnotice/portalNoticeMan.view" />';
|
var url_view = '<c:url value="/onl/apim/portalnotice/portalNoticeMan.view" />';
|
||||||
|
var apiPopupUrl = '<c:url value="/onl/apim/apigroup/apiGroupMan.view" />';
|
||||||
var file_download = '<c:url value="/file/download.do" />';
|
var file_download = '<c:url value="/file/download.do" />';
|
||||||
var isDetail = false;
|
var isDetail = false;
|
||||||
var fileInfo = null;
|
var fileInfo = null;
|
||||||
|
|
||||||
|
// 코드 상수 — PortalNoticeUI 와 동기화
|
||||||
|
var NOTICE_TYPE_NORMAL = '1';
|
||||||
|
var NOTICE_TYPE_MAINTENANCE = '2';
|
||||||
|
var NOTICE_TYPE_INCIDENT = '3';
|
||||||
|
|
||||||
|
// 영향 API 목록 (UI 상태)
|
||||||
|
var affectedApis = [];
|
||||||
|
|
||||||
|
function isIncidentType(t) {
|
||||||
|
return t === NOTICE_TYPE_INCIDENT || t === NOTICE_TYPE_MAINTENANCE;
|
||||||
|
}
|
||||||
|
|
||||||
function init() {
|
function init() {
|
||||||
var key = "${param.id}";
|
var key = "${param.id}";
|
||||||
isDetail = key != "" && key != "null";
|
isDetail = key != "" && key != "null";
|
||||||
@@ -36,7 +49,6 @@
|
|||||||
buttonControl(false);
|
buttonControl(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 초기화 로직
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
type : "POST",
|
type : "POST",
|
||||||
url:url,
|
url:url,
|
||||||
@@ -45,6 +57,7 @@
|
|||||||
success:function(json){
|
success:function(json){
|
||||||
combo = json;
|
combo = json;
|
||||||
new makeOptions("CODE","NAME").setObj($("select[name=noticeType]")).setNoValueInclude(false).setData(json.noticeTypeList).rendering();
|
new makeOptions("CODE","NAME").setObj($("select[name=noticeType]")).setNoValueInclude(false).setData(json.noticeTypeList).rendering();
|
||||||
|
toggleIncidentFields();
|
||||||
|
|
||||||
if (key) {
|
if (key) {
|
||||||
detail(key);
|
detail(key);
|
||||||
@@ -56,6 +69,22 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function toggleIncidentFields() {
|
||||||
|
var t = $('#noticeType').val();
|
||||||
|
if (isIncidentType(t)) {
|
||||||
|
$('.incident-row').show();
|
||||||
|
// INCIDENT 만 상태 영역 표시
|
||||||
|
if (t === NOTICE_TYPE_INCIDENT) {
|
||||||
|
$('.incident-state-row').show();
|
||||||
|
} else {
|
||||||
|
$('.incident-state-row').hide();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$('.incident-row').hide();
|
||||||
|
$('.incident-state-row').hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function decodeHTMLEntities(text) {
|
function decodeHTMLEntities(text) {
|
||||||
var textArea = document.createElement('textarea');
|
var textArea = document.createElement('textarea');
|
||||||
textArea.innerHTML = text;
|
textArea.innerHTML = text;
|
||||||
@@ -73,57 +102,100 @@
|
|||||||
function displayAttachFile(file) {
|
function displayAttachFile(file) {
|
||||||
var $attachFiles = $('#attachFiles');
|
var $attachFiles = $('#attachFiles');
|
||||||
$attachFiles.empty();
|
$attachFiles.empty();
|
||||||
|
|
||||||
if (file) {
|
if (file) {
|
||||||
var iconPath = '${pageContext.request.contextPath}/images/icon_file.png';
|
var iconPath = '${pageContext.request.contextPath}/images/icon_file.png';
|
||||||
var fileItem = $('<div>').addClass('file-item');
|
var fileItem = $('<div>').addClass('file-item');
|
||||||
|
|
||||||
$('<img>').attr('src', iconPath).css({'marginLeft': '15px','marginRight': '5px' ,'height': '1.2em'}).appendTo(fileItem);
|
$('<img>').attr('src', iconPath).css({'marginLeft': '15px','marginRight': '5px' ,'height': '1.2em'}).appendTo(fileItem);
|
||||||
|
var fileNameSpan = $('<span>').addClass('fileName')
|
||||||
var fileNameSpan = $('<span>')
|
.text(file.originalFileName + '.' + file.fileExtension + ' (' + formatFileSize(file.size) + ')')
|
||||||
.addClass('fileName')
|
.css('cursor', 'pointer')
|
||||||
.text(file.originalFileName + '.' + file.fileExtension + ' (' + formatFileSize(file.size) + ')')
|
.on('click', function() { if (file.fileId) downloadFile(file.fileId); });
|
||||||
.css('cursor', 'pointer')
|
|
||||||
.on('click', function() {
|
|
||||||
if (file.fileId) {
|
|
||||||
downloadFile(file.fileId);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
fileNameSpan.appendTo(fileItem);
|
fileNameSpan.appendTo(fileItem);
|
||||||
$('<button>').text('X').addClass('delete-file').css('marginLeft', '15px')
|
$('<button>').text('X').addClass('delete-file').css('marginLeft', '15px')
|
||||||
.on('click', function() {
|
.on('click', function() {
|
||||||
fileInfo = null;
|
fileInfo = null;
|
||||||
displayAttachFile(null);
|
displayAttachFile(null);
|
||||||
// 폼에 삭제 플래그 추가
|
$('<input>').attr({type: 'hidden', name: 'fileDeleted', value: 'true'}).appendTo('#ajaxForm');
|
||||||
$('<input>').attr({
|
}).appendTo(fileItem);
|
||||||
type: 'hidden',
|
|
||||||
name: 'fileDeleted',
|
|
||||||
value: 'true'
|
|
||||||
}).appendTo('#ajaxForm');
|
|
||||||
})
|
|
||||||
.appendTo(fileItem);
|
|
||||||
|
|
||||||
fileItem.appendTo($attachFiles);
|
fileItem.appendTo($attachFiles);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function downloadFile(fileId) {
|
function downloadFile(fileId) {
|
||||||
if (fileId) {
|
if (fileId) {
|
||||||
var fileSn = 1; // 단일 파일이므로 시리얼 넘버를 1로 고정
|
window.location.href = file_download + '?fileId=' + fileId + '&fileSn=1';
|
||||||
window.location.href = file_download + '?fileId=' + fileId + '&fileSn=' + fileSn;
|
|
||||||
} else {
|
|
||||||
console.log("File not yet uploaded or doesn't have an ID");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function renderAffectedApis() {
|
||||||
|
var $tb = $('#affectedApiTbody');
|
||||||
|
$tb.empty();
|
||||||
|
if (!affectedApis.length) {
|
||||||
|
$tb.append('<tr><td colspan="3" style="text-align:center;color:#999;">선택된 영향 API가 없습니다.</td></tr>');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$.each(affectedApis, function(idx, api) {
|
||||||
|
var $tr = $('<tr>');
|
||||||
|
$tr.append('<td style="text-align:center;width:40px;"><input type="checkbox" class="affected-api-check" data-idx="' + idx + '"></td>');
|
||||||
|
$tr.append('<td>' + (api.apiId || '') + '</td>');
|
||||||
|
$tr.append('<td>' + (api.apiName || '') + '</td>');
|
||||||
|
$tb.append($tr);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function openApiPopup() {
|
||||||
|
// showModal 헬퍼는 urlAddServiceType + &menuId=getMenuId() + &pop=true 를 자동 부여 →
|
||||||
|
// AuthorizeInterceptor 가 정상적으로 권한 인식. window.open 직접 호출 시 권한 부족 발생.
|
||||||
|
// 본 페이지는 summernote 본문 에디터 등 다른 iframe 이 함께 존재할 수 있어
|
||||||
|
// frames[0] 가정은 위험 → DOM 에서 .ui-dialog 내 iframe 중 src=apiGroupMan 인 것을 식별.
|
||||||
|
var selectedIds = affectedApis.map(function(a) { return a.apiId; }).join(',');
|
||||||
|
var url2 = apiPopupUrl + '?cmd=POPUP&selectedApiIds=' + encodeURIComponent(selectedIds);
|
||||||
|
var args = {};
|
||||||
|
showModal(url2, args, 1280, 860, function () {
|
||||||
|
var doc = args.self && args.self.document;
|
||||||
|
if (!doc) return;
|
||||||
|
var popupFrame = null;
|
||||||
|
var iframes = doc.querySelectorAll('iframe');
|
||||||
|
for (var i = 0; i < iframes.length; i++) {
|
||||||
|
var src = iframes[i].getAttribute('src') || '';
|
||||||
|
if (src.indexOf('apiGroupMan') >= 0) {
|
||||||
|
popupFrame = iframes[i].contentWindow;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!popupFrame) return;
|
||||||
|
var rv = popupFrame.returnValue;
|
||||||
|
if (!rv) return;
|
||||||
|
var rows = Array.isArray(rv) ? rv : [rv];
|
||||||
|
$.each(rows, function(_, item) {
|
||||||
|
if (!item || !item.apiId) return;
|
||||||
|
var exists = affectedApis.some(function(a){ return a.apiId === item.apiId; });
|
||||||
|
if (!exists) {
|
||||||
|
affectedApis.push({apiId: item.apiId, apiName: item.apiDesc || item.apiName || ''});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
renderAffectedApis();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeSelectedAffectedApis() {
|
||||||
|
var idxes = [];
|
||||||
|
$('.affected-api-check:checked').each(function() {
|
||||||
|
idxes.push(parseInt($(this).data('idx'), 10));
|
||||||
|
});
|
||||||
|
if (!idxes.length) {
|
||||||
|
alert('삭제할 항목을 선택해주세요.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 역순 삭제
|
||||||
|
idxes.sort(function(a,b){return b-a;}).forEach(function(i){ affectedApis.splice(i, 1); });
|
||||||
|
renderAffectedApis();
|
||||||
|
}
|
||||||
|
|
||||||
function detail(key) {
|
function detail(key) {
|
||||||
if (!isDetail) return;
|
if (!isDetail) return;
|
||||||
$.ajax({
|
$.ajax({
|
||||||
type: "POST",
|
type: "POST", url: url, dataType: "json",
|
||||||
url: url,
|
|
||||||
dataType: "json",
|
|
||||||
data: {cmd: 'DETAIL', id: key},
|
data: {cmd: 'DETAIL', id: key},
|
||||||
success: function (data) {
|
success: function (data) {
|
||||||
$("#id").val(key);
|
$("#id").val(key);
|
||||||
@@ -135,7 +207,18 @@
|
|||||||
var decodedContent = decodeHTMLEntities(data.noticeDetail);
|
var decodedContent = decodeHTMLEntities(data.noticeDetail);
|
||||||
$('#contents').summernote('code', decodedContent);
|
$('#contents').summernote('code', decodedContent);
|
||||||
|
|
||||||
// 파일 정보 표시
|
// 장애/점검 필드
|
||||||
|
$('#summary').val(data.summary || '');
|
||||||
|
$('#startedAt').val(data.startedAt || '');
|
||||||
|
$('#endAt').val(data.endAt || '');
|
||||||
|
$('#state').val(data.state || 'INVESTIGATING');
|
||||||
|
$('#previousState').text(data.previousState || '없음');
|
||||||
|
affectedApis = (data.affectedApis || []).map(function(a){
|
||||||
|
return {apiId: a.apiId, apiName: a.apiName || ''};
|
||||||
|
});
|
||||||
|
renderAffectedApis();
|
||||||
|
toggleIncidentFields();
|
||||||
|
|
||||||
if (data.fileInfo) {
|
if (data.fileInfo) {
|
||||||
fileInfo = {
|
fileInfo = {
|
||||||
originalFileName: data.fileInfo.fileDetails[0].originalFileName,
|
originalFileName: data.fileInfo.fileDetails[0].originalFileName,
|
||||||
@@ -146,9 +229,7 @@
|
|||||||
displayAttachFile(fileInfo);
|
displayAttachFile(fileInfo);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
error: function (e) {
|
error: function (e) { alert(e.responseText); }
|
||||||
alert(e.responseText);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -157,13 +238,12 @@
|
|||||||
|
|
||||||
init();
|
init();
|
||||||
|
|
||||||
|
initSummernote('#contents', { placeholder: '여기에 내용을 입력하세요.', height: 300 });
|
||||||
|
|
||||||
// Summernote 에디터 초기화 (이미지 붙여넣기 data-uri 방식, 리사이징 지원)
|
$('#noticeType').on('change', toggleIncidentFields);
|
||||||
initSummernote('#contents', {
|
|
||||||
placeholder: '여기에 내용을 입력하세요.',
|
|
||||||
height: 300
|
|
||||||
});
|
|
||||||
|
|
||||||
|
$('#btn_addAffectedApi').click(openApiPopup);
|
||||||
|
$('#btn_removeAffectedApi').click(removeSelectedAffectedApis);
|
||||||
|
|
||||||
$('#fileInput').change(function(e) {
|
$('#fileInput').change(function(e) {
|
||||||
var file = e.target.files[0];
|
var file = e.target.files[0];
|
||||||
@@ -171,52 +251,66 @@
|
|||||||
fileInfo = {
|
fileInfo = {
|
||||||
originalFileName: file.name.split('.').slice(0, -1).join('.'),
|
originalFileName: file.name.split('.').slice(0, -1).join('.'),
|
||||||
fileExtension: file.name.split('.').pop(),
|
fileExtension: file.name.split('.').pop(),
|
||||||
file: file,
|
file: file, size: file.size
|
||||||
size: file.size
|
|
||||||
};
|
};
|
||||||
displayAttachFile(fileInfo);
|
displayAttachFile(fileInfo);
|
||||||
}
|
}
|
||||||
this.value = ''; // 입력 필드 초기화
|
this.value = '';
|
||||||
});
|
|
||||||
|
|
||||||
$('#addFile').click(function() {
|
|
||||||
$('#fileInput').click();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$('#addFile').click(function() { $('#fileInput').click(); });
|
||||||
|
|
||||||
$("#btn_modify").click(function () {
|
$("#btn_modify").click(function () {
|
||||||
if (!checkRequired("ajaxForm")) return;
|
if (!checkRequired("ajaxForm")) return;
|
||||||
|
|
||||||
if (confirm("<%= localeMessage.getString("common.checkSave")%>") != true)
|
var noticeType = $('#noticeType').val();
|
||||||
return;
|
if (isIncidentType(noticeType)) {
|
||||||
|
if (!$('#startedAt').val()) {
|
||||||
|
alert('시작 시각을 입력해 주십시오.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (noticeType === NOTICE_TYPE_MAINTENANCE && !$('#endAt').val()) {
|
||||||
|
alert('점검 종료 시각을 입력해 주십시오.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (affectedApis.length === 0) {
|
||||||
|
alert('영향 API를 1건 이상 선택해 주십시오.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (confirm("<%= localeMessage.getString("common.checkSave")%>") != true) return;
|
||||||
|
|
||||||
var formData = new FormData($("#ajaxForm")[0]);
|
var formData = new FormData($("#ajaxForm")[0]);
|
||||||
|
|
||||||
formData.set("useYn", $("#useYn").is(":checked") ? "Y" : "N");
|
formData.set("useYn", $("#useYn").is(":checked") ? "Y" : "N");
|
||||||
formData.set("fixYn", $("#fixYn").is(":checked") ? "Y" : "N");
|
formData.set("fixYn", $("#fixYn").is(":checked") ? "Y" : "N");
|
||||||
formData.set("noticeDetail", $('#contents').summernote('code'));
|
formData.set("noticeDetail", $('#contents').summernote('code'));
|
||||||
|
|
||||||
|
// 시작/종료 시각이 비어 있으면 전송하지 않음 (서버 파싱 오류 방지)
|
||||||
|
if (!$('#startedAt').val()) formData.delete('startedAt');
|
||||||
|
if (!$('#endAt').val()) formData.delete('endAt');
|
||||||
|
|
||||||
|
// 영향 API → affectedApis[i].apiId/apiName 으로 전송
|
||||||
|
$.each(affectedApis, function(i, api) {
|
||||||
|
formData.set('affectedApis[' + i + '].apiId', api.apiId);
|
||||||
|
formData.set('affectedApis[' + i + '].apiName', api.apiName || '');
|
||||||
|
});
|
||||||
|
|
||||||
formData.append("cmd", isDetail ? "UPDATE" : "INSERT");
|
formData.append("cmd", isDetail ? "UPDATE" : "INSERT");
|
||||||
|
|
||||||
// 파일 처리
|
|
||||||
if (fileInfo && fileInfo.file) {
|
if (fileInfo && fileInfo.file) {
|
||||||
formData.set("files", fileInfo.file);
|
formData.set("files", fileInfo.file);
|
||||||
formData.set("fileName", fileInfo.originalFileName + '.' + fileInfo.fileExtension);
|
formData.set("fileName", fileInfo.originalFileName + '.' + fileInfo.fileExtension);
|
||||||
}
|
}
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
type: "POST",
|
type: "POST", url: url, data: formData,
|
||||||
url: url,
|
processData: false, contentType: false,
|
||||||
data: formData,
|
success: function () {
|
||||||
processData: false,
|
|
||||||
contentType: false,
|
|
||||||
success: function (json) {
|
|
||||||
alert("<%= localeMessage.getString("common.saveMsg") %>");
|
alert("<%= localeMessage.getString("common.saveMsg") %>");
|
||||||
goNav(returnUrl);
|
goNav(returnUrl);
|
||||||
},
|
},
|
||||||
error: function (e) {
|
error: function (e) { alert(e.responseText); }
|
||||||
alert(e.responseText);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -224,25 +318,18 @@
|
|||||||
if (confirm("<%= localeMessage.getString("common.confirmMsg")%>")) {
|
if (confirm("<%= localeMessage.getString("common.confirmMsg")%>")) {
|
||||||
var postData = $('#ajaxForm').serializeArray();
|
var postData = $('#ajaxForm').serializeArray();
|
||||||
postData.push({name: "cmd", value: "DELETE"});
|
postData.push({name: "cmd", value: "DELETE"});
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
type: "POST",
|
type: "POST", url: url, data: postData,
|
||||||
url: url,
|
success: function () {
|
||||||
data: postData,
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
$("#btn_previous").click(function () {
|
$("#btn_previous").click(function () { goNav(returnUrl); });
|
||||||
goNav(returnUrl);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
</head>
|
</head>
|
||||||
@@ -252,7 +339,7 @@
|
|||||||
<ul class="path">
|
<ul class="path">
|
||||||
<li><a href="#">${rmsMenuPath}</a></li>
|
<li><a href="#">${rmsMenuPath}</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div><!-- end content_top -->
|
</div>
|
||||||
<div class="content_middle">
|
<div class="content_middle">
|
||||||
<div class="search_wrap">
|
<div class="search_wrap">
|
||||||
<button type="button" class="cssbtn" id="btn_modify" level="W" status="DETAIL,NEW"><i class="material-icons">save</i> <%= localeMessage.getString("button.modify") %></button>
|
<button type="button" class="cssbtn" id="btn_modify" level="W" status="DETAIL,NEW"><i class="material-icons">save</i> <%= localeMessage.getString("button.modify") %></button>
|
||||||
@@ -270,9 +357,9 @@
|
|||||||
<col style="width: 40%"/>
|
<col style="width: 40%"/>
|
||||||
</colgroup>
|
</colgroup>
|
||||||
<tr>
|
<tr>
|
||||||
<th>게시유형</th>
|
<th>게시유형 <font color="red">*</font></th>
|
||||||
<td colspan="3">
|
<td colspan="3">
|
||||||
<div class="select-style" >
|
<div class="select-style">
|
||||||
<select name="noticeType" id="noticeType"></select>
|
<select name="noticeType" id="noticeType"></select>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
@@ -286,7 +373,7 @@
|
|||||||
<td>
|
<td>
|
||||||
<input type="checkbox" name="useYn" id="useYn" value="Y" ${portalNoticeUI.useYn eq 'Y' ? 'checked' : ''}> 사용
|
<input type="checkbox" name="useYn" id="useYn" value="Y" ${portalNoticeUI.useYn eq 'Y' ? 'checked' : ''}> 사용
|
||||||
</td>
|
</td>
|
||||||
<th>고정여부</th>
|
<th>상단고정</th>
|
||||||
<td>
|
<td>
|
||||||
<input type="checkbox" name="fixYn" id="fixYn" value="Y" ${portalNoticeUI.fixYn eq 'Y' ? 'checked' : ''}> 고정
|
<input type="checkbox" name="fixYn" id="fixYn" value="Y" ${portalNoticeUI.fixYn eq 'Y' ? 'checked' : ''}> 고정
|
||||||
</td>
|
</td>
|
||||||
@@ -297,16 +384,55 @@
|
|||||||
<textarea id="contents" name="noticeDetail" style="width:100%;height:300px" data-required data-warning="본문을 입력하여 주십시오."></textarea>
|
<textarea id="contents" name="noticeDetail" style="width:100%;height:300px" data-required data-warning="본문을 입력하여 주십시오."></textarea>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<!-- <tr>
|
|
||||||
<th>첨부파일</th>
|
<!-- ───── 장애/점검 전용 영역 ───── -->
|
||||||
<td colspan="3">
|
<tr class="incident-row" style="display:none;">
|
||||||
<div style="margin: 5px 0px; display: inline-block;">
|
<th>시작 <font color="red">*</font></th>
|
||||||
<input type="file" id="fileInput" name="files" style="display:none;" />
|
<td>
|
||||||
<button type="button" id="addFile" class="cssbtn smallBtn">파일 선택</button>
|
<input type="datetime-local" id="startedAt" name="startedAt" style="width:90%">
|
||||||
</div>
|
|
||||||
<div id="attachFiles" style="margin: 5px 0px; display: inline-block;"></div>
|
|
||||||
</td>
|
</td>
|
||||||
</tr> -->
|
<th>종료</th>
|
||||||
|
<td>
|
||||||
|
<input type="datetime-local" id="endAt" name="endAt" style="width:90%" placeholder="해소시 자동 채움">
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr class="incident-row incident-state-row" style="display:none;">
|
||||||
|
<th>상태</th>
|
||||||
|
<td>
|
||||||
|
<div class="select-style">
|
||||||
|
<select id="state" name="state">
|
||||||
|
<option value="INVESTIGATING">INVESTIGATING - 조사중</option>
|
||||||
|
<option value="IDENTIFIED">IDENTIFIED - 원인 파악</option>
|
||||||
|
<option value="MONITORING">MONITORING - 모니터링</option>
|
||||||
|
<option value="RESOLVED">RESOLVED - 해소</option>
|
||||||
|
<option value="CANCELED">CANCELED - 취소</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<th>이전 상태</th>
|
||||||
|
<td><span id="previousState">없음</span></td>
|
||||||
|
</tr>
|
||||||
|
<tr class="incident-row" style="display:none;">
|
||||||
|
<th>영향 API 선택</th>
|
||||||
|
<td colspan="3">
|
||||||
|
<div style="margin-bottom:5px;">
|
||||||
|
<button type="button" class="cssbtn smallBtn" id="btn_removeAffectedApi"><i class="material-icons">delete</i> 삭제</button>
|
||||||
|
<button type="button" class="cssbtn smallBtn" id="btn_addAffectedApi"><i class="material-icons">add</i> 추가</button>
|
||||||
|
</div>
|
||||||
|
<table class="table_row" cellspacing="0" style="width:100%;">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th style="width:40px;text-align:center;"><input type="checkbox" id="affectedApiAll" onclick="$('.affected-api-check').prop('checked', this.checked);"></th>
|
||||||
|
<th>API ID</th>
|
||||||
|
<th>API 명</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody id="affectedApiTbody">
|
||||||
|
<tr><td colspan="3" style="text-align:center;color:#999;">선택된 영향 API가 없습니다.</td></tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1686,7 +1686,7 @@
|
|||||||
<option value="none" selected>NONE</option>
|
<option value="none" selected>NONE</option>
|
||||||
<option value="oauth">OAUTH</option>
|
<option value="oauth">OAUTH</option>
|
||||||
<option value="api_key">API_KEY</option>
|
<option value="api_key">API_KEY</option>
|
||||||
<option value="ca">CA</option>
|
<!-- <option value="ca">CA</option> -->
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import oracledb
|
||||||
|
|
||||||
|
conn = oracledb.connect(user='AGWAPP', password='elink1234', dsn='192.168.240.177:1598/DAPM')
|
||||||
|
cursor = conn.cursor()
|
||||||
|
|
||||||
|
cursor.execute("SELECT TABLE_NAME, STATUS FROM DBA_TABLES WHERE OWNER = 'AGWAPP' AND TABLE_NAME LIKE 'API_STATS%' ORDER BY TABLE_NAME")
|
||||||
|
for row in cursor.fetchall():
|
||||||
|
print(f" {row[0]} ({row[1]})")
|
||||||
|
|
||||||
|
cursor.close()
|
||||||
|
conn.close()
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
# DJBank EAPIM Admin — user-scope systemd unit
|
||||||
|
#
|
||||||
|
# Install (one-time, on the host as `djb`):
|
||||||
|
# sudo loginctl enable-linger djb # allow services to outlive ssh logout
|
||||||
|
# mkdir -p ~/.config/systemd/user
|
||||||
|
# # workflow copies this file in on each deploy; for first boot you can also do:
|
||||||
|
# # cp /prod/.../deploy/systemd/eapim-admin.service ~/.config/systemd/user/
|
||||||
|
# systemctl --user daemon-reload
|
||||||
|
# systemctl --user enable eapim-admin
|
||||||
|
# systemctl --user start eapim-admin
|
||||||
|
#
|
||||||
|
# Why this exists:
|
||||||
|
# - `catalina.sh start` + `nohup &` does NOT escape the Gitea Actions runner
|
||||||
|
# process group. When the workflow step finishes, the runner sends SIGTERM
|
||||||
|
# to the process tree and Tomcat dies seconds after boot.
|
||||||
|
# - Running Tomcat as a systemd user service detaches lifecycle from the
|
||||||
|
# runner entirely. The workflow just calls `systemctl --user restart` and
|
||||||
|
# systemd owns the process.
|
||||||
|
|
||||||
|
[Unit]
|
||||||
|
Description=DJBank EAPIM Admin (Tomcat 9)
|
||||||
|
After=network-online.target
|
||||||
|
Wants=network-online.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
|
||||||
|
Environment=JAVA_HOME=/apps/opts/jdk17
|
||||||
|
Environment=CATALINA_BASE=/prod/eapim/adminportal
|
||||||
|
Environment=CATALINA_HOME=/prod/eapim/apache-tomcat-9.0.116
|
||||||
|
Environment=CATALINA_PID=/prod/eapim/adminportal/temp/catalina.pid
|
||||||
|
|
||||||
|
# `catalina.sh run` keeps Tomcat in the foreground so systemd can supervise it.
|
||||||
|
ExecStart=/prod/eapim/apache-tomcat-9.0.116/bin/catalina.sh run
|
||||||
|
|
||||||
|
# Graceful shutdown via Tomcat's shutdown port, 30s window.
|
||||||
|
ExecStop=/prod/eapim/apache-tomcat-9.0.116/bin/catalina.sh stop 30
|
||||||
|
|
||||||
|
# Tomcat returns 143 on SIGTERM during normal stop — treat as success.
|
||||||
|
SuccessExitStatus=143
|
||||||
|
TimeoutStartSec=180
|
||||||
|
TimeoutStopSec=60
|
||||||
|
|
||||||
|
Restart=on-failure
|
||||||
|
RestartSec=10
|
||||||
|
|
||||||
|
# Memory / fd limits — adjust as needed.
|
||||||
|
LimitNOFILE=65535
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=default.target
|
||||||
+12
-12
@@ -1,4 +1,4 @@
|
|||||||
CREATE TABLE EMSADM.API_HISTORY
|
CREATE TABLE EMSAPP.API_HISTORY
|
||||||
(
|
(
|
||||||
API_ID VARCHAR2(30),
|
API_ID VARCHAR2(30),
|
||||||
SEQ NUMBER(22),
|
SEQ NUMBER(22),
|
||||||
@@ -9,23 +9,23 @@ CREATE TABLE EMSADM.API_HISTORY
|
|||||||
LOB (SAVEDDATA) STORE AS SECUREFILE (TABLESPACE TS_AGW_D01)
|
LOB (SAVEDDATA) STORE AS SECUREFILE (TABLESPACE TS_AGW_D01)
|
||||||
TABLESPACE TS_EMS_D01;
|
TABLESPACE TS_EMS_D01;
|
||||||
|
|
||||||
COMMENT ON TABLE EMSADM.API_HISTORY IS 'API 변경이력 테이블';
|
COMMENT ON TABLE EMSAPP.API_HISTORY IS 'API 변경이력 테이블';
|
||||||
COMMENT ON COLUMN EMSADM.API_HISTORY.API_ID IS 'API ID(TSEAIHE01.EAISVCNAME)';
|
COMMENT ON COLUMN EMSAPP.API_HISTORY.API_ID IS 'API ID(TSEAIHE01.EAISVCNAME)';
|
||||||
COMMENT ON COLUMN EMSADM.API_HISTORY.SEQ IS 'Sequence 값';
|
COMMENT ON COLUMN EMSAPP.API_HISTORY.SEQ IS 'Sequence 값';
|
||||||
COMMENT ON COLUMN EMSADM.API_HISTORY.SAVEDDATA IS '변경저장 정보';
|
COMMENT ON COLUMN EMSAPP.API_HISTORY.SAVEDDATA IS '변경저장 정보';
|
||||||
COMMENT ON COLUMN EMSADM.API_HISTORY.USERID IS '사용자ID';
|
COMMENT ON COLUMN EMSAPP.API_HISTORY.USERID IS '사용자ID';
|
||||||
COMMENT ON COLUMN EMSADM.API_HISTORY.SAVEDDT IS '저장일시';
|
COMMENT ON COLUMN EMSAPP.API_HISTORY.SAVEDDT IS '저장일시';
|
||||||
|
|
||||||
CREATE UNIQUE INDEX EMSADM.PK_API_HISTORY ON EMSADM.API_HISTORY
|
CREATE UNIQUE INDEX EMSAPP.PK_API_HISTORY ON EMSAPP.API_HISTORY
|
||||||
(API_ID, SEQ)
|
(API_ID, SEQ)
|
||||||
TABLESPACE TS_EMS_I01;
|
TABLESPACE TS_EMS_I01;
|
||||||
|
|
||||||
ALTER TABLE EMSADM.API_HISTORY ADD (
|
ALTER TABLE EMSAPP.API_HISTORY ADD (
|
||||||
CONSTRAINT PK_API_HISTORY
|
CONSTRAINT PK_API_HISTORY
|
||||||
PRIMARY KEY
|
PRIMARY KEY
|
||||||
(API_ID, SEQ)
|
(API_ID, SEQ)
|
||||||
USING INDEX EMSADM.PK_API_HISTORY
|
USING INDEX EMSAPP.PK_API_HISTORY
|
||||||
ENABLE VALIDATE);
|
ENABLE VALIDATE);
|
||||||
|
|
||||||
GRANT DELETE, INSERT, SELECT, UPDATE ON EMSADM.API_HISTORY TO RL_EMS_ALL;
|
GRANT DELETE, INSERT, SELECT, UPDATE ON EMSAPP.API_HISTORY TO RL_EMS_ALL;
|
||||||
GRANT SELECT ON EMSADM.API_HISTORY TO RL_EMS_SEL;
|
GRANT SELECT ON EMSAPP.API_HISTORY TO RL_EMS_SEL;
|
||||||
|
|||||||
+30
-30
@@ -39,37 +39,37 @@
|
|||||||
|
|
||||||
```sql
|
```sql
|
||||||
-- 개발 서버용
|
-- 개발 서버용
|
||||||
insert into EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.eai_batch.url', 'http://172.31.32.111:10230/BATAppWeb/EaiBatCall');
|
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.eai_batch.url', 'http://172.31.32.111:10230/BATAppWeb/EaiBatCall');
|
||||||
insert into EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.eai.batch.connection_timeout', '5');
|
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.eai.batch.connection_timeout', '5');
|
||||||
insert into EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.eai.batch.response_timeout', '5');
|
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.eai.batch.response_timeout', '5');
|
||||||
insert into EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.eai.batch.timeout', '10');
|
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.eai.batch.timeout', '10');
|
||||||
insert into EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.eai.batch.charset', 'euc-kr');
|
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.eai.batch.charset', 'euc-kr');
|
||||||
insert into EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.eai_batch.send_dir', '/Data/eapim/portal/sendmail/snd');
|
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.eai_batch.send_dir', '/Data/eapim/portal/sendmail/snd');
|
||||||
insert into EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.eai_batch.backup_dir', '/Data/eapim/portal/sendmail/bak');
|
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.eai_batch.backup_dir', '/Data/eapim/portal/sendmail/bak');
|
||||||
insert into EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.eai_batch.error_dir', '/Data/eapim/portal/sendmail/bak');
|
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.eai_batch.error_dir', '/Data/eapim/portal/sendmail/bak');
|
||||||
insert into EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.eai_batch.retention_date', '60');
|
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.eai_batch.retention_date', '60');
|
||||||
insert into EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.eai_batch.interface_id', 'UIEFF00001UAG');
|
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.eai_batch.interface_id', 'UIEFF00001UAG');
|
||||||
insert into EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.host_url', 'http://172.31.35.144:9021');
|
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.host_url', 'http://172.31.35.144:9021');
|
||||||
insert into EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.api_key', '7cca6d58-e4f7-474d-9edd-525806bc99ff');
|
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.api_key', '7cca6d58-e4f7-474d-9edd-525806bc99ff');
|
||||||
insert into EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.connection_timeout', '5');
|
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.connection_timeout', '5');
|
||||||
insert into EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.response_timeout', '5');
|
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.response_timeout', '5');
|
||||||
insert into EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.timeout', '10');
|
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.timeout', '10');
|
||||||
|
|
||||||
|
|
||||||
-- autogen template
|
-- autogen template
|
||||||
insert into EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.eai.batch.url', '');
|
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.eai.batch.url', '');
|
||||||
insert into EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.eai.batch.connection_timeout', '5');
|
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.eai.batch.connection_timeout', '5');
|
||||||
insert into EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.eai.batch.response_timeout', '5');
|
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.eai.batch.response_timeout', '5');
|
||||||
insert into EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.eai.batch.timeout', '10');
|
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.eai.batch.timeout', '10');
|
||||||
insert into EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.eai.batch.charset', 'euc-kr');
|
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.eai.batch.charset', 'euc-kr');
|
||||||
insert into EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.eai_batch.send_dir', '');
|
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.eai_batch.send_dir', '');
|
||||||
insert into EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.eai_batch.backup_dir', '');
|
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.eai_batch.backup_dir', '');
|
||||||
insert into EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.eai_batch.error_dir', '');
|
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.eai_batch.error_dir', '');
|
||||||
insert into EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.eai_batch.interface_id', '');
|
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.eai_batch.interface_id', '');
|
||||||
insert into EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.eai_batch.retention_date', '60');
|
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.eai_batch.retention_date', '60');
|
||||||
insert into EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.host', '');
|
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.host', '');
|
||||||
insert into EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.api_key', '');
|
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.api_key', '');
|
||||||
insert into EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.connection_timeout', '5');
|
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.connection_timeout', '5');
|
||||||
insert into EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.response_timeout', '5');
|
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.response_timeout', '5');
|
||||||
insert into EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.timeout', '10');
|
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.timeout', '10');
|
||||||
```
|
```
|
||||||
@@ -163,13 +163,13 @@ SMS 인증번호 발송 - authCode: 123456
|
|||||||
|
|
||||||
```sql
|
```sql
|
||||||
-- SMS 인증 설정 초기화
|
-- SMS 인증 설정 초기화
|
||||||
INSERT INTO EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL)
|
INSERT INTO EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL)
|
||||||
VALUES ('Monitoring', 'kjb.sms_auth.enabled', 'true');
|
VALUES ('Monitoring', 'kjb.sms_auth.enabled', 'true');
|
||||||
|
|
||||||
INSERT INTO EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL)
|
INSERT INTO EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL)
|
||||||
VALUES ('Monitoring', 'kjb.sms_auth.mode', 'real');
|
VALUES ('Monitoring', 'kjb.sms_auth.mode', 'real');
|
||||||
|
|
||||||
INSERT INTO EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL)
|
INSERT INTO EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL)
|
||||||
VALUES ('Monitoring', 'kjb.sms_auth.fixed_value', '654321');
|
VALUES ('Monitoring', 'kjb.sms_auth.fixed_value', '654321');
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -187,7 +187,7 @@ SMS 발송을 위해 UMS 관련 프로퍼티가 설정되어 있어야 합니다
|
|||||||
### Q: SMS 인증을 일시적으로 비활성화하려면?
|
### Q: SMS 인증을 일시적으로 비활성화하려면?
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
UPDATE EMSADM.TSEAIRM24
|
UPDATE EMSAPP.TSEAIRM24
|
||||||
SET PRPTY2VAL = 'false'
|
SET PRPTY2VAL = 'false'
|
||||||
WHERE PRPTYGROUPNAME = 'Monitoring' AND PRPTYNAME = 'kjb.sms_auth.enabled';
|
WHERE PRPTYGROUPNAME = 'Monitoring' AND PRPTYNAME = 'kjb.sms_auth.enabled';
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -238,7 +238,7 @@ WebContent/
|
|||||||
```
|
```
|
||||||
-Deai.datasource.type=DEV
|
-Deai.datasource.type=DEV
|
||||||
-Dinst.Name=emsSvr11
|
-Dinst.Name=emsSvr11
|
||||||
-Deai.tableowner=EMSADM
|
-Deai.tableowner=EMSAPP
|
||||||
-Deai.systemmode=D
|
-Deai.systemmode=D
|
||||||
-Dfile.encoding=utf-8
|
-Dfile.encoding=utf-8
|
||||||
-DLOGBACK_LOG_LEVEL=info
|
-DLOGBACK_LOG_LEVEL=info
|
||||||
@@ -248,7 +248,7 @@ WebContent/
|
|||||||
```
|
```
|
||||||
-Deai.datasource.type=DEV
|
-Deai.datasource.type=DEV
|
||||||
-Dinst.Name=emsSvr99
|
-Dinst.Name=emsSvr99
|
||||||
-Deai.tableowner=EMSADM
|
-Deai.tableowner=EMSAPP
|
||||||
-Deai.systemmode=D
|
-Deai.systemmode=D
|
||||||
-Dfile.encoding=utf-8
|
-Dfile.encoding=utf-8
|
||||||
-Dlogin.mode=db
|
-Dlogin.mode=db
|
||||||
@@ -387,82 +387,82 @@ gradle test --tests "com.eactive.eai.rms.*"
|
|||||||
```sql
|
```sql
|
||||||
-- 로컬 개발용 (외부)
|
-- 로컬 개발용 (외부)
|
||||||
-- EAI, UMS 주소 미기입시 내부적으론 호출 시도 하지 않고 warning 로그만 남기며 정상 처리
|
-- EAI, UMS 주소 미기입시 내부적으론 호출 시도 하지 않고 warning 로그만 남기며 정상 처리
|
||||||
--insert into EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.eai_batch.url', '');
|
--insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.eai_batch.url', '');
|
||||||
insert into EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.eai.batch.connection_timeout', '5');
|
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.eai.batch.connection_timeout', '5');
|
||||||
insert into EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.eai.batch.response_timeout', '5');
|
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.eai.batch.response_timeout', '5');
|
||||||
insert into EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.eai.batch.timeout', '10');
|
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.eai.batch.timeout', '10');
|
||||||
insert into EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.eai.batch.charset', 'euc-kr');
|
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.eai.batch.charset', 'euc-kr');
|
||||||
|
|
||||||
insert into EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.eai_batch.send_dir', '/Data/eapim/portal/sendmail/snd');
|
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.eai_batch.send_dir', '/Data/eapim/portal/sendmail/snd');
|
||||||
insert into EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.eai_batch.backup_dir', '/Data/eapim/portal/sendmail/bak');
|
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.eai_batch.backup_dir', '/Data/eapim/portal/sendmail/bak');
|
||||||
insert into EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.eai_batch.error_dir', '/Data/eapim/portal/sendmail/bak');
|
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.eai_batch.error_dir', '/Data/eapim/portal/sendmail/bak');
|
||||||
insert into EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.eai_batch.retention_date', '60');
|
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.eai_batch.retention_date', '60');
|
||||||
insert into EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.eai_batch.interface_id', 'UIEFF00001UAG');
|
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.eai_batch.interface_id', 'UIEFF00001UAG');
|
||||||
|
|
||||||
-- insert into EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.host_url', '');
|
-- insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.host_url', '');
|
||||||
insert into EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.api_key', '7cca6d58-e4f7-474d-9edd-525806bc99ff');
|
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.api_key', '7cca6d58-e4f7-474d-9edd-525806bc99ff');
|
||||||
insert into EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.connection_timeout', '5');
|
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.connection_timeout', '5');
|
||||||
insert into EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.response_timeout', '5');
|
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.response_timeout', '5');
|
||||||
insert into EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.timeout', '10');
|
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.timeout', '10');
|
||||||
|
|
||||||
--insert into EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.obp.host_url', '');
|
--insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.obp.host_url', '');
|
||||||
insert into EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.obp.trust_system', 'APIMPT-0002-QVBJTVBULTAwMDI=');
|
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.obp.trust_system', 'APIMPT-0002-QVBJTVBULTAwMDI=');
|
||||||
insert into EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.obp.commission_uri', '/api/billing/findBillingForCondition');
|
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.obp.commission_uri', '/api/billing/findBillingForCondition');
|
||||||
insert into EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.obp.commission_print_uri', '/api/billing/billing/findBillingForCondition/print');
|
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.obp.commission_print_uri', '/api/billing/billing/findBillingForCondition/print');
|
||||||
insert into EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.obp.query_partnercode_uri', '/api/customer/findCustomerByBusinessManRegistrationNo/%s');
|
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.obp.query_partnercode_uri', '/api/customer/findCustomerByBusinessManRegistrationNo/%s');
|
||||||
insert into EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.obp.create_partnercode_uri', '/not-yet-implement/%s');
|
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.obp.create_partnercode_uri', '/not-yet-implement/%s');
|
||||||
commit;
|
commit;
|
||||||
|
|
||||||
|
|
||||||
-- 은행 개발 서버용
|
-- 은행 개발 서버용
|
||||||
insert into EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.eai_batch.url', 'http://172.31.32.111:10230/BATAppWeb/EaiBatCall');
|
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.eai_batch.url', 'http://172.31.32.111:10230/BATAppWeb/EaiBatCall');
|
||||||
insert into EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.eai.batch.connection_timeout', '5');
|
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.eai.batch.connection_timeout', '5');
|
||||||
insert into EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.eai.batch.response_timeout', '5');
|
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.eai.batch.response_timeout', '5');
|
||||||
insert into EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.eai.batch.timeout', '10');
|
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.eai.batch.timeout', '10');
|
||||||
insert into EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.eai.batch.charset', 'euc-kr');
|
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.eai.batch.charset', 'euc-kr');
|
||||||
insert into EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.eai_batch.send_dir', '/Data/eapim/portal/sendmail/snd');
|
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.eai_batch.send_dir', '/Data/eapim/portal/sendmail/snd');
|
||||||
insert into EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.eai_batch.backup_dir', '/Data/eapim/portal/sendmail/bak');
|
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.eai_batch.backup_dir', '/Data/eapim/portal/sendmail/bak');
|
||||||
insert into EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.eai_batch.error_dir', '/Data/eapim/portal/sendmail/bak');
|
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.eai_batch.error_dir', '/Data/eapim/portal/sendmail/bak');
|
||||||
insert into EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.eai_batch.retention_date', '60');
|
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.eai_batch.retention_date', '60');
|
||||||
insert into EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.eai_batch.interface_id', 'UIEFF00001UAG');
|
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.eai_batch.interface_id', 'UIEFF00001UAG');
|
||||||
insert into EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.host_url', 'http://172.31.35.144:9021');
|
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.host_url', 'http://172.31.35.144:9021');
|
||||||
insert into EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.api_key', '7cca6d58-e4f7-474d-9edd-525806bc99ff');
|
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.api_key', '7cca6d58-e4f7-474d-9edd-525806bc99ff');
|
||||||
insert into EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.connection_timeout', '5');
|
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.connection_timeout', '5');
|
||||||
insert into EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.response_timeout', '5');
|
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.response_timeout', '5');
|
||||||
insert into EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.timeout', '10');
|
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.timeout', '10');
|
||||||
|
|
||||||
insert into EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.obp.host_url', 'http://192.168.246.15:8181');
|
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.obp.host_url', 'http://192.168.246.15:8181');
|
||||||
insert into EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.obp.trust_system', 'APIMPT-0002-QVBJTVBULTAwMDI=');
|
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.obp.trust_system', 'APIMPT-0002-QVBJTVBULTAwMDI=');
|
||||||
insert into EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.obp.commission_uri', '/api/billing/findBillingForCondition');
|
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.obp.commission_uri', '/api/billing/findBillingForCondition');
|
||||||
insert into EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.obp.commission_print_uri', '/api/billing/billing/findBillingForCondition/print');
|
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.obp.commission_print_uri', '/api/billing/billing/findBillingForCondition/print');
|
||||||
insert into EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.obp.query_partnercode_uri', '/api/customer/findCustomerByBusinessManRegistrationNo/%s');
|
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.obp.query_partnercode_uri', '/api/customer/findCustomerByBusinessManRegistrationNo/%s');
|
||||||
insert into EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.obp.create_partnercode_uri', '/not-yet-implement/%s');
|
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.obp.create_partnercode_uri', '/not-yet-implement/%s');
|
||||||
commit;
|
commit;
|
||||||
|
|
||||||
|
|
||||||
-- 은행 운영 서버용
|
-- 은행 운영 서버용
|
||||||
insert into EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.eai_batch.url', 'http://172.21.1.40:10860/BATAppWeb/EaiBatCall');
|
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.eai_batch.url', 'http://172.21.1.40:10860/BATAppWeb/EaiBatCall');
|
||||||
insert into EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.eai.batch.connection_timeout', '5');
|
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.eai.batch.connection_timeout', '5');
|
||||||
insert into EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.eai.batch.response_timeout', '5');
|
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.eai.batch.response_timeout', '5');
|
||||||
insert into EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.eai.batch.timeout', '10');
|
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.eai.batch.timeout', '10');
|
||||||
insert into EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.eai.batch.charset', 'euc-kr');
|
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.eai.batch.charset', 'euc-kr');
|
||||||
insert into EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.eai_batch.send_dir', '/Data/eapim/portal/sendmail/snd');
|
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.eai_batch.send_dir', '/Data/eapim/portal/sendmail/snd');
|
||||||
insert into EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.eai_batch.backup_dir', '/Data/eapim/portal/sendmail/bak');
|
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.eai_batch.backup_dir', '/Data/eapim/portal/sendmail/bak');
|
||||||
insert into EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.eai_batch.error_dir', '/Data/eapim/portal/sendmail/bak');
|
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.eai_batch.error_dir', '/Data/eapim/portal/sendmail/bak');
|
||||||
insert into EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.eai_batch.retention_date', '60');
|
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.eai_batch.retention_date', '60');
|
||||||
insert into EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.eai_batch.interface_id', 'UIEFF00001UAG');
|
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.eai_batch.interface_id', 'UIEFF00001UAG');
|
||||||
insert into EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.host_url', 'http://172.24.128.66:9021');
|
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.host_url', 'http://172.24.128.66:9021');
|
||||||
insert into EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.api_key', '7cca6d58-e4f7-474d-9edd-525806bc99ff');
|
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.api_key', '7cca6d58-e4f7-474d-9edd-525806bc99ff');
|
||||||
insert into EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.connection_timeout', '5');
|
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.connection_timeout', '5');
|
||||||
insert into EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.response_timeout', '5');
|
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.response_timeout', '5');
|
||||||
insert into EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.timeout', '10');
|
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.timeout', '10');
|
||||||
|
|
||||||
insert into EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.obp.host_url', 'http://192.168.246.15:8181');
|
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.obp.host_url', 'http://192.168.246.15:8181');
|
||||||
insert into EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.obp.trust_system', 'APIMPT-0002-QVBJTVBULTAwMDI=');
|
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.obp.trust_system', 'APIMPT-0002-QVBJTVBULTAwMDI=');
|
||||||
insert into EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.obp.commission_uri', '/api/billing/findBillingForCondition');
|
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.obp.commission_uri', '/api/billing/findBillingForCondition');
|
||||||
insert into EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.obp.commission_print_uri', '/api/billing/billing/findBillingForCondition/print');
|
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.obp.commission_print_uri', '/api/billing/billing/findBillingForCondition/print');
|
||||||
insert into EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.obp.query_partnercode_uri', '/api/customer/findCustomerByBusinessManRegistrationNo/%s');
|
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.obp.query_partnercode_uri', '/api/customer/findCustomerByBusinessManRegistrationNo/%s');
|
||||||
insert into EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.obp.create_partnercode_uri', '/not-yet-implement/%s');
|
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.obp.create_partnercode_uri', '/not-yet-implement/%s');
|
||||||
commit;
|
commit;
|
||||||
```
|
```
|
||||||
## 라이선스
|
## 라이선스
|
||||||
|
|||||||
@@ -255,6 +255,13 @@ 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";
|
||||||
|
|
||||||
|
// 자동 로그아웃 타임아웃 (단위: 분, 기본값: 10)
|
||||||
|
public static final String RMS_AUTO_LOGOUT_TIMEOUT = "rms.auto.logout.timeout";
|
||||||
|
|
||||||
|
// 웹훅 재전송 설정
|
||||||
|
public static final String API_WEBHOOK_RETRY_COUNT = "api.webhook.retry_count";
|
||||||
|
public static final String API_WEBHOOK_RETRY_TIME = "api.webhook.retry_time";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public abstract String getStringProperty(String propertyName);
|
public abstract String getStringProperty(String propertyName);
|
||||||
|
|||||||
@@ -617,6 +617,8 @@ public class MainController implements InterceptorSkipController {
|
|||||||
menuId = menuId == null ? "" : menuId;
|
menuId = menuId == null ? "" : menuId;
|
||||||
model.addAttribute("mainPage", mainPage);
|
model.addAttribute("mainPage", mainPage);
|
||||||
model.addAttribute("menuId", menuId);
|
model.addAttribute("menuId", menuId);
|
||||||
|
model.addAttribute("autoLogoutTimeout",
|
||||||
|
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);
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ public class MonitoringPropertyManService extends BaseService {
|
|||||||
|
|
||||||
public MonitoringPropertyGroupUI selectDetail(String prptyGroupName) {
|
public MonitoringPropertyGroupUI selectDetail(String prptyGroupName) {
|
||||||
MonitoringPropertyGroup propertyGroup = propertyGroupService.getById(prptyGroupName);
|
MonitoringPropertyGroup propertyGroup = propertyGroupService.getById(prptyGroupName);
|
||||||
|
propertyGroup.getMonitoringProperties().sort(Comparator.comparing(p -> p.getId().getPrptyName()));
|
||||||
return propertyGroupMapper.toVo(propertyGroup);
|
return propertyGroupMapper.toVo(propertyGroup);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -107,6 +107,17 @@ public class UserInfoService extends
|
|||||||
}).collect(Collectors.toList());
|
}).collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<UserInfo> findAllByRoleId(String roleId) {
|
||||||
|
QUserInfo qUserInfo = QUserInfo.userInfo;
|
||||||
|
QUserRole qUserRole = QUserRole.userRole;
|
||||||
|
|
||||||
|
return getJPAQueryFactory()
|
||||||
|
.selectFrom(qUserInfo)
|
||||||
|
.join(qUserRole).on(qUserInfo.userid.eq(qUserRole.id.userId))
|
||||||
|
.where(qUserRole.id.roleId.eq(roleId))
|
||||||
|
.fetch();
|
||||||
|
}
|
||||||
|
|
||||||
public Page<UserInfo> findByUsername(Pageable pageable, String username) {
|
public Page<UserInfo> findByUsername(Pageable pageable, String username) {
|
||||||
QUserInfo qUserInfo = QUserInfo.userInfo;
|
QUserInfo qUserInfo = QUserInfo.userInfo;
|
||||||
BooleanExpression predicate = qUserInfo.isNotNull();
|
BooleanExpression predicate = qUserInfo.isNotNull();
|
||||||
|
|||||||
+28
@@ -0,0 +1,28 @@
|
|||||||
|
package com.eactive.eai.rms.data.entity.onl.apim.portalinquiry;
|
||||||
|
|
||||||
|
import com.eactive.apim.portal.qna.entity.InquiryComment;
|
||||||
|
import com.eactive.eai.data.jpa.BaseRepository;
|
||||||
|
import com.eactive.eai.rms.data.EMSDataSource;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
@EMSDataSource
|
||||||
|
public interface PortalInquiryCommentRepository extends BaseRepository<InquiryComment, String> {
|
||||||
|
|
||||||
|
long countByInquiry_Id(String inquiryId);
|
||||||
|
|
||||||
|
long countByInquiry_IdAndDelYn(String inquiryId, String delYn);
|
||||||
|
|
||||||
|
List<InquiryComment> findByInquiry_IdAndDelYnOrderByCreatedDateAsc(String inquiryId, String delYn);
|
||||||
|
|
||||||
|
Optional<InquiryComment> findTopByInquiry_IdAndDelYnOrderByCreatedDateDesc(
|
||||||
|
String inquiryId, String delYn);
|
||||||
|
|
||||||
|
Optional<InquiryComment> findTopByInquiry_IdAndAdminYnAndDelYnOrderByCreatedDateDesc(
|
||||||
|
String inquiryId, String adminYn, String delYn);
|
||||||
|
|
||||||
|
boolean existsByInquiry_IdAndAdminYnAndDelYnAndCreatedDateAfter(
|
||||||
|
String inquiryId, String adminYn, String delYn, LocalDateTime after);
|
||||||
|
}
|
||||||
+31
@@ -0,0 +1,31 @@
|
|||||||
|
package com.eactive.eai.rms.data.entity.onl.apim.portalinquiry;
|
||||||
|
|
||||||
|
import com.eactive.apim.portal.qna.entity.InquiryComment;
|
||||||
|
import com.eactive.eai.rms.data.jpa.AbstractEMSDataSerivce;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@Transactional(transactionManager = "transactionManagerForEMS")
|
||||||
|
public class PortalInquiryCommentService extends AbstractEMSDataSerivce<InquiryComment, String, PortalInquiryCommentRepository> {
|
||||||
|
|
||||||
|
// AbstractEMSDataSerivce의 repository 필드가 BaseRepository<T,I>로 고정되어
|
||||||
|
// 커스텀 메서드를 호출할 수 없으므로 별도 필드로 직접 주입
|
||||||
|
@Autowired
|
||||||
|
private PortalInquiryCommentRepository inquiryCommentRepository;
|
||||||
|
|
||||||
|
public long countByInquiryId(String inquiryId) {
|
||||||
|
return inquiryCommentRepository.countByInquiry_Id(inquiryId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public long countActiveByInquiryId(String inquiryId) {
|
||||||
|
return inquiryCommentRepository.countByInquiry_IdAndDelYn(inquiryId, "N");
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<InquiryComment> findByInquiryId(String inquiryId) {
|
||||||
|
return inquiryCommentRepository.findByInquiry_IdAndDelYnOrderByCreatedDateAsc(inquiryId, "N");
|
||||||
|
}
|
||||||
|
}
|
||||||
+3
@@ -4,7 +4,10 @@ import com.eactive.apim.portal.qna.entity.Inquiry;
|
|||||||
import com.eactive.eai.data.jpa.BaseRepository;
|
import com.eactive.eai.data.jpa.BaseRepository;
|
||||||
import com.eactive.eai.rms.data.EMSDataSource;
|
import com.eactive.eai.rms.data.EMSDataSource;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
@EMSDataSource
|
@EMSDataSource
|
||||||
public interface PortalInquiryRepository extends BaseRepository<Inquiry, String> {
|
public interface PortalInquiryRepository extends BaseRepository<Inquiry, String> {
|
||||||
|
|
||||||
|
List<Inquiry> findByInquiryStatus(String inquiryStatus);
|
||||||
}
|
}
|
||||||
|
|||||||
+18
-6
@@ -1,20 +1,28 @@
|
|||||||
package com.eactive.eai.rms.data.entity.onl.apim.portalinquiry;
|
package com.eactive.eai.rms.data.entity.onl.apim.portalinquiry;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
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.qna.entity.Inquiry;
|
import com.eactive.apim.portal.qna.entity.Inquiry;
|
||||||
import com.eactive.apim.portal.qna.entity.QInquiry;
|
import com.eactive.apim.portal.qna.entity.QInquiry;
|
||||||
import com.eactive.eai.rms.data.jpa.AbstractEMSDataSerivce;
|
import com.eactive.eai.rms.data.jpa.AbstractEMSDataSerivce;
|
||||||
import com.querydsl.core.BooleanBuilder;
|
import com.querydsl.core.BooleanBuilder;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
|
||||||
import org.springframework.data.domain.Page;
|
|
||||||
import org.springframework.data.domain.Pageable;
|
|
||||||
import org.springframework.stereotype.Service;
|
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
@Transactional(transactionManager = "transactionManagerForEMS")
|
@Transactional(transactionManager = "transactionManagerForEMS")
|
||||||
public class PortalInquiryService extends AbstractEMSDataSerivce<Inquiry, String, PortalInquiryRepository> {
|
public class PortalInquiryService extends AbstractEMSDataSerivce<Inquiry, String, PortalInquiryRepository> {
|
||||||
|
|
||||||
|
// AbstractEMSDataSerivce의 repository 필드가 BaseRepository<T,I>로 고정되어
|
||||||
|
// 커스텀 메서드를 호출할 수 없으므로 별도 필드로 직접 주입
|
||||||
|
@Autowired
|
||||||
|
private PortalInquiryRepository portalInquiryRepository;
|
||||||
|
|
||||||
public Page<Inquiry> findAll(Pageable pageable, PortalInquirySearch portalInquirySearch) {
|
public Page<Inquiry> findAll(Pageable pageable, PortalInquirySearch portalInquirySearch) {
|
||||||
QInquiry qInquiry = QInquiry.inquiry;
|
QInquiry qInquiry = QInquiry.inquiry;
|
||||||
|
|
||||||
@@ -58,4 +66,8 @@ public class PortalInquiryService extends AbstractEMSDataSerivce<Inquiry, String
|
|||||||
repository.deleteAll(inquiries);
|
repository.deleteAll(inquiries);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<Inquiry> findByInquiryStatus(String inquiryStatus) {
|
||||||
|
return portalInquiryRepository.findByInquiryStatus(inquiryStatus);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package com.eactive.eai.rms.data.ext.djb.apistatus;
|
package com.eactive.eai.rms.data.entity.onl.djb.apistatus;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
+2
-1
@@ -1,7 +1,8 @@
|
|||||||
package com.eactive.eai.rms.data.ext.djb.apistatus;
|
package com.eactive.eai.rms.data.entity.onl.djb.apistatus;
|
||||||
|
|
||||||
public interface ApiStatusEvent {
|
public interface ApiStatusEvent {
|
||||||
String getEaisvcname();
|
String getEaisvcname();
|
||||||
String getEaisvcdesc();
|
String getEaisvcdesc();
|
||||||
|
String getPrevStatusCode();
|
||||||
String getEvent();
|
String getEvent();
|
||||||
}
|
}
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package com.eactive.eai.rms.data.ext.djb.inflow;
|
package com.eactive.eai.rms.data.entity.onl.djb.inflow;
|
||||||
|
|
||||||
public interface InflowTokenInsufficient {
|
public interface InflowTokenInsufficient {
|
||||||
String getEaisvcname();
|
String getEaisvcname();
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package com.eactive.eai.rms.data.ext.djb.inflow;
|
package com.eactive.eai.rms.data.entity.onl.djb.inflow;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package com.eactive.eai.rms.data.ext.djb.inflow;
|
package com.eactive.eai.rms.data.entity.onl.djb.inflow;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import javax.persistence.Column;
|
import javax.persistence.Column;
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
package com.eactive.eai.rms.data.entity.onl.djb.webhook;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import org.hibernate.annotations.Comment;
|
||||||
|
|
||||||
|
import javax.persistence.Column;
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
import javax.persistence.Table;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "PTL_WEBHOOK_REQ")
|
||||||
|
@Getter
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class WebhookReq {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@Column(name = "ID", length = 36, nullable = false)
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
@Column(name = "ORG_ID", length = 100)
|
||||||
|
@Comment("법인 ID")
|
||||||
|
private String orgId;
|
||||||
|
|
||||||
|
@Column(name = "TARGET_URL", length = 500, nullable = false)
|
||||||
|
@Comment("웹훅 수신 URL")
|
||||||
|
private String targetUrl;
|
||||||
|
|
||||||
|
@Column(name = "SECRET", length = 200)
|
||||||
|
@Comment("HMAC 서명용 Secret Key")
|
||||||
|
private String secret;
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package com.eactive.eai.rms.data.entity.onl.djb.webhook;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import javax.persistence.EmbeddedId;
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.Table;
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "PTL_WEBHOOK_REQ_API")
|
||||||
|
@Getter
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class WebhookReqApi implements Serializable {
|
||||||
|
|
||||||
|
@EmbeddedId
|
||||||
|
private WebhookReqApiId id;
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package com.eactive.eai.rms.data.entity.onl.djb.webhook;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import org.hibernate.annotations.Comment;
|
||||||
|
|
||||||
|
import javax.persistence.Column;
|
||||||
|
import javax.persistence.Embeddable;
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Embeddable
|
||||||
|
public class WebhookReqApiId implements Serializable {
|
||||||
|
|
||||||
|
@Column(name = "WEBHOOK_REQ_ID", nullable = false, length = 36)
|
||||||
|
@Comment("웹훅 요청 ID")
|
||||||
|
private String webhookReqId;
|
||||||
|
|
||||||
|
@Column(name = "API_ID", nullable = false, length = 100)
|
||||||
|
@Comment("API ID")
|
||||||
|
private String apiId;
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package com.eactive.eai.rms.data.entity.onl.djb.webhook;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import javax.persistence.EmbeddedId;
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.Table;
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "PTL_WEBHOOK_REQ_EVENT")
|
||||||
|
@Getter
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class WebhookReqEvent implements Serializable {
|
||||||
|
|
||||||
|
@EmbeddedId
|
||||||
|
private WebhookReqEventId id;
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package com.eactive.eai.rms.data.entity.onl.djb.webhook;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import org.hibernate.annotations.Comment;
|
||||||
|
|
||||||
|
import javax.persistence.Column;
|
||||||
|
import javax.persistence.Embeddable;
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Embeddable
|
||||||
|
public class WebhookReqEventId implements Serializable {
|
||||||
|
|
||||||
|
@Column(name = "WEBHOOK_REQ_ID", nullable = false, length = 36)
|
||||||
|
@Comment("웹훅 요청 ID")
|
||||||
|
private String webhookReqId;
|
||||||
|
|
||||||
|
@Column(name = "EVENT_TYPE", nullable = false, length = 50)
|
||||||
|
@Comment("이벤트 유형 (CONTROL_START, ERROR_START 등)")
|
||||||
|
private String eventType;
|
||||||
|
}
|
||||||
+4
-1
@@ -1,4 +1,4 @@
|
|||||||
package com.eactive.eai.rms.data.ext.djb.webhook;
|
package com.eactive.eai.rms.data.entity.onl.djb.webhook;
|
||||||
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
@@ -42,6 +42,9 @@ public class WebhookSendLog {
|
|||||||
@Column(name = "STATUS_CODE")
|
@Column(name = "STATUS_CODE")
|
||||||
private Integer statusCode;
|
private Integer statusCode;
|
||||||
|
|
||||||
|
@Column(name = "ORG_ID")
|
||||||
|
private String orgId;
|
||||||
|
|
||||||
@Lob
|
@Lob
|
||||||
@Column(name = "RESPONSE_BODY")
|
@Column(name = "RESPONSE_BODY")
|
||||||
private String responseBody;
|
private String responseBody;
|
||||||
+43
-80
@@ -1,9 +1,6 @@
|
|||||||
package com.eactive.eai.rms.data.entity.onl.inflow;
|
package com.eactive.eai.rms.data.entity.onl.inflow;
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import javax.persistence.EntityManager;
|
import javax.persistence.EntityManager;
|
||||||
@@ -18,6 +15,7 @@ import org.springframework.stereotype.Service;
|
|||||||
|
|
||||||
import com.eactive.apim.portal.app.entity.QCredential;
|
import com.eactive.apim.portal.app.entity.QCredential;
|
||||||
import com.eactive.eai.data.entity.onl.adapter.QAdapterGroup;
|
import com.eactive.eai.data.entity.onl.adapter.QAdapterGroup;
|
||||||
|
import com.eactive.eai.data.entity.onl.authserver.QClientEntity;
|
||||||
import com.eactive.eai.data.entity.onl.inflow.InflowControl;
|
import com.eactive.eai.data.entity.onl.inflow.InflowControl;
|
||||||
import com.eactive.eai.data.entity.onl.inflow.InflowControlId;
|
import com.eactive.eai.data.entity.onl.inflow.InflowControlId;
|
||||||
import com.eactive.eai.data.entity.onl.inflow.QInflowControl;
|
import com.eactive.eai.data.entity.onl.inflow.QInflowControl;
|
||||||
@@ -29,7 +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.OrderSpecifier;
|
|
||||||
import com.querydsl.jpa.impl.JPAQuery;
|
import com.querydsl.jpa.impl.JPAQuery;
|
||||||
import com.querydsl.jpa.impl.JPAQueryFactory;
|
import com.querydsl.jpa.impl.JPAQueryFactory;
|
||||||
|
|
||||||
@@ -186,101 +183,67 @@ public class InflowControlService extends AbstractDataService<InflowControl, Inf
|
|||||||
}
|
}
|
||||||
|
|
||||||
public InflowControlServiceDto findByIdForClient(String clientId) {
|
public InflowControlServiceDto findByIdForClient(String clientId) {
|
||||||
QCredential qCredential = QCredential.credential;
|
JPAQueryFactory jpaQueryFactory = new JPAQueryFactory(entityManager);
|
||||||
QInflowControl qInflowControl = QInflowControl.inflowControl;
|
QInflowControl qInflowControl = QInflowControl.inflowControl;
|
||||||
|
QClientEntity qClientEntity = QClientEntity.clientEntity;
|
||||||
|
|
||||||
// EMSADM: Credential 조회
|
Tuple tuple = jpaQueryFactory
|
||||||
Tuple credTuple = new JPAQueryFactory(entityManagerForEMS)
|
.select(qInflowControl, qClientEntity.clientid, qClientEntity.clientname)
|
||||||
.select(qCredential.clientid, qCredential.clientname)
|
.from(qClientEntity)
|
||||||
.from(qCredential)
|
.leftJoin(qInflowControl)
|
||||||
.where(qCredential.clientid.eq(clientId))
|
.on(qInflowControl.id.type.eq(CLIENT_TYPE_INFLOW)
|
||||||
|
.and(qInflowControl.id.name.eq(qClientEntity.clientid)))
|
||||||
|
.where(qClientEntity.clientid.eq(clientId))
|
||||||
.fetchOne();
|
.fetchOne();
|
||||||
|
|
||||||
// AGWADM: InflowControl 조회
|
return toDto(qClientEntity, qInflowControl, tuple);
|
||||||
InflowControl inflowControl = new JPAQueryFactory(entityManager)
|
|
||||||
.selectFrom(qInflowControl)
|
|
||||||
.where(qInflowControl.id.type.eq(CLIENT_TYPE_INFLOW)
|
|
||||||
.and(qInflowControl.id.name.eq(clientId)))
|
|
||||||
.fetchOne();
|
|
||||||
|
|
||||||
InflowControlServiceDto dto;
|
|
||||||
if (inflowControl != null) {
|
|
||||||
dto = mapper.toDto(inflowControl);
|
|
||||||
} else {
|
|
||||||
dto = new InflowControlServiceDto();
|
|
||||||
dto.setName(clientId);
|
|
||||||
dto.setType(CLIENT_TYPE_INFLOW);
|
|
||||||
}
|
|
||||||
if (credTuple != null) {
|
|
||||||
dto.setDesc(credTuple.get(qCredential.clientname));
|
|
||||||
}
|
|
||||||
return dto;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Page<InflowControlServiceDto> findAllForClient(Pageable pageable, String searchName) {
|
public Page<InflowControlServiceDto> findAllForClient(Pageable pageable, String searchName) {
|
||||||
QCredential qCredential = QCredential.credential;
|
|
||||||
|
JPAQueryFactory jpaQueryFactory = new JPAQueryFactory(entityManager);
|
||||||
QInflowControl qInflowControl = QInflowControl.inflowControl;
|
QInflowControl qInflowControl = QInflowControl.inflowControl;
|
||||||
|
QClientEntity qClientEntity = QClientEntity.clientEntity;
|
||||||
|
|
||||||
// EMSADM: Credential 목록 및 건수 조회
|
List<Tuple> tupleList = jpaQueryFactory
|
||||||
JPAQueryFactory emsFactory = new JPAQueryFactory(entityManagerForEMS);
|
.select(qInflowControl, qClientEntity.clientid, qClientEntity.clientname)
|
||||||
BooleanBuilder credPredicate = new BooleanBuilder();
|
.from(qClientEntity)
|
||||||
if (!StringUtils.isEmpty(searchName)) {
|
.leftJoin(qInflowControl)
|
||||||
credPredicate.and(qCredential.clientname.contains(searchName));
|
.on(qInflowControl.id.type.eq(CLIENT_TYPE_INFLOW)
|
||||||
}
|
.and(qInflowControl.id.name.eq(qClientEntity.clientid)))
|
||||||
|
.where(qClientEntity.clientname.contains(searchName))
|
||||||
long totalCount = emsFactory
|
.orderBy(qClientEntity.clientname.asc())
|
||||||
.select(qCredential.clientid.count())
|
|
||||||
.from(qCredential)
|
|
||||||
.where(credPredicate)
|
|
||||||
.fetchOne();
|
|
||||||
|
|
||||||
List<Tuple> credList = emsFactory
|
|
||||||
.select(qCredential.clientid, qCredential.clientname)
|
|
||||||
.from(qCredential)
|
|
||||||
.where(credPredicate)
|
|
||||||
.orderBy(qCredential.clientname.asc())
|
|
||||||
.offset(pageable.getOffset())
|
.offset(pageable.getOffset())
|
||||||
.limit(pageable.getPageSize())
|
.limit(pageable.getPageSize())
|
||||||
.fetch();
|
.fetch();
|
||||||
|
|
||||||
if (credList.isEmpty()) {
|
List<InflowControlServiceDto> dtoList = tupleList
|
||||||
return new PageImpl<>(Collections.emptyList(), pageable, totalCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
// AGWADM: 해당 clientId의 InflowControl 일괄 조회
|
|
||||||
List<String> clientIds = credList.stream()
|
|
||||||
.map(t -> t.get(qCredential.clientid))
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
|
|
||||||
Map<String, InflowControl> inflowMap = new JPAQueryFactory(entityManager)
|
|
||||||
.selectFrom(qInflowControl)
|
|
||||||
.where(qInflowControl.id.type.eq(CLIENT_TYPE_INFLOW)
|
|
||||||
.and(qInflowControl.id.name.in(clientIds)))
|
|
||||||
.fetch()
|
|
||||||
.stream()
|
.stream()
|
||||||
.collect(Collectors.toMap(ic -> ic.getId().getName(), ic -> ic));
|
.map(tuple -> toDto(qClientEntity, qInflowControl, tuple))
|
||||||
|
|
||||||
// Java에서 병합
|
|
||||||
List<InflowControlServiceDto> dtoList = credList.stream()
|
|
||||||
.map(tuple -> {
|
|
||||||
String clientId = tuple.get(qCredential.clientid);
|
|
||||||
InflowControl inflowControl = inflowMap.get(clientId);
|
|
||||||
InflowControlServiceDto dto;
|
|
||||||
if (inflowControl != null) {
|
|
||||||
dto = mapper.toDto(inflowControl);
|
|
||||||
} else {
|
|
||||||
dto = new InflowControlServiceDto();
|
|
||||||
dto.setName(clientId);
|
|
||||||
dto.setType(CLIENT_TYPE_INFLOW);
|
|
||||||
}
|
|
||||||
dto.setDesc(tuple.get(qCredential.clientname));
|
|
||||||
return dto;
|
|
||||||
})
|
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
long totalCount = jpaQueryFactory
|
||||||
|
.select(qClientEntity.clientname.count())
|
||||||
|
.from(qClientEntity)
|
||||||
|
.where(qClientEntity.clientname.contains(searchName))
|
||||||
|
.fetchOne();
|
||||||
return new PageImpl<>(dtoList, pageable, totalCount);
|
return new PageImpl<>(dtoList, pageable, totalCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private InflowControlServiceDto toDto(QClientEntity qClientEntity, QInflowControl qInflowControl,
|
||||||
|
Tuple tuple) {
|
||||||
|
InflowControlServiceDto dto = null;
|
||||||
|
if (tuple.get(qInflowControl) != null)
|
||||||
|
dto = mapper.toDto(tuple.get(qInflowControl));
|
||||||
|
else {
|
||||||
|
dto = new InflowControlServiceDto();
|
||||||
|
dto.setName(tuple.get(qClientEntity.clientid));
|
||||||
|
dto.setType(CLIENT_TYPE_INFLOW);
|
||||||
|
}
|
||||||
|
dto.setDesc(tuple.get(qClientEntity.clientname));
|
||||||
|
return dto;
|
||||||
|
}
|
||||||
|
|
||||||
public Page<Tuple> selectLogList(Pageable pageable, InflowControlHistoryManUISearch uiSearch) {
|
public Page<Tuple> selectLogList(Pageable pageable, InflowControlHistoryManUISearch uiSearch) {
|
||||||
QInflowControlLog qlog = QInflowControlLog.inflowControlLog;
|
QInflowControlLog qlog = QInflowControlLog.inflowControlLog;
|
||||||
QInflowControlGroup qgroup = QInflowControlGroup.inflowControlGroup;
|
QInflowControlGroup qgroup = QInflowControlGroup.inflowControlGroup;
|
||||||
|
|||||||
+8
-1
@@ -5,7 +5,9 @@ import java.util.List;
|
|||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.springframework.data.domain.Page;
|
import org.springframework.data.domain.Page;
|
||||||
|
import org.springframework.data.domain.PageRequest;
|
||||||
import org.springframework.data.domain.Pageable;
|
import org.springframework.data.domain.Pageable;
|
||||||
|
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;
|
||||||
|
|
||||||
@@ -42,7 +44,12 @@ public class ExtendedColumnDefinitionService
|
|||||||
predicate = predicate.and(qExtendedColumnDefinition.columnDesc.containsIgnoreCase(searchColumnDesc));
|
predicate = predicate.and(qExtendedColumnDefinition.columnDesc.containsIgnoreCase(searchColumnDesc));
|
||||||
}
|
}
|
||||||
|
|
||||||
return repository.findAll(predicate, pageable);
|
Pageable orderedPageable = PageRequest.of(
|
||||||
|
pageable.getPageNumber(),
|
||||||
|
pageable.getPageSize(),
|
||||||
|
Sort.by(Sort.Order.asc("orderSeq"), Sort.Order.asc("columnName")));
|
||||||
|
|
||||||
|
return repository.findAll(predicate, orderedPageable);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,69 +0,0 @@
|
|||||||
package com.eactive.eai.rms.data.ext.djb.apistatus;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.context.ApplicationEventPublisher;
|
|
||||||
import org.springframework.stereotype.Service;
|
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
|
||||||
|
|
||||||
import com.eactive.eai.rms.ext.djb.event.ApiStatusChangedEvent;
|
|
||||||
|
|
||||||
@Service
|
|
||||||
@Transactional
|
|
||||||
public class ApiStatusService {
|
|
||||||
|
|
||||||
private static final Logger log = LoggerFactory.getLogger(ApiStatusService.class);
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private ApiStatusRepository apiStatusRepository;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private ApplicationEventPublisher eventPublisher;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public void updateApiStatus(HashMap<String, String> param) {
|
|
||||||
|
|
||||||
List<ApiStatusEvent> list = apiStatusRepository.findApiStatusEvents(
|
|
||||||
Integer.parseInt(param.get("errorRate")),
|
|
||||||
Integer.parseInt(param.get("errorRangeMinute")),
|
|
||||||
Integer.parseInt(param.get("delayRangeMinute")),
|
|
||||||
Integer.parseInt(param.get("delayAvgRespTime"))
|
|
||||||
);
|
|
||||||
|
|
||||||
for (ApiStatusEvent event : list) {
|
|
||||||
String newStatusCode = resolveStatusCode(event.getEvent());
|
|
||||||
if (newStatusCode == null) continue;
|
|
||||||
|
|
||||||
ApiStatus apiStatus = apiStatusRepository.findById(event.getEaisvcname()).orElse(new ApiStatus());
|
|
||||||
|
|
||||||
apiStatus.setEaisvcname(event.getEaisvcname());
|
|
||||||
apiStatus.setStatusCode(newStatusCode);
|
|
||||||
|
|
||||||
apiStatusRepository.save(apiStatus); // PK 있으면 UPDATE, 없으면 INSERT
|
|
||||||
log.debug("API 상태 변경: {}-{} {} → {}", event.getEaisvcname(), event.getEaisvcdesc(), event.getEvent(), newStatusCode);
|
|
||||||
|
|
||||||
eventPublisher.publishEvent(ApiStatusChangedEvent.from(event)); // 트랜잭션 커밋 후 리스너 실행
|
|
||||||
log.debug("이벤트 전파");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private String resolveStatusCode(String event) {
|
|
||||||
switch (event) {
|
|
||||||
case "CONTROL_START": return "C"; //점검
|
|
||||||
case "CONTROL_END": return "N"; //정상
|
|
||||||
case "ERROR_START": return "E"; //장애
|
|
||||||
case "ERROR_END": return "N"; //정상
|
|
||||||
case "DELAY_START": return "D"; //지연
|
|
||||||
case "DELAY_END": return "N"; //정상
|
|
||||||
default:
|
|
||||||
log.warn("알 수 없는 이벤트: {}", event);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
package com.eactive.eai.rms.ext.djb.apistatus;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@Transactional
|
||||||
|
public class ApiStatusDetectionService {
|
||||||
|
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(ApiStatusDetectionService.class);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 이벤트 감지
|
||||||
|
* @param event "CONTROL_START" : 점검시작
|
||||||
|
"CONTROL_END" : 점검종료
|
||||||
|
"ERROR_START" : 장애시작
|
||||||
|
"ERROR_END" : 장애종료
|
||||||
|
"DELAY_START" : 지연시작
|
||||||
|
"DELAY_END" : 지연종료
|
||||||
|
* @param apiIds API IDs
|
||||||
|
*/
|
||||||
|
public void detect(String event, List<String> apiIds) {
|
||||||
|
log.debug("이벤트 감지: {} {}", event, apiIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
// API FSM 내 아직 장애로 남아있는 API 목록
|
||||||
|
public void remainDownApiIds() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 정상 동작 - detect()에서 일괄 처리 (복구 = 점검종료, 장애종료, 지연종료)
|
||||||
|
// public void recovered(String event, String[] apiIds) {
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
|
||||||
|
}
|
||||||
+13
-8
@@ -1,10 +1,12 @@
|
|||||||
package com.eactive.eai.rms.data.ext.djb.apistatus;
|
package com.eactive.eai.rms.ext.djb.apistatus;
|
||||||
import java.util.List;
|
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 com.eactive.eai.data.jpa.BaseRepository;
|
import com.eactive.eai.data.jpa.BaseRepository;
|
||||||
|
import com.eactive.eai.rms.data.entity.onl.djb.apistatus.ApiStatus;
|
||||||
|
import com.eactive.eai.rms.data.entity.onl.djb.apistatus.ApiStatusEvent;
|
||||||
|
|
||||||
public interface ApiStatusRepository extends BaseRepository<ApiStatus, String> {
|
public interface ApiStatusRepository extends BaseRepository<ApiStatus, String> {
|
||||||
|
|
||||||
@@ -22,9 +24,11 @@ public interface ApiStatusRepository extends BaseRepository<ApiStatus, String> {
|
|||||||
@Query(nativeQuery = true, value =
|
@Query(nativeQuery = true, value =
|
||||||
" SELECT EAISVCNAME"
|
" SELECT EAISVCNAME"
|
||||||
+ " , (SELECT EAISVCDESC FROM TSEAIHE01 WHERE A.EAISVCNAME = EAISVCNAME) AS EAISVCDESC"
|
+ " , (SELECT EAISVCDESC FROM TSEAIHE01 WHERE A.EAISVCNAME = EAISVCNAME) AS EAISVCDESC"
|
||||||
|
+ " , PREV_STATUS_CODE "
|
||||||
+ " , EVENT"
|
+ " , EVENT"
|
||||||
+ " FROM ("
|
+ " FROM ("
|
||||||
+ " SELECT EAISVCNAME"
|
+ " SELECT EAISVCNAME"
|
||||||
|
+ " , STATUS_CODE AS PREV_STATUS_CODE "
|
||||||
+ " , CASE WHEN STATUS_CODE != 'C' AND CTRL_YN = 'Y' THEN 'CONTROL_START'"
|
+ " , CASE WHEN STATUS_CODE != 'C' AND CTRL_YN = 'Y' THEN 'CONTROL_START'"
|
||||||
+ " WHEN STATUS_CODE = 'C' AND CTRL_YN = 'N' THEN 'CONTROL_END'"
|
+ " WHEN STATUS_CODE = 'C' AND CTRL_YN = 'N' THEN 'CONTROL_END'"
|
||||||
+ " WHEN STATUS_CODE IN ('N','D') AND ERR_YN = 'Y' THEN 'ERROR_START'"
|
+ " WHEN STATUS_CODE IN ('N','D') AND ERR_YN = 'Y' THEN 'ERROR_START'"
|
||||||
@@ -34,14 +38,15 @@ public interface ApiStatusRepository extends BaseRepository<ApiStatus, String> {
|
|||||||
+ " ELSE 'STAY' END AS EVENT"
|
+ " ELSE 'STAY' END AS EVENT"
|
||||||
+ " FROM ("
|
+ " FROM ("
|
||||||
+ " SELECT A.EAISVCNAME"
|
+ " SELECT A.EAISVCNAME"
|
||||||
+ " , NVL(B.STATUS_CODE, '1') AS STATUS_CODE"
|
+ " , NVL(B.STATUS_CODE, 'N') AS STATUS_CODE"
|
||||||
+ " , NVL((SELECT 'Y' FROM TSEAITI01"
|
//TODO: 게시판 임시로 생성하여 사용. 유차장이 완료하면 변경할것
|
||||||
+ " WHERE (TO_CHAR(SYSDATE,'HH24MI') BETWEEN SUBSTR(EAICTRLDSTICCTNT,16,4) AND SUBSTR(EAICTRLDSTICCTNT,21,4)"
|
+ " , NVL(( SELECT 'Y' FROM PTL_NOTICE_API X "
|
||||||
+ " OR SUBSTR(EAICTRLDSTICCTNT,16,9) = '0000|0000')"
|
+ " WHERE EXISTS (SELECT 1 FROM EMSAPP.PTL_NOTICE Y WHERE X.NOTICE_ID = Y.ID)"
|
||||||
+ " AND A.EAISVCNAME = EAICTRLNAME),'N') AS CTRL_YN"
|
//+ " AND TO_CHAR(SYSDATE, 'YYYYMMDDHH24MI') BETWEEN START_TIME AND END_TIME) "
|
||||||
|
+ " AND A.EAISVCNAME = X.API_NAME),'N') AS CTRL_YN "
|
||||||
+ " , (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 SUM(SUCCESS_CNT + TIMEOUT_CNT + SYSTEM_ERR_CNT + BIZ_ERR_CNT) 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"
|
||||||
@@ -0,0 +1,133 @@
|
|||||||
|
package com.eactive.eai.rms.ext.djb.apistatus;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import com.eactive.apim.portal.template.entity.MessageCode;
|
||||||
|
import com.eactive.eai.rms.data.entity.man.role.Role;
|
||||||
|
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.ext.djb.ums.UmsManager;
|
||||||
|
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@Transactional
|
||||||
|
public class ApiStatusService {
|
||||||
|
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(ApiStatusService.class);
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ApiStatusRepository apiStatusRepository;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private UmsManager ums;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ApiStatusDetectionService apiStatusDectionService;
|
||||||
|
|
||||||
|
|
||||||
|
public void updateApiStatus(HashMap<String, String> param) {
|
||||||
|
|
||||||
|
List<ApiStatusEvent> list = apiStatusRepository.findApiStatusEvents(
|
||||||
|
Integer.parseInt(param.get("errorRate")),
|
||||||
|
Integer.parseInt(param.get("errorRangeMinute")),
|
||||||
|
Integer.parseInt(param.get("delayRangeMinute")),
|
||||||
|
Integer.parseInt(param.get("delayAvgRespTime"))
|
||||||
|
);
|
||||||
|
|
||||||
|
HashMap<String, List<String>> eventMap = new HashMap<>();
|
||||||
|
|
||||||
|
|
||||||
|
for (ApiStatusEvent event : list) {
|
||||||
|
String newStatusCode = resolveStatusCode(event.getEvent());
|
||||||
|
if (newStatusCode == null) continue;
|
||||||
|
|
||||||
|
ApiStatus apiStatus = apiStatusRepository.findById(event.getEaisvcname()).orElse(new ApiStatus());
|
||||||
|
|
||||||
|
apiStatus.setEaisvcname(event.getEaisvcname());
|
||||||
|
apiStatus.setStatusCode(newStatusCode);
|
||||||
|
|
||||||
|
apiStatusRepository.save(apiStatus); // PK 있으면 UPDATE, 없으면 INSERT
|
||||||
|
log.debug("API 상태 변경: {}-{} {} → {}", event.getEaisvcname(), event.getEaisvcdesc(), event.getEvent(), newStatusCode);
|
||||||
|
|
||||||
|
|
||||||
|
//이벤트별로 api분리 저장
|
||||||
|
List<String> apis = (List<String>)eventMap.get(event.getEvent());
|
||||||
|
if (apis == null) {
|
||||||
|
apis = new ArrayList<String>();
|
||||||
|
apis.add(event.getEaisvcname());
|
||||||
|
eventMap.put(event.getEvent(), apis);
|
||||||
|
} else {
|
||||||
|
apis.add(event.getEaisvcname());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//이벤트별로 ums, webhook을 발송한다
|
||||||
|
for (Map.Entry<String, List<String>> entry : eventMap.entrySet()) {
|
||||||
|
String event = entry.getKey();
|
||||||
|
List<String> apiIds = entry.getValue();
|
||||||
|
|
||||||
|
String message = getSwingChatMessage(event, apiIds);
|
||||||
|
ums.sendMessenger("api-monitor", MessageCode.API_STATUS_CHANGED, message);
|
||||||
|
|
||||||
|
//제휴사 웹훅 발송
|
||||||
|
ums.sendWebhook(event, apiIds);
|
||||||
|
|
||||||
|
//개발자포탈 API상태모니터링 화면을 위한 처리
|
||||||
|
apiStatusDectionService.detect(event, apiIds);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private String resolveStatusCode(String event) {
|
||||||
|
switch (event) {
|
||||||
|
case "CONTROL_START": return "C"; //점검
|
||||||
|
case "CONTROL_END": return "N"; //정상
|
||||||
|
case "ERROR_START": return "E"; //장애
|
||||||
|
case "ERROR_END": return "N"; //정상
|
||||||
|
case "DELAY_START": return "D"; //지연
|
||||||
|
case "DELAY_END": return "N"; //정상
|
||||||
|
default:
|
||||||
|
log.warn("알 수 없는 이벤트: {}", event);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private String getSwingChatMessage(String event, List<String> apiIds) {
|
||||||
|
String message = "";
|
||||||
|
switch (event) {
|
||||||
|
case "CONTROL_START":
|
||||||
|
message = "API 서비스 점검이 시작되었습니다"; //점검
|
||||||
|
break;
|
||||||
|
case "CONTROL_END":
|
||||||
|
message = "API 서비스 점검이 완료되었습니다"; //정상
|
||||||
|
break;
|
||||||
|
case "ERROR_START":
|
||||||
|
message = "API 서비스 장애가 발생하였습니다"; //장애
|
||||||
|
break;
|
||||||
|
case "ERROR_END":
|
||||||
|
message = "API 서비스 장애가 복구되었습니다"; //정상
|
||||||
|
break;
|
||||||
|
case "DELAY_START":
|
||||||
|
message = "API 서비스 지연이 발생하였습니다"; //지연
|
||||||
|
break;
|
||||||
|
case "DELAY_END":
|
||||||
|
message = "API 서비스 지연이 복구되었습니다"; //정상
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
message = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
return message + "\n- " + String.join(",", apiIds);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
package com.eactive.eai.rms.ext.djb.async;
|
|
||||||
|
|
||||||
import java.util.concurrent.Executor;
|
|
||||||
|
|
||||||
import org.springframework.context.annotation.Bean;
|
|
||||||
import org.springframework.context.annotation.Configuration;
|
|
||||||
import org.springframework.scheduling.annotation.EnableAsync;
|
|
||||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
|
||||||
|
|
||||||
@Configuration
|
|
||||||
@EnableAsync
|
|
||||||
public class AsyncConfig {
|
|
||||||
|
|
||||||
@Bean("asyncExecutor")
|
|
||||||
public Executor asyncExecutor() {
|
|
||||||
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
|
|
||||||
executor.setCorePoolSize(2);
|
|
||||||
executor.setMaxPoolSize(5);
|
|
||||||
executor.setQueueCapacity(20);
|
|
||||||
executor.setThreadNamePrefix("async-");
|
|
||||||
executor.initialize();
|
|
||||||
return executor;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
package com.eactive.eai.rms.ext.djb.async;
|
|
||||||
|
|
||||||
import org.springframework.context.event.EventListener;
|
|
||||||
import org.springframework.scheduling.annotation.Async;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
import org.springframework.transaction.event.TransactionPhase;
|
|
||||||
import org.springframework.transaction.event.TransactionalEventListener;
|
|
||||||
|
|
||||||
import com.eactive.eai.rms.ext.djb.event.ApiStatusChangedEvent;
|
|
||||||
import com.eactive.eai.rms.ext.djb.event.InflowTokenInsufficientEvent;
|
|
||||||
import com.eactive.eai.rms.ext.djb.swing.service.SwingSendManager;
|
|
||||||
import com.eactive.eai.rms.ext.djb.webhook.service.WebhookSendManager;
|
|
||||||
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
|
|
||||||
@Slf4j
|
|
||||||
@Component
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
public class AsyncEventListener {
|
|
||||||
|
|
||||||
private final WebhookSendManager webhookSendManager;
|
|
||||||
|
|
||||||
private final SwingSendManager swingSendManager;
|
|
||||||
|
|
||||||
@Async("asyncExecutor")
|
|
||||||
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
|
|
||||||
public void onApiStatusChanged(ApiStatusChangedEvent event) {
|
|
||||||
log.debug("API 상태 변경 이벤트 수신: {} {}", event.getEaisvcname(), event.getEvent());
|
|
||||||
webhookSendManager.send(event.getEaisvcname(), event.getEvent());
|
|
||||||
swingSendManager.send(null, 0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Async("asyncExecutor")
|
|
||||||
@EventListener // 트랜잭션 write 없이 read만 하므로 TransactionalEventListener 불필요
|
|
||||||
public void onInflowTokenFail(InflowTokenInsufficientEvent event) {
|
|
||||||
log.debug("유량제어 토큰획득 실패 이벤트 수신: {} {}건", event.getEaisvcname(), event.getCnt());
|
|
||||||
swingSendManager.send(event.getEaisvcname(), event.getCnt(), event.getRangeMinute());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
package com.eactive.eai.rms.ext.djb.event;
|
|
||||||
|
|
||||||
import com.eactive.eai.rms.data.ext.djb.apistatus.ApiStatusEvent;
|
|
||||||
|
|
||||||
import lombok.Getter;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
|
|
||||||
@Getter
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
public class ApiStatusChangedEvent {
|
|
||||||
private final String eaisvcname;
|
|
||||||
private final String eaisvcdesc;
|
|
||||||
private final String event;
|
|
||||||
|
|
||||||
public static ApiStatusChangedEvent from(ApiStatusEvent source) {
|
|
||||||
return new ApiStatusChangedEvent(source.getEaisvcname(), source.getEaisvcdesc(), source.getEvent());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
package com.eactive.eai.rms.ext.djb.event;
|
|
||||||
|
|
||||||
import com.eactive.eai.rms.data.ext.djb.inflow.InflowTokenInsufficient;
|
|
||||||
|
|
||||||
import lombok.Getter;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
|
|
||||||
@Getter
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
public class InflowTokenInsufficientEvent {
|
|
||||||
private final String eaisvcname;
|
|
||||||
private final String eaisvcdesc;
|
|
||||||
private final long cnt;
|
|
||||||
private final long rangeMinute;
|
|
||||||
|
|
||||||
public static InflowTokenInsufficientEvent from(InflowTokenInsufficient summary, long rangeMinute) {
|
|
||||||
return new InflowTokenInsufficientEvent(summary.getEaisvcname(), summary.getEaisvcdesc(), summary.getCnt(), rangeMinute);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+4
-1
@@ -1,9 +1,12 @@
|
|||||||
package com.eactive.eai.rms.data.ext.djb.inflow;
|
package com.eactive.eai.rms.ext.djb.inflow;
|
||||||
|
|
||||||
import java.util.List;
|
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 com.eactive.eai.data.jpa.BaseRepository;
|
import com.eactive.eai.data.jpa.BaseRepository;
|
||||||
|
import com.eactive.eai.rms.data.entity.onl.djb.inflow.InflowTokenInsufficient;
|
||||||
|
import com.eactive.eai.rms.data.entity.onl.djb.inflow.InflowTokenInsufficientLog;
|
||||||
|
import com.eactive.eai.rms.data.entity.onl.djb.inflow.InflowTokenInsufficientLogId;
|
||||||
|
|
||||||
public interface InflowTokenRepository extends BaseRepository<InflowTokenInsufficientLog, InflowTokenInsufficientLogId> {
|
public interface InflowTokenRepository extends BaseRepository<InflowTokenInsufficientLog, InflowTokenInsufficientLogId> {
|
||||||
|
|
||||||
+8
-6
@@ -1,15 +1,17 @@
|
|||||||
package com.eactive.eai.rms.data.ext.djb.inflow;
|
package com.eactive.eai.rms.ext.djb.inflow;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
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.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.context.ApplicationEventPublisher;
|
|
||||||
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.eai.rms.ext.djb.event.InflowTokenInsufficientEvent;
|
import com.eactive.apim.portal.template.entity.MessageCode;
|
||||||
|
import com.eactive.eai.rms.data.entity.man.role.Role;
|
||||||
|
import com.eactive.eai.rms.data.entity.onl.djb.inflow.InflowTokenInsufficient;
|
||||||
|
import com.eactive.eai.rms.ext.djb.ums.UmsManager;
|
||||||
|
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
@@ -22,16 +24,16 @@ public class InflowTokenService {
|
|||||||
private InflowTokenRepository inflowTokenRepository;
|
private InflowTokenRepository inflowTokenRepository;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private ApplicationEventPublisher eventPublisher;
|
private UmsManager ums;
|
||||||
|
|
||||||
public void checkRecentFails(long rangeMinute) {
|
public void checkRecentFails(long rangeMinute) {
|
||||||
List<InflowTokenInsufficient> rows = inflowTokenRepository.countTokenInsufficient(rangeMinute);
|
List<InflowTokenInsufficient> rows = inflowTokenRepository.countTokenInsufficient(rangeMinute);
|
||||||
|
|
||||||
for (InflowTokenInsufficient info : rows) {
|
for (InflowTokenInsufficient info : rows) {
|
||||||
log.debug("유량제어 토큰 획득 실패: {}-{} 최근 {}분 동안 {}건", info.getEaisvcname(), info.getEaisvcdesc(), rangeMinute, info.getCnt());
|
log.debug("유량제어 토큰 획득 실패: {}-{} 최근 {}분 동안 {}건", info.getEaisvcname(), info.getEaisvcdesc(), rangeMinute, info.getCnt());
|
||||||
|
String message = "유량제어 토큰 획득 실패하였습니다 \n" + info.getEaisvcname();
|
||||||
|
|
||||||
//내부직원 알림 발송
|
ums.sendMessenger("api-monitor", MessageCode.INFLOW_TOKEN_FAILED, message);
|
||||||
eventPublisher.publishEvent(InflowTokenInsufficientEvent.from(info, rangeMinute));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -80,7 +80,6 @@ public class ApiStatsHourlyAggregationJob implements Job {
|
|||||||
log.info("=== 시간별 통계 집계 작업 시작 === 대상: {}", targetDate);
|
log.info("=== 시간별 통계 집계 작업 시작 === 대상: {}", targetDate);
|
||||||
|
|
||||||
String searchDate = targetDate.format(DateTimeFormatter.ofPattern("yyyyMMdd"));
|
String searchDate = targetDate.format(DateTimeFormatter.ofPattern("yyyyMMdd"));
|
||||||
String schemaEMS = System.getProperty("eai.tableowner", "EMSADM").toUpperCase();
|
|
||||||
String logTableName = CommonUtil.getLogTable(searchDate, true);
|
String logTableName = CommonUtil.getLogTable(searchDate, true);
|
||||||
|
|
||||||
// 1. 기존 데이터 삭제
|
// 1. 기존 데이터 삭제
|
||||||
@@ -129,7 +128,7 @@ public class ApiStatsHourlyAggregationJob implements Job {
|
|||||||
" FROM " + logTableName +
|
" FROM " + logTableName +
|
||||||
" WHERE MSGDPSTYMS LIKE :searchDate || '%'" +
|
" WHERE MSGDPSTYMS LIKE :searchDate || '%'" +
|
||||||
" GROUP BY EAISVCSERNO" +
|
" GROUP BY EAISVCSERNO" +
|
||||||
" ) A LEFT OUTER JOIN " + schemaEMS + ".TSEAIRM28 B ON A.ERROR_CODE = B.CODE AND B.CODEGROUP = 'ERRCODE_TIMEOUT'" +
|
" ) 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";
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import com.eactive.eai.rms.common.context.MonitoringContext;
|
|||||||
import com.eactive.eai.rms.common.datasource.DataSourceContextHolder;
|
import com.eactive.eai.rms.common.datasource.DataSourceContextHolder;
|
||||||
import com.eactive.eai.rms.common.datasource.DataSourceTypeManager;
|
import com.eactive.eai.rms.common.datasource.DataSourceTypeManager;
|
||||||
import com.eactive.eai.rms.common.util.CommonUtil;
|
import com.eactive.eai.rms.common.util.CommonUtil;
|
||||||
import com.eactive.eai.rms.data.ext.djb.apistatus.ApiStatusService;
|
import com.eactive.eai.rms.ext.djb.apistatus.ApiStatusService;
|
||||||
import com.eactive.eai.rms.onl.common.util.DateUtil;
|
import com.eactive.eai.rms.onl.common.util.DateUtil;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -88,11 +88,11 @@ public class ApiStatusMonitorJob implements Job {
|
|||||||
monitoringContext = (MonitoringContext) appContext.getBean("monitoringContext");
|
monitoringContext = (MonitoringContext) appContext.getBean("monitoringContext");
|
||||||
ApiStatusService apiStatusUpdateService = appContext.getBean(ApiStatusService.class);
|
ApiStatusService apiStatusUpdateService = appContext.getBean(ApiStatusService.class);
|
||||||
|
|
||||||
|
|
||||||
DataSourceContextHolder.setDataSourceType(
|
DataSourceContextHolder.setDataSourceType(
|
||||||
DataSourceTypeManager.getDataSourceType(DataSourceTypeManager.APIGW));
|
DataSourceTypeManager.getDataSourceType(DataSourceTypeManager.APIGW));
|
||||||
try {
|
try {
|
||||||
apiStatusUpdateService.updateApiStatus(param);
|
apiStatusUpdateService.updateApiStatus(param);
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("ApiStatusUpdateJob execution failed", e);
|
log.error("ApiStatusUpdateJob execution failed", e);
|
||||||
throw new JobExecutionException(e);
|
throw new JobExecutionException(e);
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import com.eactive.eai.rms.common.context.MonitoringContext;
|
|||||||
import com.eactive.eai.rms.common.datasource.DataSourceContextHolder;
|
import com.eactive.eai.rms.common.datasource.DataSourceContextHolder;
|
||||||
import com.eactive.eai.rms.common.datasource.DataSourceTypeManager;
|
import com.eactive.eai.rms.common.datasource.DataSourceTypeManager;
|
||||||
import com.eactive.eai.rms.common.util.CommonUtil;
|
import com.eactive.eai.rms.common.util.CommonUtil;
|
||||||
import com.eactive.eai.rms.data.ext.djb.inflow.InflowTokenService;
|
import com.eactive.eai.rms.ext.djb.inflow.InflowTokenService;
|
||||||
import com.eactive.eai.rms.onl.common.util.DateUtil;
|
import com.eactive.eai.rms.onl.common.util.DateUtil;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -0,0 +1,73 @@
|
|||||||
|
package com.eactive.eai.rms.ext.djb.job;
|
||||||
|
|
||||||
|
import com.eactive.eai.rms.common.datasource.DataSourceContextHolder;
|
||||||
|
import com.eactive.eai.rms.common.datasource.DataSourceTypeManager;
|
||||||
|
import com.eactive.eai.rms.common.util.CommonUtil;
|
||||||
|
import com.eactive.eai.rms.onl.apim.portalInquiry.PortalInquiryClosingService;
|
||||||
|
import com.eactive.eai.rms.onl.common.util.DateUtil;
|
||||||
|
import org.quartz.DisallowConcurrentExecution;
|
||||||
|
import org.quartz.Job;
|
||||||
|
import org.quartz.JobExecutionContext;
|
||||||
|
import org.quartz.JobExecutionException;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 매일 00:05 실행 - RESPONDED 상태 문의글 자동 종료 배치
|
||||||
|
*
|
||||||
|
* <p>종료 조건 (기본 7일):</p>
|
||||||
|
* <ul>
|
||||||
|
* <li>답변완료 후 댓글이 7일간 없는 경우</li>
|
||||||
|
* <li>관리자 댓글 등록 후 7일간 포탈사용자 댓글이 없는 경우</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* <p>Cron: 0 5 0 * * ?</p>
|
||||||
|
*
|
||||||
|
* <p>Job 파라미터 (JobDataMap):</p>
|
||||||
|
* <ul>
|
||||||
|
* <li>{@value #KEY_INQUIRY_COMMENT_CLOSING_DAY}: 종료 기준 일수 (기본값: 7)</li>
|
||||||
|
* </ul>
|
||||||
|
*/
|
||||||
|
@DisallowConcurrentExecution
|
||||||
|
public class PortalInquiryClosingJob implements Job {
|
||||||
|
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(PortalInquiryClosingJob.class);
|
||||||
|
|
||||||
|
/** Job 파라미터 키: 댓글종료 기간(일) */
|
||||||
|
public static final String KEY_INQUIRY_COMMENT_CLOSING_DAY = "inquiry.comment.closing_day";
|
||||||
|
|
||||||
|
private static final int DEFAULT_CLOSING_DAYS = 7;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private PortalInquiryClosingService inquiryCommentClosingService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute(JobExecutionContext context) throws JobExecutionException {
|
||||||
|
log.info("*** START PortalInquiryCommentClosingService run({})", CommonUtil.getToday("yyyy-MM-dd HH:mm"));
|
||||||
|
|
||||||
|
int closingDays = DEFAULT_CLOSING_DAYS;
|
||||||
|
String closingDayParam = context.getMergedJobDataMap().getString(KEY_INQUIRY_COMMENT_CLOSING_DAY);
|
||||||
|
if (closingDayParam != null) {
|
||||||
|
try {
|
||||||
|
closingDays = Integer.parseInt(closingDayParam);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
log.warn("잘못된 {} 파라미터 값: '{}', 기본값 {}일 사용", KEY_INQUIRY_COMMENT_CLOSING_DAY, closingDayParam, DEFAULT_CLOSING_DAYS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DataSourceContextHolder.setDataSourceType(
|
||||||
|
DataSourceTypeManager.getDataSourceType(DataSourceTypeManager.APIGW));
|
||||||
|
try {
|
||||||
|
int closedCount = inquiryCommentClosingService.closeExpiredInquiries(closingDays);
|
||||||
|
log.info("PortalInquiryCommentClosingService 완료: {}건 CLOSED 처리", closedCount);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("PortalInquiryCommentClosingService execution failed", e);
|
||||||
|
throw new JobExecutionException(e);
|
||||||
|
} finally {
|
||||||
|
DataSourceContextHolder.clearDataSourceType();
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("*** END PortalInquiryCommentClosingService run({})", DateUtil.getDateTime("yyyy-MM-dd HH:mm"));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,8 +15,6 @@ import org.springframework.web.bind.annotation.GetMapping;
|
|||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
|
||||||
import com.eactive.eai.rms.common.vo.GridResponse;
|
import com.eactive.eai.rms.common.vo.GridResponse;
|
||||||
import com.eactive.eai.rms.data.ext.djb.statistics.ApiUseStatsExcelExportService;
|
|
||||||
import com.eactive.eai.rms.data.ext.djb.statistics.ApiUseStatsService;
|
|
||||||
import com.eactive.ext.kjb.statistics.ui.ApiStatsSearch;
|
import com.eactive.ext.kjb.statistics.ui.ApiStatsSearch;
|
||||||
import com.eactive.ext.kjb.statistics.ui.ApiStatsUI;
|
import com.eactive.ext.kjb.statistics.ui.ApiStatsUI;
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package com.eactive.eai.rms.data.ext.djb.statistics;
|
package com.eactive.eai.rms.ext.djb.statistics;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package com.eactive.eai.rms.data.ext.djb.statistics;
|
package com.eactive.eai.rms.ext.djb.statistics;
|
||||||
|
|
||||||
import com.eactive.eai.data.jpa.BaseRepository;
|
import com.eactive.eai.data.jpa.BaseRepository;
|
||||||
import com.eactive.eai.rms.data.entity.onl.kjb.statistics.ApiStatsDay;
|
import com.eactive.eai.rms.data.entity.onl.kjb.statistics.ApiStatsDay;
|
||||||
+4
-6
@@ -1,4 +1,4 @@
|
|||||||
package com.eactive.eai.rms.data.ext.djb.statistics;
|
package com.eactive.eai.rms.ext.djb.statistics;
|
||||||
|
|
||||||
import java.math.RoundingMode;
|
import java.math.RoundingMode;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
@@ -62,8 +62,6 @@ public class ApiUseStatsService
|
|||||||
|
|
||||||
private Query getDataQuery(ApiStatsSearch search) {
|
private Query getDataQuery(ApiStatsSearch search) {
|
||||||
|
|
||||||
String schemaEms = System.getProperty("eai.tableowner", "EMSADM").toUpperCase();
|
|
||||||
|
|
||||||
StringBuilder where = buildNativeWhere(search);
|
StringBuilder where = buildNativeWhere(search);
|
||||||
|
|
||||||
String dataSql = "";
|
String dataSql = "";
|
||||||
@@ -80,7 +78,7 @@ public class ApiUseStatsService
|
|||||||
+ ", MIN(A.MIN_RESP_TIME)"
|
+ ", MIN(A.MIN_RESP_TIME)"
|
||||||
+ ", MAX(A.MAX_RESP_TIME)"
|
+ ", MAX(A.MAX_RESP_TIME)"
|
||||||
+ " FROM API_STATS_DAY A"
|
+ " FROM API_STATS_DAY A"
|
||||||
+ " LEFT OUTER JOIN " + schemaEms + ".PTL_CREDENTIAL 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 "
|
+ " LEFT OUTER JOIN TSEAIHE01 C ON A.API_NAME = C.EAISVCNAME "
|
||||||
+ where
|
+ where
|
||||||
+ " GROUP BY B.ORGNAME"
|
+ " GROUP BY B.ORGNAME"
|
||||||
@@ -98,7 +96,7 @@ public class ApiUseStatsService
|
|||||||
+ ", MIN(A.MIN_RESP_TIME)"
|
+ ", MIN(A.MIN_RESP_TIME)"
|
||||||
+ ", MAX(A.MAX_RESP_TIME)"
|
+ ", MAX(A.MAX_RESP_TIME)"
|
||||||
+ " FROM API_STATS_DAY A "
|
+ " FROM API_STATS_DAY A "
|
||||||
+ " LEFT OUTER JOIN " + schemaEms + ".PTL_CREDENTIAL 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 "
|
+ " LEFT OUTER JOIN TSEAIHE01 C ON A.API_NAME = C.EAISVCNAME "
|
||||||
+ where
|
+ where
|
||||||
+ " GROUP BY C.EAISVCDESC"
|
+ " GROUP BY C.EAISVCDESC"
|
||||||
@@ -116,7 +114,7 @@ public class ApiUseStatsService
|
|||||||
+ ", MIN(A.MIN_RESP_TIME)"
|
+ ", MIN(A.MIN_RESP_TIME)"
|
||||||
+ ", MAX(A.MAX_RESP_TIME)"
|
+ ", MAX(A.MAX_RESP_TIME)"
|
||||||
+ " FROM API_STATS_DAY A "
|
+ " FROM API_STATS_DAY A "
|
||||||
+ " LEFT OUTER JOIN " + schemaEms + ".PTL_CREDENTIAL 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 "
|
+ " 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')"
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
package com.eactive.eai.rms.ext.djb.swing.service;
|
|
||||||
|
|
||||||
import org.springframework.stereotype.Service;
|
|
||||||
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
|
|
||||||
@Slf4j
|
|
||||||
@Service
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
public class SwingSendManager {
|
|
||||||
|
|
||||||
public void send(String eaisvcname, long cnt, long rangeMinute) {
|
|
||||||
log.info("[Swing] 발송 준비: {} 최근 {}분 {}건", eaisvcname, rangeMinute, cnt);
|
|
||||||
// TODO: 알림 발송 로직
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
package com.eactive.eai.rms.ext.djb.ums;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.scheduling.annotation.Async;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import com.eactive.apim.portal.template.entity.MessageCode;
|
||||||
|
import com.eactive.apim.portal.template.service.MessageHandlerService;
|
||||||
|
import com.eactive.apim.portal.template.service.MessageRecipient;
|
||||||
|
import com.eactive.apim.portal.user.entity.UserInfo;
|
||||||
|
import com.eactive.eai.rms.data.entity.man.user.UserInfoService;
|
||||||
|
import com.eactive.eai.rms.ext.djb.webhook.dto.WebhookSendRequest;
|
||||||
|
import com.eactive.eai.rms.ext.djb.webhook.service.WebhookService;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class UmsManager {
|
||||||
|
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(UmsManager.class);
|
||||||
|
|
||||||
|
private final MessageHandlerService messageHandlerService;
|
||||||
|
private final UserInfoService userInfoService;
|
||||||
|
private final WebhookService webhookService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* role 역할을 가진 내부직원에게 메신저(Swing chat) 발송
|
||||||
|
* @param role
|
||||||
|
* @param messageCode
|
||||||
|
* @param message
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public void sendMessenger(String roleId, MessageCode messageCode, String message) {
|
||||||
|
|
||||||
|
List<UserInfo> users = userInfoService.findAllByRoleId(roleId);
|
||||||
|
if (users.isEmpty()) {
|
||||||
|
log.info("sendMessenger: no users found for role '{}'", roleId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (UserInfo user : users) {
|
||||||
|
MessageRecipient recipient = MessageRecipient.builder()
|
||||||
|
.userId(user.getUserid())
|
||||||
|
.username(user.getUsername())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
HashMap<String, Object> params = new HashMap<>();
|
||||||
|
params.put("message", message);
|
||||||
|
|
||||||
|
messageHandlerService.publishEvent(messageCode, recipient, params);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 내부직원에게 메신저 발송
|
||||||
|
* @param user
|
||||||
|
* @param messageCode
|
||||||
|
* @param message
|
||||||
|
*/
|
||||||
|
public void sendMessenger(UserInfo user, MessageCode messageCode, String message) {
|
||||||
|
MessageRecipient recipient = MessageRecipient.builder()
|
||||||
|
.userId(user.getUserid())
|
||||||
|
.username(user.getUsername())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
HashMap<String, Object> params = new HashMap<>();
|
||||||
|
params.put("message", message);
|
||||||
|
|
||||||
|
messageHandlerService.publishEvent(messageCode, recipient, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* event를 구독중인 제휴사에게 웹훅 발송 (비동기)
|
||||||
|
* @param event
|
||||||
|
* @param apiIds
|
||||||
|
*/
|
||||||
|
@Async("webhookExecutor")
|
||||||
|
public void sendWebhook(String event, List<String> apiIds) {
|
||||||
|
List<WebhookSendRequest> list = webhookService.findSendList(event, apiIds);
|
||||||
|
for (WebhookSendRequest req : list) {
|
||||||
|
webhookService.send(req);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
package com.eactive.eai.rms.ext.djb.ums.event;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import com.eactive.apim.portal.template.entity.MessageCode;
|
||||||
|
import com.eactive.apim.portal.template.service.MessageEventHandler;
|
||||||
|
import com.eactive.apim.portal.template.service.MessageRecipient;
|
||||||
|
import com.eactive.apim.portal.template.service.MessageSendEvent;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class ApiStatusChangedEventHandler implements MessageEventHandler {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MessageCode getKey() {
|
||||||
|
return MessageCode.API_STATUS_CHANGED;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean allowAdditionalRecipients() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDisplayName() {
|
||||||
|
return "API 상태 변화";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDescription() {
|
||||||
|
return "<div>사용 가능 변수: </div><br/>"
|
||||||
|
+ "<div> - message 알림 메시지 </div><br/>";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MessageSendEvent createEvent(Object source, MessageRecipient recipient, Map<String, Object> params) {
|
||||||
|
Map<String, String> eventParams = new HashMap<>();
|
||||||
|
eventParams.put("message", (String) params.get("message"));
|
||||||
|
return new MessageSendEvent(source, MessageCode.API_STATUS_CHANGED, recipient, eventParams);
|
||||||
|
}
|
||||||
|
}
|
||||||
+43
@@ -0,0 +1,43 @@
|
|||||||
|
package com.eactive.eai.rms.ext.djb.ums.event;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import com.eactive.apim.portal.template.entity.MessageCode;
|
||||||
|
import com.eactive.apim.portal.template.service.MessageEventHandler;
|
||||||
|
import com.eactive.apim.portal.template.service.MessageRecipient;
|
||||||
|
import com.eactive.apim.portal.template.service.MessageSendEvent;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class InflowTokenFailedEventHandler implements MessageEventHandler {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MessageCode getKey() {
|
||||||
|
return MessageCode.INFLOW_TOKEN_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean allowAdditionalRecipients() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDisplayName() {
|
||||||
|
return "유량제어 토큰 획득 실패";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDescription() {
|
||||||
|
return "<div>사용 가능 변수: </div><br/>"
|
||||||
|
+ "<div> - message 알림 메시지 </div><br/>";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MessageSendEvent createEvent(Object source, MessageRecipient recipient, Map<String, Object> params) {
|
||||||
|
Map<String, String> eventParams = new HashMap<>();
|
||||||
|
eventParams.put("message", (String) params.get("message"));
|
||||||
|
return new MessageSendEvent(source, MessageCode.INFLOW_TOKEN_FAILED, recipient, eventParams);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,344 @@
|
|||||||
|
package com.eactive.eai.rms.ext.djb.ums.service;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.net.HttpURLConnection;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.security.SecureRandom;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import com.eactive.apim.portal.portalproperty.service.PortalPropertyService;
|
||||||
|
import com.eactive.apim.portal.template.entity.MessageRequest;
|
||||||
|
import com.eactive.eai.common.util.SystemUtil;
|
||||||
|
import com.eactive.eai.rms.data.entity.man.monitoringProperty.service.MonitoringPropertyService;
|
||||||
|
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.StdHeadVO;
|
||||||
|
import com.eactive.eai.rms.ext.djb.ums.vo.SwingCommonVO;
|
||||||
|
import com.eactive.eai.rms.ext.djb.ums.vo.SwingDataVO;
|
||||||
|
import com.eactive.eai.rms.ext.djb.ums.vo.SwingReceiverDataVO;
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Service
|
||||||
|
public class UmsService {
|
||||||
|
|
||||||
|
private static final Gson gson = new Gson();
|
||||||
|
|
||||||
|
private static final String SYS_DVCD = "OPA";
|
||||||
|
private final MonitoringPropertyService propertyService;
|
||||||
|
|
||||||
|
public UmsService(MonitoringPropertyService propertyService) {
|
||||||
|
this.propertyService = propertyService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean send(MessageRequest messageRequest) throws Exception {
|
||||||
|
if (messageRequest == null) {
|
||||||
|
throw new IllegalArgumentException("messageRequest is null");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.httpConnection(messageRequest);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void httpConnection(MessageRequest messageRequest) throws Exception {
|
||||||
|
HttpURLConnection conn = null;
|
||||||
|
BufferedReader br = null;
|
||||||
|
log.debug("<<<HTTP Connection>>>:" + messageRequest.toString());
|
||||||
|
try {
|
||||||
|
String host = propertyService.getPropertyValue("Monitoring", "djb.ums."+messageRequest.getMessageType().toLowerCase()+".url", "");
|
||||||
|
|
||||||
|
URL url = new URL(host);
|
||||||
|
int timeoutValue = 5 * 1000; // 타임아웃 설정을 위한 값 (단위: ms)
|
||||||
|
// Charset charset = Charset.forName("UTF-8");
|
||||||
|
Charset charset = Charset.forName("MS949");
|
||||||
|
|
||||||
|
StringBuffer sb = null;
|
||||||
|
String bodyData = this.makeBodyData(messageRequest);
|
||||||
|
|
||||||
|
String responseData = "";
|
||||||
|
String method = bodyData.isEmpty() ? "GET" : "POST";
|
||||||
|
conn = (HttpURLConnection) url.openConnection();
|
||||||
|
conn.setRequestMethod(method); // 전송방식
|
||||||
|
// conn.setRequestProperty("Content-Type", "application/json; charset=UTF-8");
|
||||||
|
conn.setRequestProperty("Content-Type", "application/json; charset=MS949");
|
||||||
|
conn.setConnectTimeout(timeoutValue); // 연결 타임아웃 설정(5초)
|
||||||
|
conn.setReadTimeout(timeoutValue); // 읽기 타임아웃 설정(5초)
|
||||||
|
conn.setDoOutput(true);
|
||||||
|
|
||||||
|
// POst 방식인 경우에만
|
||||||
|
if (method.equals("POST")) {
|
||||||
|
OutputStream os = conn.getOutputStream();
|
||||||
|
byte requestData[] = bodyData.getBytes(charset);
|
||||||
|
os.write(requestData);
|
||||||
|
os.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (log.isDebugEnabled()) {
|
||||||
|
log.debug("getContentType();" + conn.getContentType()); // 응답 콘텐츠 유형 구하기
|
||||||
|
log.debug("getResponsecode();" + conn.getResponseCode()); // 응답 코드 구하기
|
||||||
|
log.debug("getResponseMessage():" + conn.getResponseMessage()); // 응답 메세지 구하기
|
||||||
|
}
|
||||||
|
|
||||||
|
// http 요청 후 응답 받은 데이타를 버퍼에 쌓는다
|
||||||
|
if (conn.getResponseCode() >= 200 && conn.getResponseCode() <= 300) {
|
||||||
|
br = new BufferedReader(new InputStreamReader(conn.getInputStream(), charset));
|
||||||
|
} else {
|
||||||
|
br = new BufferedReader(new InputStreamReader(conn.getErrorStream(), charset));
|
||||||
|
}
|
||||||
|
|
||||||
|
String inputLine;
|
||||||
|
sb = new StringBuffer();
|
||||||
|
while ((inputLine = br.readLine()) != null) {
|
||||||
|
sb.append(inputLine);
|
||||||
|
}
|
||||||
|
|
||||||
|
responseData = sb.toString();
|
||||||
|
} finally {
|
||||||
|
// http 요청 및 응답 완료 후 BufferedReader를 닫는다
|
||||||
|
if (br != null) {
|
||||||
|
br.close();
|
||||||
|
}
|
||||||
|
if (conn != null) {
|
||||||
|
conn.disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private String makeBodyData(MessageRequest messageRequest) {
|
||||||
|
// 리턴값
|
||||||
|
String rtnVal = "";
|
||||||
|
|
||||||
|
// guID 및 날짜 시간 정보
|
||||||
|
String jsonData[] = this.makeStdGuidInfo();
|
||||||
|
|
||||||
|
// 표준 헤더
|
||||||
|
StdHeadVO stdHeadVO = new StdHeadVO();
|
||||||
|
stdHeadVO.setNxgn_stnd_idfr("JERA");
|
||||||
|
stdHeadVO.setGuid(jsonData[2]);
|
||||||
|
stdHeadVO.setGuid_prgs_no("1");
|
||||||
|
stdHeadVO.setStnd_mesg_ver("R10");
|
||||||
|
stdHeadVO.setOrtr_guid(jsonData[2]);
|
||||||
|
stdHeadVO.setSect_ecrpt_yn("N");
|
||||||
|
stdHeadVO.setFrst_trnm_ipad(propertyService.getPropertyValue("Monitoring", "djb.ums.was_ip_address", ""));
|
||||||
|
stdHeadVO.setFrst_trnm_ipv4_addr(propertyService.getPropertyValue("Monitoring", "djb.ums.was_ip_address", ""));
|
||||||
|
stdHeadVO.setFrst_trnm_mac(propertyService.getPropertyValue("Monitoring", "djb.ums.was_mac_address", ""));
|
||||||
|
stdHeadVO.setFrst_mesg_dman_dt(jsonData[0]);
|
||||||
|
stdHeadVO.setFrst_mesg_dman_time(jsonData[1]);
|
||||||
|
stdHeadVO.setFrst_trnm_sys_dvcd(SYS_DVCD);
|
||||||
|
stdHeadVO.setTrnm_sys_dvcd(SYS_DVCD);
|
||||||
|
stdHeadVO.setDman_rspn_dvcd("S"); // 요청응답구분코드 S:요청 R:응답
|
||||||
|
stdHeadVO.setSys_env_dvcd(SystemUtil.getSysOperEvirnDstcd()); // D:개발 T:테스트 P:운영
|
||||||
|
stdHeadVO.setTx_tycd("O"); // O:온라인 B:배치
|
||||||
|
stdHeadVO.setSynz_dvcd("S"); // S:동기 A:비동기
|
||||||
|
stdHeadVO.setChnl_tycd(""); // 채널유형코드
|
||||||
|
stdHeadVO.setHmab_dvcd("1"); // 내외구분코드 1:내부 2:외부(타발)
|
||||||
|
stdHeadVO.setIf_id(messageRequest.getEaiInterfaceId()); // 인터페이스ID
|
||||||
|
stdHeadVO.setTx_id(messageRequest.getServiceId()); // 거래ID
|
||||||
|
|
||||||
|
if ("MESSENGER".equals(messageRequest.getMessageType()) ) {
|
||||||
|
|
||||||
|
// 스윙챗 알림 공용
|
||||||
|
SwingCommonVO swiComVO = new SwingCommonVO();
|
||||||
|
swiComVO.setClientld(propertyService.getPropertyValue("Monitoring", "djb.ums.messenger.client_id", ""));
|
||||||
|
swiComVO.setClientSecret(propertyService.getPropertyValue("Monitoring", "djb.ums.messenger.client_secret", ""));
|
||||||
|
swiComVO.setCompanyCode("CB");
|
||||||
|
swiComVO.setRequestUniqueKey(this.genrateRandomStringNumber26());
|
||||||
|
|
||||||
|
// 스윙챗 알림 데이터
|
||||||
|
SwingDataVO swiDataVO = new SwingDataVO();
|
||||||
|
swiDataVO.setNotiCode("CNO_FEP_HMP_0002");
|
||||||
|
swiDataVO.setNotiTitle("알림");
|
||||||
|
swiDataVO.setNotiContent(messageRequest.getMessage());
|
||||||
|
|
||||||
|
// 스윙챗 알림 데이터
|
||||||
|
List<SwingReceiverDataVO> swiReDataVOList = new ArrayList<SwingReceiverDataVO>();
|
||||||
|
SwingReceiverDataVO receiver = new SwingReceiverDataVO();
|
||||||
|
receiver.setCode(messageRequest.getMessengerId());
|
||||||
|
swiReDataVOList.add(receiver);
|
||||||
|
|
||||||
|
swiDataVO.setReceiverInfo(swiReDataVOList);
|
||||||
|
|
||||||
|
String header = gson.toJson(stdHeadVO);
|
||||||
|
String common = gson.toJson(swiComVO);
|
||||||
|
String data = gson.toJson(swiDataVO);
|
||||||
|
|
||||||
|
// 헤더 + 데이터
|
||||||
|
rtnVal = "{\"HEAD\":" + header + " ,\r\n" + "\"DATA\":\r\n" + " [" + "{\r\n"
|
||||||
|
+ " \"data_dvcd\":\"IO\", \r\n" + " \"BIZ_DATA\":\r\n" + " {\r\n"
|
||||||
|
+ " \"common\":\r\n" + common + ",\r\n" + " \"data\":" + data
|
||||||
|
+ " }\r\n" + " }\r\n" + " ]\r\n" + "}";
|
||||||
|
|
||||||
|
}
|
||||||
|
else if ("SMS".equals(messageRequest.getMessageType())
|
||||||
|
|| "KAKAO".equals(messageRequest.getMessageType())) {
|
||||||
|
|
||||||
|
// Sms 데이터
|
||||||
|
SmsDataVO dataVO = new SmsDataVO();
|
||||||
|
dataVO.setSnd_dman_dt(jsonData[0]);
|
||||||
|
dataVO.setSnd_dman_id("");
|
||||||
|
dataVO.setSnd_dvcd("EAI");
|
||||||
|
dataVO.setMsg_titl("");
|
||||||
|
dataVO.setMsg_ctnt(messageRequest.getMessage());
|
||||||
|
dataVO.setRecvr_no(messageRequest.getPhone());
|
||||||
|
dataVO.setSndn_no("15880079"); //발신번호
|
||||||
|
dataVO.setSnd_empno(""); //요청자
|
||||||
|
dataVO.setSnd_emp_dprm_cd(""); //요청부서코드
|
||||||
|
if ("SMS".equals(messageRequest.getMessageType())) {
|
||||||
|
dataVO.setLtrs_snd_tycd("1"); // 1:SMS 2:LMS/MMS
|
||||||
|
dataVO.setMsg_snd_tycd("0000"); // 알림톡 메세지 종류 0000:SMS, 1000:LMS, 1008:알림톡, ....
|
||||||
|
} else if ("KAKAO".equals(messageRequest.getMessageType())) {
|
||||||
|
dataVO.setLtrs_snd_tycd("2"); // 1:SMS 2:LMS/MMS
|
||||||
|
dataVO.setMsg_snd_tycd("1008"); // 알림톡 메세지 종류 0000:SMS, 1000:LMS, 1008:알림톡, ....
|
||||||
|
dataVO.setAlmtk_snd_prfl_key(""); // 플러스친구아이디
|
||||||
|
dataVO.setAlmtk_tmplt_cd(""); // 템플릿코드
|
||||||
|
dataVO.setAlmtk_err_ltrs_snd_yn("Y"); // 오류자문자발송여부
|
||||||
|
dataVO.setAlmtk_btn_trnm_tycd("2"); // 카카오버튼전송방식 1-format string 2-JSON 3-XML
|
||||||
|
}
|
||||||
|
dataVO.setSms_trnm_bzwk_dvcd(""); //업무구분코드
|
||||||
|
dataVO.setSms_bzwk_dcls_dvcd(""); //업무세분코드
|
||||||
|
dataVO.setSms_sys_dvcd("83"); //시스템코드
|
||||||
|
dataVO.setTx_dt(jsonData[0]); //거래일자
|
||||||
|
dataVO.setTx_tktm(jsonData[1].substring(0,6)); //거래시간
|
||||||
|
|
||||||
|
List<SmsDataVO> dataList = new ArrayList<SmsDataVO>();
|
||||||
|
dataList.add(dataVO);
|
||||||
|
|
||||||
|
String header = gson.toJson(stdHeadVO);
|
||||||
|
String data = gson.toJson(dataList);
|
||||||
|
|
||||||
|
// 헤더 + 데이터
|
||||||
|
rtnVal = "{\"HEAD\":" + header + " ,\r\n" + "\"DATA\":\r\n" + " [" + "{\r\n"
|
||||||
|
+ " \"data_dvcd\":\"IO\", \r\n" + " \"BIZ_DATA\":\r\n" + " {\r\n"
|
||||||
|
+ " \"ums_if_data_ncnt\":1, \r\n"
|
||||||
|
+ " \"ums_if_data\":" + data
|
||||||
|
+ " }\r\n" + " }\r\n" + " ]\r\n" + "}";
|
||||||
|
|
||||||
|
}
|
||||||
|
else if ("EMAIL".equals(messageRequest.getMessageType())) {
|
||||||
|
|
||||||
|
// 이메일 데이터
|
||||||
|
EmailDataVO dataVO = new EmailDataVO();
|
||||||
|
dataVO.setUms_evnt_id(""); // UMS이벤트ID(필수)
|
||||||
|
dataVO.setSnd_dman_dt(jsonData[0]);
|
||||||
|
dataVO.setSnd_dman_id("");
|
||||||
|
dataVO.setSnd_dman_sno("1");
|
||||||
|
dataVO.setEmail_titl(messageRequest.getSubject()); // 메일제목
|
||||||
|
dataVO.setCstno(""); // 고객번호(필수)
|
||||||
|
dataVO.setSendr_emaddr(""); // 발신자 이메일
|
||||||
|
dataVO.setSendr_nm(""); // 발신자 이름
|
||||||
|
dataVO.setSndbk_emaddr(""); // 리턴 이메일
|
||||||
|
dataVO.setRecvr_emaddr(messageRequest.getEmail()); // 수신자 이메일(필수)
|
||||||
|
dataVO.setRecvr_nm(messageRequest.getUsername()); // 수신자명(필수)
|
||||||
|
dataVO.setTmplt_mpng_info(messageRequest.getMessage()); // 탬플릿매핑정보
|
||||||
|
dataVO.setTmplt_mpng_rptt_info(""); // 탬플릿매핑반복정보
|
||||||
|
dataVO.setSnd_empno(""); // 발신자ID(필수)
|
||||||
|
dataVO.setSnd_emp_dprm_cd(""); // 발신자부서코드(필수)
|
||||||
|
dataVO.setBzwk_cd(""); // 업무구분코드
|
||||||
|
dataVO.setSub_bzwk_cd(""); // 업무세부코드
|
||||||
|
dataVO.setSys_dvcd(SYS_DVCD);
|
||||||
|
dataVO.setTx_dt(jsonData[0]); //거래일자
|
||||||
|
dataVO.setTx_tktm(jsonData[1].substring(0,6)); //거래시간
|
||||||
|
|
||||||
|
List<EmailDataVO> dataList = new ArrayList<EmailDataVO>();
|
||||||
|
dataList.add(dataVO);
|
||||||
|
|
||||||
|
String header = gson.toJson(stdHeadVO);
|
||||||
|
String data = gson.toJson(dataList);
|
||||||
|
|
||||||
|
// 헤더 + 데이터
|
||||||
|
rtnVal = "{\"HEAD\":" + header + " ,\r\n" + "\"DATA\":\r\n" + " [" + "{\r\n"
|
||||||
|
+ " \"data_dvcd\":\"IO\", \r\n" + " \"BIZ_DATA\":\r\n" + " {\r\n"
|
||||||
|
+ " \"ums_if_data_ncnt\":1, \r\n"
|
||||||
|
+ " \"ums_if_data\":" + data
|
||||||
|
+ " }\r\n" + " }\r\n" + " ]\r\n" + "}";
|
||||||
|
}
|
||||||
|
|
||||||
|
log.debug(rtnVal);
|
||||||
|
|
||||||
|
return rtnVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private String[] makeStdGuidInfo() {
|
||||||
|
// 리턴값
|
||||||
|
String rtnVal[] = new String[3];
|
||||||
|
|
||||||
|
// 현재시간
|
||||||
|
Date currentDate = new Date();
|
||||||
|
|
||||||
|
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS");
|
||||||
|
String formattedDate = sdf.format(currentDate);
|
||||||
|
|
||||||
|
rtnVal[0] = formattedDate.substring(0,8);
|
||||||
|
rtnVal[1] = formattedDate.substring(8);
|
||||||
|
|
||||||
|
// 랜던값
|
||||||
|
String random20 = this.genrateRandomStringNumber20();
|
||||||
|
|
||||||
|
rtnVal[2] = formattedDate + SYS_DVCD + random20;
|
||||||
|
|
||||||
|
return rtnVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private String genrateRandomStringNumber20() {
|
||||||
|
String rtnVal= "";
|
||||||
|
|
||||||
|
StringBuilder buffer= new StringBuilder(10);
|
||||||
|
|
||||||
|
// 랜덤값
|
||||||
|
String characters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
||||||
|
|
||||||
|
// 랜덤객체 생성
|
||||||
|
SecureRandom random = new SecureRandom();
|
||||||
|
|
||||||
|
// 반복문
|
||||||
|
for (int i= 0;i< 10;i++) {
|
||||||
|
// 랜덤수
|
||||||
|
int randomIndex = random.nextInt(characters.length());
|
||||||
|
|
||||||
|
// 생성된 랜덤값
|
||||||
|
char randomChar = characters.charAt(randomIndex);
|
||||||
|
|
||||||
|
// 랜덤값 저장
|
||||||
|
buffer.append(randomChar);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 랜덤객체 재생성
|
||||||
|
random = new SecureRandom();
|
||||||
|
|
||||||
|
// 난수 10개 생성
|
||||||
|
int random10 = (int) (Math.pow(10, 9) + random.nextDouble() * Math.pow(10, 9));
|
||||||
|
|
||||||
|
// 문자 숫자 조합(10) + 숫자(10)
|
||||||
|
rtnVal = buffer.toString() + Integer.toString(random10);
|
||||||
|
|
||||||
|
//리턴
|
||||||
|
return rtnVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String genrateRandomStringNumber26() {
|
||||||
|
StringBuilder buffer = new StringBuilder(26);
|
||||||
|
String characters = "abcdefghijklmnopqrstuvwxyzABCDEFGHJKLMNOPQRSTUVWXYZ0123456789";
|
||||||
|
SecureRandom random = new SecureRandom();
|
||||||
|
for (int i = 0; i < 26; i++) {
|
||||||
|
buffer.append(characters.charAt(random.nextInt(characters.length())));
|
||||||
|
}
|
||||||
|
return buffer.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
package com.eactive.eai.rms.ext.djb.ums.vo;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class EmailDataVO {
|
||||||
|
|
||||||
|
/** UMS이벤트ID */
|
||||||
|
private String ums_evnt_id;
|
||||||
|
|
||||||
|
/** 발송요청일자 */
|
||||||
|
private String snd_dman_dt;
|
||||||
|
|
||||||
|
/** UUID */
|
||||||
|
private String snd_dman_id;
|
||||||
|
|
||||||
|
/** 발송요청순번 */
|
||||||
|
private String snd_dman_sno;
|
||||||
|
|
||||||
|
/** 메일제목 */
|
||||||
|
private String email_titl;
|
||||||
|
|
||||||
|
/** 고객번호 */
|
||||||
|
private String cstno;
|
||||||
|
|
||||||
|
/** 발신자 이메일 */
|
||||||
|
private String sendr_emaddr;
|
||||||
|
|
||||||
|
/** 발신자 이름 */
|
||||||
|
private String sendr_nm;
|
||||||
|
|
||||||
|
/** 리턴 이메일 */
|
||||||
|
private String sndbk_emaddr;
|
||||||
|
|
||||||
|
/** 수신자 이메일 */
|
||||||
|
private String recvr_emaddr;
|
||||||
|
|
||||||
|
/** 수신자명 */
|
||||||
|
private String recvr_nm;
|
||||||
|
|
||||||
|
/** 탬플릿매핑정보 */
|
||||||
|
private String tmplt_mpng_info;
|
||||||
|
|
||||||
|
/** 탬플릿매핑반복정보 */
|
||||||
|
private String tmplt_mpng_rptt_info;
|
||||||
|
|
||||||
|
/** 발신자ID */
|
||||||
|
private String snd_empno;
|
||||||
|
|
||||||
|
/** 발신자부서코드 */
|
||||||
|
private String snd_emp_dprm_cd;
|
||||||
|
|
||||||
|
/** 업무구분코드 */
|
||||||
|
private String bzwk_cd;
|
||||||
|
|
||||||
|
/** 업무세부코드 */
|
||||||
|
private String sub_bzwk_cd;
|
||||||
|
|
||||||
|
/** 시스템코드 */
|
||||||
|
private String sys_dvcd;
|
||||||
|
|
||||||
|
/** 거래일자 */
|
||||||
|
private String tx_dt;
|
||||||
|
|
||||||
|
/** 거래시간 */
|
||||||
|
private String tx_tktm;
|
||||||
|
}
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
package com.eactive.eai.rms.ext.djb.ums.vo;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class SmsDataVO {
|
||||||
|
|
||||||
|
/** 발송요청일자 */
|
||||||
|
private String snd_dman_dt;
|
||||||
|
|
||||||
|
/** UUID */
|
||||||
|
private String snd_dman_id;
|
||||||
|
|
||||||
|
/** 발송구분 */
|
||||||
|
private String snd_dvcd;
|
||||||
|
|
||||||
|
/** 발송타입 */
|
||||||
|
private String ltrs_snd_tycd;
|
||||||
|
|
||||||
|
/** LMS 제목 */
|
||||||
|
private String msg_titl;
|
||||||
|
|
||||||
|
/** 메세지 */
|
||||||
|
private String msg_ctnt;
|
||||||
|
|
||||||
|
/** 수신자번호 */
|
||||||
|
private String recvr_no;
|
||||||
|
|
||||||
|
/** 발신번호 */
|
||||||
|
private String sndn_no;
|
||||||
|
|
||||||
|
/** 요청자(직원번호) */
|
||||||
|
private String snd_empno;
|
||||||
|
|
||||||
|
/** 요청부서코드 */
|
||||||
|
private String snd_emp_dprm_cd;
|
||||||
|
|
||||||
|
/** 알림톡 메시지 종류 */
|
||||||
|
private String msg_snd_tycd;
|
||||||
|
|
||||||
|
/** 플러스친구아이디 */
|
||||||
|
private String almtk_snd_prfl_key;
|
||||||
|
|
||||||
|
/** 템플릿코드 */
|
||||||
|
private String almtk_tmplt_cd;
|
||||||
|
|
||||||
|
/** 오류자문자발송여부 */
|
||||||
|
private String almtk_err_ltrs_snd_yn;
|
||||||
|
|
||||||
|
/** 카카오톡전송방식 */
|
||||||
|
private String almtk_btn_trnm_tycd;
|
||||||
|
|
||||||
|
/** 업무구분 코드 */
|
||||||
|
private String sms_trnm_bzwk_dvcd;
|
||||||
|
|
||||||
|
/** 업무세부코드 */
|
||||||
|
private String sms_bzwk_dcls_dvcd;
|
||||||
|
|
||||||
|
/** 시스템코드 */
|
||||||
|
private String sms_sys_dvcd;
|
||||||
|
|
||||||
|
/** 거래일자 */
|
||||||
|
private String tx_dt;
|
||||||
|
|
||||||
|
/** 거래시간 */
|
||||||
|
private String tx_tktm;
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
package com.eactive.eai.rms.ext.djb.ums.vo;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class StdHeadVO {
|
||||||
|
|
||||||
|
private String nxgn_stnd_idfr;
|
||||||
|
|
||||||
|
private String guid;
|
||||||
|
|
||||||
|
private String guid_prgs_no;
|
||||||
|
|
||||||
|
private String stnd_mesg_ver;
|
||||||
|
|
||||||
|
private String ortr_guid;
|
||||||
|
|
||||||
|
private String sect_ecrpt_yn;
|
||||||
|
|
||||||
|
private String frst_trnm_ipad;
|
||||||
|
|
||||||
|
private String frst_trnm_ipv4_addr;
|
||||||
|
|
||||||
|
private String frst_trnm_mac;
|
||||||
|
|
||||||
|
private String frst_mesg_dman_dt;
|
||||||
|
|
||||||
|
private String frst_mesg_dman_time;
|
||||||
|
|
||||||
|
private String frst_trnm_sys_dvcd;
|
||||||
|
|
||||||
|
private String trnm_sys_dvcd;
|
||||||
|
|
||||||
|
private String dman_rspn_dvcd;
|
||||||
|
|
||||||
|
private String sys_env_dvcd;
|
||||||
|
|
||||||
|
private String tx_tycd;
|
||||||
|
|
||||||
|
private String synz_dvcd;
|
||||||
|
|
||||||
|
private String tx_id;
|
||||||
|
|
||||||
|
private String osdch_dvcd;
|
||||||
|
|
||||||
|
private String chnl_tycd;
|
||||||
|
|
||||||
|
private String if_id;
|
||||||
|
|
||||||
|
private String hmab_dvcd;
|
||||||
|
|
||||||
|
private String tx_brcd;
|
||||||
|
|
||||||
|
private String tlrno;
|
||||||
|
|
||||||
|
private String insl_brcd;
|
||||||
|
|
||||||
|
private String blng_brcd;
|
||||||
|
|
||||||
|
private String mgmt_brcd;
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package com.eactive.eai.rms.ext.djb.ums.vo;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class SwingCommonVO {
|
||||||
|
|
||||||
|
/** 클라이업트 D */
|
||||||
|
private String clientld;
|
||||||
|
|
||||||
|
/** 클라이언트 시크릿 */
|
||||||
|
private String clientSecret;
|
||||||
|
|
||||||
|
/** 회사코드 */
|
||||||
|
private String companyCode;
|
||||||
|
|
||||||
|
/** 요청고유키 */
|
||||||
|
private String requestUniqueKey;
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package com.eactive.eai.rms.ext.djb.ums.vo;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class SwingDataVO {
|
||||||
|
|
||||||
|
/** 알림코드 */
|
||||||
|
private String notiCode;
|
||||||
|
|
||||||
|
/** 알림제목 */
|
||||||
|
private String notiTitle;
|
||||||
|
|
||||||
|
/** 알림내용 */
|
||||||
|
private String notiContent;
|
||||||
|
|
||||||
|
/** 수신자정보 */
|
||||||
|
private List<SwingReceiverDataVO> receiverInfo;
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package com.eactive.eai.rms.ext.djb.ums.vo;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class SwingReceiverDataVO {
|
||||||
|
|
||||||
|
/** 알림코드 */
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
/** 알림제목 */
|
||||||
|
private String type;
|
||||||
|
|
||||||
|
/** 알림내용 */
|
||||||
|
private String companyCode;
|
||||||
|
|
||||||
|
/** 수신자정보 */
|
||||||
|
private String code;
|
||||||
|
|
||||||
|
/** 상품명 */
|
||||||
|
private String prdNm;
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package com.eactive.eai.rms.ext.djb.webhook.config;
|
||||||
|
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.scheduling.annotation.EnableAsync;
|
||||||
|
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableAsync
|
||||||
|
public class WebhookAsyncConfig {
|
||||||
|
|
||||||
|
@Bean(name = "webhookExecutor")
|
||||||
|
public Executor webhookExecutor() {
|
||||||
|
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
|
||||||
|
executor.setCorePoolSize(2);
|
||||||
|
executor.setMaxPoolSize(10);
|
||||||
|
executor.setQueueCapacity(100);
|
||||||
|
executor.setThreadNamePrefix("webhook-");
|
||||||
|
executor.setWaitForTasksToCompleteOnShutdown(true);
|
||||||
|
executor.setAwaitTerminationSeconds(30);
|
||||||
|
executor.initialize();
|
||||||
|
return executor;
|
||||||
|
}
|
||||||
|
}
|
||||||
+5
-9
@@ -11,7 +11,7 @@ import org.springframework.web.bind.annotation.RequestHeader;
|
|||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
import com.eactive.eai.rms.data.ext.djb.webhook.WebhookSendLog;
|
import com.eactive.eai.rms.data.entity.onl.djb.webhook.WebhookSendLog;
|
||||||
import com.eactive.eai.rms.ext.djb.webhook.dto.WebhookSendRequest;
|
import com.eactive.eai.rms.ext.djb.webhook.dto.WebhookSendRequest;
|
||||||
import com.eactive.eai.rms.ext.djb.webhook.service.WebhookReceiveService;
|
import com.eactive.eai.rms.ext.djb.webhook.service.WebhookReceiveService;
|
||||||
import com.eactive.eai.rms.ext.djb.webhook.service.WebhookService;
|
import com.eactive.eai.rms.ext.djb.webhook.service.WebhookService;
|
||||||
@@ -38,16 +38,12 @@ public class WebhookSendController {
|
|||||||
public ResponseEntity<Map<String, Object>> sendWebhook(
|
public ResponseEntity<Map<String, Object>> sendWebhook(
|
||||||
@RequestBody WebhookSendRequest request) {
|
@RequestBody WebhookSendRequest request) {
|
||||||
|
|
||||||
WebhookSendLog result = webhookService.send(
|
webhookService.send(request);
|
||||||
request.getTargetUrl(),
|
|
||||||
request.getEventType(),
|
|
||||||
request.getData()
|
|
||||||
);
|
|
||||||
|
|
||||||
Map<String, Object> response = new HashMap<>();
|
Map<String, Object> response = new HashMap<>();
|
||||||
response.put("logId", result.getId());
|
// response.put("logId", result.getId());
|
||||||
response.put("success", result.getSuccess());
|
// response.put("success", result.getSuccess());
|
||||||
response.put("statusCode", result.getStatusCode());
|
// response.put("statusCode", result.getStatusCode());
|
||||||
|
|
||||||
return ResponseEntity.ok(response);
|
return ResponseEntity.ok(response);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,8 +6,9 @@ import lombok.Setter;
|
|||||||
@Getter
|
@Getter
|
||||||
@Setter
|
@Setter
|
||||||
public class WebhookSendRequest {
|
public class WebhookSendRequest {
|
||||||
|
private String orgId;
|
||||||
private String targetUrl;
|
private String targetUrl;
|
||||||
private String eventType;
|
private String eventType;
|
||||||
|
private String secret;
|
||||||
private Object data;
|
private Object data;
|
||||||
}
|
}
|
||||||
+11
@@ -0,0 +1,11 @@
|
|||||||
|
package com.eactive.eai.rms.ext.djb.webhook.repository;
|
||||||
|
|
||||||
|
import com.eactive.eai.rms.data.EMSDataSource;
|
||||||
|
import com.eactive.eai.rms.data.entity.onl.djb.webhook.WebhookReq;
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
@Repository
|
||||||
|
@EMSDataSource
|
||||||
|
public interface WebhookReqRepository extends JpaRepository<WebhookReq, String> {
|
||||||
|
}
|
||||||
+1
-1
@@ -10,7 +10,7 @@ import org.springframework.data.repository.query.Param;
|
|||||||
import org.springframework.stereotype.Repository;
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
import com.eactive.eai.rms.data.EMSDataSource;
|
import com.eactive.eai.rms.data.EMSDataSource;
|
||||||
import com.eactive.eai.rms.data.ext.djb.webhook.WebhookSendLog;
|
import com.eactive.eai.rms.data.entity.onl.djb.webhook.WebhookSendLog;
|
||||||
|
|
||||||
@Repository
|
@Repository
|
||||||
@EMSDataSource
|
@EMSDataSource
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
package com.eactive.eai.rms.ext.djb.webhook.service;
|
|
||||||
|
|
||||||
import org.springframework.stereotype.Service;
|
|
||||||
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
|
|
||||||
@Slf4j
|
|
||||||
@Service
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
public class WebhookSendManager {
|
|
||||||
|
|
||||||
public void send(String eaisvcname, String eventType) {
|
|
||||||
log.info("[Webhook] 발송 준비: {} {}", eaisvcname, eventType);
|
|
||||||
// TODO: PTL_WEBHOOK_REQ 조인 조회 후 WebhookService.send() 호출
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -2,12 +2,16 @@ package com.eactive.eai.rms.ext.djb.webhook.service;
|
|||||||
|
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import javax.crypto.Mac;
|
import javax.crypto.Mac;
|
||||||
import javax.crypto.spec.SecretKeySpec;
|
import javax.crypto.spec.SecretKeySpec;
|
||||||
|
import javax.persistence.EntityManager;
|
||||||
|
import javax.persistence.PersistenceContext;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
|
||||||
import org.springframework.http.HttpEntity;
|
import org.springframework.http.HttpEntity;
|
||||||
import org.springframework.http.HttpHeaders;
|
import org.springframework.http.HttpHeaders;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
@@ -18,10 +22,17 @@ 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.eai.rms.data.ext.djb.webhook.WebhookSendLog;
|
import com.eactive.eai.rms.common.context.MonitoringContext;
|
||||||
|
import com.eactive.eai.rms.data.entity.onl.djb.webhook.QWebhookReq;
|
||||||
|
import com.eactive.eai.rms.data.entity.onl.djb.webhook.QWebhookReqApi;
|
||||||
|
import com.eactive.eai.rms.data.entity.onl.djb.webhook.QWebhookReqEvent;
|
||||||
|
import com.eactive.eai.rms.data.entity.onl.djb.webhook.WebhookSendLog;
|
||||||
import com.eactive.eai.rms.ext.djb.webhook.dto.WebhookPayload;
|
import com.eactive.eai.rms.ext.djb.webhook.dto.WebhookPayload;
|
||||||
|
import com.eactive.eai.rms.ext.djb.webhook.dto.WebhookSendRequest;
|
||||||
import com.eactive.eai.rms.ext.djb.webhook.repository.WebhookSendLogRepository;
|
import com.eactive.eai.rms.ext.djb.webhook.repository.WebhookSendLogRepository;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.querydsl.core.Tuple;
|
||||||
|
import com.querydsl.jpa.impl.JPAQueryFactory;
|
||||||
|
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
@@ -34,19 +45,63 @@ public class WebhookService {
|
|||||||
private final WebhookSendLogRepository sendLogRepository;
|
private final WebhookSendLogRepository sendLogRepository;
|
||||||
private final ObjectMapper objectMapper;
|
private final ObjectMapper objectMapper;
|
||||||
private final RestTemplate restTemplate;
|
private final RestTemplate restTemplate;
|
||||||
|
private final MonitoringContext monitoringContext;
|
||||||
|
|
||||||
|
@PersistenceContext(unitName = "entityManagerFactoryForEMS")
|
||||||
private String secretKey = "HW8JtFpkPmQqsVmr0Rb81P4qDypaNIVbGf3ZNMMPfyerhOHshoKABLaMBo1HA4BldTxhw1NjYmVnoPu9IIV7cyRXjecL3b2UctyR7DnVaJausltZLLr8Qm4Bzs4wRmgf";
|
private EntityManager entityManager;
|
||||||
|
|
||||||
private static final String HMAC_ALGORITHM = "HmacSHA256";
|
private static final String HMAC_ALGORITHM = "HmacSHA256";
|
||||||
private static final int MAX_RETRY = 3;
|
private static final int DEFAULT_RETRY_COUNT = 3;
|
||||||
|
private static final int DEFAULT_RETRY_TIME = 10000;
|
||||||
|
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public List<WebhookSendRequest> findSendList(String event, List<String> apiIds) {
|
||||||
|
QWebhookReq req = QWebhookReq.webhookReq;
|
||||||
|
QWebhookReqEvent evt = QWebhookReqEvent.webhookReqEvent;
|
||||||
|
QWebhookReqApi api = QWebhookReqApi.webhookReqApi;
|
||||||
|
|
||||||
|
List<Tuple> tuples = new JPAQueryFactory(entityManager)
|
||||||
|
.select(req.orgId, req.targetUrl, req.secret, api.id.apiId)
|
||||||
|
.from(req)
|
||||||
|
.join(evt).on(req.id.eq(evt.id.webhookReqId))
|
||||||
|
.join(api).on(req.id.eq(api.id.webhookReqId))
|
||||||
|
.where(evt.id.eventType.eq(event)
|
||||||
|
.and(api.id.apiId.in(apiIds)))
|
||||||
|
.fetch();
|
||||||
|
|
||||||
|
// orgId + targetUrl + secret 기준으로 그룹핑, apiId를 data(List)로 집계
|
||||||
|
Map<String, WebhookSendRequest> resultMap = new LinkedHashMap<>();
|
||||||
|
for (Tuple t : tuples) {
|
||||||
|
String key = t.get(req.orgId) + "|" + t.get(req.targetUrl);
|
||||||
|
resultMap.computeIfAbsent(key, k -> {
|
||||||
|
WebhookSendRequest wr = new WebhookSendRequest();
|
||||||
|
wr.setOrgId(t.get(req.orgId));
|
||||||
|
wr.setTargetUrl(t.get(req.targetUrl));
|
||||||
|
wr.setSecret(t.get(req.secret));
|
||||||
|
wr.setEventType(event);
|
||||||
|
wr.setData(new ArrayList<String>());
|
||||||
|
return wr;
|
||||||
|
});
|
||||||
|
((List<String>) resultMap.get(key).getData()).add(t.get(api.id.apiId));
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ArrayList<>(resultMap.values());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 웹훅 발송 메인 메서드
|
* 웹훅 발송 메인 메서드
|
||||||
*/
|
*/
|
||||||
@Transactional
|
@Transactional
|
||||||
public WebhookSendLog send(String targetUrl, String eventType, Object data) {
|
public void send(WebhookSendRequest req) {
|
||||||
|
|
||||||
|
String targetUrl = req.getTargetUrl();
|
||||||
|
String secret = req.getSecret();
|
||||||
|
String eventType = req.getEventType();
|
||||||
|
Object data = req.getData();
|
||||||
|
|
||||||
WebhookSendLog sendLog = new WebhookSendLog();
|
WebhookSendLog sendLog = new WebhookSendLog();
|
||||||
|
sendLog.setOrgId(req.getOrgId());
|
||||||
sendLog.setTargetUrl(targetUrl);
|
sendLog.setTargetUrl(targetUrl);
|
||||||
sendLog.setEventType(eventType);
|
sendLog.setEventType(eventType);
|
||||||
|
|
||||||
@@ -57,7 +112,7 @@ public class WebhookService {
|
|||||||
sendLog.setPayload(payloadJson);
|
sendLog.setPayload(payloadJson);
|
||||||
|
|
||||||
// 2. Secret Key로 HMAC-SHA256 서명 생성
|
// 2. Secret Key로 HMAC-SHA256 서명 생성
|
||||||
String signature = generateSignature(payloadJson);
|
String signature = generateSignature(secret, payloadJson);
|
||||||
sendLog.setSignature(signature);
|
sendLog.setSignature(signature);
|
||||||
|
|
||||||
// 3. HTTP 요청 헤더 구성
|
// 3. HTTP 요청 헤더 구성
|
||||||
@@ -67,18 +122,20 @@ public class WebhookService {
|
|||||||
headers.set("X-Webhook-Event", eventType);
|
headers.set("X-Webhook-Event", eventType);
|
||||||
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.API_WEBHOOK_RETRY_COUNT, DEFAULT_RETRY_COUNT);
|
||||||
|
int retryTime = monitoringContext.getIntProperty(MonitoringContext.API_WEBHOOK_RETRY_TIME, DEFAULT_RETRY_TIME);
|
||||||
sendLog.setSentAt(LocalDateTime.now());
|
sendLog.setSentAt(LocalDateTime.now());
|
||||||
HttpEntity<String> request = new HttpEntity<>(payloadJson, headers);
|
HttpEntity<String> httpRequest = new HttpEntity<>(payloadJson, headers);
|
||||||
ResponseEntity<String> response = restTemplate.postForEntity(targetUrl, request, String.class);
|
ResponseEntity<String> response = sendWithRetry(targetUrl, httpRequest, sendLog, retryCount, retryTime);
|
||||||
|
|
||||||
// 5. 성공 로그
|
// 5. 성공 로그
|
||||||
sendLog.setStatusCode(response.getStatusCode().value());
|
sendLog.setStatusCode(response.getStatusCode().value());
|
||||||
sendLog.setResponseBody(response.getBody());
|
sendLog.setResponseBody(response.getBody());
|
||||||
sendLog.setSuccess(response.getStatusCode().is2xxSuccessful());
|
sendLog.setSuccess(response.getStatusCode().is2xxSuccessful());
|
||||||
|
|
||||||
log.info("[Webhook] 발송 성공 - eventType: {}, url: {}, status: {}",
|
log.info("[Webhook] 발송 성공 - eventType: {}, url: {}, status: {}, retryCount: {}",
|
||||||
eventType, targetUrl, response.getStatusCode());
|
eventType, targetUrl, response.getStatusCode(), sendLog.getRetryCount());
|
||||||
|
|
||||||
} catch (HttpClientErrorException | HttpServerErrorException e) {
|
} catch (HttpClientErrorException | HttpServerErrorException e) {
|
||||||
sendLog.setStatusCode(e.getStatusCode().value());
|
sendLog.setStatusCode(e.getStatusCode().value());
|
||||||
@@ -95,40 +152,46 @@ public class WebhookService {
|
|||||||
eventType, targetUrl, e.getMessage());
|
eventType, targetUrl, e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
return sendLogRepository.save(sendLog);
|
sendLogRepository.save(sendLog);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 실패 건 재시도
|
* 재시도 포함 HTTP 발송
|
||||||
|
* - 4xx: 클라이언트 오류이므로 즉시 throw (재시도 불필요)
|
||||||
|
* - 5xx / 네트워크 오류: 최대 RETRY_COUNT회까지 exponential backoff 재시도 (1s → 2s → 4s)
|
||||||
*/
|
*/
|
||||||
@Transactional
|
private ResponseEntity<String> sendWithRetry(String targetUrl, HttpEntity<String> request, WebhookSendLog sendLog, int retryCount, int retryTime) throws Exception {
|
||||||
public void retryFailedWebhooks() {
|
Exception lastException = null;
|
||||||
List<WebhookSendLog> failedLogs =
|
for (int attempt = 0; attempt <= retryCount; attempt++) {
|
||||||
sendLogRepository.findFailedLogs(MAX_RETRY);
|
|
||||||
|
|
||||||
for (WebhookSendLog failedLog : failedLogs) {
|
|
||||||
try {
|
try {
|
||||||
log.info("[Webhook] 재시도 - id: {}, retry: {}", failedLog.getId(), failedLog.getRetryCount());
|
if (attempt > 0) {
|
||||||
failedLog.setRetryCount(failedLog.getRetryCount() + 1);
|
long delayMs = retryTime * (1L << (attempt - 1));
|
||||||
sendLogRepository.save(failedLog);
|
log.info("[Webhook] 재시도 {}/{} - url: {}, delay: {}ms", attempt, retryCount, targetUrl, delayMs);
|
||||||
|
Thread.sleep(delayMs);
|
||||||
send(failedLog.getTargetUrl(), failedLog.getEventType(),
|
sendLog.setRetryCount(attempt);
|
||||||
objectMapper.readValue(failedLog.getPayload(), Object.class));
|
}
|
||||||
|
return restTemplate.postForEntity(targetUrl, request, String.class);
|
||||||
|
} catch (HttpClientErrorException e) {
|
||||||
|
throw e; // 4xx는 재시도 불필요
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("[Webhook] 재시도 실패 - id: {}", failedLog.getId(), e);
|
lastException = e;
|
||||||
|
log.warn("[Webhook] 발송 실패 (attempt {}/{}) - url: {}, error: {}", attempt, retryCount, targetUrl, e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
throw lastException;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* HMAC-SHA256 서명 생성
|
* HMAC-SHA256 서명 생성
|
||||||
* Java 17 미만은 HexFormat 미지원이므로 직접 변환
|
* Java 17 미만은 HexFormat 미지원이므로 직접 변환
|
||||||
*/
|
*/
|
||||||
private String generateSignature(String payload) throws Exception {
|
private String generateSignature(String secret, String payload) throws Exception {
|
||||||
Mac mac = Mac.getInstance(HMAC_ALGORITHM);
|
Mac mac = Mac.getInstance(HMAC_ALGORITHM);
|
||||||
SecretKeySpec keySpec = new SecretKeySpec(
|
SecretKeySpec keySpec = new SecretKeySpec(
|
||||||
secretKey.getBytes(StandardCharsets.UTF_8), HMAC_ALGORITHM);
|
secret.getBytes(StandardCharsets.UTF_8), HMAC_ALGORITHM);
|
||||||
mac.init(keySpec);
|
mac.init(keySpec);
|
||||||
byte[] hash = mac.doFinal(payload.getBytes(StandardCharsets.UTF_8));
|
byte[] hash = mac.doFinal(payload.getBytes(StandardCharsets.UTF_8));
|
||||||
return bytesToHex(hash);
|
return bytesToHex(hash);
|
||||||
|
|||||||
@@ -124,4 +124,10 @@ public class PortalApprovalManController extends BaseAnnotationController {
|
|||||||
}
|
}
|
||||||
return ResponseEntity.ok(resultMap);
|
return ResponseEntity.ok(resultMap);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PostMapping(value = "/onl/apim/approval/portalApprovalMan.json", params = "cmd=UPDATE")
|
||||||
|
public ResponseEntity<Void> update(String id, String expectEndDate) {
|
||||||
|
portalApprovalManService.update(id, expectEndDate);
|
||||||
|
return ResponseEntity.ok().build();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -87,6 +87,16 @@ public class PortalApprovalManService extends BaseService {
|
|||||||
* 마스킹된 상세 정보 조회
|
* 마스킹된 상세 정보 조회
|
||||||
*/
|
*/
|
||||||
public PortalApprovalUI selectDetail(String id) {
|
public PortalApprovalUI selectDetail(String id) {
|
||||||
|
//2026.06.05 관리자가 승인요청을 조회시 심사중으로 상태 변경
|
||||||
|
//APP 유형이고 REQUESTED 상태인 경우 관리자 조회 시 PROCESSING으로 변경
|
||||||
|
approvalService.findById(id).ifPresent(approval -> {
|
||||||
|
if (approval.getApprovalType().equals(ApprovalType.APP)
|
||||||
|
&& approval.getApprovalStatus() instanceof RequestedState) {
|
||||||
|
approval.setApprovalStatus(new ProcessingState());
|
||||||
|
approvalService.save(approval);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
PortalApprovalUI result = getDetailWithMaskOption(id, false);
|
PortalApprovalUI result = getDetailWithMaskOption(id, false);
|
||||||
if (result == null) {
|
if (result == null) {
|
||||||
throw new IllegalArgumentException("승인 정보를 찾을 수 없습니다. ID: " + id);
|
throw new IllegalArgumentException("승인 정보를 찾을 수 없습니다. ID: " + id);
|
||||||
@@ -331,6 +341,13 @@ public class PortalApprovalManService extends BaseService {
|
|||||||
sendEvent(id, ApprovalEvent.DENY, options);
|
sendEvent(id, ApprovalEvent.DENY, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void update(String id, String expectEndDate) {
|
||||||
|
Approval approval = approvalService.findById(id)
|
||||||
|
.orElseThrow(() -> new IllegalArgumentException("승인 정보를 찾을 수 없습니다. ID: " + id));
|
||||||
|
approval.setExpectEndDate(expectEndDate);
|
||||||
|
approvalService.save(approval);
|
||||||
|
}
|
||||||
|
|
||||||
public void redeploy(String id) {
|
public void redeploy(String id) {
|
||||||
Map options = new HashMap<>();
|
Map options = new HashMap<>();
|
||||||
Approval approval = approvalService.findById(id).orElseThrow(() -> new IllegalArgumentException("승인 정보를 찾을 수 없습니다. ID: " + id));
|
Approval approval = approvalService.findById(id).orElseThrow(() -> new IllegalArgumentException("승인 정보를 찾을 수 없습니다. ID: " + id));
|
||||||
|
|||||||
@@ -38,6 +38,8 @@ public class PortalApprovalUI {
|
|||||||
@DateTimeFormat(pattern = "yyyyMMddHHmmssSSS")
|
@DateTimeFormat(pattern = "yyyyMMddHHmmssSSS")
|
||||||
private String approvalDate;
|
private String approvalDate;
|
||||||
|
|
||||||
|
private String expectEndDate;
|
||||||
|
|
||||||
public String getApprovalTypeDescription() {
|
public String getApprovalTypeDescription() {
|
||||||
return approvalType != null ? approvalType.getDescription() : "";
|
return approvalType != null ? approvalType.getDescription() : "";
|
||||||
}
|
}
|
||||||
|
|||||||
+78
@@ -0,0 +1,78 @@
|
|||||||
|
package com.eactive.eai.rms.onl.apim.portalInquiry;
|
||||||
|
|
||||||
|
import com.eactive.apim.portal.qna.entity.Inquiry;
|
||||||
|
import com.eactive.apim.portal.qna.entity.InquiryComment;
|
||||||
|
import com.eactive.eai.rms.data.entity.onl.apim.portalinquiry.PortalInquiryCommentRepository;
|
||||||
|
import com.eactive.eai.rms.data.entity.onl.apim.portalinquiry.PortalInquiryCommentService;
|
||||||
|
import com.eactive.eai.rms.data.entity.onl.apim.portalinquiry.PortalInquiryService;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RESPONDED 상태 문의글 중 아래 조건에 해당하는 경우 CLOSED로 변경한다.
|
||||||
|
* - 조건 A: 활성 댓글이 없고 답변완료일(responseDate)로부터 N일 경과
|
||||||
|
* - 조건 B: 마지막 관리자 댓글(adminYn=Y) 이후 N일간 포탈사용자(adminYn=N) 댓글 없음
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
@Transactional(transactionManager = "transactionManagerForEMS")
|
||||||
|
@Slf4j
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class PortalInquiryClosingService {
|
||||||
|
|
||||||
|
private static final String RESPONDED = "RESPONDED";
|
||||||
|
private static final String CLOSED = "CLOSED";
|
||||||
|
|
||||||
|
private final PortalInquiryService portalInquiryService;
|
||||||
|
private final PortalInquiryCommentService portalInquiryCommentService;
|
||||||
|
private final PortalInquiryCommentRepository inquiryCommentRepository;
|
||||||
|
|
||||||
|
public int closeExpiredInquiries(int closingDays) {
|
||||||
|
LocalDateTime threshold = LocalDateTime.now().minusDays(closingDays);
|
||||||
|
List<Inquiry> respondedInquiries = portalInquiryService.findByInquiryStatus(RESPONDED);
|
||||||
|
|
||||||
|
int closedCount = 0;
|
||||||
|
for (Inquiry inquiry : respondedInquiries) {
|
||||||
|
if (shouldClose(inquiry, threshold)) {
|
||||||
|
inquiry.setInquiryStatus(CLOSED);
|
||||||
|
portalInquiryService.save(inquiry);
|
||||||
|
closedCount++;
|
||||||
|
log.info("문의글 CLOSED 처리: id={}", inquiry.getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.info("InquiryCommentClosingService: 총 {}건 CLOSED 처리 (기준={}일)", closedCount, closingDays);
|
||||||
|
return closedCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean shouldClose(Inquiry inquiry, LocalDateTime threshold) {
|
||||||
|
long activeCommentCount = portalInquiryCommentService.countActiveByInquiryId(inquiry.getId());
|
||||||
|
|
||||||
|
// 댓글 없음 → 답변완료일 기준 N일 경과 여부만 확인
|
||||||
|
if (activeCommentCount == 0) {
|
||||||
|
LocalDateTime responseDate = inquiry.getResponseDate();
|
||||||
|
return responseDate != null && !responseDate.isAfter(threshold);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 가장 최근 활성 댓글 확인
|
||||||
|
Optional<InquiryComment> lastComment = inquiryCommentRepository
|
||||||
|
.findTopByInquiry_IdAndDelYnOrderByCreatedDateDesc(inquiry.getId(), "N");
|
||||||
|
|
||||||
|
if (!lastComment.isPresent()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 포탈사용자(adminYn != 'Y')가 마지막 댓글 작성자 → CLOSED 불가
|
||||||
|
if (!"Y".equals(lastComment.get().getAdminYn())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 관리자가 마지막 댓글 작성자 → N일 경과 여부 확인
|
||||||
|
LocalDateTime lastAdminDate = lastComment.get().getCreatedDate();
|
||||||
|
return lastAdminDate != null && !lastAdminDate.isAfter(threshold);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
package com.eactive.eai.rms.onl.apim.portalInquiry;
|
||||||
|
|
||||||
|
import com.eactive.apim.portal.file.entity.FileInfo;
|
||||||
|
import com.eactive.eai.data.converter.LocalDateTimeFormatters;
|
||||||
|
import com.eactive.eai.rms.onl.apim.portaluser.PortalUserUI;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
|
import lombok.Data;
|
||||||
|
import org.springframework.format.annotation.DateTimeFormat;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class PortalInquiryCommentUI {
|
||||||
|
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
private String inquiryId;
|
||||||
|
|
||||||
|
private String commentDetail;
|
||||||
|
|
||||||
|
private String delYn;
|
||||||
|
|
||||||
|
private String createdBy;
|
||||||
|
|
||||||
|
private String writerName;
|
||||||
|
|
||||||
|
private String lastModifiedBy;
|
||||||
|
|
||||||
|
@JsonFormat(pattern = LocalDateTimeFormatters.FORMAT_YYYYMMDDHHMMSS_14)
|
||||||
|
@DateTimeFormat(pattern = "yyyyMMddHHmmss")
|
||||||
|
private LocalDateTime createdDate;
|
||||||
|
|
||||||
|
@JsonFormat(pattern = LocalDateTimeFormatters.FORMAT_YYYYMMDDHHMMSS_14)
|
||||||
|
@DateTimeFormat(pattern = "yyyyMMddHHmmss")
|
||||||
|
private LocalDateTime lastModifiedDate;
|
||||||
|
|
||||||
|
}
|
||||||
+17
@@ -0,0 +1,17 @@
|
|||||||
|
package com.eactive.eai.rms.onl.apim.portalInquiry;
|
||||||
|
|
||||||
|
import com.eactive.apim.portal.qna.entity.InquiryComment;
|
||||||
|
import org.mapstruct.Mapper;
|
||||||
|
import org.mapstruct.Mapping;
|
||||||
|
import org.mapstruct.ReportingPolicy;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.IGNORE)
|
||||||
|
public interface PortalInquiryCommentUIMapper {
|
||||||
|
|
||||||
|
@Mapping(target = "inquiryId", source = "inquiry.id")
|
||||||
|
PortalInquiryCommentUI toVo(InquiryComment entity);
|
||||||
|
|
||||||
|
List<PortalInquiryCommentUI> toVoList(List<InquiryComment> entities);
|
||||||
|
}
|
||||||
+18
@@ -74,4 +74,22 @@ public class PortalInquiryManController extends BaseAnnotationController {
|
|||||||
return ResponseEntity.ok().build();
|
return ResponseEntity.ok().build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PostMapping(value = "/onl/apim/portalinquiry/portalInquiryMan.json", params = "cmd=LIST_COMMENT")
|
||||||
|
public ResponseEntity<List<PortalInquiryCommentUI>> selectCommentList(String id) {
|
||||||
|
return ResponseEntity.ok(portalInquiryManService.selectCommentList(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping(value = "/onl/apim/portalinquiry/portalInquiryMan.json", params = "cmd=INSERT_COMMENT")
|
||||||
|
public ResponseEntity<String> insertComment(HttpServletRequest request, PortalInquiryCommentUI portalInquiryCommentUI) {
|
||||||
|
String userid = SessionManager.getUserId(request);
|
||||||
|
portalInquiryManService.insertComment(portalInquiryCommentUI, userid);
|
||||||
|
return ResponseEntity.ok().build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping(value = "/onl/apim/portalinquiry/portalInquiryMan.json", params = "cmd=DELETE_COMMENT")
|
||||||
|
public ResponseEntity<Void> deleteComment(String id) {
|
||||||
|
portalInquiryManService.deleteComment(id);
|
||||||
|
return ResponseEntity.ok().build();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user