Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a2824830cb |
@@ -1,140 +0,0 @@
|
||||
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
|
||||
+2
-3
@@ -1,3 +1,5 @@
|
||||
gradle.properties
|
||||
|
||||
# Package Files #
|
||||
*.war
|
||||
|
||||
@@ -246,6 +248,3 @@ eapim-admin-kjb/
|
||||
*.report.md
|
||||
*.claude.md
|
||||
tmpclaude-*-cwd
|
||||
logs
|
||||
rinjae_
|
||||
tomcat-base
|
||||
@@ -10,7 +10,7 @@ AI 어시스턴트가 작업을 시작하기 전에 반드시 이해해야 할
|
||||
- **신규 기능**: 항상 **JPA**와 Spring Data 리포지토리를 사용해야 합니다.
|
||||
- **레거시 코드**: 기존 iBATIS (`*.xml` 매퍼) 코드를 수정할 때는 해당 패턴을 따라야 합니다.
|
||||
- **멀티 모듈 Gradle 프로젝트**: `settings.gradle`에 정의된 것처럼 여러 하위 모듈(`elink-online-*`, `kjb-*` 등)로 구성되어 있습니다. 특정 기능은 다른 모듈에 위치할 수 있습니다.
|
||||
- **QueryDSL 코드 생성**: 빌드 시 `QueryDSL`을 사용하여 Q-class가 자동으로 생성됩니다. IDE에서 엔티티를 찾을 수 없다고 표시되면, `./gradlew compileJava` 를 먼저 실행하여 코드를 생성해야 합니다.
|
||||
- **QueryDSL 코드 생성**: 빌드 시 `QueryDSL`을 사용하여 Q-class가 자동으로 생성됩니다. IDE에서 엔티티를 찾을 수 없다고 표시되면, `kjb-gradle.sh compileJava`를 먼저 실행하여 코드를 생성해야 합니다.
|
||||
- **고객사별 커스터마이징**: `ext.kjb` 또는 `custom` 패키지는 특정 고객사(광주은행)를 위한 코드를 포함하므로, 일반적인 기능 수정 시에는 주의가 필요합니다.
|
||||
|
||||
## 프로젝트 개요
|
||||
@@ -39,48 +39,50 @@ eLink EMS (eLink Management System)는 eLink의 웹 기반 관리 서비스로,
|
||||
|
||||
### 빌드 명령어
|
||||
|
||||
프로젝트 루트의 Gradle wrapper(`./gradlew`)를 사용합니다. JDK 8 toolchain 위치는 `.envrc` 등으로 잡습니다(아래 "기술 스택" 섹션 참조).
|
||||
**⚠️ 중요**: 환경변수 문제로 인해 `gradle` 대신 `kjb-gradle.sh` 스크립트를 사용해야 합니다.
|
||||
- 스크립트 위치: `/c/eactive/workspaces/shell-scripts/kjb-gradle.sh`
|
||||
- PATH에 등록되어 있으므로 바로 `kjb-gradle.sh` 명령어 사용 가능
|
||||
|
||||
```bash
|
||||
# 표준 빌드
|
||||
./gradlew build
|
||||
kjb-gradle.sh build
|
||||
|
||||
# Weblogic 배포용 빌드 (테스트 제외)
|
||||
./gradlew build -x test -Pprofile=weblogic
|
||||
kjb-gradle.sh build -x test -Pprofile=weblogic
|
||||
|
||||
# WAR 파일 빌드
|
||||
./gradlew war
|
||||
kjb-gradle.sh war
|
||||
|
||||
# 클린 빌드
|
||||
./gradlew clean build
|
||||
kjb-gradle.sh clean build
|
||||
```
|
||||
|
||||
### 테스트 실행
|
||||
```bash
|
||||
# 모든 테스트 실행
|
||||
./gradlew test
|
||||
kjb-gradle.sh test
|
||||
|
||||
# 특정 테스트 클래스 실행
|
||||
./gradlew test --tests "com.example.ClassName"
|
||||
kjb-gradle.sh test --tests "com.example.ClassName"
|
||||
|
||||
# 특정 테스트 패키지 실행 (JUnit 플랫폼 사용)
|
||||
./gradlew test --tests "com.eactive.eai.rms.*"
|
||||
kjb-gradle.sh test --tests "com.eactive.eai.rms.*"
|
||||
```
|
||||
|
||||
### 개발 태스크
|
||||
|
||||
```bash
|
||||
# 패키징 없이 클래스만 컴파일
|
||||
./gradlew classes
|
||||
kjb-gradle.sh classes
|
||||
|
||||
# QueryDSL Q-classes 및 기타 애노테이션 생성
|
||||
./gradlew compileJava
|
||||
kjb-gradle.sh compileJava
|
||||
|
||||
# 의존성 트리 보기
|
||||
./gradlew dependencies
|
||||
kjb-gradle.sh dependencies
|
||||
|
||||
# 사용 가능한 모든 태스크 목록
|
||||
./gradlew tasks --all
|
||||
kjb-gradle.sh tasks --all
|
||||
```
|
||||
|
||||
## 로컬 개발 빠른 시작
|
||||
@@ -90,26 +92,26 @@ eLink EMS (eLink Management System)는 eLink의 웹 기반 관리 서비스로,
|
||||
1. **IDE 설정 파일 생성 (최초 1회)**
|
||||
```bash
|
||||
# Eclipse 사용 시 (기본)
|
||||
./gradlew eclipse
|
||||
kjb-gradle.sh eclipse
|
||||
|
||||
# IntelliJ IDEA 사용 시 (build.gradle.intellij 파일 사용 필요)
|
||||
# 1. build.gradle을 build.gradle.eclipse로 백업
|
||||
# 2. build.gradle.intellij를 build.gradle로 복사
|
||||
# 3. ./gradlew idea 실행
|
||||
# 3. kjb-gradle.sh idea 실행
|
||||
```
|
||||
|
||||
2. **QueryDSL 등 소스 코드 생성**
|
||||
```bash
|
||||
./gradlew compileJava
|
||||
kjb-gradle.sh compileJava
|
||||
```
|
||||
|
||||
3. **전체 빌드 및 테스트 실행**
|
||||
```bash
|
||||
./gradlew build
|
||||
kjb-gradle.sh build
|
||||
```
|
||||
|
||||
4. **IDE에서 프로젝트 열기**
|
||||
- Eclipse: `./gradlew eclipse` 실행 후, IDE에서 프로젝트를 엽니다.
|
||||
- Eclipse: `kjb-gradle.sh eclipse` 실행 후, IDE에서 프로젝트를 엽니다.
|
||||
- IntelliJ: IntelliJ 전용 `build.gradle.intellij`를 `build.gradle`로 교체 후 사용합니다.
|
||||
|
||||
5. **실행 구성(Run Configuration) 설정**
|
||||
@@ -160,14 +162,14 @@ eapim-admin (root project)
|
||||
#### Q-Class 생성 위치
|
||||
|
||||
Q-classes는 `build/generated/java/` 디렉토리에 생성됩니다.
|
||||
- `./gradlew compileJava` 또는 `./gradlew build` 실행 시 Gradle이 자동으로 생성
|
||||
- `kjb-gradle.sh compileJava` 또는 `kjb-gradle.sh build` 실행 시 Gradle이 자동으로 생성
|
||||
- 이 디렉토리는 `.gitignore`에 포함되어 있으며 커밋해서는 안 됩니다
|
||||
|
||||
#### 생성된 코드 작업하기
|
||||
|
||||
- **Gradle 빌드**: `build/generated/java/`의 Q-classes가 자동으로 classpath에 포함됩니다
|
||||
- **업데이트 pull 후**: `./gradlew compileJava` 를 실행하여 Q-classes를 재생성합니다
|
||||
- **Q-classes IDE 오류 시**: `./gradlew clean compileJava` 로 재생성합니다
|
||||
- **업데이트 pull 후**: `kjb-gradle.sh compileJava`를 실행하여 Q-classes를 재생성합니다
|
||||
- **Q-classes IDE 오류 시**: `kjb-gradle.sh clean compileJava`로 재생성합니다
|
||||
|
||||
#### IDE별 빌드 설정 파일
|
||||
|
||||
@@ -253,21 +255,12 @@ IntelliJ를 사용하려면 `build.gradle.intellij`를 `build.gradle`로 교체
|
||||
WebLogic용 빌드 시, DefaultServlet 문제를 해결하기 위해 `web.xml`을 `weblogic-web.xml`로 교체하는 `weblogic` 프로필을 사용합니다:
|
||||
|
||||
```bash
|
||||
./gradlew build -x test -Pprofile=weblogic
|
||||
kjb-gradle.sh build -x test -Pprofile=weblogic
|
||||
```
|
||||
|
||||
## 기술 스택
|
||||
|
||||
- **Java 8**: Gradle toolchain을 통해 강제 (`JavaLanguageVersion.of(8)`). 프로젝트의 `gradle.properties` 에는 JDK 경로를 기입하지 않습니다 — 개인 환경별로 `.envrc`(direnv) 또는 셸 rc 파일에서 `JAVA_HOME` 을 JDK 8 위치로 export 하세요. Gradle 8 의 toolchain auto-detect 가 `JAVA_HOME` 도 후보 경로로 인식합니다.
|
||||
|
||||
```bash
|
||||
# 예: 프로젝트 루트의 .envrc (Zulu 8 / macOS Apple Silicon)
|
||||
JAVA_HOME=${HOME}/opts/jdks/zulu8.94.0.17-ca-jdk8.0.492-macosx_aarch64
|
||||
GRADLE_USER_HOME=${HOME}/eactive/djb-eapim/gradle-home
|
||||
export PATH=${JAVA_HOME}/bin:${PATH}
|
||||
```
|
||||
|
||||
Gradle 표준 위치(`/Library/Java/JavaVirtualMachines/...`, Homebrew, SDKMAN 등)에 설치한 경우는 별도 설정 없이도 auto-detect 됩니다.
|
||||
- **Java 8**: Gradle toolchain을 통해 강제
|
||||
- **Spring Framework 5.3.27**: 코어 프레임워크 (MVC, Data JPA)
|
||||
- **Spring Data JPA 2.5.2**: 리포지토리 추상화
|
||||
- **Hibernate 5.4.33/5.6.15**: ORM 구현 (JPA/ORM)
|
||||
@@ -278,10 +271,9 @@ WebLogic용 빌드 시, DefaultServlet 문제를 해결하기 위해 `web.xml`
|
||||
- **Jackson 2.13.1**: JSON 처리
|
||||
- **Logback 1.2.10**: 로깅 프레임워크 (SLF4J와 함께)
|
||||
- **AWS SDK 2.20.142**: S3 연동
|
||||
- **Kubernetes Client 18.0.1**: K8s 관리
|
||||
- **EhCache**: 캐싱, **Lombok**: 코드 생성, **MapStruct**: 객체 매핑
|
||||
|
||||
> Kubernetes Client 의존성은 JDK 8 호환 문제로 제거됨(`io.kubernetes:client-java:18.0.1` 은 JDK 11+ 필요). K8s 모드 기능이 필요하면 `≤13.0.2` 로 다운그레이드 후 재도입 검토.
|
||||
|
||||
## 프로젝트 규칙
|
||||
|
||||
### 패키지 구조
|
||||
@@ -384,7 +376,7 @@ com.eactive.eai
|
||||
### IntelliJ IDEA (권장)
|
||||
|
||||
```bash
|
||||
./gradlew idea
|
||||
kjb-gradle.sh idea
|
||||
```
|
||||
|
||||
또는 IntelliJ에서 직접 "Import Gradle Project"를 사용합니다.
|
||||
@@ -403,7 +395,7 @@ cp build.gradle build.gradle.intellij
|
||||
cp build.gradle.eclipse build.gradle
|
||||
|
||||
# 3. Eclipse 프로젝트 생성
|
||||
./gradlew eclipse
|
||||
kjb-gradle.sh eclipse
|
||||
```
|
||||
|
||||
Eclipse 전용 설정(`build.gradle.eclipse`)은 다음을 포함합니다:
|
||||
|
||||
Vendored
-174
@@ -1,174 +0,0 @@
|
||||
pipeline {
|
||||
agent none
|
||||
|
||||
options {
|
||||
timestamps()
|
||||
disableConcurrentBuilds()
|
||||
skipDefaultCheckout() // stage별 agent의 자동 SCM checkout 방지 (Deploy 노드는 git 접근 불필요, unstash로만 WAR 수신)
|
||||
buildDiscarder(logRotator(numToKeepStr: '20', artifactNumToKeepStr: '20'))
|
||||
}
|
||||
|
||||
environment {
|
||||
GIT_SSH_COMMAND = 'ssh -o StrictHostKeyChecking=accept-new'
|
||||
WEBHOOK_URL = 'http://172.30.1.50:18000/api/hooks/01ba51b01d3c4c98ae8f3183a23a84ed713197857d024b5da4f303458195eb32'
|
||||
}
|
||||
|
||||
stages {
|
||||
stage('Build (djb-vm)') {
|
||||
agent { label 'djb-vm' }
|
||||
environment {
|
||||
JAVA_HOME = '/apps/opts/jdk8'
|
||||
GRADLE_HOME = '/apps/opts/gradle-8.7'
|
||||
GRADLE_USER_HOME = '/apps/opts/gradle-home'
|
||||
NODE_HOME = '/apps/opts/node-v24'
|
||||
PATH = "/apps/opts/jdk8/bin:/apps/opts/gradle-8.7/bin:/apps/opts/node-v24/bin:/apps/opts/bin:${env.PATH}"
|
||||
}
|
||||
stages {
|
||||
stage('Checkout') {
|
||||
steps { checkout scm }
|
||||
}
|
||||
|
||||
stage('Checkout dependencies') {
|
||||
steps {
|
||||
sh '''
|
||||
set -eu
|
||||
cd "$WORKSPACE/.."
|
||||
|
||||
mkdir -p eapim-online
|
||||
|
||||
for REPO in elink-online-core elink-online-core-jpa elink-online-transformer elink-online-common elink-online-emsclient; do
|
||||
TARGET="eapim-online/$REPO"
|
||||
if [ ! -d "$TARGET/.git" ]; then
|
||||
rm -rf "$TARGET"
|
||||
git clone --depth=1 --branch master \
|
||||
"ssh://git@172.30.1.50:2222/djb-eapim/$REPO.git" \
|
||||
"$TARGET"
|
||||
else
|
||||
git -C "$TARGET" fetch --depth=1 origin master
|
||||
git -C "$TARGET" reset --hard origin/master
|
||||
git -C "$TARGET" clean -fdx
|
||||
fi
|
||||
done
|
||||
|
||||
for REPO in elink-portal-common eapim-admin-djb; do
|
||||
if [ ! -d "$REPO/.git" ]; then
|
||||
rm -rf "$REPO"
|
||||
git clone --depth=1 --branch master \
|
||||
"ssh://git@172.30.1.50:2222/djb-eapim/$REPO.git" \
|
||||
"$REPO"
|
||||
else
|
||||
git -C "$REPO" fetch --depth=1 origin master
|
||||
git -C "$REPO" reset --hard origin/master
|
||||
git -C "$REPO" clean -fdx
|
||||
fi
|
||||
done
|
||||
'''
|
||||
}
|
||||
}
|
||||
|
||||
stage('Verify toolchain') {
|
||||
steps {
|
||||
sh '''
|
||||
set -eu
|
||||
java -version
|
||||
gradle --version
|
||||
'''
|
||||
}
|
||||
}
|
||||
|
||||
stage('Build WAR') {
|
||||
steps { sh 'gradle clean build -x test --no-daemon -Pprofile=weblogic' }
|
||||
post {
|
||||
success {
|
||||
sh '''
|
||||
set -eu
|
||||
cd build/libs
|
||||
sha256sum eapim-admin.war > eapim-admin.war.sha256
|
||||
'''
|
||||
archiveArtifacts artifacts: 'build/libs/eapim-admin.war,build/libs/eapim-admin.war.sha256', fingerprint: true
|
||||
stash name: 'war', includes: 'build/libs/eapim-admin.war'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stage('Deploy (weblogic)') {
|
||||
agent { label 'weblogic' }
|
||||
environment {
|
||||
JENKINS_NODE_COOKIE = 'dontKillMe' // background weblogic를 빌드 종료 시 죽이지 않게
|
||||
WL_HOME = '/app/eapim/adminportal'
|
||||
WL_DEPLOY_DIR = '/app/eapim/adminportal'
|
||||
WL_WAR_NAME = 'eapim-admin.war'
|
||||
WL_HTTP_PORT = '39120'
|
||||
WL_NOHUP = '/logs/weblogic/domains/eapimDomain/nohup.emsSvr11.out'
|
||||
}
|
||||
stages {
|
||||
stage('Stop WebLogic') {
|
||||
steps {
|
||||
sh '"$WL_HOME/stopEms11.sh"' // 동기: 완전 종료까지 블록, 미기동 시에도 안전
|
||||
}
|
||||
}
|
||||
stage('Deploy WAR') {
|
||||
steps {
|
||||
unstash 'war'
|
||||
sh '''
|
||||
set -eu
|
||||
cp build/libs/eapim-admin.war "$WL_DEPLOY_DIR/$WL_WAR_NAME"
|
||||
ls -la "$WL_DEPLOY_DIR"
|
||||
'''
|
||||
}
|
||||
}
|
||||
stage('Start WebLogic and readiness') {
|
||||
steps {
|
||||
sh '''
|
||||
set -eu
|
||||
"$WL_HOME/startEms11.sh" # nohup & 로 백그라운드 기동, 즉시 리턴
|
||||
|
||||
DEADLINE=$(($(date +%s) + 300))
|
||||
STATUS=000
|
||||
while [ "$(date +%s)" -lt "$DEADLINE" ]; do
|
||||
STATUS=$(curl -sS -o /dev/null -w '%{http_code}' --max-time 3 \
|
||||
"http://localhost:$WL_HTTP_PORT/monitoring/loginForm.do" 2>/dev/null || echo "000")
|
||||
case "$STATUS" in
|
||||
200|302|401) echo "Readiness OK (/monitoring/loginForm.do HTTP $STATUS)"; break ;;
|
||||
esac
|
||||
sleep 3
|
||||
done
|
||||
|
||||
case "$STATUS" in
|
||||
200|302|401) ;;
|
||||
*)
|
||||
echo "Readiness failed within 300s, last HTTP=$STATUS"
|
||||
[ -f "$WL_NOHUP" ] && tail -120 "$WL_NOHUP"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
'''
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
post {
|
||||
success {
|
||||
node('weblogic') {
|
||||
sh '''
|
||||
set +e
|
||||
payload=$(printf '{"text":"[%s #%s](%s) SUCCESS"}' "$JOB_NAME" "$BUILD_NUMBER" "$BUILD_URL")
|
||||
curl -sS --fail --max-time 5 -H 'Content-Type: application/json' -X POST --data "$payload" "$WEBHOOK_URL" >/dev/null || echo "Webhook failed"
|
||||
'''
|
||||
}
|
||||
}
|
||||
failure {
|
||||
node('weblogic') {
|
||||
sh '''
|
||||
set +e
|
||||
payload=$(printf '{"text":"[%s #%s](%s) FAILURE"}' "$JOB_NAME" "$BUILD_NUMBER" "$BUILD_URL")
|
||||
curl -sS --fail --max-time 5 -H 'Content-Type: application/json' -X POST --data "$payload" "$WEBHOOK_URL" >/dev/null || echo "Webhook failed"
|
||||
'''
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,7 +25,7 @@
|
||||
class="com.eactive.eai.rms.common.datasource.DataSourceType"
|
||||
p:name="APIGW"
|
||||
p:text="APIGW"
|
||||
p:schema="AGWAPP"
|
||||
p:schema="AGWADM"
|
||||
p:dynamic="true"
|
||||
p:online="true"
|
||||
p:jndiName="jdbc/dsOBP_AGW"
|
||||
@@ -37,19 +37,19 @@
|
||||
class="com.eactive.eai.rms.common.datasource.DataSourceType"
|
||||
p:name="BAP"
|
||||
p:text="BAP"
|
||||
p:schema="BAPAPP"
|
||||
p:schema="BAPADM"
|
||||
p:dynamic="true"
|
||||
p:online="true"
|
||||
p:jndiName="jdbc/dsOBP_BAP"
|
||||
p:render="BAP,COM"
|
||||
/>
|
||||
/>
|
||||
<!-- RMS_DEFAULT -->
|
||||
<bean
|
||||
id="MONITORING"
|
||||
class="com.eactive.eai.rms.common.datasource.DataSourceType"
|
||||
p:name="MONITORING"
|
||||
p:text="MONITORING"
|
||||
p:schema="EMSAPP"
|
||||
p:schema="EMSADM"
|
||||
p:dynamic="false"
|
||||
p:online="false"
|
||||
p:jndiName="jdbc/dsOBP_EMS"
|
||||
|
||||
@@ -25,31 +25,31 @@
|
||||
class="com.eactive.eai.rms.common.datasource.DataSourceType"
|
||||
p:name="APIGW"
|
||||
p:text="APIGW"
|
||||
p:schema="AGWAPP"
|
||||
p:schema="AGWADM"
|
||||
p:dynamic="true"
|
||||
p:online="true"
|
||||
p:jndiName="jdbc/dsOBP_AGW"
|
||||
p:render="ONL,COM"
|
||||
/>
|
||||
<!-- BAP 일괄전송FTP -->
|
||||
<bean
|
||||
id="BAP"
|
||||
class="com.eactive.eai.rms.common.datasource.DataSourceType"
|
||||
p:name="BAP"
|
||||
p:text="BAP"
|
||||
p:schema="BAPAPP"
|
||||
p:dynamic="true"
|
||||
p:online="true"
|
||||
p:jndiName="jdbc/dsOBP_BAP"
|
||||
p:render="BAP,COM"
|
||||
/>
|
||||
<!-- <bean-->
|
||||
<!-- id="BAP"-->
|
||||
<!-- class="com.eactive.eai.rms.common.datasource.DataSourceType"-->
|
||||
<!-- p:name="BAP"-->
|
||||
<!-- p:text="BAP"-->
|
||||
<!-- p:schema="BAPADM"-->
|
||||
<!-- p:dynamic="true"-->
|
||||
<!-- p:online="true"-->
|
||||
<!-- p:jndiName="jdbc/dsOBP_BAP"-->
|
||||
<!-- p:render="BAP,COM"-->
|
||||
<!-- />-->
|
||||
<!-- RMS_DEFAULT -->
|
||||
<bean
|
||||
id="MONITORING"
|
||||
class="com.eactive.eai.rms.common.datasource.DataSourceType"
|
||||
p:name="MONITORING"
|
||||
p:text="MONITORING"
|
||||
p:schema="EMSAPP"
|
||||
p:schema="EMSADM"
|
||||
p:dynamic="false"
|
||||
p:online="false"
|
||||
p:jndiName="jdbc/dsOBP_EMS"
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
class="com.eactive.eai.rms.common.datasource.DataSourceType"
|
||||
p:name="APIGW"
|
||||
p:text="APIGW"
|
||||
p:schema="AGWAPP"
|
||||
p:schema="AGWADM"
|
||||
p:dynamic="true"
|
||||
p:online="true"
|
||||
p:jndiName="jdbc/dsOBP_AGW"
|
||||
@@ -37,7 +37,7 @@
|
||||
class="com.eactive.eai.rms.common.datasource.DataSourceType"
|
||||
p:name="BAP"
|
||||
p:text="BAP"
|
||||
p:schema="BAPAPP"
|
||||
p:schema="BAPADM"
|
||||
p:dynamic="true"
|
||||
p:online="true"
|
||||
p:jndiName="jdbc/dsOBP_BAP"
|
||||
@@ -49,7 +49,7 @@
|
||||
class="com.eactive.eai.rms.common.datasource.DataSourceType"
|
||||
p:name="MONITORING"
|
||||
p:text="MONITORING"
|
||||
p:schema="EMSAPP"
|
||||
p:schema="EMSADM"
|
||||
p:dynamic="false"
|
||||
p:online="false"
|
||||
p:jndiName="jdbc/dsOBP_EMS"
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
class="com.eactive.eai.rms.common.datasource.DataSourceType"
|
||||
p:name="APIGW"
|
||||
p:text="APIGW"
|
||||
p:schema="AGWAPP"
|
||||
p:schema="AGWADM"
|
||||
p:dynamic="true"
|
||||
p:online="true"
|
||||
p:jndiName="jdbc/dsOBP_AGW"
|
||||
@@ -37,7 +37,7 @@
|
||||
class="com.eactive.eai.rms.common.datasource.DataSourceType"
|
||||
p:name="BAP"
|
||||
p:text="BAP"
|
||||
p:schema="BAPAPP"
|
||||
p:schema="BAPADM"
|
||||
p:dynamic="true"
|
||||
p:online="true"
|
||||
p:jndiName="jdbc/dsOBP_BAP"
|
||||
@@ -49,7 +49,7 @@
|
||||
class="com.eactive.eai.rms.common.datasource.DataSourceType"
|
||||
p:name="MONITORING"
|
||||
p:text="MONITORING"
|
||||
p:schema="EMSAPP"
|
||||
p:schema="EMSADM"
|
||||
p:dynamic="false"
|
||||
p:online="false"
|
||||
p:jndiName="jdbc/dsOBP_EMS"
|
||||
|
||||
@@ -30,9 +30,9 @@
|
||||
<entry
|
||||
key="APIGW"
|
||||
value-ref="APIGW" />
|
||||
<entry
|
||||
key="BAP"
|
||||
value-ref="BAP" />
|
||||
<!-- <entry-->
|
||||
<!-- key="BAP"-->
|
||||
<!-- value-ref="BAP" />-->
|
||||
</map>
|
||||
</property>
|
||||
<property
|
||||
@@ -91,7 +91,7 @@
|
||||
<property name="configLocations">
|
||||
<list>
|
||||
<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}/sqlMapConfigOfService.xml</value> -->
|
||||
</list>
|
||||
|
||||
@@ -69,7 +69,6 @@
|
||||
<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.djb" />
|
||||
|
||||
|
||||
<bean id="cacheManager"
|
||||
|
||||
@@ -115,7 +115,7 @@
|
||||
</bean>
|
||||
|
||||
<bean id="ioAcceptor" class="com.eactive.eai.rms.onl.server.UdpServer" init-method="init" destroy-method="destory">
|
||||
<property name="port" value="39126" />
|
||||
<property name="port" value="10800" />
|
||||
<property name="handler" ref="handler"/>
|
||||
</bean>
|
||||
|
||||
|
||||
@@ -12,8 +12,6 @@ PortalTermsManController_약관관리_APIGW_INSERT,UPDATE,DELETE
|
||||
ProdClientManController_운영Client (키정보)관리_APIGW_INSERT,UPDATE,DELETE
|
||||
ApiSpecController_API스펙관리_APIGW_INSERT,DELETE
|
||||
MessageTemplateManController_메세지템플릿관리_APIGW_INSERT,UPDATE,DELETE
|
||||
PortalPartnershipManController_피드백/개선요청관리_APIGW_UNMASK,DELETE
|
||||
PortalPartnershipManController_사업제휴신청관리_APIGW_UNMASK
|
||||
PortalPropertyController_포탈 프로퍼티 관리_APIGW_UPDATE
|
||||
PortalUserTermsManController_약관동의 이력_APIGW_UNMASK
|
||||
MessageRequestManController_메세지발송내역_APIGW_UPDATE_STATUS
|
||||
PortalInquiryManController_Q&A문의관리_APIGW_VISIBILITY
|
||||
PortalUserTermsManController_약관동의 이력_APIGW_UNMASK
|
||||
@@ -10,7 +10,7 @@ hibernate.dialect=org.hibernate.dialect.Oracle12cDialect
|
||||
|
||||
# Priority of the was -D option
|
||||
inst.Name=emsSvr11
|
||||
eai.tableowner=EMSAPP
|
||||
eai.tableowner=EMSADM
|
||||
# P/T/D/S
|
||||
eai.systemmode=D
|
||||
eai.drmode=N
|
||||
|
||||
@@ -10,7 +10,7 @@ hibernate.dialect=org.hibernate.dialect.Oracle12cDialect
|
||||
|
||||
# Priority of the was -D option
|
||||
inst.Name=emsSvr96
|
||||
eai.tableowner=EMSAPP
|
||||
eai.tableowner=EMSADM
|
||||
# P/T/D/S
|
||||
eai.systemmode=L
|
||||
eai.drmode=N
|
||||
|
||||
@@ -10,7 +10,7 @@ hibernate.dialect=org.hibernate.dialect.Oracle12cDialect
|
||||
|
||||
# Priority of the was -D option
|
||||
inst.Name=emsSvr11
|
||||
eai.tableowner=EMSAPP
|
||||
eai.tableowner=EMSADM
|
||||
# P/T/D/S
|
||||
eai.systemmode=P
|
||||
eai.drmode=N
|
||||
|
||||
@@ -10,7 +10,7 @@ hibernate.dialect=org.hibernate.dialect.Oracle12cDialect
|
||||
|
||||
# Priority of the was -D option
|
||||
inst.Name=emsSvr11
|
||||
eai.tableowner=EMSAPP
|
||||
eai.tableowner=EMSADM
|
||||
# P/T/D/S
|
||||
eai.systemmode=T
|
||||
eai.drmode=N
|
||||
|
||||
@@ -9,7 +9,15 @@
|
||||
<param-name>contextConfigLocation</param-name>
|
||||
<param-value>/WEB-INF/applicationContext.xml</param-value>
|
||||
</context-param>
|
||||
|
||||
<!-- XSS Filter -->
|
||||
<filter>
|
||||
<filter-name>CrossScriptingFilter</filter-name>
|
||||
<filter-class>com.eactive.eai.rms.common.filter.CrossScriptingFilter</filter-class>
|
||||
</filter>
|
||||
<filter-mapping>
|
||||
<filter-name>CrossScriptingFilter</filter-name>
|
||||
<url-pattern>/*</url-pattern>
|
||||
</filter-mapping>
|
||||
|
||||
<filter>
|
||||
<filter-name>encodingFilter</filter-name>
|
||||
@@ -48,17 +56,6 @@
|
||||
<filter-name>encodingFilter</filter-name>
|
||||
<url-pattern>*.json</url-pattern>
|
||||
</filter-mapping>
|
||||
|
||||
|
||||
<!-- XSS Filter -->
|
||||
<filter>
|
||||
<filter-name>CrossScriptingFilter</filter-name>
|
||||
<filter-class>com.eactive.eai.rms.common.filter.CrossScriptingFilter</filter-class>
|
||||
</filter>
|
||||
<filter-mapping>
|
||||
<filter-name>CrossScriptingFilter</filter-name>
|
||||
<url-pattern>/*</url-pattern>
|
||||
</filter-mapping>
|
||||
|
||||
<!--
|
||||
<filter>
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
||||
targetNamespace="http://xmlns.oracle.com/weblogic/weblogic-web-app"
|
||||
xmlns="http://xmlns.oracle.com/weblogic/weblogic-web-app"
|
||||
elementFormDefault="qualified">
|
||||
|
||||
<xs:element name="weblogic-web-app">
|
||||
<xs:complexType>
|
||||
<xs:all>
|
||||
<xs:element name="context-root" type="xs:string" minOccurs="0"/>
|
||||
<xs:element name="session-descriptor" minOccurs="0">
|
||||
<xs:complexType>
|
||||
<xs:all>
|
||||
<xs:element name="timeout-secs" type="xs:integer" minOccurs="0"/>
|
||||
<xs:element name="persistent-store-type" type="xs:string" minOccurs="0"/>
|
||||
</xs:all>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="container-descriptor" minOccurs="0">
|
||||
<xs:complexType>
|
||||
<xs:all>
|
||||
<xs:element name="prefer-application-packages" minOccurs="0">
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element name="package-name" type="xs:string" minOccurs="0" maxOccurs="unbounded"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="prefer-application-resources" minOccurs="0">
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element name="resource-name" type="xs:string" minOccurs="0" maxOccurs="unbounded"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
</xs:all>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
</xs:all>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
|
||||
</xs:schema>
|
||||
@@ -1,10 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<weblogic-web-app xmlns="http://xmlns.oracle.com/weblogic/weblogic-web-app">
|
||||
<weblogic-web-app
|
||||
xmlns="http://xmlns.oracle.com/weblogic/weblogic-web-app"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://xmlns.oracle.com/weblogic/weblogic-web-app weblogic-web-app.xsd">
|
||||
|
||||
<context-root>monitoring</context-root>
|
||||
<session-descriptor>
|
||||
<timeout-secs>1800</timeout-secs>
|
||||
<cookie-name>JSESSIONID_PORTAL</cookie-name>
|
||||
<persistent-store-type>replicated_if_clustered</persistent-store-type>
|
||||
</session-descriptor>
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
@charset "utf-8";
|
||||
|
||||
.gnb .depth1 > li:hover > a{color:#008cc5;} /* theme */
|
||||
.gnb .sitemap-link:hover{color:#008cc5;} /* theme */
|
||||
.gnb .depth1 > li > .red_box{background:#008cc5;}/* theme */
|
||||
.gnb .depth2 > li > a:hover{color:#008cc5;}/* theme */
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
@charset "utf-8";
|
||||
|
||||
.gnb .depth1 > li:hover > a{color:#16a085;} /* theme */
|
||||
.gnb .sitemap-link:hover{color:#16a085;} /* theme */
|
||||
.gnb .depth1 > li > .red_box{background:#16a085;}/* theme */
|
||||
.gnb .depth2 > li > a:hover{color:#16a085;}/* theme */
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
@charset "utf-8";
|
||||
|
||||
.gnb .depth1 > li:hover > a{color:#e74c3c;} /* theme */
|
||||
.gnb .sitemap-link:hover{color:#e74c3c;} /* theme */
|
||||
.gnb .depth1 > li > .red_box{background:#e74c3c;}/* theme */
|
||||
.gnb .depth2 > li > a:hover{color:#e74c3c;}/* theme */
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
@charset "utf-8";
|
||||
|
||||
.gnb .depth1 > li:hover > a{color:#ff80c0;} /* theme */
|
||||
.gnb .sitemap-link:hover{color:#ff80c0;} /* theme */
|
||||
.gnb .depth1 > li > .red_box{background:#ff80c0;}/* theme */
|
||||
.gnb .depth2 > li > a:hover{color:#ff80c0;}/* theme */
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
@charset "utf-8";
|
||||
|
||||
.gnb .depth1 > li:hover > a{color:#be8200;} /* theme */
|
||||
.gnb .sitemap-link:hover{color:#be8200;} /* theme */
|
||||
.gnb .depth1 > li > .red_box{background:#be8200;}/* theme */
|
||||
.gnb .depth2 > li > a:hover{color:#be8200;}/* theme */
|
||||
|
||||
|
||||
@@ -62,8 +62,8 @@ header.sub{position:relative; padding-top:80px;}
|
||||
100% { color:rgba(255,96,78,0); }
|
||||
} */
|
||||
|
||||
.gnb_bg{position:absolute; top:80px; left:0; display:none; width:100%; height:282px; box-sizing:border-box; background:white; box-shadow: 0px 5px 5px 0px rgba(0,0,0,0.2);}
|
||||
.gnb{position:absolute; top:0px; left:180px; display:inline-block; height:80px; overflow-y:hidden;}
|
||||
.gnb_bg{position:absolute; top:80px; left:0; display:none; width:100%; height:382px; box-sizing:border-box; background:white; box-shadow: 0px 5px 5px 0px rgba(0,0,0,0.2);}
|
||||
.gnb{position:absolute; top:0px; left:230px; display:inline-block; height:80px; overflow-y:hidden;}
|
||||
.gnb:hover{height:auto; overflow-y:visible;}
|
||||
.gnb:hover + .gnb_bg{display:block;}
|
||||
.gnb h1{float:left; display:block; width:auto; height:100%; line-height:80px;}
|
||||
@@ -71,15 +71,15 @@ header.sub{position:relative; padding-top:80px;}
|
||||
|
||||
.gnb .depth1 > li{position:relative;}
|
||||
.gnb .depth1 > li > a{position:relative; display:block; box-sizing:border-box; color:#000; font-size:14px; width:160px; height:80px; text-align:center; line-height:80px; letter-spacing:-0.5px; opacity:0.7; transition:all 0.3s;}
|
||||
.gnb .depth1 > li > a:after{content:''; position:absolute; top:34px; right:0; display:inline-block; width:1px; height:13px; }
|
||||
.gnb .depth1 > li:first-child > a:before{content:''; position:absolute; top:34px; left:0; display:inline-block; width:1px; height:13px; }
|
||||
.gnb .depth1 > li > a:after{content:''; position:absolute; top:34px; right:0; display:inline-block; width:1px; height:13px; background:#ddd;}
|
||||
.gnb .depth1 > li:first-child > a:before{content:''; position:absolute; top:34px; left:0; display:inline-block; width:1px; height:13px; background:#ddd;}
|
||||
/*
|
||||
.gnb .depth1 > li:hover .red_box{transform:scaleX(1);}
|
||||
.gnb .depth1 > li:hover > a{opacity:1;}
|
||||
.gnb .depth1 > li > .red_box{display:block; position:absolute; width:100%; height:2px; top:78px; box-sizing:border-box; background:#ed1c24; transform-origin:center center; transform:scaleX(0); transition:all 0.3s;}
|
||||
*/
|
||||
.gnb .depth2{position:absolute; left:0; top:80px; width:160px; height:310px; box-sizing:border-box; padding:20px 0; border-right:1px solid #eee; text-align:center; z-index:9;}
|
||||
.gnb .depth1 > li:first-child .depth2{border-left:1px solid #eee;}
|
||||
.gnb .depth2{position:absolute; left:0; top:80px; width:160px; height:410px; box-sizing:border-box; padding:20px 0; border-right:1px solid #eee; text-align:center; z-index:9;}
|
||||
.gnb .depth2.first{border-left:1px solid #eee;}
|
||||
.gnb .depth2 > li > a{display:block; color:#000; font-size:13px; line-height:30px;}
|
||||
.gnb .depth2 > li > a:hover{color:#ff0000;}
|
||||
|
||||
@@ -94,9 +94,9 @@ header.sub{position:relative; padding-top:80px;}
|
||||
.left_box .depth3 > li:hover > a, .left_box .depth3 > li.on > a{color:#ed1c24;}
|
||||
|
||||
.content_top{position:fixed; top:0; left:0; padding:0 30px; width:100%; height:50px; line-height:50px; background:#eee; border-top:1px solid #ddd; border-left:1px solid #ddd; z-index:100;}
|
||||
.content_top .path{margin:0; padding:0;}
|
||||
.content_top .path > li{display:inline-block; margin:0; padding:0;}
|
||||
.content_top .path > li a{display:block; height:50px; line-height:50px; padding-left:10px; font-size:12px; color:#999; text-decoration:none;}
|
||||
.content_top .path{}
|
||||
.content_top .path > li{display:inline-block;}
|
||||
.content_top .path > li a{display:block; height:50px; line-height:50px; padding-left:10px; font-size:12px; color:#999;}
|
||||
.content_top .path > li a:hover{color:#666;}
|
||||
.content_top .path > li a:before{content:'>'; margin-right:10px;}
|
||||
.content_top .path > li:first-child a:before{content:'';}
|
||||
|
||||
@@ -1,170 +0,0 @@
|
||||
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8" %>
|
||||
<%@ page import="java.lang.reflect.Method" %>
|
||||
<%@ page import="org.apache.commons.lang3.StringUtils" %>
|
||||
<%@ page import="com.eactive.eai.rms.common.login.SessionManager" %>
|
||||
<%!
|
||||
// 최소 HTML 이스케이프 (진단 페이지 XSS 방지)
|
||||
private String esc(String s) {
|
||||
if (s == null) return "";
|
||||
StringBuilder b = new StringBuilder(s.length() + 16);
|
||||
for (int i = 0; i < s.length(); i++) {
|
||||
char c = s.charAt(i);
|
||||
switch (c) {
|
||||
case '&': b.append("&"); break;
|
||||
case '<': b.append("<"); break;
|
||||
case '>': b.append(">"); break;
|
||||
case '"': b.append("""); break;
|
||||
case '\'': b.append("'"); break;
|
||||
default: b.append(c);
|
||||
}
|
||||
}
|
||||
return b.toString();
|
||||
}
|
||||
%>
|
||||
<%
|
||||
// 로그인 사용자만 접근 — real 모드에서 운영 키 암복호화 오라클이 되지 않도록 보호
|
||||
if (StringUtils.isBlank(SessionManager.getUserId(request))
|
||||
&& StringUtils.isBlank(SessionManager.getRoleIdString(request))) {
|
||||
response.sendRedirect(request.getContextPath() + "/loginForm.do");
|
||||
return;
|
||||
}
|
||||
|
||||
response.setHeader("Pragma", "No-cache");
|
||||
response.setHeader("Cache-Control", "no-cache");
|
||||
response.setHeader("Expires", "0");
|
||||
request.setCharacterEncoding("utf-8");
|
||||
|
||||
String op = request.getParameter("op");
|
||||
String input = request.getParameter("input");
|
||||
boolean run = request.getParameter("run") != null;
|
||||
if (op == null) op = "encrypt";
|
||||
if (input == null) input = "";
|
||||
|
||||
String mode = "UNKNOWN";
|
||||
String revision = "-";
|
||||
String returnCode = "-";
|
||||
String result = "";
|
||||
String error = "";
|
||||
boolean loaded = false;
|
||||
|
||||
try {
|
||||
Class<?> cls = Class.forName("com.eactive.ext.djb.DamoManager");
|
||||
Object damo = cls.getMethod("getInstance").invoke(null);
|
||||
loaded = true;
|
||||
|
||||
boolean bypass = (Boolean) cls.getMethod("isBypassMode").invoke(damo);
|
||||
boolean fake = (Boolean) cls.getMethod("isFakeMode").invoke(damo);
|
||||
mode = bypass ? "BYPASS" : (fake ? "FAKE" : "REAL");
|
||||
revision = String.valueOf(cls.getMethod("getRevision").invoke(damo));
|
||||
returnCode = String.valueOf(cls.getMethod("getReturnCode").invoke(damo));
|
||||
|
||||
if (run) {
|
||||
if ("encrypt".equals(op)) {
|
||||
result = (String) cls.getMethod("encrypt", String.class).invoke(damo, input);
|
||||
} else if ("decrypt".equals(op)) {
|
||||
result = (String) cls.getMethod("decrypt", String.class).invoke(damo, input);
|
||||
} else if ("sha256".equals(op)) {
|
||||
int t = cls.getField("SHA256").getInt(null);
|
||||
result = (String) cls.getMethod("hash", int.class, String.class).invoke(damo, t, input);
|
||||
} else if ("sha512".equals(op)) {
|
||||
int t = cls.getField("SHA512").getInt(null);
|
||||
result = (String) cls.getMethod("hash", int.class, String.class).invoke(damo, t, input);
|
||||
} else {
|
||||
error = "알 수 없는 op: " + op;
|
||||
}
|
||||
}
|
||||
} catch (ClassNotFoundException e) {
|
||||
error = "com.eactive.ext.djb.DamoManager 클래스를 찾을 수 없습니다. "
|
||||
+ "damo-manager.jar 를 WAS lib (운영) 또는 WEB-INF/lib 에 배포하세요.";
|
||||
} catch (Throwable t) {
|
||||
// require-real fail-fast(ExceptionInInitializerError) 또는 초기화 실패(NoClassDefFoundError) 포함
|
||||
Throwable c = (t.getCause() != null) ? t.getCause() : t;
|
||||
error = c.toString();
|
||||
if (c instanceof NoClassDefFoundError
|
||||
|| error.contains("require-real")
|
||||
|| error.contains("Could not initialize")) {
|
||||
error += " (※ -Ddamo-manager.require-real=true 인데 scpdb 가 없어 기동에 실패했을 수 있습니다. "
|
||||
+ "scpdb 를 갖추거나 옵션을 내리세요.)";
|
||||
}
|
||||
}
|
||||
|
||||
String badgeColor = "REAL".equals(mode) ? "#1a7f37"
|
||||
: "BYPASS".equals(mode) ? "#9a3412"
|
||||
: "FAKE".equals(mode) ? "#9a6700" : "#6e7781";
|
||||
%>
|
||||
<!DOCTYPE html>
|
||||
<html lang="ko">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<title>DamoManager 테스트</title>
|
||||
<style>
|
||||
body { font-family: -apple-system, "Malgun Gothic", sans-serif; margin: 24px; color: #24292f; }
|
||||
h1 { font-size: 20px; margin: 0 0 4px; }
|
||||
.sub { color: #6e7781; font-size: 13px; margin-bottom: 16px; }
|
||||
.badge { display:inline-block; color:#fff; padding:2px 10px; border-radius:12px; font-size:12px; font-weight:600; background:<%= badgeColor %>; }
|
||||
.meta { font-size:12px; color:#57606a; margin-left:8px; }
|
||||
.card { border:1px solid #d0d7de; border-radius:8px; padding:16px; max-width:760px; }
|
||||
label { display:block; font-size:13px; font-weight:600; margin:12px 0 4px; }
|
||||
textarea, select { width:100%; box-sizing:border-box; font-size:14px; padding:8px; border:1px solid #d0d7de; border-radius:6px; font-family:ui-monospace, Menlo, monospace; }
|
||||
textarea { height:90px; resize:vertical; }
|
||||
.row { display:flex; gap:12px; }
|
||||
.row > div { flex:1; }
|
||||
button { margin-top:14px; background:#1f6feb; color:#fff; border:0; border-radius:6px; padding:9px 18px; font-size:14px; font-weight:600; cursor:pointer; }
|
||||
.result { margin-top:16px; }
|
||||
.out { background:#f6f8fa; border:1px solid #d0d7de; border-radius:6px; padding:12px; white-space:pre-wrap; word-break:break-all; font-family:ui-monospace, Menlo, monospace; font-size:13px; min-height:20px; }
|
||||
.err { color:#cf222e; background:#fff5f5; border-color:#ffc1c1; }
|
||||
.hint { font-size:12px; color:#6e7781; margin-top:18px; line-height:1.6; }
|
||||
code { background:#eff1f3; padding:1px 5px; border-radius:4px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>DamoManager 테스트</h1>
|
||||
<div class="sub">웹에서 encrypt / decrypt / hash 동작을 즉시 확인 (eapim-admin)</div>
|
||||
|
||||
<div class="card">
|
||||
<div>
|
||||
<% if (loaded) { %>
|
||||
<span class="badge"><%= mode %> MODE</span>
|
||||
<span class="meta">revision=<%= esc(revision) %> · returnCode=<%= esc(returnCode) %></span>
|
||||
<% } else { %>
|
||||
<span class="badge" style="background:#cf222e;">NOT LOADED</span>
|
||||
<span class="meta">damo-manager.jar 미배포</span>
|
||||
<% } %>
|
||||
</div>
|
||||
|
||||
<form method="post" action="<%= request.getContextPath() %>/damo.jsp">
|
||||
<div class="row">
|
||||
<div>
|
||||
<label for="op">동작</label>
|
||||
<select id="op" name="op">
|
||||
<option value="encrypt" <%= "encrypt".equals(op) ? "selected" : "" %>>encrypt (암호화)</option>
|
||||
<option value="decrypt" <%= "decrypt".equals(op) ? "selected" : "" %>>decrypt (복호화)</option>
|
||||
<option value="sha256" <%= "sha256".equals(op) ? "selected" : "" %>>hash SHA-256</option>
|
||||
<option value="sha512" <%= "sha512".equals(op) ? "selected" : "" %>>hash SHA-512</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<label for="input">입력</label>
|
||||
<textarea id="input" name="input" placeholder="암호화/복호화/해시할 문자열"><%= esc(input) %></textarea>
|
||||
|
||||
<input type="hidden" name="run" value="1">
|
||||
<button type="submit">실행</button>
|
||||
</form>
|
||||
|
||||
<% if (run || error.length() > 0) { %>
|
||||
<div class="result">
|
||||
<label>결과<% if (run && error.length() == 0) { %> <span class="meta">(<%= esc(op) %>)</span><% } %></label>
|
||||
<div class="out <%= error.length() > 0 ? "err" : "" %>"><%= error.length() > 0 ? esc(error) : esc(result) %></div>
|
||||
</div>
|
||||
<% } %>
|
||||
|
||||
<div class="hint">
|
||||
모드 의미 — <b>BYPASS</b>: 무변환 원문 통과(<b>기본값</b>, 옵션 없음) · <b>FAKE</b>: Base64/JRE 해시(<code>-Ddamo-manager.enabled=true</code>, scpdb 없음) · <b>REAL</b>: penta scpdb 실 암복호화(<code>enabled=true</code> + scpdb).<br>
|
||||
BYPASS 모드에서는 encrypt/decrypt/hash 모두 입력을 그대로 반환합니다.<br>
|
||||
운영은 <code>-Ddamo-manager.require-real=true</code> 로 real 강제(아니면 기동 실패). CLI 로도 동일 확인: <code>java -jar damo-manager.jar enc <text></code>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,7 +1,6 @@
|
||||
<%@ page language="java" contentType="text/html; charset=utf-8"%>
|
||||
<%@page import="org.apache.commons.lang3.StringUtils"%>
|
||||
<%@page import="com.eactive.eai.rms.common.login.SessionManager"%>
|
||||
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
|
||||
@@ -36,14 +36,6 @@
|
||||
// save the returnValue in options so that it is available in the callback function
|
||||
optns.returnValue = $frame[0].contentWindow.window.returnValue;
|
||||
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){
|
||||
$('iframe[name=topFrame]',window.parent.document).css('display', 'block');
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,334 +0,0 @@
|
||||
/**
|
||||
* Editor Content Styles (editor-content.css)
|
||||
*
|
||||
* Summernote 에디터로 작성된 HTML 콘텐츠의 공통 스타일
|
||||
* 관리자포탈/개발자포탈 양쪽에서 동일하게 사용
|
||||
*
|
||||
* 사용법:
|
||||
* - 관리자포탈: Summernote .note-editable에 .editor-content 클래스 추가
|
||||
* - 개발자포탈: 콘텐츠 wrapper에 .editor-content 클래스 추가
|
||||
*
|
||||
* @version 1.0.0
|
||||
* @date 2025-12-30
|
||||
*/
|
||||
|
||||
/* ==========================================================================
|
||||
CSS Reset + 기본 설정
|
||||
========================================================================== */
|
||||
|
||||
.editor-content {
|
||||
all: revert;
|
||||
font-family: 'Noto Sans KR', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif !important;
|
||||
font-size: 16px !important;
|
||||
font-weight: 400 !important;
|
||||
line-height: 1.8 !important;
|
||||
color: #1A1A2E !important;
|
||||
word-wrap: break-word;
|
||||
text-align: left !important;
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
헤딩 (Headings)
|
||||
========================================================================== */
|
||||
|
||||
.editor-content h1 {
|
||||
font-size: 20px !important;
|
||||
font-weight: 700 !important;
|
||||
margin: 32px 0 16px !important;
|
||||
line-height: 1.4 !important;
|
||||
color: #1A1A2E !important;
|
||||
}
|
||||
|
||||
.editor-content h1:first-child {
|
||||
margin-top: 0 !important;
|
||||
}
|
||||
|
||||
.editor-content h2 {
|
||||
font-size: 18px !important;
|
||||
font-weight: 700 !important;
|
||||
margin: 24px 0 8px !important;
|
||||
line-height: 1.4 !important;
|
||||
color: #1A1A2E !important;
|
||||
}
|
||||
|
||||
.editor-content h3 {
|
||||
font-size: 16px !important;
|
||||
font-weight: 600 !important;
|
||||
margin: 16px 0 8px !important;
|
||||
line-height: 1.4 !important;
|
||||
color: #1A1A2E !important;
|
||||
}
|
||||
|
||||
.editor-content h4,
|
||||
.editor-content h5,
|
||||
.editor-content h6 {
|
||||
font-size: 16px !important;
|
||||
font-weight: 600 !important;
|
||||
margin: 16px 0 8px !important;
|
||||
line-height: 1.4 !important;
|
||||
color: #1A1A2E !important;
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
단락 (Paragraphs)
|
||||
========================================================================== */
|
||||
|
||||
.editor-content p {
|
||||
margin-bottom: 16px !important;
|
||||
line-height: 1.8 !important;
|
||||
font-size: 16px !important;
|
||||
}
|
||||
|
||||
.editor-content p:last-child {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
리스트 (Lists) - 글로벌 reset 대응
|
||||
========================================================================== */
|
||||
|
||||
.editor-content ul {
|
||||
list-style-type: disc !important;
|
||||
padding-left: 32px !important;
|
||||
margin: 16px 0 !important;
|
||||
}
|
||||
|
||||
.editor-content ol {
|
||||
list-style-type: decimal !important;
|
||||
padding-left: 32px !important;
|
||||
margin: 16px 0 !important;
|
||||
}
|
||||
|
||||
.editor-content li {
|
||||
margin-bottom: 8px !important;
|
||||
line-height: 1.8 !important;
|
||||
font-size: 16px !important;
|
||||
}
|
||||
|
||||
.editor-content li:last-child {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
|
||||
/* 중첩 리스트 */
|
||||
.editor-content ul ul {
|
||||
list-style-type: circle !important;
|
||||
margin: 8px 0 !important;
|
||||
}
|
||||
|
||||
.editor-content ul ul ul {
|
||||
list-style-type: square !important;
|
||||
}
|
||||
|
||||
.editor-content ol ol {
|
||||
list-style-type: lower-alpha !important;
|
||||
margin: 8px 0 !important;
|
||||
}
|
||||
|
||||
.editor-content ol ol ol {
|
||||
list-style-type: lower-roman !important;
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
테이블 (Tables)
|
||||
========================================================================== */
|
||||
|
||||
.editor-content table {
|
||||
width: 100% !important;
|
||||
border-collapse: collapse !important;
|
||||
margin: 24px 0 !important;
|
||||
border: 1px solid #E2E8F0 !important;
|
||||
border-radius: 8px !important;
|
||||
overflow: hidden !important;
|
||||
font-size: 14px !important;
|
||||
}
|
||||
|
||||
.editor-content th,
|
||||
.editor-content td {
|
||||
border: 1px solid #E2E8F0 !important;
|
||||
padding: 8px 16px !important;
|
||||
text-align: left !important;
|
||||
vertical-align: top !important;
|
||||
}
|
||||
|
||||
.editor-content th {
|
||||
background: #EFF6FF !important;
|
||||
font-weight: 600 !important;
|
||||
color: #1A1A2E !important;
|
||||
}
|
||||
|
||||
.editor-content tr:hover {
|
||||
background: #F8FAFC !important;
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
링크 (Links)
|
||||
========================================================================== */
|
||||
|
||||
.editor-content a {
|
||||
color: #0049b4 !important;
|
||||
text-decoration: underline !important;
|
||||
transition: color 0.3s ease !important;
|
||||
}
|
||||
|
||||
.editor-content a:hover {
|
||||
color: #003080 !important;
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
인용문 (Blockquote)
|
||||
========================================================================== */
|
||||
|
||||
.editor-content blockquote {
|
||||
border-left: 4px solid #0049b4 !important;
|
||||
padding: 16px 24px !important;
|
||||
margin: 24px 0 !important;
|
||||
background: #F8FAFC !important;
|
||||
font-style: italic !important;
|
||||
color: #64748B !important;
|
||||
border-radius: 0 8px 8px 0 !important;
|
||||
}
|
||||
|
||||
.editor-content blockquote p {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
이미지 (Images)
|
||||
========================================================================== */
|
||||
|
||||
.editor-content img {
|
||||
max-width: 100% !important;
|
||||
height: auto !important;
|
||||
border-radius: 8px !important;
|
||||
margin: 16px 0 !important;
|
||||
display: block !important;
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
코드 (Code)
|
||||
========================================================================== */
|
||||
|
||||
.editor-content code {
|
||||
background: #F8FAFC !important;
|
||||
padding: 2px 6px !important;
|
||||
border-radius: 4px !important;
|
||||
font-family: 'Fira Code', 'Courier New', monospace !important;
|
||||
font-size: 0.9em !important;
|
||||
color: #0049b4 !important;
|
||||
}
|
||||
|
||||
.editor-content pre {
|
||||
background: #F8FAFC !important;
|
||||
border: 1px solid #E2E8F0 !important;
|
||||
border-radius: 8px !important;
|
||||
padding: 16px !important;
|
||||
overflow-x: auto !important;
|
||||
margin: 16px 0 !important;
|
||||
}
|
||||
|
||||
.editor-content pre code {
|
||||
background: transparent !important;
|
||||
padding: 0 !important;
|
||||
color: #1A1A2E !important;
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
구분선 (Horizontal Rule)
|
||||
========================================================================== */
|
||||
|
||||
.editor-content hr {
|
||||
border: none !important;
|
||||
border-top: 1px solid #E2E8F0 !important;
|
||||
margin: 32px 0 !important;
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
텍스트 강조 (Text Emphasis)
|
||||
========================================================================== */
|
||||
|
||||
.editor-content strong,
|
||||
.editor-content b {
|
||||
font-weight: 700 !important;
|
||||
}
|
||||
|
||||
.editor-content em,
|
||||
.editor-content i {
|
||||
font-style: italic !important;
|
||||
}
|
||||
|
||||
.editor-content u {
|
||||
text-decoration: underline !important;
|
||||
}
|
||||
|
||||
.editor-content s,
|
||||
.editor-content strike,
|
||||
.editor-content del {
|
||||
text-decoration: line-through !important;
|
||||
}
|
||||
|
||||
.editor-content mark {
|
||||
background-color: #FEF3C7 !important;
|
||||
padding: 0 2px !important;
|
||||
}
|
||||
|
||||
.editor-content sub {
|
||||
vertical-align: sub !important;
|
||||
font-size: 0.8em !important;
|
||||
}
|
||||
|
||||
.editor-content sup {
|
||||
vertical-align: super !important;
|
||||
font-size: 0.8em !important;
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
반응형 (Responsive)
|
||||
========================================================================== */
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.editor-content {
|
||||
font-size: 14px !important;
|
||||
}
|
||||
|
||||
.editor-content h1 {
|
||||
font-size: 18px !important;
|
||||
}
|
||||
|
||||
.editor-content h2 {
|
||||
font-size: 16px !important;
|
||||
}
|
||||
|
||||
.editor-content h3,
|
||||
.editor-content h4,
|
||||
.editor-content h5,
|
||||
.editor-content h6 {
|
||||
font-size: 14px !important;
|
||||
}
|
||||
|
||||
.editor-content p,
|
||||
.editor-content li {
|
||||
font-size: 14px !important;
|
||||
}
|
||||
|
||||
.editor-content table {
|
||||
font-size: 12px !important;
|
||||
}
|
||||
|
||||
.editor-content th,
|
||||
.editor-content td {
|
||||
padding: 4px 8px !important;
|
||||
}
|
||||
|
||||
.editor-content ul,
|
||||
.editor-content ol {
|
||||
padding-left: 24px !important;
|
||||
}
|
||||
|
||||
.editor-content blockquote {
|
||||
padding: 8px 16px !important;
|
||||
}
|
||||
|
||||
.editor-content pre {
|
||||
padding: 8px !important;
|
||||
}
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
// OpenAPI 에디터 초기 상태 골격(빈 껍데기).
|
||||
// - specToData() 가 deepClone 하여 서버 spec 값으로 덮어쓰는 구조 기준값.
|
||||
// - 실제 데모/샘플 값은 두지 않는다(팝업은 항상 서버 spec 로드 후 렌더).
|
||||
// - docOptions 만 기능 기본값으로 유지(문서 미리보기 옵션).
|
||||
// locked:true = 게이트웨이 자동 주입(회색+자물쇠) / locked:false = 사용자 편집.
|
||||
|
||||
window.SAMPLE_DATA = (function () {
|
||||
function f() { return { value: '', locked: false }; }
|
||||
return {
|
||||
info: {
|
||||
title: f(),
|
||||
version: f(),
|
||||
summary: f(),
|
||||
description: f(),
|
||||
termsOfService: f(),
|
||||
contact: { name: f(), email: f(), url: f() },
|
||||
license: { name: f(), url: f() }
|
||||
},
|
||||
|
||||
tags: [],
|
||||
|
||||
externalDocs: { description: f(), url: f() },
|
||||
|
||||
servers: [],
|
||||
|
||||
serverVariables: [],
|
||||
|
||||
operation: {
|
||||
method: f(),
|
||||
path: f(),
|
||||
operationId: f(),
|
||||
summary: f(),
|
||||
description: f(),
|
||||
tags: { value: [], locked: false },
|
||||
deprecated: { value: false, locked: false }
|
||||
},
|
||||
|
||||
// 파라미터 (Path/Query/Header/Cookie)
|
||||
parameters: [],
|
||||
|
||||
// Request Body 스키마
|
||||
requestBody: {
|
||||
mediaType: 'application/json',
|
||||
required: { value: false, locked: false },
|
||||
schema: []
|
||||
},
|
||||
|
||||
// 응답 스키마 (상태코드 별)
|
||||
responses: {
|
||||
'200': { description: f(), schema: [] }
|
||||
},
|
||||
|
||||
// 보안 스킴
|
||||
securitySchemes: [],
|
||||
globalSecurity: [],
|
||||
|
||||
// 예제
|
||||
examples: {
|
||||
request: {},
|
||||
response: {}
|
||||
},
|
||||
|
||||
// 문서 옵션
|
||||
docOptions: {
|
||||
theme: 'light',
|
||||
lang: 'ko',
|
||||
includeExamples: true,
|
||||
tryItOut: true,
|
||||
defaultExpandDepth: -1,
|
||||
layout: 'BaseLayout',
|
||||
tagFilter: [],
|
||||
sideToc: true
|
||||
}
|
||||
};
|
||||
})();
|
||||
@@ -1,103 +0,0 @@
|
||||
/* DJB OpenAPI Editor POC — Tailwind 보강 커스텀 */
|
||||
|
||||
html, body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Apple SD Gothic Neo",
|
||||
"Malgun Gothic", "Noto Sans KR", system-ui, sans-serif;
|
||||
}
|
||||
|
||||
/* === 잠금 행/카드 좌측 띠 === */
|
||||
.locked-row td:first-child {
|
||||
position: relative;
|
||||
}
|
||||
.locked-row td:first-child::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 6px;
|
||||
bottom: 6px;
|
||||
width: 3px;
|
||||
background: #cbd5e1;
|
||||
border-radius: 2px;
|
||||
}
|
||||
.locked-card {
|
||||
position: relative;
|
||||
background: linear-gradient(to right, #f8fafc 0%, #ffffff 4px) #ffffff;
|
||||
}
|
||||
.locked-card::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0; top: 8px; bottom: 8px;
|
||||
width: 3px;
|
||||
background: #cbd5e1;
|
||||
border-radius: 0 2px 2px 0;
|
||||
}
|
||||
|
||||
/* === 스키마 테이블 — 가독성 보조 === */
|
||||
.schema-table th { white-space: nowrap; }
|
||||
.schema-table tbody tr:hover { background: #fafbfc; }
|
||||
.schema-table input,
|
||||
.schema-table select {
|
||||
font-size: 12.5px;
|
||||
}
|
||||
.schema-table input[type="text"] {
|
||||
padding-top: 4px;
|
||||
padding-bottom: 4px;
|
||||
}
|
||||
|
||||
/* === 스텝퍼 항목 hover === */
|
||||
.step-item:hover { background: #f8fafc; }
|
||||
|
||||
/* === 미리보기 패널: Swagger UI 크기 미세조정 === */
|
||||
#preview-swagger .swagger-ui {
|
||||
font-size: 13px;
|
||||
}
|
||||
#preview-swagger .swagger-ui .info { margin: 18px 0; }
|
||||
#preview-swagger .swagger-ui .info .title { font-size: 22px; }
|
||||
#preview-swagger .swagger-ui .scheme-container { padding: 12px 0; box-shadow: none; }
|
||||
#preview-swagger .swagger-ui .opblock { margin: 0 0 12px 0; }
|
||||
|
||||
/* === 다크 테마 (POC 시뮬레이션 — Swagger 자체 다크는 미지원이라 컨테이너만) === */
|
||||
body.theme-dark #preview-swagger { background: #1e293b; }
|
||||
|
||||
/* === 토스트 애니메이션 === */
|
||||
#toast { transition: opacity 0.2s; }
|
||||
|
||||
/* === Monaco 컨테이너가 hidden 일 때 layout 깨짐 방지 === */
|
||||
#preview-monaco {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* === Stepper 6단 grid 셀 좌우 라인 (마지막 셀 제외) === */
|
||||
#stepper li {
|
||||
position: relative;
|
||||
}
|
||||
#stepper li:not(:last-child)::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: -8px;
|
||||
width: 16px;
|
||||
height: 1px;
|
||||
background: #e2e8f0;
|
||||
}
|
||||
|
||||
/* === 보안 스킴 카드 — 잠금 표시 === */
|
||||
.locked-card {
|
||||
background: #fafbfc;
|
||||
}
|
||||
|
||||
/* === 칩(태그) 미세 조정 === */
|
||||
.tag-chip { transition: background 0.15s; }
|
||||
|
||||
/* === Export menu fade === */
|
||||
#export-menu { animation: fadeIn 0.12s ease-out; }
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; transform: translateY(-4px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
|
||||
/* === 1600px 기준 폼 패널 내부 가로 여유 확보 === */
|
||||
#form-content { max-width: 100%; }
|
||||
|
||||
/* === readonly input 위에서 텍스트 커서 안 보이게 === */
|
||||
input[readonly] { user-select: text; }
|
||||
@@ -54,18 +54,10 @@ function detail(key){
|
||||
$('#systemCode').val(data.systemCode);
|
||||
$('#logTypeText').val(data.logTypeText);
|
||||
$('#remoteAddress').val(data.remoteAddress);
|
||||
$('#logMsg').val(data.message);
|
||||
$('#logSubMsg').val(data.command);
|
||||
$('#parameters').val(data.parameters);
|
||||
|
||||
// 사유(message)는 값이 있을 때만 노출
|
||||
if (data.message) {
|
||||
$('#logMsg').val(data.message);
|
||||
$('#messageRow').show();
|
||||
} else {
|
||||
$('#logMsg').val('');
|
||||
$('#messageRow').hide();
|
||||
}
|
||||
|
||||
},
|
||||
error:function(e){
|
||||
alert(e.responseText);
|
||||
@@ -129,9 +121,6 @@ $(document).ready(function() {
|
||||
<tr>
|
||||
<th><%= localeMessage.getString("auditLogMan.command") %></th><td><input type="text" id="logSubMsg" name="logSubMsg" readonly="readonly"/></td>
|
||||
</tr>
|
||||
<tr id="messageRow" style="display:none;">
|
||||
<th><%= localeMessage.getString("auditLogMan.message") %></th><td><textarea id="logMsg" name="logMsg" style="width:100%;height:80px" readonly="readonly"></textarea></td>
|
||||
</tr>
|
||||
<tr height="100px">
|
||||
<th><%= localeMessage.getString("auditLogMan.parameters") %></th><td><textarea id="parameters" name="parameters" style="width:100%;height:200px" readonly="readonly"></textarea></td>
|
||||
</tr>
|
||||
|
||||
@@ -1,120 +0,0 @@
|
||||
<%@ page language="java" contentType="text/html; charset=utf-8"%>
|
||||
<%@ page import="java.util.List"%>
|
||||
<%@ page import="com.eactive.eai.rms.common.acl.sitemap.ui.SitemapNode"%>
|
||||
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
|
||||
<%!
|
||||
private void printNodeLabel(javax.servlet.jsp.JspWriter out, SitemapNode node) throws java.io.IOException {
|
||||
String url = node.getMenuUrl();
|
||||
boolean hasLink = url != null && !url.trim().isEmpty() && !"NAN".equalsIgnoreCase(url.trim());
|
||||
if (hasLink) {
|
||||
out.print("<a href=\"javascript:void(0);\" class=\"sitemap-name\" onclick=\"goPage('"
|
||||
+ escapeJs(url) + "','" + escapeJs(node.getMenuId()) + "');return false;\">"
|
||||
+ escapeHtml(node.getMenuName()) + "</a>");
|
||||
} else {
|
||||
out.print("<span class=\"sitemap-name\">" + escapeHtml(node.getMenuName()) + "</span>");
|
||||
}
|
||||
}
|
||||
|
||||
private void printSitemapNode(javax.servlet.jsp.JspWriter out, SitemapNode node) throws java.io.IOException {
|
||||
out.print("<li>");
|
||||
printNodeLabel(out, node);
|
||||
|
||||
List<SitemapNode> children = node.getChildren();
|
||||
if (children != null && !children.isEmpty()) {
|
||||
out.print("<ul>");
|
||||
for (SitemapNode child : children) {
|
||||
printSitemapNode(out, child);
|
||||
}
|
||||
out.print("</ul>");
|
||||
}
|
||||
out.print("</li>");
|
||||
}
|
||||
|
||||
private String escapeHtml(String value) {
|
||||
if (value == null) {
|
||||
return "";
|
||||
}
|
||||
return value.replace("&", "&").replace("<", "<").replace(">", ">").replace("\"", """);
|
||||
}
|
||||
|
||||
private String escapeJs(String value) {
|
||||
if (value == null) {
|
||||
return "";
|
||||
}
|
||||
return value.replace("\\", "\\\\").replace("'", "\\'");
|
||||
}
|
||||
%>
|
||||
<%
|
||||
response.setHeader("Pragma", "No-cache");
|
||||
response.setHeader("Cache-Control", "no-cache");
|
||||
response.setHeader("Expires", "0");
|
||||
|
||||
List<SitemapNode> sitemapTree = (List<SitemapNode>) request.getAttribute("sitemapTree");
|
||||
%>
|
||||
<html>
|
||||
<head>
|
||||
<title></title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<jsp:include page="/jsp/common/include/css.jsp"/>
|
||||
<jsp:include page="/jsp/common/include/script.jsp"/>
|
||||
<style>
|
||||
.sitemap-columns { display: flex; flex-wrap: wrap; gap: 40px; }
|
||||
.sitemap-column { min-width: 180px; }
|
||||
.sitemap-category { font-size: 1.1em; font-weight: bold; padding-bottom: 6px; margin-bottom: 8px; border-bottom: 2px solid #ccc; }
|
||||
.sitemap-tree, .sitemap-tree ul { list-style-type: disc; margin: 0; padding-left: 18px; }
|
||||
.sitemap-tree { padding-left: 0; list-style-type: none; }
|
||||
.sitemap-tree li { line-height: 1.8; }
|
||||
a.sitemap-name { text-decoration: none; cursor: pointer; }
|
||||
a.sitemap-name:hover { text-decoration: underline; }
|
||||
</style>
|
||||
<script language="javascript">
|
||||
function goPage(url, menuId) {
|
||||
var serviceType = sessionStorage["serviceType"];
|
||||
|
||||
var pageUrl = url;
|
||||
pageUrl += (pageUrl.indexOf("?") > -1 ? "&" : "?") + "menuId=" + menuId;
|
||||
pageUrl += "&serviceType=" + serviceType;
|
||||
|
||||
parent.leftFrame.location.href = '<c:url value="/leftMenu.do"/>?menuId=' + menuId + '&serviceType=' + serviceType;
|
||||
location.href = pageUrl;
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="right_box">
|
||||
<div class="content_top">
|
||||
<ul class="path">
|
||||
<li><a href="#">${rmsMenuPath}</a></li>
|
||||
</ul>
|
||||
</div><!-- end content_top -->
|
||||
<div class="content_middle">
|
||||
<!-- <div class="title">사이트맵</div> -->
|
||||
<div class="sitemap-columns">
|
||||
<%
|
||||
for (SitemapNode category : sitemapTree) {
|
||||
%>
|
||||
<div class="sitemap-column">
|
||||
<div class="sitemap-category">
|
||||
<%
|
||||
printNodeLabel(out, category);
|
||||
%>
|
||||
</div>
|
||||
<ul class="sitemap-tree">
|
||||
<%
|
||||
List<SitemapNode> children = category.getChildren();
|
||||
if (children != null) {
|
||||
for (SitemapNode child : children) {
|
||||
printSitemapNode(out, child);
|
||||
}
|
||||
}
|
||||
%>
|
||||
</ul>
|
||||
</div>
|
||||
<%
|
||||
}
|
||||
%>
|
||||
</div>
|
||||
</div><!-- end content_middle -->
|
||||
</div><!-- end right_box -->
|
||||
</body>
|
||||
</html>
|
||||
@@ -30,10 +30,6 @@
|
||||
<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/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.searchabledropdown-1.0.8.min.js"/>"></script>
|
||||
<%-- <script language="javascript" src="<c:url value="/js/jquery.searchable-1.1.0.min.js"/>"></script> --%>
|
||||
@@ -531,88 +527,6 @@
|
||||
}
|
||||
}, 200);
|
||||
}
|
||||
|
||||
/**
|
||||
* 사유 입력 모달 - 확인 시 입력한 사유 문자열을 onConfirm(reason)으로 전달
|
||||
* @param {string} message - 안내 메시지
|
||||
* @param {object} options - (title, confirmText, cancelText, placeholder, required, onConfirm, onCancel)
|
||||
* required 기본 true (빈 사유 차단)
|
||||
*/
|
||||
function showReasonPrompt(message, options) {
|
||||
options = options || {};
|
||||
var title = options.title || '사유 입력';
|
||||
var confirmText = options.confirmText || '확인';
|
||||
var cancelText = options.cancelText || '취소';
|
||||
var placeholder = options.placeholder || '사유를 입력하세요.';
|
||||
var required = options.required !== false;
|
||||
var onConfirm = options.onConfirm || function(){};
|
||||
var onCancel = options.onCancel || function(){};
|
||||
|
||||
// 기존 모달 제거
|
||||
$('#commonReasonModal').remove();
|
||||
|
||||
var iconHtml = getAlertIcon('warning');
|
||||
|
||||
var modalHtml =
|
||||
'<div id="commonReasonModal" class="common-alert-overlay">' +
|
||||
'<div class="common-alert-modal alert-warning">' +
|
||||
'<div class="common-alert-header">' +
|
||||
'<span class="common-alert-icon">' + iconHtml + '</span>' +
|
||||
'<span class="common-alert-title">' + title + '</span>' +
|
||||
'</div>' +
|
||||
'<div class="common-alert-body">' +
|
||||
'<p class="common-alert-message">' + message + '</p>' +
|
||||
'<textarea id="commonReasonInput" maxlength="200" placeholder="' + placeholder + '" style="width:100%;height:80px;margin-top:10px;box-sizing:border-box;font-size:13px;padding:8px;border:1px solid #ddd;border-radius:4px;resize:vertical;"></textarea>' +
|
||||
'<p id="commonReasonError" style="display:none;color:#f44336;font-size:12px;margin:6px 0 0;">사유를 입력해 주세요.</p>' +
|
||||
'</div>' +
|
||||
'<div class="common-alert-footer">' +
|
||||
'<button type="button" class="common-confirm-cancel-btn" id="commonReasonCancelBtn">' + cancelText + '</button>' +
|
||||
'<button type="button" class="common-alert-btn" id="commonReasonOkBtn">' + confirmText + '</button>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'</div>';
|
||||
|
||||
$('body').append(modalHtml);
|
||||
|
||||
setTimeout(function() {
|
||||
$('#commonReasonModal').addClass('show');
|
||||
$('#commonReasonInput').focus();
|
||||
}, 10);
|
||||
|
||||
// 확인 버튼
|
||||
$('#commonReasonOkBtn').on('click', function() {
|
||||
var reason = $.trim($('#commonReasonInput').val());
|
||||
if (required && reason === '') {
|
||||
$('#commonReasonError').show();
|
||||
$('#commonReasonInput').focus();
|
||||
return;
|
||||
}
|
||||
closeCommonReason(function() { onConfirm(reason); });
|
||||
});
|
||||
|
||||
// 취소 버튼
|
||||
$('#commonReasonCancelBtn').on('click', function() {
|
||||
closeCommonReason(onCancel);
|
||||
});
|
||||
|
||||
// ESC 키로 취소
|
||||
$(document).on('keydown.commonReason', function(e) {
|
||||
if (e.keyCode === 27) {
|
||||
closeCommonReason(onCancel);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function closeCommonReason(callback) {
|
||||
$('#commonReasonModal').removeClass('show');
|
||||
setTimeout(function() {
|
||||
$('#commonReasonModal').remove();
|
||||
$(document).off('keydown.commonReason');
|
||||
if (typeof callback === 'function') {
|
||||
callback();
|
||||
}
|
||||
}, 200);
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- 공용 Alert 모달 스타일 -->
|
||||
|
||||
@@ -31,19 +31,19 @@ function init(){
|
||||
new makeOptions("CODE","NAME").setObj($("select[name=searchJobInst]")).setNoValueInclude(true).setNoValue("",
|
||||
"<%= localeMessage.getString("combo.all")%>").setData(json.instanceList).rendering();
|
||||
|
||||
putSelectFromParam();
|
||||
|
||||
initGrid();
|
||||
if(typeof callback === 'function') {
|
||||
callback(url,key);
|
||||
}
|
||||
},
|
||||
error:function(e){
|
||||
alert(e.responseText);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function initGrid() {
|
||||
$(document).ready(function() {
|
||||
init();
|
||||
var gridPostData = getSearchForJqgrid("cmd","LIST"); //jqgrid에서는 object 로
|
||||
|
||||
console.log(gridPostData);
|
||||
$('#grid').jqGrid({
|
||||
datatype:"json",
|
||||
mtype: 'POST',
|
||||
@@ -125,13 +125,9 @@ function initGrid() {
|
||||
}
|
||||
});
|
||||
|
||||
resizeJqGridWidth('grid','content_middle','1000');
|
||||
}
|
||||
|
||||
|
||||
$(document).ready(function() {
|
||||
init();
|
||||
|
||||
resizeJqGridWidth('grid','content_middle','1000');
|
||||
|
||||
$("#btn_search").click(function(){
|
||||
var postData = getSearchForJqgrid("cmd","LIST");
|
||||
$("#grid").setGridParam({ postData: postData ,page:"1" }).trigger("reloadGrid");
|
||||
|
||||
@@ -465,21 +465,21 @@ $(document).ready(function() {
|
||||
<table class="table_row" cellspacing="0">
|
||||
<tr>
|
||||
<th style="width:180px;">Default Scheduler</th>
|
||||
<td style="width:150px">On Memory: <span id="deft_onMemory"></span></td>
|
||||
<td >Previous Fired: <span id="deft_previous"></span></td>
|
||||
<td >Next Fire Time: <span id="deft_next"></span></td>
|
||||
<td style="width: 250px;">isConcurrentExectionDisallowed: <span id="deft_concurruntExecution"></span></td>
|
||||
<td style="width: 120px;">PersistJobData: <span id="deft_persistJobData"></span></td>
|
||||
<td style="width: 120px;">Durable: <span id="deft_durable"></span></td>
|
||||
<td>On Memory: <span id="deft_onMemory"></span></td>
|
||||
<td style="width: 260px;">Previous Fired: <span id="deft_previous"></span></td>
|
||||
<td style="width: 260px;">Next Fire Time: <span id="deft_next"></span></td>
|
||||
<td style="width: 220px;">isConcurrentExectionDisallowed: <span id="deft_concurruntExecution"></span></td>
|
||||
<td style="width: 200px;">PersistJobData: <span id="deft_persistJobData"></span></td>
|
||||
<td style="width: 200px;">Durable: <span id="deft_durable"></span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th style="width:180px;">Clustered Scheduler</th>
|
||||
<td style="width:150px">On Memory: <span id="clus_onMemory"></span></td>
|
||||
<td>Previous Fired: <span id="clus_previous"></span></td>
|
||||
<td>Next Fire Time: <span id="clus_next"></span></td>
|
||||
<td style="width: 250px;">isConcurrentExectionDisallowed: <span id="clus_concurruntExecution"></span></td>
|
||||
<td style="width: 120px;">PersistJobData: <span id="clus_persistJobData"></span></td>
|
||||
<td style="width: 120px;">Durable: <span id="clus_durable"></span></td>
|
||||
<td>On Memory: <span id="clus_onMemory"></span></td>
|
||||
<td style="width: 260px;">Previous Fired: <span id="clus_previous"></span></td>
|
||||
<td style="width: 260px;">Next Fire Time: <span id="clus_next"></span></td>
|
||||
<td style="width: 220px;">isConcurrentExectionDisallowed: <span id="clus_concurruntExecution"></span></td>
|
||||
<td style="width: 200px;">PersistJobData: <span id="clus_persistJobData"></span></td>
|
||||
<td style="width: 200px;">Durable: <span id="clus_durable"></span></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
@@ -91,10 +91,7 @@
|
||||
data: $('#loginForm').serialize(),
|
||||
dataType: 'json',
|
||||
success: function(response) {
|
||||
if (response.changePassword) {
|
||||
showChangeInitPassword(response);
|
||||
}
|
||||
else if (response.smsAuthRequired) {
|
||||
if (response.smsAuthRequired) {
|
||||
// SMS 인증 필요
|
||||
showSmsAuthModal(response);
|
||||
} else if (response.success) {
|
||||
@@ -111,13 +108,6 @@
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function showChangeInitPassword(data) {
|
||||
$('input[name="resetUserId"]').val($('input[name="userId"]').val());
|
||||
$('input[name="resetPassword"]').val($('input[name="password"]').val());
|
||||
$('#changeInitPassword').hide();
|
||||
$('#pwdChgModal').modal('show');
|
||||
}
|
||||
|
||||
// SMS 인증 모달 표시
|
||||
function showSmsAuthModal(data) {
|
||||
@@ -228,39 +218,44 @@
|
||||
clearInterval(resendTimer);
|
||||
$('#smsAuthModal').modal('hide');
|
||||
}
|
||||
|
||||
function checkPwd(){
|
||||
function changePwd(){
|
||||
if ($("input[name=resetUserId]").val() == null || $("input[name=resetUserId]").val().length == 0 ){
|
||||
alert("<%= localeMessage.getString("login.checkid") %>");
|
||||
$("input[name=resetUserId]").trigger("focus");
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
if ($("input[name=resetPassword]").val() == null || $("input[name=resetPassword]").val().trim().length == 0 ){
|
||||
alert("<%= localeMessage.getString("login.checkpwd1") %>");
|
||||
$("input[name=resetPassword]").trigger("focus");
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
if ($("input[name=changePassword]").val() == null || $("input[name=changePassword]").val().trim().length == 0 ){
|
||||
alert("<%= localeMessage.getString("login.checkpwd2") %>");
|
||||
$("input[name=changePassword]").trigger("focus");
|
||||
return false;
|
||||
return ;
|
||||
}
|
||||
if ($("input[name=confirmPassword]").val() == null || $("input[name=confirmPassword]").val().trim().length == 0 ){
|
||||
alert("<%= localeMessage.getString("login.checkpwd3") %>");
|
||||
return false;
|
||||
return ;
|
||||
}
|
||||
if ($("input[name=changePassword]").val() != $("input[name=confirmPassword]").val()){
|
||||
if ($("input[name=confirmPassword]").val() != $("input[name=confirmPassword]").val()){
|
||||
alert("<%= localeMessage.getString("login.checkpwd4") %>");
|
||||
$("input[name=confirmPassword]").trigger("focus");
|
||||
return false;
|
||||
return ;
|
||||
}
|
||||
if ($("input[name=resetUserId]").val() == $("input[name=changePassword]").val()){
|
||||
alert("<%= localeMessage.getString("login.checkpwd5") %>");
|
||||
$("input[name=changePassword]").trigger("focus");
|
||||
return false;
|
||||
return ;
|
||||
}
|
||||
|
||||
return true;
|
||||
//if (idCheck.checked){
|
||||
// setCookie("key",id);
|
||||
//}
|
||||
<%-- $("form").attr("action","<c:url value="/changePassword.do"/>");
|
||||
//$("form").action = "<%=request.getContextPath()%>/changePassword.do";
|
||||
--%>
|
||||
$("form").submit();
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
@@ -276,22 +271,14 @@
|
||||
});
|
||||
$("input[name=changePassword]").keydown(function(event){
|
||||
if ( event.which == 13 ) {
|
||||
$('#modalLoginForm').submit();
|
||||
changePwd();
|
||||
}
|
||||
});
|
||||
$("input[name=confirmPassword]").keydown(function(event){
|
||||
if ( event.which == 13 ) {
|
||||
$('#modalLoginForm').submit();
|
||||
changePwd();
|
||||
}
|
||||
});
|
||||
// Password Change 링크로 직접 열 때만 초기화 (JS에서 modal('show') 호출 시 relatedTarget 없음)
|
||||
$('#pwdChgModal').on('show.bs.modal', function(event) {
|
||||
if (event.relatedTarget) {
|
||||
$('#modalLoginForm')[0].reset();
|
||||
$('#changeInitPassword').show();
|
||||
}
|
||||
});
|
||||
|
||||
$("select[name=serviceType]").change(function(){
|
||||
fncPreMain();
|
||||
});
|
||||
@@ -299,7 +286,9 @@
|
||||
$("#btn_login").click(function(){
|
||||
fncPreMain();
|
||||
});
|
||||
|
||||
$("#changePwd").click(function(){
|
||||
changePwd();
|
||||
});
|
||||
$("#btn_sso_login").click(function(){
|
||||
fncSsoLogin();
|
||||
});
|
||||
@@ -364,10 +353,10 @@
|
||||
</div>
|
||||
<!-- SSO 로그인 버튼 (비상모드 시 숨김) -->
|
||||
<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"
|
||||
value="SSO Login">
|
||||
</div> -->
|
||||
</div>
|
||||
</c:if>
|
||||
<p style="color:red;"><%=resultMsg %></p>
|
||||
</form>
|
||||
@@ -377,7 +366,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 비밀번호 변경 모달 -->
|
||||
|
||||
<div class="modal fade" id="pwdChgModal" tabindex="-1" role="dialog" aria-labelledby="pwdChgModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
@@ -387,15 +376,13 @@
|
||||
<span aria-hidden="true">X</span>
|
||||
</button>
|
||||
</div>
|
||||
<form id="modalLoginForm" action="<c:url value="/changePassword.do"/>" method="post" onsubmit="return checkPwd()" novalidate>
|
||||
<form name="form" id="modalLoginForm" action="<c:url value="/changePassword.do"/>" method="post">
|
||||
<div class="modal-body">
|
||||
<div id="changeInitPassword">
|
||||
<div class="form-group d-flex">
|
||||
<input type="text" name="resetUserId" class="form-control rounded-left" placeholder="User Id" required>
|
||||
</div>
|
||||
<div class="form-group d-flex">
|
||||
<input type="password" name="resetPassword" class="form-control rounded-left" placeholder="<%= localeMessage.getString("login.placeholderCurrentPassword") %>" autocomplete="off" required>
|
||||
</div>
|
||||
<div class="form-group d-flex">
|
||||
<input type="text" name="resetUserId" class="form-control rounded-left" placeholder="User Id" required>
|
||||
</div>
|
||||
<div class="form-group d-flex">
|
||||
<input type="password" name="resetPassword" class="form-control rounded-left" placeholder="<%= localeMessage.getString("login.placeholderCurrentPassword") %>" autocomplete="off" required>
|
||||
</div>
|
||||
<p><%= localeMessage.getString("login.toChangePasswordMessage") %></p>
|
||||
<div class="form-group d-flex">
|
||||
@@ -404,12 +391,9 @@
|
||||
<div class="form-group d-flex">
|
||||
<input type="password" name="confirmPassword" class="form-control rounded-left" placeholder="<%= localeMessage.getString("login.placeholderConfirmationPassword") %>" autocomplete="off" required>
|
||||
</div>
|
||||
<span style="font-size:0.8em; color:red">
|
||||
※ 7글자 이상 & 영문/숫자/특수문자 2종류 이상
|
||||
</span>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="submit" id="changePwd" class="form-control btn btn-primary rounded submit px-3">변경</button>
|
||||
<input type="button" id ="changePwd" class="form-control btn btn-primary rounded submit px-3" value="변경">
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@@ -52,13 +52,6 @@
|
||||
var urlParam = window.document.location.search;
|
||||
var mod = "";
|
||||
var strServiceType = "<%=request.getParameter("serviceType")%>";
|
||||
function getServiceType() {
|
||||
var serviceType = sessionStorage.getItem("serviceType");
|
||||
if (!serviceType || serviceType == "undefined" || serviceType == "null") {
|
||||
serviceType = localStorage.getItem("serviceType") || "";
|
||||
}
|
||||
return serviceType;
|
||||
}
|
||||
if(urlParam != ""){
|
||||
if(urlParam.indexOf("mod=") > -1){
|
||||
mod = urlParam.substring((urlParam.indexOf("mod=") + 4), urlParam.lastIndexOf("&"));
|
||||
@@ -74,18 +67,15 @@
|
||||
}
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
$(document).ready(function() {
|
||||
var url = '<%=topPage%>';
|
||||
var serviceType = getServiceType();
|
||||
if (serviceType) {
|
||||
if (url.indexOf("?")>=0){
|
||||
url = url + "&serviceType="+ serviceType;
|
||||
}else{
|
||||
url = url + "?serviceType="+ serviceType;
|
||||
}
|
||||
if (url.indexOf("?")>=0){
|
||||
url = url + "&serviceType="+ sessionStorage["serviceType"];
|
||||
}else{
|
||||
url = url + "?serviceType="+ sessionStorage["serviceType"];
|
||||
}
|
||||
|
||||
$("#topFrame").attr('src',url);
|
||||
|
||||
$("#topFrame").attr('src',url);
|
||||
|
||||
$(".topMenu").load(function(){ // iframe이 모두 load된후 제어
|
||||
$(".topMenu").contents().find('.gnb').on("mouseenter", function(e){
|
||||
@@ -109,93 +99,6 @@
|
||||
<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="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>
|
||||
|
||||
|
||||
@@ -9,8 +9,6 @@
|
||||
<%@page import="com.eactive.eai.rms.common.util.StringUtils" %>
|
||||
<%@page import="com.eactive.eai.rms.common.datasource.DataSourceType" %>
|
||||
<%@page import="com.eactive.eai.rms.common.datasource.DataSourceTypeManager" %>
|
||||
<%@page import="com.eactive.eai.common.util.ApplicationContextProvider" %>
|
||||
<%@page import="com.eactive.eai.rms.data.entity.man.role.RoleMenuAuthService" %>
|
||||
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
|
||||
<%@ taglib uri="http://www.springframework.org/tags" prefix="spring" %>
|
||||
<%@ include file="/jsp/common/include/localemessage.jsp" %>
|
||||
@@ -18,12 +16,6 @@
|
||||
<%
|
||||
String serviceKey = SessionManager.getServiceTypeKey(request);
|
||||
String serviceText = DataSourceTypeManager.getDataSourceType(serviceKey).getText();
|
||||
|
||||
final String SITEMAP_MENU_ID = "0510013";
|
||||
RoleMenuAuthService roleMenuAuthService = ApplicationContextProvider.getContext().getBean(RoleMenuAuthService.class);
|
||||
String sitemapMenuAuth = roleMenuAuthService.getRoleList(SessionManager.getUserId(request), SITEMAP_MENU_ID, serviceKey);
|
||||
boolean showSitemapLink = "R".equals(sitemapMenuAuth) || "W".equals(sitemapMenuAuth);
|
||||
|
||||
String roleScreenId = (String) request.getAttribute("roleScreenId");
|
||||
String mainPage = (String) request.getAttribute("mainPage");
|
||||
String menuId = (String) request.getAttribute("menuId");
|
||||
@@ -248,12 +240,6 @@
|
||||
|
||||
<link rel="stylesheet" type="text/css" media="screen" href="<c:url value="/css/web_ui.css"/>"/>
|
||||
<link rel="stylesheet" type="text/css" media="screen" href="<c:url value="/css/theme_${themeColor}.css"/>"/>
|
||||
<style>
|
||||
.topmenu_box .sitemap-link { display: inline-block; width: 80px; height: 80px; line-height: 80px; box-sizing: border-box; font-size: 14px; color: #666; text-align: center; opacity: 0.7; transition: all 0.3s; margin-top: 0px; }
|
||||
.topmenu_box .sitemap-link:hover { opacity: 1; }
|
||||
.topmenu_box .logout-link { line-height: 80px; margin-top:0; }
|
||||
.topmenu_box > ul li:last-child > a:before { display: none; }
|
||||
</style>
|
||||
<script language="javascript" src="<c:url value="/js/jquery-1.12.1.min.js"/>"></script>
|
||||
<script language="javascript" src="<c:url value="/js/prefixfree.min.js"/>"></script>
|
||||
<script language="javascript" src="<c:url value="/js/jquery.cookie.js"/>"></script>
|
||||
@@ -317,11 +303,11 @@
|
||||
}
|
||||
|
||||
//왼쪽메뉴, 메인 페이지 이동
|
||||
function goPage2(page1, page2, page2_id) {
|
||||
function goPage2(page1, page2, page2_id) {
|
||||
parent.leftFrame.location.href = goNavUrl(page1);
|
||||
parent.mainFrame.location.href = goNavUrl(page2);
|
||||
}
|
||||
|
||||
|
||||
var sessionAjax = createAjaxRequest();
|
||||
|
||||
function getAjaxData() {
|
||||
@@ -543,7 +529,6 @@
|
||||
console.log("selectLocaleValue : " + selectLocaleValue);
|
||||
parent.changeLocale($(this).val());
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
$(function() {
|
||||
@@ -566,28 +551,21 @@
|
||||
</h1><!-- /monitoring/images/top_logo.png -->
|
||||
<div class="topmenu_box">
|
||||
<ul>
|
||||
<li style="width:240px;" id="newDashboardShow">
|
||||
<li style="width:240px;" id="newDashboardShow">
|
||||
<a style="width:240px;"
|
||||
href="#"><span><%=SessionManager.getUserName(request) %>(<%=SessionManager.getUserId(request) %>-<%=System.getProperty("inst.Name") %>)</span>
|
||||
href="#"><span><%=SessionManager.getUserName(request) %>(<%=SessionManager.getUserId(request) %>)</span>
|
||||
<span onClick="javascript:openColorPopup();"><%=localeMessage.getString("screen.customer") %></span>
|
||||
<% if (!"".equals(LastLoginYms.trim())) { %>
|
||||
<span style="display:block;font-size:10px;"><%=localeMessage.getString("screen.lastLogin") %> : <%=LastLoginYms%> [ <%=LastLoginIp%> ]</span>
|
||||
<% } %>
|
||||
</a></li>
|
||||
<li style="width:100px; line-height:80px; text-align:center;">
|
||||
<select id="selectLocale" name="locale" style="width:60px; padding: 2px; text-align:center;">
|
||||
<li>
|
||||
<select id="selectLocale" name="locale" style="margin-top: 20px; width:80px">
|
||||
<option value="en">English</option>
|
||||
<option value="ko">한글</option>
|
||||
</select>
|
||||
</li>
|
||||
<% if (showSitemapLink) { %>
|
||||
<li>
|
||||
<a href="<c:url value="/common/acl/sitemap/sitemapMan.view"><c:param name="menuId" value="<%=SITEMAP_MENU_ID%>"/><c:param name="serviceType" value="<%=serviceKey%>"/></c:url>" target="mainFrame" class="sitemap-link">
|
||||
<%= localeMessage.getString("screen.sitemap") %>
|
||||
</a>
|
||||
</li>
|
||||
<% } %>
|
||||
<li onclick="logout()"><a href="#" class="logout-link"><img src="<c:url value="/img/icon_logout.png"/>"
|
||||
<li onclick="logout()"><a href="#" class=""><img src="<c:url value="/img/icon_logout.png"/>"
|
||||
alt=""/><%= localeMessage.getString("screen.logout") %>
|
||||
</a></li>
|
||||
</ul>
|
||||
|
||||
@@ -36,14 +36,6 @@
|
||||
// save the returnValue in options so that it is available in the callback function
|
||||
optns.returnValue = $frame[0].contentWindow.window.returnValue;
|
||||
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){
|
||||
$('iframe[name=topFrame]',window.parent.document).css('display', 'block');
|
||||
|
||||
@@ -1,10 +1,5 @@
|
||||
/**
|
||||
* 문자열 마스킹 처리를 위한 유틸리티
|
||||
*
|
||||
* 보안아키텍처 표준 마스킹 규칙(Java MaskingUtils)과 동일한 결과를 내도록 구현한다.
|
||||
* 이름 2자: 뒤 1자리(가나→가*) / 3자 이상: 앞뒤 제외 가운데 전체(가나다라→가**라)
|
||||
* 이메일 아이디 앞2·뒤2 마스킹, 4자 이하 전체(test→****, test123→**st1**)
|
||||
* 전화 2·3번째 세그먼트 각각 뒤 2자리(010-1234-1234→010-12**-12**)
|
||||
*/
|
||||
const StringMaskingUtil = {
|
||||
|
||||
@@ -13,51 +8,41 @@ const StringMaskingUtil = {
|
||||
return str != null && str !== '';
|
||||
},
|
||||
|
||||
// '*' 반복 생성
|
||||
_stars: function(count) {
|
||||
return count > 0 ? '*'.repeat(count) : '';
|
||||
},
|
||||
|
||||
// 세그먼트 뒤 count 자리 마스킹
|
||||
_maskSegmentTail: function(segment, count) {
|
||||
if (segment.length <= count) {
|
||||
return this._stars(segment.length);
|
||||
}
|
||||
return segment.substring(0, segment.length - count) + this._stars(count);
|
||||
},
|
||||
|
||||
// 이름 마스킹 처리 (2자: 뒤 1자리, 3자 이상: 앞뒤 제외 가운데 전체)
|
||||
// 이름 마스킹 처리
|
||||
maskName: function(name) {
|
||||
if (!this.isValidString(name)) {
|
||||
return name;
|
||||
}
|
||||
const len = name.length;
|
||||
if (len === 1) {
|
||||
return name;
|
||||
}
|
||||
if (len === 2) {
|
||||
return name.charAt(0) + '*';
|
||||
}
|
||||
return name.charAt(0) + this._stars(len - 2) + name.charAt(len - 1);
|
||||
return name.length > 1 ?
|
||||
name.substring(0, name.length - 1) + "*" :
|
||||
name;
|
||||
},
|
||||
|
||||
// 이메일 마스킹 처리 (아이디 앞2·뒤2, 4자 이하 전체)
|
||||
// 이메일 마스킹 처리
|
||||
maskEmail: function(email) {
|
||||
if (!this.isValidString(email) || !email.includes('@')) {
|
||||
return email;
|
||||
}
|
||||
|
||||
const atIdx = email.indexOf('@');
|
||||
const local = email.substring(0, atIdx);
|
||||
const domain = email.substring(atIdx);
|
||||
|
||||
if (local.length <= 4) {
|
||||
return this._stars(local.length) + domain;
|
||||
const parts = email.split('@');
|
||||
if (parts.length !== 2) {
|
||||
return email;
|
||||
}
|
||||
return this._stars(2) + local.substring(2, local.length - 2) + this._stars(2) + domain;
|
||||
|
||||
const localPart = parts[0];
|
||||
let maskedLocal;
|
||||
|
||||
if (localPart.length <= 3) {
|
||||
maskedLocal = localPart.substring(0, 1) +
|
||||
'*'.repeat(localPart.length - 1);
|
||||
} else {
|
||||
maskedLocal = localPart.substring(0, localPart.length - 3) + '***';
|
||||
}
|
||||
|
||||
return maskedLocal + '@' + parts[1];
|
||||
},
|
||||
|
||||
// 휴대폰/전화 번호 마스킹 처리 (2·3번째 세그먼트 각각 뒤 2자리)
|
||||
// 휴대폰 번호 마스킹 처리
|
||||
maskMobileNumber: function(number) {
|
||||
if (!this.isValidString(number)) {
|
||||
return number;
|
||||
@@ -65,10 +50,8 @@ const StringMaskingUtil = {
|
||||
|
||||
const parts = number.split('-');
|
||||
if (parts.length === 3) {
|
||||
return parts[0] + '-' +
|
||||
this._maskSegmentTail(parts[1], 2) + '-' +
|
||||
this._maskSegmentTail(parts[2], 2);
|
||||
return parts[0] + '-****-' + parts[2];
|
||||
}
|
||||
return number;
|
||||
}
|
||||
};
|
||||
};
|
||||
@@ -19,6 +19,7 @@
|
||||
var url = '<c:url value="/onl/admin/authserver/scopeMan.json" />';
|
||||
var url_view = '<c:url value="/onl/admin/authserver/scopeMan.view" />';
|
||||
var api_url_view = '<c:url value="/onl/apim/apigroup/apiGroupMan.view" />';
|
||||
var mapping_url = '<c:url value="/onl/admin/authserver/apiScopeMan.json" />';
|
||||
|
||||
var isDetail = false;
|
||||
function isValid(){
|
||||
@@ -64,9 +65,9 @@ function mappingInfo(key){
|
||||
|
||||
$.ajax({
|
||||
type : "POST",
|
||||
url: url,
|
||||
url: mapping_url,
|
||||
dataType: "json",
|
||||
data: {cmd: 'API_LIST', searchScopeId : key},
|
||||
data: {cmd: 'LIST', searchScopeId : key},
|
||||
success: function(json){
|
||||
var mappingData = json.rows;
|
||||
|
||||
@@ -130,6 +131,45 @@ function init(key, callback){
|
||||
}
|
||||
}
|
||||
|
||||
function saveApiGridData(scopeId, returnUrl) {
|
||||
var postData = [];
|
||||
var rows = $("#selectedApiGrid").jqGrid('getRowData');
|
||||
|
||||
$.each(rows, function(index, item) {
|
||||
item.scopeId = scopeId;
|
||||
});
|
||||
|
||||
postData.push({
|
||||
name: "apiList",
|
||||
value: JSON.stringify(rows)
|
||||
});
|
||||
|
||||
postData.push({ name: "cmd" , value:"INSERT_APILIST" });
|
||||
postData.push({ name: "scopeId" , value:scopeId });
|
||||
|
||||
$.ajax({
|
||||
type : "POST",
|
||||
url:mapping_url,
|
||||
data: postData,
|
||||
beforeSend: function() {
|
||||
$("[id^='btn_']").prop("disabled", true);
|
||||
$("#btn_modify").text("처리중 . . . . .");
|
||||
},
|
||||
success:function(args){
|
||||
alert("저장 되었습니다.");
|
||||
|
||||
goNav(returnUrl);//LIST로 이동
|
||||
},
|
||||
error:function(e){
|
||||
alert(e.responseText);
|
||||
},
|
||||
complete: function() {
|
||||
$("[id^='btn_']").prop("disabled", false);
|
||||
$("#btn_modify").text("수정");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
var returnUrl = getReturnUrlForReturn();
|
||||
var key ="${param.scopeId}";
|
||||
@@ -142,39 +182,28 @@ $(document).ready(function() {
|
||||
if (!isValid()){
|
||||
return;
|
||||
}
|
||||
var scopeId = $('input[name=scopeId]').val();
|
||||
var postData = $('#ajaxForm').serializeArray();
|
||||
|
||||
var rows = $("#selectedApiGrid").jqGrid('getRowData');
|
||||
$.each(rows, function(index, item) {
|
||||
item.scopeId = scopeId;
|
||||
});
|
||||
postData.push({ name: "apiList" , value: JSON.stringify(rows) });
|
||||
|
||||
if (isDetail){
|
||||
postData.push({ name: "cmd" , value:"UPDATE"});
|
||||
}else{
|
||||
postData.push({ name: "cmd" , value:"INSERT"});
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
type : "POST",
|
||||
url:url,
|
||||
data:postData,
|
||||
beforeSend: function() {
|
||||
$("[id^='btn_']").prop("disabled", true);
|
||||
$("#btn_modify").text("처리중 . . . . .");
|
||||
},
|
||||
success:function(args){
|
||||
alert("저장 되었습니다.");
|
||||
goNav(returnUrl);//LIST로 이동
|
||||
alert("SCOPE 정보가 저장 되었습니다. 이어서 API 리스트를 저장합니다.");
|
||||
|
||||
if(!isDetail) { // INSERT 일 경우 ScopeId를 업데이트
|
||||
key = $('input[name=scopeId]').val();
|
||||
}
|
||||
saveApiGridData(key, returnUrl); // API LIST 저장.
|
||||
|
||||
|
||||
},
|
||||
error:function(e){
|
||||
alert(e.responseText);
|
||||
},
|
||||
complete: function() {
|
||||
$("[id^='btn_']").prop("disabled", false);
|
||||
$("#btn_modify").text("수정");
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -250,29 +250,6 @@ var url_view = '<c:url value="/onl/admin/common/propertyMan.view" />';
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
$("#btn_pop_encrypt").click(function() {
|
||||
var prpty2Val = $("input[name=prpty2Val]").val();
|
||||
if (prpty2Val == '') {
|
||||
alert("프라퍼티 값을 입력하세요");
|
||||
return;
|
||||
}
|
||||
var postData = {
|
||||
cmd : "ENCRYPT",
|
||||
prpty2Val : prpty2Val,
|
||||
};
|
||||
$.ajax({
|
||||
type : "POST",
|
||||
url : url,
|
||||
data : postData,
|
||||
success : function(args) {
|
||||
$("input[name=prpty2Val]").val(args);
|
||||
},
|
||||
error : function(e) {
|
||||
alert(e.responseText);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
buttonControl(key);
|
||||
titleControl(key);
|
||||
@@ -312,17 +289,12 @@ var url_view = '<c:url value="/onl/admin/common/propertyMan.view" />';
|
||||
<table class="table_row" cellspacing="0">
|
||||
<tr>
|
||||
<th style="width:20%;"><%= localeMessage.getString("propertyDetail.propertyKey") %></th>
|
||||
<td>
|
||||
<input type="text" name="prptyName" style="width:calc(100% - 85px);" /> <!-- <img src="<c:url value="/img/btn_pop_input.png"/>" id="btn_pop_input" /></td> -->
|
||||
<button type="button" class="cssbtn smallBtn2" id="btn_pop_input" style="vertical-align:middle; font-weight:bold;"><i class="material-icons">input</i><%= localeMessage.getString("button.input") %></button>
|
||||
</td>
|
||||
<td><input type="text" name="prptyName" style="width:calc(100% - 85px);" /> <!-- <img src="<c:url value="/img/btn_pop_input.png"/>" id="btn_pop_input" /></td> -->
|
||||
<button type="button" class="cssbtn smallBtn2" id="btn_pop_input" style="vertical-align:middle; font-weight:bold;"><i class="material-icons">input</i><%= localeMessage.getString("button.input") %></button>
|
||||
</tr>
|
||||
<tr>
|
||||
<th style="width:20%;"><%= localeMessage.getString("propertyDetail.propertyValue") %></th>
|
||||
<td>
|
||||
<input type="text" name="prpty2Val" style="width:calc(100% - 85px);"/>
|
||||
<button type="button" class="cssbtn smallBtn2" id="btn_pop_encrypt" style="vertical-align:middle; font-weight:bold;"><i class="material-icons">lock</i><%= localeMessage.getString("button.encode") %></button>
|
||||
</td>
|
||||
<th><%= localeMessage.getString("propertyDetail.propertyValue") %></th>
|
||||
<td><input type="text" name="prpty2Val"/></td>
|
||||
</tr>
|
||||
</table>
|
||||
<!-- grid -->
|
||||
|
||||
@@ -16,486 +16,173 @@
|
||||
<jsp:include page="/jsp/common/include/css.jsp"/>
|
||||
<jsp:include page="/jsp/common/include/script.jsp"/>
|
||||
|
||||
<!-- 페이지 커스텀 스타일 (fragment) -->
|
||||
<jsp:include page="fragments/inflowGroupDetailStyle.jsp"/>
|
||||
|
||||
<script language="javascript">
|
||||
<script language="javascript" >
|
||||
var url ='<c:url value="/onl/admin/inflow/inflowAdapterControlMan.json" />';
|
||||
var url_view ='<c:url value="/onl/admin/inflow/inflowAdapterControlMan.view" />';
|
||||
var isDetail = false;
|
||||
|
||||
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({
|
||||
type: "POST",
|
||||
url: url,
|
||||
dataType: "json",
|
||||
data: {cmd: 'LIST_INIT_COMBO'},
|
||||
success: function(json) {
|
||||
function init(url,key,callback){
|
||||
$.ajax({
|
||||
type : "POST",
|
||||
url:url,
|
||||
dataType:"json",
|
||||
data:{cmd: 'LIST_INIT_COMBO'},
|
||||
success:function(json){
|
||||
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();
|
||||
updateThresholdLabel();
|
||||
|
||||
if (typeof callback === 'function') {
|
||||
callback(url, key);
|
||||
|
||||
if(typeof callback === 'function') {
|
||||
callback(url,key);
|
||||
}
|
||||
},
|
||||
error: function(e) {
|
||||
error:function(e){
|
||||
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) {
|
||||
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() {
|
||||
$("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 tag = $(this).prop("tagName").toLowerCase();
|
||||
$(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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
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 key = "${param.name}";
|
||||
if (key != "" && key != "null") {
|
||||
var key ="${param.name}";
|
||||
if (key != "" && key !="null"){
|
||||
isDetail = true;
|
||||
}
|
||||
init(url, key, detail);
|
||||
|
||||
$("select[name=thresholdTimeUnit]").on('change', function() {
|
||||
updateThresholdLabel();
|
||||
});
|
||||
|
||||
$("#btn_modify").click(function() {
|
||||
$(this).blur();
|
||||
var confirmMsg = isDetail ? '변경사항을 저장하시겠습니까?' : '새 어댑터를 등록하시겠습니까?';
|
||||
showConfirm(confirmMsg, {
|
||||
title: '저장 확인',
|
||||
confirmText: '저장',
|
||||
onConfirm: function() {
|
||||
var postData = $('#ajaxForm').serializeArray();
|
||||
if (isDetail) {
|
||||
postData.push({name: "cmd", value: "UPDATE"});
|
||||
} else {
|
||||
postData.push({name: "cmd", value: "INSERT"});
|
||||
}
|
||||
var isInsert = !isDetail;
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
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'});
|
||||
}
|
||||
});
|
||||
}
|
||||
init(url,key,detail);
|
||||
|
||||
|
||||
$("#btn_modify").click(function(){
|
||||
var postData = $('#ajaxForm').serializeArray();
|
||||
if (isDetail){
|
||||
postData.push({ name: "cmd" , value:"UPDATE"});
|
||||
}else{
|
||||
postData.push({ name: "cmd" , value:"INSERT"});
|
||||
}
|
||||
$.ajax({
|
||||
type : "POST",
|
||||
url:url,
|
||||
data:postData,
|
||||
success:function(args){
|
||||
alert("<%= localeMessage.getString("common.saveMsg") %>");
|
||||
goNav(returnUrl);//LIST로 이동
|
||||
},
|
||||
error:function(e){
|
||||
alert(e.responseText);
|
||||
}
|
||||
});
|
||||
});
|
||||
$("#btn_delete").click(function(){
|
||||
|
||||
if ( confirm( '<%=localeMessage.getString("common.confirmMsg")%>' ) != true ) return;
|
||||
|
||||
var postData = $('#ajaxForm').serializeArray();
|
||||
postData.push({ name: "cmd" , value:"DELETE"});
|
||||
$.ajax({
|
||||
type : "POST",
|
||||
url:url,
|
||||
data:postData,
|
||||
success:function(args){
|
||||
alert("<%= localeMessage.getString("common.deleteMsg") %>");
|
||||
goNav(returnUrl);//LIST로 이동
|
||||
|
||||
$("#btn_delete").click(function() {
|
||||
$(this).blur();
|
||||
var adapterName = $('input[name=desc]').val() || $('input[name=name]').val();
|
||||
showConfirm('<strong>[' + adapterName + ']</strong> 어댑터를 삭제하시겠습니까?', {
|
||||
type: 'error',
|
||||
title: '삭제 확인',
|
||||
confirmText: '삭제',
|
||||
onConfirm: function() {
|
||||
var postData = $('#ajaxForm').serializeArray();
|
||||
postData.push({name: "cmd", value: "DELETE"});
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: url,
|
||||
data: postData,
|
||||
success: function(args) {
|
||||
showAlert("<%= localeMessage.getString("common.deleteMsg") %>", {
|
||||
type: 'success',
|
||||
title: '삭제 완료',
|
||||
onClose: function() { goNav(returnUrl); }
|
||||
});
|
||||
},
|
||||
error: function(e) {
|
||||
showAlert(e.responseText, {type: 'error'});
|
||||
}
|
||||
});
|
||||
},
|
||||
error:function(e){
|
||||
alert(e.responseText);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$("#btn_previous").click(function() {
|
||||
goNav(returnUrl);
|
||||
});
|
||||
$("#btn_previous").click(function(){
|
||||
goNav(returnUrl);//LIST로 이동
|
||||
});
|
||||
|
||||
buttonControl(isDetail);
|
||||
titleControl(isDetail);
|
||||
|
||||
$('.detail-tab').click(function() {
|
||||
switchTab($(this).data('tab'));
|
||||
});
|
||||
|
||||
if (isDetail) {
|
||||
$('#tabStatus').show();
|
||||
}
|
||||
|
||||
$('#btn_refresh_bucket').click(function() {
|
||||
loadBucketStatus();
|
||||
});
|
||||
});
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="right_box">
|
||||
<div class="content_top">
|
||||
<ul class="path">
|
||||
<li><a href="#">${rmsMenuPath}</a></li>
|
||||
</ul>
|
||||
</div><!-- end content_top -->
|
||||
<div class="content_middle" id="content_middle">
|
||||
<div class="search_wrap">
|
||||
<button type="button" class="cssbtn" id="btn_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.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>
|
||||
</div>
|
||||
|
||||
<div class="title-wrap">
|
||||
<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>
|
||||
|
||||
<!-- 탭 네비게이션 -->
|
||||
<div class="detail-tabs">
|
||||
<button type="button" class="detail-tab active" data-tab="config">
|
||||
<i class="material-icons">settings</i> 설정
|
||||
</button>
|
||||
<button type="button" class="detail-tab" data-tab="status" id="tabStatus" style="display:none;">
|
||||
<i class="material-icons">analytics</i> 현황
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- 설정 탭 -->
|
||||
<div class="tab-content active" id="tabConfigContent">
|
||||
<form id="ajaxForm">
|
||||
<span class="section-label">기본 정보</span>
|
||||
<table class="form-table" cellspacing="0">
|
||||
<colgroup>
|
||||
<col style="width:150px" />
|
||||
<col />
|
||||
</colgroup>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th><%= localeMessage.getString("infAdpConMan.adaNm") %></th>
|
||||
<td><input type="text" name="name" style="width:300px; background-color: #ebebec;" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><%= localeMessage.getString("infAdpConMan.adaDes") %></th>
|
||||
<td><input type="text" name="desc" style="width:300px; background-color: #ebebec;" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><%= localeMessage.getString("infAdpConMan.useYn") %></th>
|
||||
<td>
|
||||
<select name="useYn" class="styled-select"></select>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<span class="section-label">유량제어 설정</span>
|
||||
<table class="form-table" cellspacing="0">
|
||||
<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>
|
||||
<body>
|
||||
<div class="right_box">
|
||||
<div class="content_top">
|
||||
<ul class="path">
|
||||
<li><a href="#">${rmsMenuPath}</a></li>
|
||||
</ul>
|
||||
</div><!-- end content_top -->
|
||||
<div class="content_middle">
|
||||
<div class="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_modify" level="W" status="DETAIL,NEW"><i class="material-icons">save</i> <%= localeMessage.getString("button.modify") %></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"/> --%>
|
||||
<%-- <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><!-- /tabStatusContent -->
|
||||
|
||||
</div><!-- end content_middle -->
|
||||
</div><!-- end right_box -->
|
||||
</body>
|
||||
<div class="title"><%= localeMessage.getString("infAdpConMan.title") %><span class="tooltip" ><%= localeMessage.getString("infAdpConMan.title") %></span></div>
|
||||
|
||||
<table id="grid" ></table>
|
||||
<div id="pager"></div>
|
||||
|
||||
<!-- detail -->
|
||||
<form id="ajaxForm">
|
||||
<table class="table_row" cellspacing="0">
|
||||
<tr>
|
||||
<th style="width:20%;"><%= localeMessage.getString("infAdpConMan.adaNm") %></th>
|
||||
<td><input type="text" name="name" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th style="width:20%;"><%= localeMessage.getString("infAdpConMan.adaDes") %></th>
|
||||
<td><input type="text" name="desc" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th style="width:20%;"><%= localeMessage.getString("infAdpConMan.thrPerSecond") %></th>
|
||||
<td><input type="text" name="thresholdPerSecond" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th style="width:20%;"><%= localeMessage.getString("infAdpConMan.thr") %></th>
|
||||
<td><input type="text" name="threshold" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th style="width:20%;"><%= localeMessage.getString("infAdpConMan.thrTimeUnit") %></th>
|
||||
<td>
|
||||
<div class="select-style">
|
||||
<select name="thresholdTimeUnit" />
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th style="width:20%;"><%= localeMessage.getString("infAdpConMan.useYn") %></th>
|
||||
<td>
|
||||
<div class="select-style">
|
||||
<select name="useYn" />
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</form>
|
||||
|
||||
</div><!-- end content_middle -->
|
||||
</div><!-- end right_box -->
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
||||
@@ -1,138 +0,0 @@
|
||||
<%@ page language="java" contentType="text/html; charset=utf-8"%>
|
||||
<%@ page import="java.io.*"%>
|
||||
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
|
||||
<%@ taglib uri="http://www.springframework.org/tags" prefix="spring"%>
|
||||
<%@ include file="/jsp/common/include/localemessage.jsp" %>
|
||||
<%
|
||||
response.setHeader("Pragma", "No-cache");
|
||||
response.setHeader("Cache-Control", "no-cache");
|
||||
response.setHeader("Expires", "0");
|
||||
|
||||
%>
|
||||
<html>
|
||||
<head>
|
||||
<title></title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<jsp:include page="/jsp/common/include/css.jsp"/>
|
||||
<jsp:include page="/jsp/common/include/script.jsp"/>
|
||||
<script language="javascript" >
|
||||
var url ='<c:url value="/onl/admin/inflow/inflowClientControlMan.json" />';
|
||||
var url_view ='<c:url value="/onl/admin/inflow/inflowClientControlMan.view" />';
|
||||
|
||||
|
||||
$(document).ready(function() {
|
||||
$('#grid').jqGrid({
|
||||
datatype:"json",
|
||||
mtype: 'POST',
|
||||
url: url,
|
||||
postData : { cmd : 'LIST', searchName: $('input[name=searchName]').val()},
|
||||
colNames:['클라이언트 ID',
|
||||
'클라이언트명',
|
||||
'<%= localeMessage.getString("infAdpConMan.thrPerSecond") %>',
|
||||
'<%= localeMessage.getString("infAdpConMan.thr") %>',
|
||||
'<%= localeMessage.getString("infAdpConMan.thrTimeUnit") %>',
|
||||
'<%= localeMessage.getString("infAdpConMan.useYn") %>'
|
||||
],
|
||||
colModel:[
|
||||
{ name : 'NAME' , align:'left' , width:'100' , sortable:false },
|
||||
{ name : 'DESC' , align:'left' , width:'200'},
|
||||
{ name : 'THRESHOLDPERSECOND' , align:'right' , width:'60'},
|
||||
{ name : 'THRESHOLD' , align:'right' , width:'60'},
|
||||
{ name : 'THRESHOLDTIMEUNIT' , align:'center' , width:'80'},
|
||||
{ name : 'USEYN' , align:'center' , width:'60' , editoptions:{value:"0:사용안함;1:사용함"}, formatter:"select" }
|
||||
],
|
||||
jsonReader: {
|
||||
repeatitems:false
|
||||
},
|
||||
pager : $('#pager'),
|
||||
page : '${param.page}',
|
||||
rowNum : '${rmsDefaultRowNum}',
|
||||
autoheight: true,
|
||||
height: $("#container").height(),
|
||||
autowidth: true,
|
||||
viewrecords: true,
|
||||
rowList : eval('[${rmsDefaultRowList}]'),
|
||||
ondblClickRow: function(rowId) {
|
||||
var rowData = $(this).getRowData(rowId);
|
||||
var name = rowData['NAME'];
|
||||
var url2 = url_view;
|
||||
url2 += '?cmd=DETAIL';
|
||||
url2 += '&page='+$(this).getGridParam("page");
|
||||
url2 += '&returnUrl='+getReturnUrl();
|
||||
url2 += '&menuId='+'${param.menuId}';
|
||||
//검색값
|
||||
url2 += '&searchName='+$("input[name=searchName]").val();
|
||||
//key값
|
||||
url2 += '&name='+name;
|
||||
goNav(url2);
|
||||
|
||||
},
|
||||
gridComplete:function (d){
|
||||
var colModel = $(this).getGridParam("colModel");
|
||||
for(var i = 0 ; i< colModel.length; i++){
|
||||
$(this).setColProp(colModel[i].name, {sortable : false});
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
resizeJqGridWidth('grid','content_middle','1000');
|
||||
|
||||
$("#btn_search").click(function(){
|
||||
$("#grid").setGridParam({ postData: { searchName: $('input[name=searchName]').val()}, page:1 }).trigger("reloadGrid");
|
||||
});
|
||||
$("#btn_new").click(function(){
|
||||
var url2 = url_view;
|
||||
url2 += '?cmd=DETAIL';
|
||||
url2 += '&page='+$("#grid").getGridParam("page");
|
||||
url2 += '&returnUrl='+getReturnUrl();
|
||||
url2 += '&menuId='+'${param.menuId}';
|
||||
//검색값
|
||||
url2 += '&searchName=';
|
||||
|
||||
goNav(url2);
|
||||
});
|
||||
|
||||
$("input[name^=search]").keydown(function(key){
|
||||
if (key.keyCode == 13){
|
||||
$("#btn_search").click();
|
||||
}
|
||||
});
|
||||
|
||||
buttonControl();
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="right_box">
|
||||
<div class="content_top">
|
||||
<ul class="path">
|
||||
<li><a href="#">${rmsMenuPath}</a></li>
|
||||
</ul>
|
||||
</div><!-- end content_top -->
|
||||
<div class="content_middle" id="content_middle">
|
||||
<div class="search_wrap">
|
||||
<button type="button" class="cssbtn" id="btn_search" level="R"><i class="material-icons">search</i> <%= localeMessage.getString("button.search") %></button>
|
||||
</div>
|
||||
<div class="title">클라이언트 유량제어<span class="tooltip">클라이언트(API Key) 유량제어를 관리한다</span></div>
|
||||
|
||||
<table class="search_condition" cellspacing=0;>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th style="width:180px;">클라이언트명</th>
|
||||
<td><input type="text" name="searchName" value="${param.searchName}"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<table id="grid" ></table>
|
||||
<div id="pager"></div>
|
||||
|
||||
</div><!-- end content_middle -->
|
||||
</div><!-- end right_box -->
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,517 +0,0 @@
|
||||
<%@ page language="java" contentType="text/html; charset=utf-8"%>
|
||||
<%@ page import="java.io.*"%>
|
||||
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
|
||||
<%@ taglib uri="http://www.springframework.org/tags" prefix="spring"%>
|
||||
<%@ include file="/jsp/common/include/localemessage.jsp" %>
|
||||
<%
|
||||
response.setHeader("Pragma", "No-cache");
|
||||
response.setHeader("Cache-Control", "no-cache");
|
||||
response.setHeader("Expires", "0");
|
||||
%>
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<title></title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<jsp:include page="/jsp/common/include/css.jsp"/>
|
||||
<jsp:include page="/jsp/common/include/script.jsp"/>
|
||||
|
||||
<!-- 페이지 커스텀 스타일 (fragment) -->
|
||||
<jsp:include page="fragments/inflowGroupDetailStyle.jsp"/>
|
||||
|
||||
<script language="javascript">
|
||||
var url ='<c:url value="/onl/admin/inflow/inflowClientControlMan.json" />';
|
||||
var url_view ='<c:url value="/onl/admin/inflow/inflowClientControlMan.view" />';
|
||||
var isDetail = false;
|
||||
|
||||
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({
|
||||
type: "POST",
|
||||
url: url,
|
||||
dataType: "json",
|
||||
data: {cmd: 'LIST_INIT_COMBO'},
|
||||
success: function(json) {
|
||||
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();
|
||||
updateThresholdLabel();
|
||||
|
||||
if (typeof callback === 'function') {
|
||||
callback(url, key);
|
||||
}
|
||||
},
|
||||
error: function(e) {
|
||||
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() {
|
||||
var name = $(this).attr("name");
|
||||
var tag = $(this).prop("tagName").toLowerCase();
|
||||
$(tag+"[name="+name+"]").val(data[name.toUpperCase()]);
|
||||
});
|
||||
|
||||
updateThresholdLabel();
|
||||
|
||||
var modifiedAt = data.MODIFIEDAT || data.modifiedAt;
|
||||
if (modifiedAt) {
|
||||
$('#modifiedAtText').text(modifiedAt);
|
||||
$('#modifiedAtInfo').show();
|
||||
}
|
||||
},
|
||||
error: function(e) {
|
||||
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() {
|
||||
var returnUrl = getReturnUrlForReturn();
|
||||
var key = "${param.name}";
|
||||
if (key != "" && key != "null") {
|
||||
isDetail = true;
|
||||
}
|
||||
init(url, key, detail);
|
||||
|
||||
$("select[name=thresholdTimeUnit]").on('change', function() {
|
||||
updateThresholdLabel();
|
||||
});
|
||||
|
||||
$("#btn_modify").click(function() {
|
||||
$(this).blur();
|
||||
var confirmMsg = isDetail ? '변경사항을 저장하시겠습니까?' : '새 클라이언트를 등록하시겠습니까?';
|
||||
showConfirm(confirmMsg, {
|
||||
title: '저장 확인',
|
||||
confirmText: '저장',
|
||||
onConfirm: function() {
|
||||
var postData = $('#ajaxForm').serializeArray();
|
||||
if (isDetail) {
|
||||
postData.push({name: "cmd", value: "UPDATE"});
|
||||
} else {
|
||||
postData.push({name: "cmd", value: "INSERT"});
|
||||
}
|
||||
var isInsert = !isDetail;
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: url,
|
||||
data: postData,
|
||||
success: function(args) {
|
||||
console.log('mod', 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() {
|
||||
$(this).blur();
|
||||
var clientName = $('input[name=desc]').val() || $('input[name=name]').val();
|
||||
showConfirm('<strong>[' + clientName + ']</strong> 클라이언트를 삭제하시겠습니까?', {
|
||||
type: 'error',
|
||||
title: '삭제 확인',
|
||||
confirmText: '삭제',
|
||||
onConfirm: function() {
|
||||
var postData = $('#ajaxForm').serializeArray();
|
||||
postData.push({name: "cmd", value: "DELETE"});
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: url,
|
||||
data: postData,
|
||||
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);
|
||||
});
|
||||
|
||||
buttonControl(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>
|
||||
</head>
|
||||
<body>
|
||||
<div class="right_box">
|
||||
<div class="content_top">
|
||||
<ul class="path">
|
||||
<li><a href="#">${rmsMenuPath}</a></li>
|
||||
</ul>
|
||||
</div><!-- end content_top -->
|
||||
<div class="content_middle" id="content_middle">
|
||||
<div class="search_wrap">
|
||||
<button type="button" class="cssbtn" id="btn_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.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>
|
||||
</div>
|
||||
|
||||
<div class="title-wrap">
|
||||
<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>
|
||||
|
||||
<!-- 탭 네비게이션 -->
|
||||
<div class="detail-tabs">
|
||||
<button type="button" class="detail-tab active" data-tab="config">
|
||||
<i class="material-icons">settings</i> 설정
|
||||
</button>
|
||||
<button type="button" class="detail-tab" data-tab="status" id="tabStatus" style="display:none;">
|
||||
<i class="material-icons">analytics</i> 현황
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- 설정 탭 -->
|
||||
<div class="tab-content active" id="tabConfigContent">
|
||||
<form id="ajaxForm">
|
||||
<span class="section-label">기본 정보</span>
|
||||
<table class="form-table" cellspacing="0">
|
||||
<colgroup>
|
||||
<col style="width:150px" />
|
||||
<col />
|
||||
</colgroup>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>클라이언트 ID</th>
|
||||
<td><input type="text" name="name" style="width:300px; background-color: #ebebec;" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>클라이언트명</th>
|
||||
<td><input type="text" name="desc" style="width:300px; background-color: #ebebec;" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><%= localeMessage.getString("infAdpConMan.useYn") %></th>
|
||||
<td>
|
||||
<select name="useYn" class="styled-select"></select>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<span class="section-label">유량제어 설정</span>
|
||||
<table class="form-table" cellspacing="0">
|
||||
<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>
|
||||
@@ -16,486 +16,173 @@
|
||||
<jsp:include page="/jsp/common/include/css.jsp"/>
|
||||
<jsp:include page="/jsp/common/include/script.jsp"/>
|
||||
|
||||
<!-- 페이지 커스텀 스타일 (fragment) -->
|
||||
<jsp:include page="fragments/inflowGroupDetailStyle.jsp"/>
|
||||
|
||||
<script language="javascript">
|
||||
<script language="javascript" >
|
||||
var url ='<c:url value="/onl/admin/inflow/inflowInterfaceControlMan.json" />';
|
||||
var url_view ='<c:url value="/onl/admin/inflow/inflowInterfaceControlMan.view" />';
|
||||
var isDetail = false;
|
||||
|
||||
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({
|
||||
type: "POST",
|
||||
url: url,
|
||||
dataType: "json",
|
||||
data: {cmd: 'LIST_INIT_COMBO'},
|
||||
success: function(json) {
|
||||
function init(url,key,callback){
|
||||
$.ajax({
|
||||
type : "POST",
|
||||
url:url,
|
||||
dataType:"json",
|
||||
data:{cmd: 'LIST_INIT_COMBO'},
|
||||
success:function(json){
|
||||
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();
|
||||
updateThresholdLabel();
|
||||
|
||||
if (typeof callback === 'function') {
|
||||
callback(url, key);
|
||||
|
||||
if(typeof callback === 'function') {
|
||||
callback(url,key);
|
||||
}
|
||||
},
|
||||
error: function(e) {
|
||||
error:function(e){
|
||||
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) {
|
||||
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() {
|
||||
$("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 tag = $(this).prop("tagName").toLowerCase();
|
||||
$(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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
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 key = "${param.name}";
|
||||
if (key != "" && key != "null") {
|
||||
var key ="${param.name}";
|
||||
if (key != "" && key !="null"){
|
||||
isDetail = true;
|
||||
}
|
||||
init(url, key, detail);
|
||||
|
||||
$("select[name=thresholdTimeUnit]").on('change', function() {
|
||||
updateThresholdLabel();
|
||||
});
|
||||
|
||||
$("#btn_modify").click(function() {
|
||||
$(this).blur();
|
||||
var confirmMsg = isDetail ? '변경사항을 저장하시겠습니까?' : '새 인터페이스를 등록하시겠습니까?';
|
||||
showConfirm(confirmMsg, {
|
||||
title: '저장 확인',
|
||||
confirmText: '저장',
|
||||
onConfirm: function() {
|
||||
var postData = $('#ajaxForm').serializeArray();
|
||||
if (isDetail) {
|
||||
postData.push({name: "cmd", value: "UPDATE"});
|
||||
} else {
|
||||
postData.push({name: "cmd", value: "INSERT"});
|
||||
}
|
||||
var isInsert = !isDetail;
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
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'});
|
||||
}
|
||||
});
|
||||
}
|
||||
init(url,key,detail);
|
||||
|
||||
|
||||
$("#btn_modify").click(function(){
|
||||
var postData = $('#ajaxForm').serializeArray();
|
||||
if (isDetail){
|
||||
postData.push({ name: "cmd" , value:"UPDATE"});
|
||||
}else{
|
||||
postData.push({ name: "cmd" , value:"INSERT"});
|
||||
}
|
||||
$.ajax({
|
||||
type : "POST",
|
||||
url:url,
|
||||
data:postData,
|
||||
success:function(args){
|
||||
alert("<%= localeMessage.getString("common.saveMsg") %>");
|
||||
goNav(returnUrl);//LIST로 이동
|
||||
},
|
||||
error:function(e){
|
||||
alert(e.responseText);
|
||||
}
|
||||
});
|
||||
});
|
||||
$("#btn_delete").click(function(){
|
||||
|
||||
if ( confirm( '<%=localeMessage.getString("common.confirmMsg")%>' ) != true ) return;
|
||||
|
||||
var postData = $('#ajaxForm').serializeArray();
|
||||
postData.push({ name: "cmd" , value:"DELETE"});
|
||||
$.ajax({
|
||||
type : "POST",
|
||||
url:url,
|
||||
data:postData,
|
||||
success:function(args){
|
||||
alert("<%= localeMessage.getString("common.deleteMsg") %>");
|
||||
goNav(returnUrl);//LIST로 이동
|
||||
|
||||
$("#btn_delete").click(function() {
|
||||
$(this).blur();
|
||||
var ifName = $('input[name=desc]').val() || $('input[name=name]').val();
|
||||
showConfirm('<strong>[' + ifName + ']</strong> 인터페이스를 삭제하시겠습니까?', {
|
||||
type: 'error',
|
||||
title: '삭제 확인',
|
||||
confirmText: '삭제',
|
||||
onConfirm: function() {
|
||||
var postData = $('#ajaxForm').serializeArray();
|
||||
postData.push({name: "cmd", value: "DELETE"});
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: url,
|
||||
data: postData,
|
||||
success: function(args) {
|
||||
showAlert("<%= localeMessage.getString("common.deleteMsg") %>", {
|
||||
type: 'success',
|
||||
title: '삭제 완료',
|
||||
onClose: function() { goNav(returnUrl); }
|
||||
});
|
||||
},
|
||||
error: function(e) {
|
||||
showAlert(e.responseText, {type: 'error'});
|
||||
}
|
||||
});
|
||||
},
|
||||
error:function(e){
|
||||
alert(e.responseText);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$("#btn_previous").click(function() {
|
||||
goNav(returnUrl);
|
||||
});
|
||||
$("#btn_previous").click(function(){
|
||||
goNav(returnUrl);//LIST로 이동
|
||||
});
|
||||
|
||||
buttonControl(isDetail);
|
||||
titleControl(isDetail);
|
||||
|
||||
$('.detail-tab').click(function() {
|
||||
switchTab($(this).data('tab'));
|
||||
});
|
||||
|
||||
if (isDetail) {
|
||||
$('#tabStatus').show();
|
||||
}
|
||||
|
||||
$('#btn_refresh_bucket').click(function() {
|
||||
loadBucketStatus();
|
||||
});
|
||||
});
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="right_box">
|
||||
<div class="content_top">
|
||||
<ul class="path">
|
||||
<li><a href="#">${rmsMenuPath}</a></li>
|
||||
</ul>
|
||||
</div><!-- end content_top -->
|
||||
<div class="content_middle" id="content_middle">
|
||||
<div class="search_wrap">
|
||||
<button type="button" class="cssbtn" id="btn_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.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>
|
||||
</div>
|
||||
|
||||
<div class="title-wrap">
|
||||
<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>
|
||||
|
||||
<!-- 탭 네비게이션 -->
|
||||
<div class="detail-tabs">
|
||||
<button type="button" class="detail-tab active" data-tab="config">
|
||||
<i class="material-icons">settings</i> 설정
|
||||
</button>
|
||||
<button type="button" class="detail-tab" data-tab="status" id="tabStatus" style="display:none;">
|
||||
<i class="material-icons">analytics</i> 현황
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- 설정 탭 -->
|
||||
<div class="tab-content active" id="tabConfigContent">
|
||||
<form id="ajaxForm">
|
||||
<span class="section-label">기본 정보</span>
|
||||
<table class="form-table" cellspacing="0">
|
||||
<colgroup>
|
||||
<col style="width:150px" />
|
||||
<col />
|
||||
</colgroup>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th><%= localeMessage.getString("infIfConMan.ifNm") %></th>
|
||||
<td><input type="text" name="name" style="width:300px;background-color: #ebebec;" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><%= localeMessage.getString("infIfConMan.ifDesc") %></th>
|
||||
<td><input type="text" name="desc" style="width:300px;background-color: #ebebec;" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><%= localeMessage.getString("infAdpConMan.useYn") %></th>
|
||||
<td>
|
||||
<select name="useYn" class="styled-select"></select>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<span class="section-label">유량제어 설정</span>
|
||||
<table class="form-table" cellspacing="0">
|
||||
<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>
|
||||
<body>
|
||||
<div class="right_box">
|
||||
<div class="content_top">
|
||||
<ul class="path">
|
||||
<li><a href="#">${rmsMenuPath}</a></li>
|
||||
</ul>
|
||||
</div><!-- end content_top -->
|
||||
<div class="content_middle">
|
||||
<div class="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_modify" level="W" status="DETAIL,NEW"><i class="material-icons">save</i> <%= localeMessage.getString("button.modify") %></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"/> --%>
|
||||
<%-- <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><!-- /tabStatusContent -->
|
||||
|
||||
</div><!-- end content_middle -->
|
||||
</div><!-- end right_box -->
|
||||
</body>
|
||||
<div class="title"><%= localeMessage.getString("infIfConMan.title") %><span class="tooltip" ><%= localeMessage.getString("infIfConMan.title") %></span></div>
|
||||
|
||||
<table id="grid" ></table>
|
||||
<div id="pager"></div>
|
||||
|
||||
<!-- detail -->
|
||||
<form id="ajaxForm">
|
||||
<table class="table_row" cellspacing="0">
|
||||
<tr>
|
||||
<th style="width:20%;"><%= localeMessage.getString("infIfConMan.ifNm") %></th>
|
||||
<td><input type="text" name="name" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th style="width:20%;"><%= localeMessage.getString("infIfConMan.ifDesc") %></th>
|
||||
<td><input type="text" name="desc" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th style="width:20%;"><%= localeMessage.getString("infAdpConMan.thrPerSecond") %></th>
|
||||
<td><input type="text" name="thresholdPerSecond" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th style="width:20%;"><%= localeMessage.getString("infAdpConMan.thr") %></th>
|
||||
<td><input type="text" name="threshold" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th style="width:20%;"><%= localeMessage.getString("infAdpConMan.thrTimeUnit") %></th>
|
||||
<td>
|
||||
<div class="select-style">
|
||||
<select name="thresholdTimeUnit" />
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th style="width:20%;"><%= localeMessage.getString("infAdpConMan.useYn") %></th>
|
||||
<td>
|
||||
<div class="select-style">
|
||||
<select name="useYn" />
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</form>
|
||||
|
||||
</div><!-- end content_middle -->
|
||||
</div><!-- end right_box -->
|
||||
</body>
|
||||
</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:'LLLBIN',name:'[ISO8583] LLLBIN'}];
|
||||
|
||||
const basicDataTypes = [{id:'BigDecimal',name:'BigDecimal'},{id:'String',name:'String'},{id:'Boolean',name:'Boolean'}];
|
||||
const basicDataTypes = [{id:'BigDecimal',name:'BigDecimal'},{id:'String',name:'String'}];
|
||||
|
||||
function gridRenderingInit() {
|
||||
$('#effectGrid').jqGrid({
|
||||
@@ -71,11 +71,11 @@
|
||||
|
||||
$('#grid').children().remove();
|
||||
|
||||
const itemTypes = ['Field', 'Grid', 'Group', 'Attr', 'Array'];
|
||||
const itemTypes = ['Field', 'Grid', 'Group', 'Attr'];
|
||||
|
||||
columns = [ // for columnData prop
|
||||
{ title: '<%= localeMessage.getString("layoutMan.itemDesc") %>', name: 'loutItemDesc', type: 'text', width:280, resize:true, minWidth:120 },
|
||||
{ title: '<%= localeMessage.getString("layoutMan.itmeEn") %>', name: 'loutItemName', type: 'text', width:280, resize:true, minWidth:120 },
|
||||
{ title: '<%= localeMessage.getString("layoutMan.itemDesc") %>', name: 'loutItemDesc', type: 'text', width:280, resize:true, minWidth:120 },
|
||||
{ title: '<%=localeMessage.getString("standardLayout.itemLevel")%>', name: 'loutItemDepth', type: 'text', width:50 },
|
||||
{ title: '<%= localeMessage.getString("standardLayout.itemType") %>', name: 'loutItemType', type: 'autocomplete', source: itemTypes, width:70 },
|
||||
{ title: '<%= localeMessage.getString("standardLayout.arraySize") %>', name: 'loutItemOccCnt', type: 'text', width:90 },
|
||||
|
||||
@@ -54,7 +54,6 @@
|
||||
const LAYOUT_ITEM_TYPE_GROUP = "Group";
|
||||
const LAYOUT_ITEM_TYPE_GRID = "Grid";
|
||||
const LAYOUT_ITEM_TYPE_FIELD = "Field";
|
||||
const LAYOUT_ITEM_TYPE_ARRAY = "Array";
|
||||
|
||||
var isDetail = false;
|
||||
|
||||
@@ -551,14 +550,6 @@
|
||||
return data;
|
||||
}
|
||||
|
||||
// 항목 자신이 Array인 경우, 경로/변환명령 끝에 반복 표시 [*]를 추가
|
||||
function addArrayTag(loutItemType, data) {
|
||||
if (loutItemType == LAYOUT_ITEM_TYPE_ARRAY && !data.endsWith("[*]")) {
|
||||
data = data + "[*]";
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
//변환명에 있는 서비스코드와 if서비스 코드 매칭
|
||||
function validation(){
|
||||
if ($('input[name=cnvsnName]').val() == '') {
|
||||
@@ -898,10 +889,10 @@
|
||||
let selectValue = "";
|
||||
let targetValue = "";
|
||||
for ( var i = 0; i < sourceData.length; i++) {
|
||||
if (sourceData[i]['loutItemType'] != "Field" && sourceData[i]['loutItemType'] != "Attr" && sourceData[i]['loutItemType'] != "Array") continue;
|
||||
if (sourceData[i]['loutItemType'] != "Field" && sourceData[i]['loutItemType'] != "Attr") continue;
|
||||
selectValue = (sourceData[i]['LOUTITEMPATH']).substr(sourceData[i]['loutName'].length+1);
|
||||
for (var j = 0; j <targetData.length; j++) {
|
||||
if (targetData[j]['loutItemType'] != "Field" && targetData[j]['loutItemType'] != "Attr" && targetData[j]['loutItemType'] != "Array") continue;
|
||||
if (targetData[j]['loutItemType'] != "Field" && targetData[j]['loutItemType'] != "Attr") continue;
|
||||
targetValue = (targetData[j]['LOUTITEMPATH']).substr(targetData[j]['loutName'].length+1);
|
||||
if (targetValue == selectValue ) {
|
||||
var data = sourceData[i]['LOUTITEMPATH'];
|
||||
@@ -925,8 +916,8 @@
|
||||
//field, ATTR 여부 판단
|
||||
var sourceRow = sourceGrid.getRowData( srcRowId );
|
||||
var targetRow = targetGrid.getRowData( tgtRowId );
|
||||
if ( (sourceRow["loutItemType"] == 'Field' || sourceRow["loutItemType"] == 'Attr' || sourceRow["loutItemType"] == 'Array') &&
|
||||
(targetRow["loutItemType"] == 'Field' || targetRow["loutItemType"] == 'Attr' || targetRow["loutItemType"] == 'Array') ){
|
||||
if ( (sourceRow["loutItemType"] == 'Field' || sourceRow["loutItemType"] == 'Attr') &&
|
||||
(targetRow["loutItemType"] == 'Field' || targetRow["loutItemType"] == 'Attr') ){
|
||||
;
|
||||
}else{
|
||||
alert('<%=localeMessage.getString("transformManDetail.alert3")%>');
|
||||
@@ -945,15 +936,15 @@
|
||||
var targetValue = "";
|
||||
var j=tgtIndex-1-1;
|
||||
for ( var i = srcIndex-1; i < sourceData.length; i++) {
|
||||
if (sourceData[i]['loutItemType'] != LAYOUT_ITEM_TYPE_FIELD && sourceData[i]['loutItemType'] != LAYOUT_ITEM_TYPE_ARRAY) continue;
|
||||
if (sourceData[i]['loutItemType'] != LAYOUT_ITEM_TYPE_FIELD) continue;
|
||||
j++;
|
||||
while (targetData.length -1 >= j && targetData[j]["loutItemType"] != LAYOUT_ITEM_TYPE_FIELD && targetData[j]["loutItemType"] != LAYOUT_ITEM_TYPE_ARRAY){
|
||||
while (targetData.length -1 >= j &&targetData[j]["loutItemType"] != LAYOUT_ITEM_TYPE_FIELD){
|
||||
j++;
|
||||
}
|
||||
if (targetData.length -1 < j) break;
|
||||
var targetRowId = targetData[j]['id'];
|
||||
targetGrid.jqGrid('setCell',targetRowId,'CNVSNCMDNAME',addArrayTag(sourceData[i]['loutItemType'], addGroupTag(srcGroup,sourceData[i]['LOUTITEMPATH'])));
|
||||
targetGrid.jqGrid('setCell',targetRowId,'CNVSNRSULTITEMPATHNAME',addArrayTag(targetData[j]['loutItemType'], addGroupTag(tgtGroup,targetData[j]['LOUTITEMPATH'])));
|
||||
targetGrid.jqGrid('setCell',targetRowId,'CNVSNCMDNAME',addGroupTag(srcGroup,sourceData[i]['LOUTITEMPATH']));
|
||||
targetGrid.jqGrid('setCell',targetRowId,'CNVSNRSULTITEMPATHNAME',addGroupTag(tgtGroup,targetData[j]['LOUTITEMPATH']));
|
||||
targetGrid.jqGrid('setCell',targetRowId,'CNVSNITEMSERNO',targetData[j]['loutItemSerno']);
|
||||
|
||||
const sourceEndpointElementId = sourceGrid.getEndpointElementId(sourceData[i]['id']);
|
||||
@@ -1024,7 +1015,7 @@
|
||||
var targetData = targetGrid.getRowData();
|
||||
var gridData = new Array();
|
||||
for (var i = 0; i <targetData.length; i++) {
|
||||
if ((targetData[i]['loutItemType'] != "Field") && (targetData[i]['loutItemType'] != "Attr") && (targetData[i]['loutItemType'] != "Array")) {
|
||||
if ((targetData[i]['loutItemType'] != "Field") && (targetData[i]['loutItemType'] != "Attr")) {
|
||||
continue;
|
||||
}
|
||||
if (targetData[i]['CNVSNCMDNAME']==null
|
||||
@@ -1167,7 +1158,7 @@
|
||||
function srcformatterFunction(cellvalue,options,rowObject){
|
||||
var rowId = options["rowId"];
|
||||
var loutItemType = rowObject["loutItemType"];
|
||||
if (loutItemType == 'Field' || loutItemType == 'Attr' || loutItemType == 'Array'){
|
||||
if (loutItemType == 'Field' || loutItemType == 'Attr'){
|
||||
return "<div id='div_src_"+rowId+"' name='src_"+rowId+"' style='width:28px;height:17px;background-color: #D12F35; border-color: #ab1717; border-style: solid; border-width: 1px; border-radius: 3px;' />";
|
||||
}else{
|
||||
return "<div id='div_srcgroup_"+rowId+"' name='srcgroup_"+rowId+"' style='width:28px;height:17px;' />";
|
||||
@@ -1179,7 +1170,7 @@
|
||||
function tgtformatterFunction(cellvalue,options,rowObject){
|
||||
var rowId = options["rowId"];
|
||||
var loutItemType = rowObject["loutItemType"];
|
||||
if (loutItemType == 'Field' || loutItemType == 'Attr' || loutItemType == 'Array'){
|
||||
if (loutItemType == 'Field' || loutItemType == 'Attr'){
|
||||
return "<div id='div_tgt_"+rowId+"_base' name='div_tgt_"+rowId+"_base' style='width:30px;height:20px; background-color:gray;' >"
|
||||
+ "<div id='div_tgt_"+rowId+"' name='tgt_"+rowId+"' style='width:28px;height:17px; background-color: #3E7E9C; border-color: #217ca7; border-style: solid; border-width: 1px; border-radius: 3px;' onclick='jqGridOnTargetGridEndpointClick(this)'>"
|
||||
+ "</div></div>";
|
||||
@@ -1678,8 +1669,8 @@
|
||||
return;
|
||||
}
|
||||
|
||||
if ((sourceRow["loutItemType"] != 'Field' && sourceRow["loutItemType"] != 'Attr' && sourceRow["loutItemType"] != 'Array')
|
||||
|| (targetRow["loutItemType"] != 'Field' && targetRow["loutItemType"] != 'Attr' && targetRow["loutItemType"] != 'Array')) {
|
||||
if ((sourceRow["loutItemType"] != 'Field' && sourceRow["loutItemType"] != 'Attr')
|
||||
|| (targetRow["loutItemType"] != 'Field' && targetRow["loutItemType"] != 'Attr')) {
|
||||
alert('<%=localeMessage.getString("transformManDetail.alert3")%>');
|
||||
return ;
|
||||
}
|
||||
@@ -1693,7 +1684,7 @@
|
||||
|
||||
if (targetRow['CNVSNCMDNAME'] == '') {
|
||||
// 변환 명령 값이 한번 설정 되면 변경 되지 않게 함.
|
||||
var cnvsnCmdName = addArrayTag(sourceRow["loutItemType"], addGroupTag(srcGroup,sourceRow["LOUTITEMPATH"]));
|
||||
var cnvsnCmdName = addGroupTag(srcGroup,sourceRow["LOUTITEMPATH"]);
|
||||
// function까지 처리되도록 수정했으나, parameter가 하나인 경우에만 정상처리됨
|
||||
if($('#functionCombo').val() != "") {
|
||||
cnvsnCmdName = $('#functionCombo').val() + "(" + cnvsnCmdName + ")";
|
||||
@@ -1702,7 +1693,7 @@
|
||||
targetGrid.jqGrid('setCell',tgtRowId,'CNVSNCMDNAME', cnvsnCmdName);
|
||||
}
|
||||
|
||||
targetGrid.jqGrid('setCell',tgtRowId,'CNVSNRSULTITEMPATHNAME',addArrayTag(targetRow["loutItemType"], addGroupTag(tgtGroup,targetRow["LOUTITEMPATH"])));
|
||||
targetGrid.jqGrid('setCell',tgtRowId,'CNVSNRSULTITEMPATHNAME',addGroupTag(tgtGroup,targetRow["LOUTITEMPATH"]));
|
||||
targetGrid.jqGrid('setCell',tgtRowId,'CNVSNITEMSERNO',targetRow['loutItemSerno']);
|
||||
targetGrid.jqGrid('setCell',tgtRowId,'CNVSNSRCITEMS', cnvsnSrcItems);
|
||||
targetGrid.jqGrid('setCell',tgtRowId,'ISVALIDMAPPING', true);
|
||||
@@ -1894,8 +1885,8 @@
|
||||
if (loutItemType == LAYOUT_ITEM_TYPE_GRID || loutItemType == LAYOUT_ITEM_TYPE_GROUP) {
|
||||
// group 인 경우
|
||||
items = createGridGroupContextMenuItems(grid, rowId);
|
||||
} else if(loutItemType == LAYOUT_ITEM_TYPE_FIELD || loutItemType == LAYOUT_ITEM_TYPE_ARRAY) {
|
||||
// field, array 인 경우
|
||||
} else if(loutItemType == LAYOUT_ITEM_TYPE_FIELD) {
|
||||
// field 인 경우
|
||||
items = createFieldContextMenuItems(grid, positionElement.attr('id'));
|
||||
} else {
|
||||
return false;
|
||||
@@ -1998,7 +1989,7 @@
|
||||
break;
|
||||
}
|
||||
|
||||
if (loutItemType == LAYOUT_ITEM_TYPE_FIELD || loutItemType == LAYOUT_ITEM_TYPE_ARRAY) {
|
||||
if (loutItemType == LAYOUT_ITEM_TYPE_FIELD) {
|
||||
loutItemPath = loutItemPath.replace(new RegExp("^" + prefix), "");
|
||||
console.debug(grid.getId() + " : loutItemPath : " + loutItemPath);
|
||||
rowData['GROUP_LOUTITEMPATH'] = loutItemPath;
|
||||
|
||||
@@ -1,153 +0,0 @@
|
||||
<%@ page language="java" contentType="text/html; charset=utf-8"%>
|
||||
<%@ page import="java.io.*"%>
|
||||
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
|
||||
<%@ taglib uri="http://www.springframework.org/tags" prefix="spring"%>
|
||||
<%@ include file="/jsp/common/include/localemessage.jsp" %>
|
||||
<%
|
||||
response.setHeader("Pragma", "No-cache");
|
||||
response.setHeader("Cache-Control", "no-cache");
|
||||
response.setHeader("Expires", "0");
|
||||
%>
|
||||
<html>
|
||||
<head>
|
||||
<title></title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<jsp:include page="/jsp/common/include/css.jsp"/>
|
||||
<jsp:include page="/jsp/common/include/script.jsp"/>
|
||||
<script language="javascript">
|
||||
var url = '<c:url value="/onl/admin/security/cryptoModuleMan.json" />';
|
||||
var url_view = '<c:url value="/onl/admin/security/cryptoModuleMan.view" />';
|
||||
|
||||
$(document).ready(function() {
|
||||
$('#grid').jqGrid({
|
||||
datatype: "json",
|
||||
mtype: 'POST',
|
||||
url: url,
|
||||
postData: {
|
||||
cmd: 'LIST',
|
||||
searchName: $('input[name=searchName]').val(),
|
||||
algType: $('select[name=algType]').val(),
|
||||
keySourceType: $('select[name=keySourceType]').val(),
|
||||
useYn: $('select[name=useYn]').val()
|
||||
},
|
||||
colNames: ['ID', '모듈명', '설명', '알고리즘', '운영모드', '키 소스', '사용'],
|
||||
colModel: [
|
||||
{ name: 'CRYPTO_ID', hidden: true },
|
||||
{ name: 'CRYPTO_NAME', align: 'left' },
|
||||
{ name: 'CRYPTO_DESC', align: 'left', sortable: false },
|
||||
{ name: 'ALG_TYPE', align: 'center', width: 80 },
|
||||
{ name: 'CIPHER_MODE', align: 'center', width: 80 },
|
||||
{ name: 'KEY_SOURCE_TYPE', align: 'center', width: 100 },
|
||||
{ name: 'USE_YN', align: 'center', width: 60 }
|
||||
],
|
||||
jsonReader: { repeatitems: false },
|
||||
pager: $('#pager'),
|
||||
page: '${param.page}',
|
||||
rowNum: '${rmsDefaultRowNum}',
|
||||
autoheight: true,
|
||||
height: $("#container").height(),
|
||||
autowidth: true,
|
||||
viewrecords: true,
|
||||
rowList: eval('[${rmsDefaultRowList}]'),
|
||||
ondblClickRow: function(rowId) {
|
||||
var rowData = $(this).getRowData(rowId);
|
||||
var cryptoId = rowData['CRYPTO_ID'];
|
||||
var url2 = url_view + '?cmd=DETAIL';
|
||||
url2 += '&page=' + $(this).getGridParam("page");
|
||||
url2 += '&returnUrl=' + getReturnUrl();
|
||||
url2 += '&menuId=' + '${param.menuId}';
|
||||
url2 += '&searchName=' + $("input[name=searchName]").val();
|
||||
url2 += '&cryptoId=' + cryptoId;
|
||||
goNav(url2);
|
||||
},
|
||||
gridComplete: function() {
|
||||
var colModel = $(this).getGridParam("colModel");
|
||||
for (var i = 0; i < colModel.length; i++) {
|
||||
$(this).setColProp(colModel[i].name, { sortable: false });
|
||||
}
|
||||
},
|
||||
loadError: function(jqXHR, textStatus, errorThrown) {
|
||||
var location = '<%=request.getContextPath()%>/';
|
||||
comloadError(jqXHR, textStatus, errorThrown, location);
|
||||
}
|
||||
});
|
||||
|
||||
resizeJqGridWidth('grid', 'content_middle', '1000');
|
||||
|
||||
$("#btn_search").click(function() {
|
||||
var postData = getSearchForJqgrid("cmd", "LIST");
|
||||
postData['algType'] = $('select[name=algType]').val();
|
||||
postData['keySourceType'] = $('select[name=keySourceType]').val();
|
||||
postData['useYn'] = $('select[name=useYn]').val();
|
||||
$("#grid").setGridParam({ url: url, postData: postData, page: 1 }).trigger("reloadGrid");
|
||||
});
|
||||
|
||||
$("#btn_new").click(function() {
|
||||
var url2 = url_view + '?cmd=DETAIL';
|
||||
url2 += '&page=' + $("#grid").getGridParam("page");
|
||||
url2 += '&returnUrl=' + getReturnUrl();
|
||||
url2 += '&menuId=' + '${param.menuId}';
|
||||
goNav(url2);
|
||||
});
|
||||
|
||||
$("input[name=searchName]").keydown(function(key) {
|
||||
if (key.keyCode == 13) { $("#btn_search").click(); }
|
||||
});
|
||||
|
||||
buttonControl();
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="right_box">
|
||||
<div class="content_top">
|
||||
<ul class="path">
|
||||
<li><a href="#">${rmsMenuPath}</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="content_middle" id="content_middle">
|
||||
<div class="search_wrap">
|
||||
<button type="button" class="cssbtn" id="btn_new" level="W"><i class="material-icons">add</i> 신규</button>
|
||||
<button type="button" class="cssbtn" id="btn_search" level="R"><i class="material-icons">search</i> 검색</button>
|
||||
</div>
|
||||
<div class="title">암호화 모듈 설정</div>
|
||||
<form id="ajaxForm" onsubmit="return false;">
|
||||
<table class="search_condition" cellspacing="0">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th style="width:100px;">모듈명</th>
|
||||
<td><input type="text" name="searchName" value="${param.searchName}"></td>
|
||||
<th style="width:100px;">알고리즘</th>
|
||||
<td>
|
||||
<select name="algType">
|
||||
<option value="">전체</option>
|
||||
<option value="AES">AES</option>
|
||||
<option value="ARIA">ARIA</option>
|
||||
</select>
|
||||
</td>
|
||||
<th style="width:100px;">키 소스</th>
|
||||
<td>
|
||||
<select name="keySourceType">
|
||||
<option value="">전체</option>
|
||||
<option value="STATIC">STATIC</option>
|
||||
<option value="DYNAMIC">DYNAMIC</option>
|
||||
</select>
|
||||
</td>
|
||||
<th style="width:80px;">사용</th>
|
||||
<td>
|
||||
<select name="useYn">
|
||||
<option value="">전체</option>
|
||||
<option value="Y">Y</option>
|
||||
<option value="N">N</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</form>
|
||||
<table id="grid"></table>
|
||||
<div id="pager"></div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,365 +0,0 @@
|
||||
<%@ page language="java" contentType="text/html; charset=utf-8"%>
|
||||
<%@ page import="java.io.*"%>
|
||||
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
|
||||
<%@ taglib uri="http://www.springframework.org/tags" prefix="spring"%>
|
||||
<%@ include file="/jsp/common/include/localemessage.jsp" %>
|
||||
<%
|
||||
response.setHeader("Pragma", "No-cache");
|
||||
response.setHeader("Cache-Control", "no-cache");
|
||||
response.setHeader("Expires", "0");
|
||||
%>
|
||||
<html>
|
||||
<head>
|
||||
<title></title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<jsp:include page="/jsp/common/include/css.jsp"/>
|
||||
<jsp:include page="/jsp/common/include/script.jsp"/>
|
||||
<script language="javascript">
|
||||
var url = '<c:url value="/onl/admin/security/cryptoModuleMan.json" />';
|
||||
var url_view = '<c:url value="/onl/admin/security/cryptoModuleMan.view" />';
|
||||
var isDetail = false;
|
||||
|
||||
function isValid() {
|
||||
if ($('input[name=cryptoName]').val().trim() === '') {
|
||||
alert('모듈명을 입력하세요.');
|
||||
$('input[name=cryptoName]').focus();
|
||||
return false;
|
||||
}
|
||||
if ($('select[name=algType]').val() === '') {
|
||||
alert('알고리즘을 선택하세요.');
|
||||
return false;
|
||||
}
|
||||
if ($('select[name=cipherMode]').val() === '') {
|
||||
alert('운영모드를 선택하세요.');
|
||||
return false;
|
||||
}
|
||||
var keySourceType = $('select[name=keySourceType]').val();
|
||||
if (keySourceType === '') {
|
||||
alert('키 소스 유형을 선택하세요.');
|
||||
return false;
|
||||
}
|
||||
if (keySourceType === 'STATIC') {
|
||||
if ($('input[name=encKeyHex]').val().trim() === '') {
|
||||
alert('암호화 키(Hex)를 입력하세요.');
|
||||
$('input[name=encKeyHex]').focus();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (keySourceType === 'DYNAMIC') {
|
||||
if ($('input[name=keyDerivStrategy]').val().trim() === '') {
|
||||
alert('키 도출 전략 FQCN을 입력하세요.');
|
||||
$('input[name=keyDerivStrategy]').focus();
|
||||
return false;
|
||||
}
|
||||
if ($('textarea[name=keyDerivParams]').val().trim() === '') {
|
||||
alert('키 도출 파라미터(JSON)를 입력하세요.');
|
||||
$('textarea[name=keyDerivParams]').focus();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function textToHex(text) {
|
||||
if (!text) return '';
|
||||
var encoder = new TextEncoder();
|
||||
var bytes = encoder.encode(text);
|
||||
return Array.from(bytes).map(function(b) {
|
||||
return b.toString(16).padStart(2, '0');
|
||||
}).join('').toUpperCase();
|
||||
}
|
||||
|
||||
function updateHexFromText(textEl, hexName, lenSpanId) {
|
||||
var hex = textToHex($(textEl).val());
|
||||
$('input[name=' + hexName + ']').val(hex);
|
||||
updateHexLength(hex, lenSpanId);
|
||||
}
|
||||
|
||||
function updateHexLength(hex, lenSpanId) {
|
||||
var charLen = hex ? hex.replace(/\s/g, '').length : 0;
|
||||
var byteLen = Math.floor(charLen / 2);
|
||||
var msg = charLen > 0 ? byteLen + ' bytes (' + charLen + '자)' : '';
|
||||
$('#' + lenSpanId).text(msg);
|
||||
}
|
||||
|
||||
function toggleKeySourceFields() {
|
||||
var keySourceType = $('select[name=keySourceType]').val();
|
||||
if (keySourceType === 'STATIC') {
|
||||
$('.static-section').show();
|
||||
$('.dynamic-section').hide();
|
||||
} else if (keySourceType === 'DYNAMIC') {
|
||||
$('.static-section').hide();
|
||||
$('.dynamic-section').show();
|
||||
} else {
|
||||
$('.static-section').show();
|
||||
$('.dynamic-section').show();
|
||||
}
|
||||
}
|
||||
|
||||
function detail(url, key) {
|
||||
if (!isDetail) return;
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: url,
|
||||
dataType: 'json',
|
||||
data: { cmd: 'DETAIL', cryptoId: key },
|
||||
success: function(data) {
|
||||
$('input[name=cryptoId]').val(data['CRYPTO_ID']);
|
||||
$('input[name=cryptoName]').val(data['CRYPTO_NAME']).attr('readonly', true);
|
||||
$('input[name=cryptoDesc]').val(data['CRYPTO_DESC']);
|
||||
$('select[name=algType]').val(data['ALG_TYPE']);
|
||||
$('select[name=cipherMode]').val(data['CIPHER_MODE']);
|
||||
$('select[name=padding]').val(data['PADDING'] || '');
|
||||
$('input[name=ivHex]').val(data['IV_HEX']);
|
||||
$('select[name=keySourceType]').val(data['KEY_SOURCE_TYPE']);
|
||||
$('input[name=encKeyHex]').val(data['ENC_KEY_HEX']);
|
||||
$('input[name=decKeyHex]').val(data['DEC_KEY_HEX']);
|
||||
$('input[name=keyDerivStrategy]').val(data['KEY_DERIV_STRATEGY']);
|
||||
$('textarea[name=keyDerivParams]').val(data['KEY_DERIV_PARAMS']);
|
||||
$('select[name=cacheYn]').val(data['CACHE_YN']);
|
||||
$('input[name=cacheTtlSec]').val(data['CACHE_TTL_SEC']);
|
||||
$('select[name=useYn]').val(data['USE_YN']);
|
||||
updateHexLength(data['ENC_KEY_HEX'], 'encKeyLen');
|
||||
updateHexLength(data['DEC_KEY_HEX'], 'decKeyLen');
|
||||
updateHexLength(data['IV_HEX'], 'ivHexLen');
|
||||
if (data['MODIFIED_BY'] || data['MODIFIED_AT']) {
|
||||
$('#span_modified_by').text(data['MODIFIED_BY'] || '');
|
||||
$('#span_modified_at').text((data['MODIFIED_AT'] || '').replace('T', ' '));
|
||||
$('#row_modified_info').show();
|
||||
}
|
||||
toggleKeySourceFields();
|
||||
},
|
||||
error: function(e) {
|
||||
alert(e.responseText);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
var returnUrl = getReturnUrlForReturn();
|
||||
var key = '${param.cryptoId}';
|
||||
if (key !== '' && key !== 'null') {
|
||||
isDetail = true;
|
||||
}
|
||||
detail(url, key);
|
||||
|
||||
$('select[name=keySourceType]').change(function() {
|
||||
toggleKeySourceFields();
|
||||
});
|
||||
|
||||
$('input[name=encKeyHex]').on('input', function() {
|
||||
updateHexLength($(this).val(), 'encKeyLen');
|
||||
});
|
||||
$('input[name=decKeyHex]').on('input', function() {
|
||||
updateHexLength($(this).val(), 'decKeyLen');
|
||||
});
|
||||
$('input[name=ivHex]').on('input', function() {
|
||||
updateHexLength($(this).val(), 'ivHexLen');
|
||||
});
|
||||
|
||||
toggleKeySourceFields();
|
||||
|
||||
$('#btn_modify').click(function() {
|
||||
if (!isValid()) return;
|
||||
var postData = $('#ajaxForm').serializeArray();
|
||||
postData.push({ name: 'cmd', value: isDetail ? 'UPDATE' : 'INSERT' });
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: url,
|
||||
dataType: 'json',
|
||||
data: postData,
|
||||
success: function(data) {
|
||||
var msg = '<%= localeMessage.getString("common.saveMsg") %>';
|
||||
if (data && data.broadcastResult) {
|
||||
msg += '\n\n[서버 반영 결과]\n' + data.broadcastResult;
|
||||
}
|
||||
alert(msg);
|
||||
goNav(returnUrl);
|
||||
},
|
||||
error: function(e) {
|
||||
alert(e.responseText);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$('#btn_delete').click(function() {
|
||||
if (confirm('<%= localeMessage.getString("common.confirmMsg") %>') !== true) return;
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: url,
|
||||
dataType: 'json',
|
||||
data: { cmd: 'DELETE', cryptoId: $('input[name=cryptoId]').val() },
|
||||
success: function(data) {
|
||||
var msg = '<%= localeMessage.getString("common.deleteMsg") %>';
|
||||
if (data && data.broadcastResult) {
|
||||
msg += '\n\n[서버 반영 결과]\n' + data.broadcastResult;
|
||||
}
|
||||
alert(msg);
|
||||
goNav(returnUrl);
|
||||
},
|
||||
error: function(e) {
|
||||
alert(e.responseText);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$('#btn_previous').click(function() {
|
||||
goNav(returnUrl);
|
||||
});
|
||||
|
||||
buttonControl(isDetail);
|
||||
titleControl(isDetail);
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="right_box">
|
||||
<div class="content_top">
|
||||
<ul class="path">
|
||||
<li><a href="#">${rmsMenuPath}</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="content_middle">
|
||||
<div class="search_wrap">
|
||||
<button type="button" class="cssbtn" id="btn_delete" level="W" status="DETAIL"><i class="material-icons">delete</i> 삭제</button>
|
||||
<button type="button" class="cssbtn" id="btn_modify" level="W" status="DETAIL,NEW"><i class="material-icons">save</i> 저장</button>
|
||||
<button type="button" class="cssbtn" id="btn_previous" level="R" status="DETAIL,NEW"><i class="material-icons">arrow_back</i> 이전</button>
|
||||
</div>
|
||||
<div class="title">암호화 모듈 설정 상세</div>
|
||||
|
||||
<form id="ajaxForm">
|
||||
<input type="hidden" name="cryptoId" />
|
||||
<table class="table_row" cellspacing="0">
|
||||
<tr>
|
||||
<th style="width:20%;">모듈명 *</th>
|
||||
<td><input type="text" name="cryptoName" style="width:300px;" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>설명</th>
|
||||
<td><input type="text" name="cryptoDesc" style="width:400px;" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>알고리즘 *</th>
|
||||
<td>
|
||||
<select name="algType">
|
||||
<option value="">선택</option>
|
||||
<option value="AES">AES</option>
|
||||
<option value="ARIA">ARIA</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>운영모드 *</th>
|
||||
<td>
|
||||
<select name="cipherMode">
|
||||
<option value="">선택</option>
|
||||
<option value="CBC">CBC</option>
|
||||
<option value="GCM">GCM</option>
|
||||
<option value="ECB">ECB</option>
|
||||
<option value="FF1">FF1</option>
|
||||
<option value="FF3-1">FF3-1</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>패딩</th>
|
||||
<td>
|
||||
<select name="padding">
|
||||
<option value="">없음</option>
|
||||
<option value="PKCS5Padding">PKCS5Padding</option>
|
||||
<option value="NoPadding">NoPadding</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>IV (Hex)</th>
|
||||
<td>
|
||||
<input type="text" name="ivHex" style="width:380px;" placeholder="HEX 직접 입력" />
|
||||
<span id="ivHexLen" style="margin-left:8px; color:#888; font-size:12px;"></span>
|
||||
<br/>
|
||||
<input type="text" id="ivHexText" style="width:260px; margin-top:4px;" placeholder="텍스트 입력 → HEX 자동 변환"
|
||||
oninput="updateHexFromText(this, 'ivHex', 'ivHexLen')" />
|
||||
<span class="help-inline">CBC: 16바이트(32자) 필수 / GCM: 설정 시 기본 AAD로 사용</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>키 소스 유형 *</th>
|
||||
<td>
|
||||
<select name="keySourceType">
|
||||
<option value="">선택</option>
|
||||
<option value="STATIC">STATIC</option>
|
||||
<option value="DYNAMIC">DYNAMIC</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<!-- STATIC 전용 -->
|
||||
<tr class="static-section">
|
||||
<th>암호화 키 (Hex) *</th>
|
||||
<td>
|
||||
<input type="text" name="encKeyHex" style="width:380px;" placeholder="HEX 직접 입력" />
|
||||
<span id="encKeyLen" style="margin-left:8px; color:#888; font-size:12px;"></span>
|
||||
<br/>
|
||||
<input type="text" id="encKeyText" style="width:260px; margin-top:4px;" placeholder="텍스트 입력 → HEX 자동 변환"
|
||||
oninput="updateHexFromText(this, 'encKeyHex', 'encKeyLen')" />
|
||||
<span class="help-inline">AES-128: 16bytes(32자) / AES-192: 24bytes(48자) / AES-256: 32bytes(64자)</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="static-section">
|
||||
<th>복호화 키 (Hex)</th>
|
||||
<td>
|
||||
<input type="text" name="decKeyHex" style="width:380px;" placeholder="HEX 직접 입력" />
|
||||
<span id="decKeyLen" style="margin-left:8px; color:#888; font-size:12px;"></span>
|
||||
<br/>
|
||||
<input type="text" id="decKeyText" style="width:260px; margin-top:4px;" placeholder="텍스트 입력 → HEX 자동 변환"
|
||||
oninput="updateHexFromText(this, 'decKeyHex', 'decKeyLen')" />
|
||||
<span class="help-inline">미입력 시 암호화 키와 동일</span>
|
||||
</td>
|
||||
</tr>
|
||||
<!-- DYNAMIC 전용 -->
|
||||
<tr class="dynamic-section">
|
||||
<th>키 도출 전략 (FQCN) *</th>
|
||||
<td><input type="text" name="keyDerivStrategy" style="width:500px;" /></td>
|
||||
</tr>
|
||||
<tr class="dynamic-section">
|
||||
<th>키 도출 파라미터 (JSON) *</th>
|
||||
<td><textarea name="keyDerivParams" style="width:500px; height:80px;"></textarea></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>동적 키 캐시 *</th>
|
||||
<td>
|
||||
<select name="cacheYn">
|
||||
<option value="N">N</option>
|
||||
<option value="Y">Y</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>캐시 TTL (초)</th>
|
||||
<td>
|
||||
<input type="number" name="cacheTtlSec" style="width:100px;" />
|
||||
<span class="help-inline">기본값 300</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>사용 여부 *</th>
|
||||
<td>
|
||||
<select name="useYn">
|
||||
<option value="Y">Y</option>
|
||||
<option value="N">N</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr id="row_modified_info" style="display:none;">
|
||||
<th>수정자 / 수정일시</th>
|
||||
<td>
|
||||
<span id="span_modified_by" style="font-weight:bold;"></span>
|
||||
<span style="margin:0 8px; color:#ccc;">|</span>
|
||||
<span id="span_modified_at" style="color:#555;"></span>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -44,16 +44,14 @@
|
||||
'<%=localeMessage.getString("stdDefaultMan.columDesc")%>',
|
||||
'<%=localeMessage.getString("stdDefaultMan.columLen")%>',
|
||||
'<%=localeMessage.getString("stdDefaultMan.keyOr")%>',
|
||||
'<%=localeMessage.getString("stdDefaultMan.ifOr")%>',
|
||||
'<%=localeMessage.getString("stdDefaultMan.orderSeq")%>'
|
||||
'<%=localeMessage.getString("stdDefaultMan.ifOr")%>'
|
||||
],
|
||||
colModel:[
|
||||
{ name : 'COLUMNNAME' , align:'left' , width: '200px', sortable:false },
|
||||
{ name : 'COLUMNDESC' , align:'left' , width: '200px' },
|
||||
{ name : 'COLUMNLENGTH' , align:'center' , width: '100px' },
|
||||
{ name : 'ISKEY' , align:'center', width: '100px' },
|
||||
{ name : 'ISINTERFACE' , align:'center', width: '100px' },
|
||||
{ name : 'ORDERSEQ' , align:'center', width: '100px' }
|
||||
{ name : 'COLUMNNAME' , align:'left' , sortable:false },
|
||||
{ name : 'COLUMNDESC' , align:'left' },
|
||||
{ name : 'COLUMNLENGTH' , align:'right' },
|
||||
{ name : 'ISKEY' , align:'center' },
|
||||
{ name : 'ISINTERFACE' , align:'center' }
|
||||
],
|
||||
jsonReader: {
|
||||
repeatitems:false
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
<script language="javascript" >
|
||||
var url = '<c:url value="/onl/apim/apigroup/apiGroupMan.json"/>';
|
||||
var url_view = '<c:url value="/onl/apim/apigroup/apiGroupMan.view"/>';
|
||||
var url_popup_view = '<c:url value="/onl/transaction/apim/apiSpecMan.view"/>';
|
||||
var url_popup_view = '<c:url value="/onl/transaction/apim/apiSpecManPopup.view"/>';
|
||||
|
||||
var isDetail = false;
|
||||
let dialog;
|
||||
|
||||
@@ -38,22 +38,12 @@
|
||||
var loginUser = '<%= loginVo.getUserId() %>';
|
||||
var url = '<c:url value="/onl/apim/approval/portalApprovalMan.json" />';
|
||||
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 () {
|
||||
var key = "${param.id}";
|
||||
if (key) {
|
||||
detail(key);
|
||||
}
|
||||
|
||||
$("#expectEndDate").datepicker().inputmask("yyyy-mm-dd", {'autoUnmask': true});
|
||||
|
||||
$("#btn_app_approve").click(function () {
|
||||
if (confirm("승인하시겠습니까?")) {
|
||||
@@ -122,28 +112,6 @@
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
$("#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 () {
|
||||
window.close();
|
||||
@@ -158,25 +126,12 @@
|
||||
data: {cmd: 'DETAIL', id: key},
|
||||
success: function (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 사용");
|
||||
$("#approvalStatus").text(data.approvalStatus.description);
|
||||
$("#requesterName").text(data.requester.userName);
|
||||
$("#approvalSubject").text(data.approvalSubject);
|
||||
$("#approvalDetail").html(data.approvalDetail);
|
||||
$("#createdDate").text(data.createdDate).inputmask("9999-99-99 99:99:99.999", {'autoUnmask': true});
|
||||
$("#expectEndDate").val(data.expectEndDate);
|
||||
|
||||
if (data.approvers.some(approver =>
|
||||
approver.approverStatus === 'CURRENT' &&
|
||||
@@ -189,7 +144,7 @@
|
||||
document.getElementById("btn_app_reject").style.display = "none";
|
||||
}
|
||||
|
||||
if (data.approvalStatus.code === 'FAILED') {
|
||||
if (data.approvalStatus.description === '배포실패') {
|
||||
document.getElementById("btn_app_redeploy").style.display = "";
|
||||
}
|
||||
|
||||
@@ -287,23 +242,14 @@
|
||||
</tr>
|
||||
<tr>
|
||||
<th>상세 내용</th>
|
||||
<td>
|
||||
<td colspan="3">
|
||||
<div id="approvalDetail" style="white-space: pre-wrap;"></div>
|
||||
</td>
|
||||
<th>예상완료일</th>
|
||||
<td>
|
||||
<input type="text" id="expectEndDate" style="width: 120px; text-align:center"/>
|
||||
<span id="spExpectEndDate"></span>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<div class="title">Client 정보</div>
|
||||
<table id="client" class="table_row" cellspacing="0">
|
||||
<colgroup>
|
||||
<col style="width:20%"/>
|
||||
<col style="width:80%"/>
|
||||
</colgroup>
|
||||
<tr>
|
||||
<th style="width:20%;">이름</th>
|
||||
<td><span id="clientName"></span></td>
|
||||
@@ -336,9 +282,6 @@
|
||||
</table>
|
||||
</div>
|
||||
<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
|
||||
class="material-icons">check</i>승인
|
||||
</button>
|
||||
|
||||
@@ -17,59 +17,10 @@
|
||||
<script language="javascript" >
|
||||
var url = '<c:url value="/onl/apim/approval/portalApprovalMan.json" />';
|
||||
var url_view = '<c:url value="/onl/apim/approval/portalApprovalMan.view" />';
|
||||
var hardDeleteEnabled = ${hardDeleteEnabled};
|
||||
const APPROVAL_TYPE_DISPLAY = {
|
||||
'USER': '법인사용자',
|
||||
'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() {
|
||||
var selectedIds = $("#grid").jqGrid('getGridParam', 'selarrrow');
|
||||
|
||||
if (selectedIds.length === 0) {
|
||||
showAlert("삭제할 승인 요청을 선택해주세요.", {type: 'warning'});
|
||||
return;
|
||||
}
|
||||
|
||||
if (!hardDeleteEnabled) {
|
||||
showAlert("완전삭제 기능이 비활성화되어 있습니다.", {type: 'error'});
|
||||
return;
|
||||
}
|
||||
|
||||
var confirmMsg = "선택된 " + selectedIds.length + "건을 완전삭제(DB 영구 삭제)하시겠습니까?";
|
||||
showConfirm(confirmMsg, {
|
||||
type: 'error',
|
||||
onConfirm: function() {
|
||||
showConfirm("최종 확인: 완전삭제는 되돌릴 수 없습니다. 정말 실행하시겠습니까?", {
|
||||
type: 'error',
|
||||
title: '최종 확인',
|
||||
onConfirm: function() {
|
||||
$.ajax({
|
||||
type: "POST", url: url, dataType: "json",
|
||||
data: { cmd: 'HARD_DELETE_MULTIPLE', ids: selectedIds.join(',') },
|
||||
success: function(data) {
|
||||
showAlert(data.message, {
|
||||
type: data.status === 'success' ? 'success' : 'error',
|
||||
onClose: function() { $("#grid").trigger("reloadGrid"); }
|
||||
});
|
||||
},
|
||||
error: function(e) {
|
||||
showAlert("완전삭제 오류: " + e.responseText, {type: 'error'});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
$('#grid').jqGrid({
|
||||
@@ -82,7 +33,7 @@
|
||||
searchApprovalStatus: $('select[name=searchApprovalStatus]').val(),
|
||||
searchApprovalSubject: $('input[name=searchApprovalSubject]').val()
|
||||
},
|
||||
colNames: ['No.', 'id', '제목', '승인 유형', '승인 유형', '승인 상태','요청자', '생성일', '예상완료일'],
|
||||
colNames: ['No.', 'id', '제목', '승인 유형', '승인 유형', '승인 상태','요청자', '생성일'],
|
||||
colModel: [
|
||||
{ name: 'rowNum', align: 'center', width: '30', sortable: false},
|
||||
{ name: 'id', align: 'center', key: true, hidden: true },
|
||||
@@ -91,8 +42,7 @@
|
||||
{ name: 'approvalType', align: 'center', width: 80, hidden: true},
|
||||
{ name: 'approvalStatus.description', align: 'center', width: 80 },
|
||||
{ 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: 'expectEndDate', align: 'center', width: 100, formatter: dateFormat},
|
||||
{ name: 'createdDate', align: 'center', width: 120, formatter: 'date', formatoptions: { srcformat: 'ISO8601Long', newformat: 'Y-m-d H:i:s' } }
|
||||
],
|
||||
jsonReader: {
|
||||
repeatitems: false
|
||||
@@ -103,8 +53,6 @@
|
||||
viewrecords: true,
|
||||
autowidth: true,
|
||||
height: 'auto',
|
||||
multiselect: true,
|
||||
multiboxonly: true,
|
||||
ondblClickRow: function(rowId) {
|
||||
var rowData = $(this).getRowData(rowId);
|
||||
var id = rowData['id'];
|
||||
@@ -131,24 +79,6 @@
|
||||
// var reverseNumber = records - ((page - 1) * rowNum + i);
|
||||
$(this).setCell(rows[i], 'rowNum', number);
|
||||
}
|
||||
|
||||
// 승인 상태별 행 배경색: 요청됨(#fff3cd) / 진행중(#d1ecf1) 강조
|
||||
if (d && d.rows) {
|
||||
for (var j = 0; j < d.rows.length; j++) {
|
||||
var st = d.rows[j].approvalStatus || {};
|
||||
var code = st.code || '';
|
||||
var desc = st.description || '';
|
||||
var bg = '';
|
||||
if (code === 'REQUESTED' || desc === '요청됨') {
|
||||
bg = '#fff3cd';
|
||||
} else if (code === 'PROCESSING' || desc === '진행중') {
|
||||
bg = '#d1ecf1';
|
||||
}
|
||||
if (bg) {
|
||||
$('#grid tr#' + $.jgrid.jqID(d.rows[j].id)).css('background-color', bg);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
loadError: function(jqXHR, textStatus, errorThrown) {
|
||||
var location = '<%=request.getContextPath()%>/';
|
||||
@@ -163,10 +93,6 @@
|
||||
$("#grid").setGridParam({ url: url, postData: postData, page: 1 }).trigger("reloadGrid");
|
||||
});
|
||||
|
||||
$("#btn_delete_selected").click(function() {
|
||||
deleteSelectedApprovals();
|
||||
});
|
||||
|
||||
$("select[name^=search], input[name^=search]").keydown(function(key) {
|
||||
if (key.keyCode == 13) {
|
||||
$("#btn_search").click();
|
||||
@@ -186,17 +112,9 @@
|
||||
</div><!-- end content_top -->
|
||||
<div class="content_middle" id="content_middle">
|
||||
<div class="search_wrap">
|
||||
<c:if test="${hardDeleteEnabled}">
|
||||
<button type="button" class="cssbtn" id="btn_delete_selected" level="W" style="background-color: #dc3545; border-color: #dc3545; color: white;"><i class="material-icons">delete_forever</i> 선택 삭제</button>
|
||||
</c:if>
|
||||
<button type="button" class="cssbtn" id="btn_search" level="R"><i class="material-icons">search</i> <%= localeMessage.getString("button.search") %></button>
|
||||
</div>
|
||||
<div class="title">승인 요청 목록<span class="tooltip">승인 요청을 관리합니다.</span></div>
|
||||
<c:if test="${hardDeleteEnabled}">
|
||||
<div style="background:#fff3cd; border:1px solid #ffc107; padding:8px 15px; margin:5px 0; border-radius:4px; color:#856404;">
|
||||
<strong>[주의]</strong> 삭제 기능은 <strong>개발(dev) 환경에서만</strong> 동작합니다. 선택 삭제 시 승인 요청과 승인자 이력이 DB에서 영구 삭제됩니다.
|
||||
</div>
|
||||
</c:if>
|
||||
<form id="ajaxForm" onsubmit="return false;">
|
||||
<table class="search_condition" cellspacing="0">
|
||||
<tbody>
|
||||
@@ -216,8 +134,8 @@
|
||||
<div class="select-style">
|
||||
<select name="searchApprovalStatus">
|
||||
<option value="">전체</option>
|
||||
<option value="CREATED">생성됨</option>
|
||||
<option value="REQUESTED">요청됨</option>
|
||||
<option value="PROCESSING">진행중</option>
|
||||
<option value="DENIED">반려됨</option>
|
||||
<option value="END">승인됨</option>
|
||||
</select>
|
||||
|
||||
@@ -392,21 +392,15 @@
|
||||
|
||||
<div class="title">사용자 정보</div>
|
||||
<table class="table_row" cellspacing="0">
|
||||
<colgroup>
|
||||
<col style="width:20%"/>
|
||||
<col style="width:30%"/>
|
||||
<col style="width:20%"/>
|
||||
<col style="width:30%"/>
|
||||
</colgroup>
|
||||
<tr>
|
||||
<th>이메일 아이디</th>
|
||||
<td colspan="3"><span id="loginId"></span></td>
|
||||
<th style="width:20%;">이메일 아이디</th>
|
||||
<td style="width:30%;" colspan="3"><span id="loginId"></span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>이름</th>
|
||||
<td><span id="userName"></span></td>
|
||||
<th>권한</th>
|
||||
<td><span id="roleCodeDescription"></span></td>
|
||||
<th style="width:20%;">이름</th>
|
||||
<td style="width:30%;"><span id="userName"></span></td>
|
||||
<th style="width:20%;">권한</th>
|
||||
<td style="width:30%;"><span id="roleCodeDescription"></span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>핸드폰 번호</th>
|
||||
|
||||
@@ -1,213 +0,0 @@
|
||||
<%@ page language="java" contentType="text/html; charset=utf-8" %>
|
||||
<%@ page import="java.io.*" %>
|
||||
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
|
||||
<%@ taglib uri="http://www.springframework.org/tags" prefix="spring" %>
|
||||
<%@ include file="/jsp/common/include/localemessage.jsp" %>
|
||||
<%
|
||||
response.setHeader("Pragma", "No-cache");
|
||||
response.setHeader("Cache-Control", "no-cache");
|
||||
response.setHeader("Expires", "0");
|
||||
%>
|
||||
<html>
|
||||
<head>
|
||||
<title></title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<jsp:include page="/jsp/common/include/css.jsp"/>
|
||||
<jsp:include page="/jsp/common/include/script.jsp"/>
|
||||
<style>
|
||||
.mask-email { color:#1a73e8; font-weight:bold; }
|
||||
.mask-phone { color:#d93025; font-weight:bold; }
|
||||
.mask-digits { color:#9334e6; font-weight:bold; }
|
||||
.msg-cell { display:inline-block; max-width:380px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; vertical-align:middle; }
|
||||
</style>
|
||||
<script language="javascript">
|
||||
var url = '<c:url value="/onl/apim/messagerequest/messageRequestMan.json" />';
|
||||
var url_view = '<c:url value="/onl/apim/messagerequest/messageRequestMan.view" />';
|
||||
|
||||
function escapeHtmlJs(s) { return $('<div>').text(s == null ? '' : s).html(); }
|
||||
function escapeAttr(s) { return escapeHtmlJs(s).replace(/"/g, '"'); }
|
||||
|
||||
// 제목: 없으면 messageCode, 마우스오버에 코드명(코드) 표기
|
||||
function formatSubject(cellvalue, options, rowObject) {
|
||||
var code = rowObject.messageCode || '';
|
||||
var subject = (cellvalue && cellvalue !== '') ? cellvalue : '';
|
||||
var title = rowObject.messageCodeName ? (rowObject.messageCodeName + ' (' + code + ')') : code;
|
||||
if (subject) {
|
||||
// 제목이 있으면 2줄: 제목 + 하단에 메세지코드
|
||||
var codeLine = code ? '<br><span style="color:#888;font-size:0.85em;">' + escapeHtmlJs(code) + '</span>' : '';
|
||||
return '<span title="' + escapeAttr(title) + '">' + escapeHtmlJs(subject) + codeLine + '</span>';
|
||||
}
|
||||
// 제목이 없으면 메세지코드만
|
||||
return '<span title="' + escapeAttr(title) + '">' + escapeHtmlJs(code) + '</span>';
|
||||
}
|
||||
|
||||
// 본문: 서버에서 마스킹+escape 된 안전 HTML 그대로 렌더
|
||||
function formatMessage(cellvalue, options, rowObject) {
|
||||
return '<span class="msg-cell">' + (rowObject.messageHtml || '') + '</span>';
|
||||
}
|
||||
|
||||
// 수신자 정보: 휴대폰 / 이메일 / 메신저ID (모두 마스킹됨)
|
||||
function formatReceiverInfo(cellvalue, options, rowObject) {
|
||||
var parts = [];
|
||||
if (rowObject.phone) parts.push(escapeHtmlJs(rowObject.phone));
|
||||
if (rowObject.email) parts.push(escapeHtmlJs(rowObject.email));
|
||||
if (rowObject.messengerId) parts.push(escapeHtmlJs(rowObject.messengerId));
|
||||
return parts.join('<br>');
|
||||
}
|
||||
|
||||
function formatRequestDate(cellvalue, options, rowObject) {
|
||||
return '<span title="' + escapeAttr(rowObject.requestDateFull || '') + '">' + escapeHtmlJs(rowObject.requestDateShort || '') + '</span>';
|
||||
}
|
||||
function formatSentDate(cellvalue, options, rowObject) {
|
||||
return '<span title="' + escapeAttr(rowObject.sentDateFull || '') + '">' + escapeHtmlJs(rowObject.sentDateShort || '') + '</span>';
|
||||
}
|
||||
|
||||
function init() {
|
||||
$.ajax({
|
||||
type: "POST", url: url, dataType: "json",
|
||||
data: {cmd: 'LIST_INIT_COMBO'},
|
||||
success: function (json) {
|
||||
new makeOptions("CODE", "NAME").setObj($("select[name=searchRequestStatus]"))
|
||||
.setNoValueInclude(true).setNoValue("", "<%=localeMessage.getString("combo.all")%>")
|
||||
.setData(json.statusList).rendering();
|
||||
new makeOptions("CODE", "NAME").setObj($("select[name=searchMessageCode]"))
|
||||
.setNoValueInclude(true).setNoValue("", "<%=localeMessage.getString("combo.all")%>")
|
||||
.setData(json.messageCodeList).rendering();
|
||||
|
||||
putSelectFromParam();
|
||||
|
||||
// 콤보 로드 후 그리드 생성 (경합 방지)
|
||||
buildGrid();
|
||||
},
|
||||
error: function (e) { alert(e.responseText); }
|
||||
});
|
||||
}
|
||||
|
||||
function buildGrid() {
|
||||
$('#grid').jqGrid({
|
||||
datatype: "json", mtype: 'POST', url: url,
|
||||
postData: getSearchForJqgrid("cmd", "LIST"),
|
||||
colNames: ['id', 'No.', '제목', '메세지 내용', '수신자명', '수신자 정보', '유형', '상태', '요청시간', '발송시간'],
|
||||
colModel: [
|
||||
{name: 'id', align: 'center', key: true, hidden: true},
|
||||
{name: 'rowNum', align: 'center', width: 45, sortable: false},
|
||||
{name: 'subject', align: 'left', width: 160, formatter: formatSubject},
|
||||
{name: 'messageHtml', align: 'left', width: 300, formatter: formatMessage},
|
||||
{name: 'username', align: 'center', width: 80},
|
||||
{name: 'receiverInfo', align: 'left', width: 150, formatter: formatReceiverInfo},
|
||||
{name: 'messageType', align: 'center', width: 60},
|
||||
{name: 'requestStatus', align: 'center', width: 70},
|
||||
{name: 'requestDateShort', align: 'center', width: 110, formatter: formatRequestDate},
|
||||
{name: 'sentDateShort', align: 'center', width: 110, formatter: formatSentDate}
|
||||
],
|
||||
jsonReader: {repeatitems: false},
|
||||
pager: $('#pager'),
|
||||
page: '${param.page}',
|
||||
rowNum: '${rmsDefaultRowNum}',
|
||||
autoheight: true,
|
||||
height: $("#container").height(),
|
||||
autowidth: true,
|
||||
viewrecords: true,
|
||||
rowList: eval('[${rmsDefaultRowList}]'),
|
||||
ondblClickRow: function (rowId) {
|
||||
var rowData = $(this).getRowData(rowId);
|
||||
var id = rowData['id'];
|
||||
var url2 = url_view + '?cmd=DETAIL';
|
||||
url2 += '&page=' + $(this).getGridParam("page");
|
||||
url2 += '&returnUrl=' + getReturnUrl();
|
||||
url2 += '&menuId=' + '${param.menuId}';
|
||||
url2 += '&id=' + id;
|
||||
goNav(url2);
|
||||
},
|
||||
loadComplete: function () {
|
||||
var page = $(this).getGridParam('page');
|
||||
var rowNum = $(this).getGridParam('rowNum');
|
||||
var rows = $(this).getDataIDs();
|
||||
var colModel = $(this).getGridParam("colModel");
|
||||
for (var i = 0; i < rows.length; i++) {
|
||||
var number = ((page - 1) * rowNum + i) + 1;
|
||||
$(this).setCell(rows[i], 'rowNum', number);
|
||||
}
|
||||
// 서버 고정 정렬(요청일 역순) — 컬럼 정렬 비활성
|
||||
for (var i = 0; i < colModel.length; i++) {
|
||||
$(this).setColProp(colModel[i].name, {sortable: false});
|
||||
}
|
||||
},
|
||||
loadError: function (jqXHR, textStatus, errorThrown) {
|
||||
var location = '<%=request.getContextPath()%>/';
|
||||
comloadError(jqXHR, textStatus, errorThrown, location);
|
||||
}
|
||||
});
|
||||
resizeJqGridWidth('grid', 'content_middle', '1000');
|
||||
}
|
||||
|
||||
$(document).ready(function () {
|
||||
init();
|
||||
|
||||
$("#btn_search").click(function () {
|
||||
var postData = getSearchForJqgrid("cmd", "LIST");
|
||||
$("#grid").setGridParam({url: url, postData: postData, page: 1}).trigger("reloadGrid");
|
||||
});
|
||||
|
||||
$("select[name^=search]").change(function () { $("#btn_search").click(); });
|
||||
$("input[name^=search]").keydown(function (key) {
|
||||
if (key.keyCode == 13) { $("#btn_search").click(); }
|
||||
});
|
||||
|
||||
buttonControl();
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="right_box">
|
||||
<div class="content_top">
|
||||
<ul class="path"><li><a href="#">${rmsMenuPath}</a></li></ul>
|
||||
</div><!-- end content_top -->
|
||||
<div class="content_middle" id="content_middle">
|
||||
<div class="search_wrap">
|
||||
<button type="button" class="cssbtn" id="btn_search" level="R"><i class="material-icons">search</i> <%= localeMessage.getString("button.search") %></button>
|
||||
</div>
|
||||
<div class="title">메세지 발송 내역<span class="tooltip">발송 요청 이력을 조회합니다. 개인정보는 마스킹되어 표시됩니다.</span></div>
|
||||
<form id="ajaxForm" onsubmit="return false;">
|
||||
<table class="search_condition" cellspacing=0;>
|
||||
<colgroup>
|
||||
<col style="width:120px;"><col style="width:200px;">
|
||||
<col style="width:120px;"><col style="width:200px;">
|
||||
<col style="width:120px;"><col style="width:200px;">
|
||||
</colgroup>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>상태</th>
|
||||
<td><div class="select-style"><select name="searchRequestStatus"></select></div></td>
|
||||
<th>메세지 코드</th>
|
||||
<td><div class="select-style"><select name="searchMessageCode"></select></div></td>
|
||||
<th>수신자명</th>
|
||||
<td><input type="text" name="searchRecipient" autocomplete="off"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>유형</th>
|
||||
<td>
|
||||
<div class="select-style">
|
||||
<select name="searchMessageType">
|
||||
<option value="">전체</option>
|
||||
<option value="EMAIL">EMAIL</option>
|
||||
<option value="SMS">SMS</option>
|
||||
<option value="KAKAO">KAKAO</option>
|
||||
<option value="MESSENGER">MESSENGER</option>
|
||||
</select>
|
||||
</div>
|
||||
</td>
|
||||
<th>요청기간</th>
|
||||
<td colspan="3">
|
||||
<input type="date" name="searchFromDate"> ~ <input type="date" name="searchToDate">
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</form>
|
||||
<table id="grid"></table>
|
||||
<div id="pager"></div>
|
||||
</div><!-- end content_middle -->
|
||||
</div><!-- end right_box -->
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,149 +0,0 @@
|
||||
<%@ page language="java" contentType="text/html; charset=utf-8" %>
|
||||
<%@ page import="java.io.*" %>
|
||||
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
|
||||
<%@ taglib uri="http://www.springframework.org/tags" prefix="spring" %>
|
||||
<%@ include file="/jsp/common/include/localemessage.jsp" %>
|
||||
<%
|
||||
response.setHeader("Pragma", "No-cache");
|
||||
response.setHeader("Cache-Control", "no-cache");
|
||||
response.setHeader("Expires", "0");
|
||||
%>
|
||||
<html>
|
||||
<head>
|
||||
<title></title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<jsp:include page="/jsp/common/include/css.jsp"/>
|
||||
<jsp:include page="/jsp/common/include/script.jsp"/>
|
||||
<style>
|
||||
.mask-email { color:#1a73e8; font-weight:bold; }
|
||||
.mask-phone { color:#d93025; font-weight:bold; }
|
||||
.mask-digits { color:#9334e6; font-weight:bold; }
|
||||
.msg-detail { white-space:pre-wrap; word-break:break-all; min-height:80px; line-height:1.6; }
|
||||
</style>
|
||||
<script language="javascript">
|
||||
var url = '<c:url value="/onl/apim/messagerequest/messageRequestMan.json" />';
|
||||
var url_view = '<c:url value="/onl/apim/messagerequest/messageRequestMan.view" />';
|
||||
var key = '${param.id}';
|
||||
|
||||
function detail() {
|
||||
if (!key) return;
|
||||
$.ajax({
|
||||
type: "POST", url: url, dataType: "json",
|
||||
data: {cmd: 'DETAIL', id: key},
|
||||
success: function (data) {
|
||||
var codeText = (data.messageCodeName || '') + (data.messageCode ? ' (' + data.messageCode + ')' : '');
|
||||
$('#messageCodeName').text(codeText);
|
||||
$('#subject').text((data.subject && data.subject !== '') ? data.subject : (data.messageCode || ''));
|
||||
$('#messageType').text(data.messageType || '');
|
||||
$('#requestStatus').text(data.requestStatus || '');
|
||||
$('#username').text(data.username || '');
|
||||
$('#email').text(data.email || '');
|
||||
$('#phone').text(data.phone || '');
|
||||
$('#messengerId').text(data.messengerId || '');
|
||||
$('#umsUid').text(data.umsUid || '');
|
||||
$('#eaiInterfaceId').text(data.eaiInterfaceId || '');
|
||||
$('#serviceId').text(data.serviceId || '');
|
||||
$('#requestDate').text(data.requestDateFull || '');
|
||||
$('#sentDate').text(data.sentDateFull || '');
|
||||
// 서버에서 마스킹+escape 된 안전 HTML
|
||||
$('#messageHtml').html(data.messageHtml || '');
|
||||
|
||||
// PENDING 건만 '실패 처리' 노출 (권한 제어는 buttonControl 이 담당)
|
||||
if (data.requestStatus !== 'PENDING') {
|
||||
$('#btn_fail').hide();
|
||||
}
|
||||
},
|
||||
error: function (e) { alert(e.responseText); }
|
||||
});
|
||||
}
|
||||
|
||||
$(document).ready(function () {
|
||||
var returnUrl = getReturnUrlForReturn();
|
||||
|
||||
buttonControl();
|
||||
detail();
|
||||
|
||||
$('#btn_fail').click(function () {
|
||||
if (!confirm('이 건을 FAILED(발송 실패) 상태로 변경하시겠습니까?')) return;
|
||||
$.ajax({
|
||||
type: "POST", url: url, dataType: "json",
|
||||
data: {cmd: 'UPDATE_STATUS', id: key},
|
||||
success: function () {
|
||||
alert("변경되었습니다.");
|
||||
detail();
|
||||
},
|
||||
error: function (e) { alert(e.responseText); }
|
||||
});
|
||||
});
|
||||
|
||||
$('#btn_previous').click(function () { goNav(returnUrl); });
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="right_box">
|
||||
<div class="content_top">
|
||||
<ul class="path"><li><a href="#">${rmsMenuPath}</a></li></ul>
|
||||
</div>
|
||||
<div class="content_middle">
|
||||
<div class="search_wrap">
|
||||
<button type="button" class="cssbtn" id="btn_fail" level="W" status="DETAIL" style="background-color:#dc3545;border-color:#dc3545;color:#fff;"><i class="material-icons">report</i> 실패 처리</button>
|
||||
<button type="button" class="cssbtn" id="btn_previous" level="R" status="DETAIL,NEW"><i class="material-icons">arrow_back</i> <%= localeMessage.getString("button.previous") %></button>
|
||||
</div>
|
||||
<div class="title" id="title">메세지 발송 내역 상세<span class="tooltip">개인정보는 마스킹되어 표시됩니다.</span></div>
|
||||
<table class="table_row" cellspacing="0">
|
||||
<colgroup>
|
||||
<col style="width:12%"/><col style="width:38%"/>
|
||||
<col style="width:12%"/><col style="width:38%"/>
|
||||
</colgroup>
|
||||
<tr>
|
||||
<th>메세지 코드</th>
|
||||
<td colspan="3"><span id="messageCodeName"></span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>제목</th>
|
||||
<td colspan="3"><span id="subject"></span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>유형</th>
|
||||
<td><span id="messageType"></span></td>
|
||||
<th>상태</th>
|
||||
<td><span id="requestStatus"></span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>수신자명</th>
|
||||
<td><span id="username"></span></td>
|
||||
<th>메신저 ID</th>
|
||||
<td><span id="messengerId"></span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>이메일</th>
|
||||
<td><span id="email"></span></td>
|
||||
<th>휴대폰</th>
|
||||
<td><span id="phone"></span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>요청 시간</th>
|
||||
<td><span id="requestDate"></span></td>
|
||||
<th>발송 시간</th>
|
||||
<td><span id="sentDate"></span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>EAI 인터페이스 ID</th>
|
||||
<td><span id="eaiInterfaceId"></span></td>
|
||||
<th>서비스 ID</th>
|
||||
<td><span id="serviceId"></span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>UMS UID</th>
|
||||
<td colspan="3"><span id="umsUid"></span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>메세지 내용</th>
|
||||
<td colspan="3"><div id="messageHtml" class="msg-detail"></div></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -20,35 +20,15 @@
|
||||
var select_orgId = "";
|
||||
|
||||
function formatInquiryStatus(cellvalue, options, rowObject) {
|
||||
if (cellvalue == 'PENDING') {
|
||||
return '<span style="color: red;">문의중</span>';
|
||||
} else if (cellvalue == 'REVIEWING') {
|
||||
return '<span >문의검토중</span>'
|
||||
} else if (cellvalue == 'RESPONDED') {
|
||||
return '<span >답변완료</span>'
|
||||
} else if (cellvalue == 'CLOSED') {
|
||||
return '<span >답변종료</span>'
|
||||
} else {
|
||||
return cellvalue;
|
||||
}
|
||||
}
|
||||
|
||||
function formatVisibility(cellvalue, options, rowObject) {
|
||||
if (cellvalue == 'PRIVATE') {
|
||||
return '<span style="color:#c0392b; font-weight:bold;">비공개</span>';
|
||||
} else if (cellvalue == 'ALL') {
|
||||
return '전체공개';
|
||||
} else if (cellvalue == 'ORG') {
|
||||
return '법인공개';
|
||||
}
|
||||
return cellvalue;
|
||||
return cellvalue === 'PENDING'
|
||||
? '<span style="color: red;">문의중</span>'
|
||||
: '<span>답변완료</span>';
|
||||
}
|
||||
|
||||
function formatFile(cellvalue, options, rowObject) {
|
||||
var iconPath = '${pageContext.request.contextPath}/images/icon_file.png';
|
||||
var icon = rowObject.hasAttachment ? '<img src="' + iconPath + '" alt="File Attached" style="vertical-align: middle; margin-left: 5px; width: 16px; height: 16px;">' : '';
|
||||
var commentCount = rowObject.commentCount > 0 ? ' <span style="color: #337ab7;">[' + rowObject.commentCount + ']</span>' : '';
|
||||
return cellvalue + icon + commentCount;
|
||||
return cellvalue + icon;
|
||||
}
|
||||
|
||||
function deleteSelectedRows() {
|
||||
@@ -112,14 +92,13 @@
|
||||
mtype: 'POST',
|
||||
url: url,
|
||||
postData : { cmd : 'LIST' },
|
||||
colNames:['No.', 'id', '제목', '법인명', '상태', '공개범위', '작성자', '등록일', '답변자','답변일'],
|
||||
colNames:['No.', 'id', '제목', '법인명', '상태', '작성자', '등록일', '답변자','답변일'],
|
||||
colModel:[
|
||||
{ name : 'rowNum' , align:'center', width:50, sortable:false },
|
||||
{ name : 'id' , align:'center', key:true, hidden:true},
|
||||
{ name : 'inquirySubject' , align:'left' , width:200 , formatter: formatFile},
|
||||
{ name : 'orgName' , align:'center' , width:100 , },
|
||||
{ name : 'inquiryStatus' , align:'center', width:70, formatter: formatInquiryStatus },
|
||||
{ name : 'visibility' , align:'center', width:70, formatter: formatVisibility },
|
||||
{ name : 'inquirer.userName', align:'center', width:70 },
|
||||
{ name : 'createdDate' , align:'center', width:100, formatter: timeStampFormat },
|
||||
{ name : 'responderName' , align:'center', width:70 },
|
||||
@@ -221,9 +200,7 @@
|
||||
<select name="searchInquiryStatus">
|
||||
<option value="">전체</option>
|
||||
<option value="PENDING">문의중</option>
|
||||
<option value="REVIEWING">문의검토중</option>
|
||||
<option value="RESPONDED">답변완료</option>
|
||||
<option value="CLOSED">답변종료</option>
|
||||
</select>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
@@ -36,13 +36,6 @@
|
||||
padding-bottom: 10px;
|
||||
border-bottom: 2px solid #e0e0e0; /* 제목 아래 줄 긋기 */
|
||||
}
|
||||
|
||||
.comment-section {
|
||||
margin-bottom: 30px;
|
||||
border: 1px solid #e0e0e0; /* 동일한 박스 스타일 */
|
||||
padding: 20px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.info-row {
|
||||
margin-bottom: 10px;
|
||||
@@ -64,61 +57,17 @@
|
||||
width: 100%;
|
||||
min-height: 200px;
|
||||
}
|
||||
|
||||
#commentDetail {
|
||||
min-height: 100px;
|
||||
border: 1px solid #00000032;
|
||||
border-radius: 4px;
|
||||
padding: 10px;
|
||||
flex: 1;
|
||||
font-family: '맑은고딕'
|
||||
}
|
||||
|
||||
.required-mark {
|
||||
color: red;
|
||||
margin-left: 2px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.reason-modal-overlay {
|
||||
display: none;
|
||||
position: fixed;
|
||||
top: 0; left: 0;
|
||||
width: 100%; height: 100%;
|
||||
background: rgba(0, 0, 0, 0.4);
|
||||
z-index: 1000;
|
||||
}
|
||||
.reason-modal-box {
|
||||
position: absolute;
|
||||
top: 50%; left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
background: #fff;
|
||||
border-radius: 6px;
|
||||
padding: 20px;
|
||||
width: 400px;
|
||||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
.reason-modal-box h3 {
|
||||
margin: 0 0 12px;
|
||||
font-size: 16px;
|
||||
}
|
||||
.reason-modal-box textarea {
|
||||
width: 100%;
|
||||
min-height: 80px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.reason-modal-actions {
|
||||
margin-top: 12px;
|
||||
text-align: right;
|
||||
}
|
||||
</style>
|
||||
<script language="javascript">
|
||||
var url = '<c:url value="/onl/apim/portalinquiry/portalInquiryMan.json" />';
|
||||
var url_view = '<c:url value="/onl/apim/portalinquiry/portalInquiryMan.view" />';
|
||||
var file_download = '<c:url value="/file/download.do" />';
|
||||
var inquiryStatus = 'PENDING';
|
||||
var originalVisibility = 'ORG';
|
||||
var reasonModalCallback = null;
|
||||
|
||||
function decodeHTMLEntities(text) {
|
||||
var textArea = document.createElement('textarea');
|
||||
@@ -133,23 +82,10 @@
|
||||
dataType: "json",
|
||||
data: {cmd: 'DETAIL', id: key},
|
||||
success: function (data) {
|
||||
|
||||
|
||||
$("#id").val(key);
|
||||
|
||||
$("#inquirySubject").text(data.inquirySubject);
|
||||
$("#inquiryDetail").html(data.inquiryDetail);
|
||||
|
||||
inquiryStatus = data.inquiryStatus;
|
||||
if (data.inquiryStatus == 'PENDING') {
|
||||
$("#inquiryStatus").text('문의중');
|
||||
} else if (data.inquiryStatus == 'REVIEWING') {
|
||||
$("#inquiryStatus").text('문의검토중');
|
||||
} else if (data.inquiryStatus == 'RESPONDED') {
|
||||
$("#inquiryStatus").text('답변완료');
|
||||
} else if (data.inquiryStatus == 'CLOSED') {
|
||||
$("#inquiryStatus").text('답변종료');
|
||||
}
|
||||
$("#inquiryStatus").text(data.inquiryStatus === 'PENDING' ? '문의중' : '답변완료');
|
||||
$("#inquirer").text(data.inquirer.userName);
|
||||
$("#createdDate").text(timeStampFormat(data.createdDate));
|
||||
$("#responder").text(data.responderName || '');
|
||||
@@ -158,26 +94,6 @@
|
||||
var decodedContent = decodeHTMLEntities(data.responseDetail);
|
||||
$("#responseDetail").summernote('code', decodedContent || '');
|
||||
|
||||
// 공개범위 / 조회수
|
||||
$('#visibilitySelect').val(data.visibility || 'ORG');
|
||||
originalVisibility = data.visibility || 'ORG';
|
||||
$('#viewCount').text(data.viewCount || 0);
|
||||
|
||||
// 조회수 증가 (sessionStorage dedup — 새로고침 시 중복 증가 억제)
|
||||
var viewedKey = 'inquiry_viewed_' + key;
|
||||
if (!sessionStorage.getItem(viewedKey)) {
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: url,
|
||||
data: {cmd: 'INCREMENT_VIEW', id: key},
|
||||
success: function () {
|
||||
sessionStorage.setItem(viewedKey, 'true');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 댓글 표시
|
||||
listComment(key);
|
||||
|
||||
// 첨부 파일 표시
|
||||
displayAttachFiles(data.fileInfo);
|
||||
@@ -188,159 +104,6 @@
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
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 isPrivate = comment.visibility === 'PRIVATE';
|
||||
var row = $('<div>').addClass('info-row');
|
||||
if (isPrivate) {
|
||||
// 비공개 댓글은 색상으로 구별
|
||||
row.css({'background-color': '#FFF5F5', 'border-radius': '4px', 'padding': '4px'});
|
||||
}
|
||||
var labelDiv = $('<div>').addClass('info-label').css('color', '#555').text(comment.writerName || '');
|
||||
if (isPrivate) {
|
||||
$('<span>').text(' 비공개').css({'color': '#c0392b', 'font-size': '0.85em', 'font-weight': 'bold'}).appendTo(labelDiv);
|
||||
}
|
||||
labelDiv.appendTo(row);
|
||||
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);
|
||||
|
||||
// 댓글 공개범위 변경 컨트롤 (사유 필수)
|
||||
var visCtrl = $('<div>').css({'margin-top': '4px', 'font-size': '0.85em'});
|
||||
var sel = $('<select>').append('<option value="ALL">공개</option>').append('<option value="PRIVATE">비공개</option>');
|
||||
sel.val(comment.visibility || 'ALL');
|
||||
var reasonInput = $('<input>').attr({'type': 'text', 'placeholder': '변경 사유'}).css({'margin-left': '4px'});
|
||||
var applyBtn = $('<button>').attr('type', 'button').addClass('cssbtn smallBtn2').css({'margin-left': '4px', 'font-size': '0.85em', 'padding': '1px 6px'})
|
||||
.text('공개범위변경').on('click', (function(id, inquiryId, selEl, reasonEl) {
|
||||
return function() { changeCommentVisibility(id, inquiryId, selEl.val(), reasonEl.val()); };
|
||||
})(comment.id, comment.inquiryId, sel, reasonInput));
|
||||
visCtrl.append(sel).append(reasonInput).append(applyBtn);
|
||||
visCtrl.appendTo(content);
|
||||
|
||||
content.appendTo(row);
|
||||
row.appendTo($commentList);
|
||||
});
|
||||
},
|
||||
error: function (e) {
|
||||
alert(e.responseText);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function openReasonModal(callback) {
|
||||
reasonModalCallback = callback;
|
||||
$('#reasonModalInput').val('');
|
||||
$('#reasonModal').show();
|
||||
$('#reasonModalInput').focus();
|
||||
}
|
||||
|
||||
function closeReasonModal() {
|
||||
$('#reasonModal').hide();
|
||||
reasonModalCallback = null;
|
||||
}
|
||||
|
||||
// 답변(summernote) 내용이 실제로 입력됐는지 확인
|
||||
function hasAnswerContent() {
|
||||
var v = $('#responseDetail').summernote('code');
|
||||
var text = $('<div>').html(v || '').text().replace(/ /g, ' ').trim();
|
||||
return text.length > 0;
|
||||
}
|
||||
|
||||
function saveInquiryAnswer(returnUrl) {
|
||||
var postData = $('#ajaxForm').serializeArray();
|
||||
postData.push({name: "cmd", value: "INSERT"});
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: url,
|
||||
data: postData,
|
||||
success: function (json) {
|
||||
alert("<%= localeMessage.getString("common.saveMsg") %>");
|
||||
goNav(returnUrl);
|
||||
},
|
||||
error: function (e) {
|
||||
alert(e.responseText);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 게시물 공개범위 변경(사유 필수). 성공 후 답변 내용이 있으면 이어서 저장.
|
||||
function changeInquiryVisibility(newVis, reason, returnUrl, thenSaveAnswer) {
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: url,
|
||||
data: {cmd: 'VISIBILITY', serviceType: 'APIGW', targetType: 'INQUIRY', id: $('#id').val(),
|
||||
visibility: newVis, auditReason: reason},
|
||||
success: function () {
|
||||
originalVisibility = newVis;
|
||||
if (thenSaveAnswer) {
|
||||
saveInquiryAnswer(returnUrl);
|
||||
} else {
|
||||
alert('변경되었습니다.');
|
||||
goNav(returnUrl);
|
||||
}
|
||||
},
|
||||
error: function (e) {
|
||||
alert(e.responseText);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function changeCommentVisibility(commentId, inquiryId, visibility, reason) {
|
||||
if (!reason || !reason.trim()) {
|
||||
alert('공개범위 변경 사유를 입력하세요.');
|
||||
return;
|
||||
}
|
||||
if (!confirm('댓글 공개범위를 변경하시겠습니까?')) return;
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: url,
|
||||
data: {cmd: 'VISIBILITY', serviceType: 'APIGW', targetType: 'COMMENT', id: commentId, visibility: visibility, auditReason: reason},
|
||||
success: function () {
|
||||
alert('변경되었습니다.');
|
||||
listComment(inquiryId);
|
||||
},
|
||||
error: function (e) {
|
||||
alert(e.responseText);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function deleteComment(commentId, inquiryId) {
|
||||
if (inquiryStatus == 'CLOSED') {
|
||||
alert('답변이 종료되어 삭제할 수 없습니다');
|
||||
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 () {
|
||||
var returnUrl = getReturnUrlForReturn();
|
||||
@@ -351,70 +114,25 @@
|
||||
placeholder: '여기에 답변을 입력하세요.',
|
||||
height: 200
|
||||
});
|
||||
|
||||
|
||||
if (key) {
|
||||
detail(key);
|
||||
}
|
||||
|
||||
$("#btn_modify").click(function () {
|
||||
if (inquiryStatus == 'CLOSED') {
|
||||
alert('답변이 종료되어 수정할 수 없습니다');
|
||||
return;
|
||||
}
|
||||
|
||||
var newVis = $('#visibilitySelect').val();
|
||||
var visChanged = (newVis !== originalVisibility);
|
||||
|
||||
if (visChanged) {
|
||||
// 공개범위 변경 감지 → 사유 입력 모달 (답변 내용이 있으면 함께 저장)
|
||||
var saveAnswerToo = hasAnswerContent();
|
||||
openReasonModal(function (reason) {
|
||||
changeInquiryVisibility(newVis, reason, returnUrl, saveAnswerToo);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// 공개범위 변경 없음 → 기존 답변 저장
|
||||
if (!checkRequired("ajaxForm")) return;
|
||||
if (!confirm("<%= localeMessage.getString("common.checkSave")%>")) return;
|
||||
saveInquiryAnswer(returnUrl);
|
||||
});
|
||||
|
||||
$("#reasonModalOk").click(function () {
|
||||
var reason = ($('#reasonModalInput').val() || '').trim();
|
||||
if (!reason) {
|
||||
alert('변경 사유를 입력하세요.');
|
||||
return;
|
||||
}
|
||||
var cb = reasonModalCallback;
|
||||
closeReasonModal();
|
||||
if (cb) cb(reason);
|
||||
});
|
||||
|
||||
$("#reasonModalCancel").click(function () {
|
||||
closeReasonModal();
|
||||
});
|
||||
|
||||
$("#btn_comment").click(function () {
|
||||
if (inquiryStatus == 'CLOSED') {
|
||||
alert('답변이 종료되어 댓글을 등록할 수 없습니다');
|
||||
return;
|
||||
}
|
||||
if (!confirm("<%= localeMessage.getString("common.checkSave")%>")) return;
|
||||
var postData = $('#ajaxForm').serializeArray();
|
||||
postData.push({name: "cmd", value: "INSERT"});
|
||||
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: url,
|
||||
data: {
|
||||
cmd: "INSERT_COMMENT",
|
||||
inquiryId: $('#id').val(),
|
||||
commentDetail: $('#commentDetail').val(),
|
||||
},
|
||||
data: postData,
|
||||
success: function (json) {
|
||||
alert("<%= localeMessage.getString("common.saveMsg") %>");
|
||||
$('#commentDetail').val('');
|
||||
listComment(key);
|
||||
goNav(returnUrl);
|
||||
},
|
||||
error: function (e) {
|
||||
alert(e.responseText);
|
||||
@@ -476,22 +194,7 @@
|
||||
<div class="info-label">작성일</div>
|
||||
<div id="createdDate" class="info-content"></div>
|
||||
</div>
|
||||
<div class="info-row">
|
||||
<div class="info-label">조회수</div>
|
||||
<div id="viewCount" class="info-content">0</div>
|
||||
</div>
|
||||
<div class="info-row">
|
||||
<div class="info-label">공개범위</div>
|
||||
<div class="info-content" style="display:flex; gap:8px; align-items:center;">
|
||||
<select id="visibilitySelect">
|
||||
<option value="ALL">전체공개</option>
|
||||
<option value="ORG">법인공개</option>
|
||||
<option value="PRIVATE">비공개</option>
|
||||
</select>
|
||||
<span style="font-size:0.85em; color:#999;">변경 후 상단 [수정] 버튼을 누르면 사유 입력창이 표시됩니다.</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="info-row" id="attachFileRow" style="display: none;">
|
||||
<div class="info-row" id="attachFileRow" style="display: none;">
|
||||
<div class="info-label">첨부파일</div>
|
||||
<div id="attachFiles" class="info-content"></div>
|
||||
</div>
|
||||
@@ -503,10 +206,6 @@
|
||||
|
||||
<!-- 답변 작성 부분 -->
|
||||
<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-label">답변자</div>
|
||||
<div id="responder" class="info-content"></div>
|
||||
@@ -522,34 +221,9 @@
|
||||
</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">
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 공개범위 변경 사유 입력 모달 -->
|
||||
<div id="reasonModal" class="reason-modal-overlay">
|
||||
<div class="reason-modal-box">
|
||||
<h3>공개범위 변경 사유</h3>
|
||||
<textarea id="reasonModalInput" placeholder="변경 사유를 입력하세요."></textarea>
|
||||
<div class="reason-modal-actions">
|
||||
<button type="button" class="cssbtn" id="reasonModalOk">확인</button>
|
||||
<button type="button" class="cssbtn" id="reasonModalCancel">취소</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -17,31 +17,12 @@
|
||||
<script language="javascript" >
|
||||
var url = '<c:url value="/onl/apim/portalnotice/portalNoticeMan.json" />';
|
||||
var url_view = '<c:url value="/onl/apim/portalnotice/portalNoticeMan.view" />';
|
||||
var combo;
|
||||
|
||||
function formatNoticeType(cellvalue, options, rowObject) {
|
||||
if (!combo || !combo.noticeTypeList) {
|
||||
return cellvalue;
|
||||
}
|
||||
for (var i = 0; i < combo.noticeTypeList.length; i++) {
|
||||
if (combo.noticeTypeList[i].CODE == cellvalue) {
|
||||
return combo.noticeTypeList[i].NAME;
|
||||
}
|
||||
}
|
||||
return cellvalue;
|
||||
}
|
||||
|
||||
|
||||
function formatuseYn(cellvalue, options, rowObject) {
|
||||
return cellvalue === 'N'
|
||||
? '<span style="color: red;">미사용</span>'
|
||||
: '<span>사용</span>';
|
||||
}
|
||||
|
||||
function formatFixYn(cellvalue, options, rowObject) {
|
||||
return cellvalue === 'Y'
|
||||
? '고정'
|
||||
: '';
|
||||
}
|
||||
|
||||
function formatFile(cellvalue, options, rowObject) {
|
||||
var iconPath = '${pageContext.request.contextPath}/images/icon_file.png';
|
||||
@@ -49,79 +30,21 @@
|
||||
return cellvalue + icon;
|
||||
}
|
||||
|
||||
function deleteSelectedRows() {
|
||||
var selectedIds = $("#grid").jqGrid('getGridParam', 'selarrrow');
|
||||
|
||||
if (selectedIds.length === 0) {
|
||||
alert('항목을 선택하세요.');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!confirm('선택된 ' + selectedIds.length + '개 항목을 삭제하시겠습니까?')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: url,
|
||||
dataType: "json",
|
||||
data: {
|
||||
cmd: 'DELETE_BULK',
|
||||
ids: selectedIds.join(',')
|
||||
},
|
||||
success: function(data) {
|
||||
alert('삭제되었습니다.');
|
||||
$("#grid").jqGrid('clearGridData', true);
|
||||
$("#grid").trigger("reloadGrid");
|
||||
},
|
||||
error: function(e) {
|
||||
alert('삭제 중 오류가 발생했습니다.');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function init(){
|
||||
$.ajax({
|
||||
type : "POST",
|
||||
url:url,
|
||||
dataType:"json",
|
||||
data:{cmd: 'LIST_INIT_COMBO'},
|
||||
success:function(json){
|
||||
console.log('init', json);
|
||||
combo = json;
|
||||
new makeOptions("CODE","NAME").setObj($("select[name=searchNoticeType]")).setNoValueInclude(true).setNoValue('','<%=localeMessage.getString("combo.all")%>').setData(json.noticeTypeList).rendering();
|
||||
|
||||
putSelectFromParam();
|
||||
|
||||
// 콤보(combo)가 채워진 뒤에 그리드를 생성해야 formatNoticeType 경합이 발생하지 않음
|
||||
buildGrid();
|
||||
},
|
||||
error:function(e){
|
||||
alert(e.responseText);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function buildGrid(){
|
||||
|
||||
$(document).ready(function() {
|
||||
$('#grid').jqGrid({
|
||||
datatype:"json",
|
||||
mtype: 'POST',
|
||||
url: url,
|
||||
postData : { cmd : 'LIST',
|
||||
searchNoticeType: $('select[name=searchNoticeType]').val(),
|
||||
searchUseYn: $('select[name=searchUseYn]').val(),
|
||||
searchNoticeSubject: $('input[name=searchNoticeSubject]').val(),
|
||||
searchNoticeDetail: $('input[name=searchNoticeDetail]').val()
|
||||
},
|
||||
colNames:['id', 'No.', '게시유형', '제목', '고정여부', '사용', '마지막 수정일', '등록일', '등록자', '조회수'],
|
||||
colNames:['id', 'No.', '제목', '사용', '마지막 수정일', '등록일', '등록자', '조회수'],
|
||||
colModel:[
|
||||
{ name : 'id' , align:'center', key:true, hidden:true},
|
||||
{ name : 'rowNum' , align:'center', width:60 },
|
||||
{ name : 'noticeType' , align:'center', width:60, formatter: formatNoticeType },
|
||||
{ name : 'noticeSubject' , align:'left' , width:300, formatter: formatFile },
|
||||
{ name : 'fixYn' , align:'center', width:60, formatter: formatFixYn },
|
||||
{ name : 'rowNum' , align:'center', width:80 },
|
||||
{ name : 'noticeSubject' , align:'left' , width:200 , formatter: formatFile },
|
||||
{ name : 'useYn' , align:'center', width:60, formatter: formatuseYn },
|
||||
{ name : 'lastModifiedDate', align:'center', width:120 , formatter: timeStampFormat},
|
||||
{ name : 'createdDate' , align:'center', width:120, formatter: timeStampFormat },
|
||||
@@ -139,8 +62,6 @@
|
||||
autowidth: true,
|
||||
viewrecords: true,
|
||||
rowList : eval('[${rmsDefaultRowList}]'),
|
||||
multiselect: true,
|
||||
multiboxonly: true,
|
||||
ondblClickRow: function(rowId) {
|
||||
var rowData = $(this).getRowData(rowId);
|
||||
var id = rowData['id'];
|
||||
@@ -178,12 +99,6 @@
|
||||
});
|
||||
|
||||
resizeJqGridWidth('grid','content_middle','1000');
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
|
||||
// 콤보 로드 → 콤보 성공 콜백에서 buildGrid() 호출 (경합 방지)
|
||||
init();
|
||||
|
||||
$("#btn_search").click(function(){
|
||||
var postData = getSearchForJqgrid("cmd","LIST");
|
||||
@@ -199,10 +114,6 @@
|
||||
goNav(url2);
|
||||
});
|
||||
|
||||
$("#btn_delete_selected").click(function(){
|
||||
deleteSelectedRows();
|
||||
});
|
||||
|
||||
$("select[name=searchUseYn], input[name^=search]").keydown(function(key){
|
||||
if (key.keyCode == 13){
|
||||
$("#btn_search").click();
|
||||
@@ -225,30 +136,13 @@
|
||||
<div class="search_wrap">
|
||||
<button type="button" class="cssbtn" id="btn_new" level="W"><i class="material-icons">add</i> <%= localeMessage.getString("button.new") %></button>
|
||||
<button type="button" class="cssbtn" id="btn_search" level="R"><i class="material-icons">search</i> <%= localeMessage.getString("button.search") %></button>
|
||||
<button type="button" class="cssbtn" id="btn_delete_selected" level="W" style="background-color: #dc3545; border-color: #dc3545; color: white;"><i class="material-icons">delete_forever</i> 선택 삭제</button>
|
||||
</div>
|
||||
<div class="title">공지사항 목록<span class="tooltip">공지사항을 관리합니다.</span></div>
|
||||
<form id="ajaxForm" onsubmit="return false;">
|
||||
<table class="search_condition" cellspacing=0;>
|
||||
<colgroup>
|
||||
<col style="width:180px;">
|
||||
<col style="width:240px;">
|
||||
<col style="width:180px;">
|
||||
<col style="width:240px;">
|
||||
<col style="width:180px;">
|
||||
<col style="width:240px;">
|
||||
</colgroup>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>게시유형</th>
|
||||
<td>
|
||||
<div class="select-style">
|
||||
<select name="searchNoticeType"></select>
|
||||
</div>
|
||||
</td>
|
||||
<th>제목/내용</th>
|
||||
<td><input type="text" name="searchSubjectDetail"></td>
|
||||
<th>사용여부</th>
|
||||
<th style="width:180px;">사용여부</th>
|
||||
<td>
|
||||
<div class="select-style">
|
||||
<select name="searchUseYn">
|
||||
@@ -258,6 +152,8 @@
|
||||
</select>
|
||||
</div>
|
||||
</td>
|
||||
<th style="width:180px;">제목/내용</th>
|
||||
<td><input type="text" name="searchSubjectDetail"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@@ -19,70 +19,12 @@
|
||||
<script language="javascript" >
|
||||
var url = '<c:url value="/onl/apim/portalnotice/portalNoticeMan.json" />';
|
||||
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 isDetail = false;
|
||||
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() {
|
||||
var key = "${param.id}";
|
||||
isDetail = key != "" && key != "null";
|
||||
|
||||
if (isDetail) {
|
||||
$("#title").append(" 수정");
|
||||
buttonControl(true);
|
||||
} else {
|
||||
$("#title").append(" 등록");
|
||||
$("#btn_modify").html('<i class="material-icons">save</i> <%= localeMessage.getString("button.register") %>');
|
||||
buttonControl(false);
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
type : "POST",
|
||||
url:url,
|
||||
dataType:"json",
|
||||
data:{cmd: 'LIST_INIT_COMBO'},
|
||||
success:function(json){
|
||||
combo = json;
|
||||
new makeOptions("CODE","NAME").setObj($("select[name=noticeType]")).setNoValueInclude(false).setData(json.noticeTypeList).rendering();
|
||||
toggleIncidentFields();
|
||||
|
||||
if (key) {
|
||||
detail(key);
|
||||
}
|
||||
},
|
||||
error:function(e){
|
||||
alert(e.responseText);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
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) {
|
||||
@@ -102,123 +44,67 @@
|
||||
function displayAttachFile(file) {
|
||||
var $attachFiles = $('#attachFiles');
|
||||
$attachFiles.empty();
|
||||
|
||||
if (file) {
|
||||
var iconPath = '${pageContext.request.contextPath}/images/icon_file.png';
|
||||
var fileItem = $('<div>').addClass('file-item');
|
||||
|
||||
$('<img>').attr('src', iconPath).css({'marginLeft': '15px','marginRight': '5px' ,'height': '1.2em'}).appendTo(fileItem);
|
||||
var fileNameSpan = $('<span>').addClass('fileName')
|
||||
.text(file.originalFileName + '.' + file.fileExtension + ' (' + formatFileSize(file.size) + ')')
|
||||
.css('cursor', 'pointer')
|
||||
.on('click', function() { if (file.fileId) downloadFile(file.fileId); });
|
||||
|
||||
var fileNameSpan = $('<span>')
|
||||
.addClass('fileName')
|
||||
.text(file.originalFileName + '.' + file.fileExtension + ' (' + formatFileSize(file.size) + ')')
|
||||
.css('cursor', 'pointer')
|
||||
.on('click', function() {
|
||||
if (file.fileId) {
|
||||
downloadFile(file.fileId);
|
||||
}
|
||||
});
|
||||
|
||||
fileNameSpan.appendTo(fileItem);
|
||||
$('<button>').text('X').addClass('delete-file').css('marginLeft', '15px')
|
||||
.on('click', function() {
|
||||
fileInfo = null;
|
||||
displayAttachFile(null);
|
||||
$('<input>').attr({type: 'hidden', name: 'fileDeleted', value: 'true'}).appendTo('#ajaxForm');
|
||||
}).appendTo(fileItem);
|
||||
.on('click', function() {
|
||||
fileInfo = null;
|
||||
displayAttachFile(null);
|
||||
// 폼에 삭제 플래그 추가
|
||||
$('<input>').attr({
|
||||
type: 'hidden',
|
||||
name: 'fileDeleted',
|
||||
value: 'true'
|
||||
}).appendTo('#ajaxForm');
|
||||
})
|
||||
.appendTo(fileItem);
|
||||
|
||||
fileItem.appendTo($attachFiles);
|
||||
}
|
||||
}
|
||||
|
||||
function downloadFile(fileId) {
|
||||
if (fileId) {
|
||||
window.location.href = file_download + '?fileId=' + fileId + '&fileSn=1';
|
||||
var fileSn = 1; // 단일 파일이므로 시리얼 넘버를 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) {
|
||||
if (!isDetail) return;
|
||||
$.ajax({
|
||||
type: "POST", url: url, dataType: "json",
|
||||
type: "POST",
|
||||
url: url,
|
||||
dataType: "json",
|
||||
data: {cmd: 'DETAIL', id: key},
|
||||
success: function (data) {
|
||||
$("#id").val(key);
|
||||
$('#noticeType').val(data.noticeType);
|
||||
$("#noticeSubject").val(data.noticeSubject);
|
||||
$("#useYn").prop("checked", data.useYn === "Y");
|
||||
$("#fixYn").prop("checked", data.fixYn === "Y");
|
||||
|
||||
var decodedContent = decodeHTMLEntities(data.noticeDetail);
|
||||
$('#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) {
|
||||
fileInfo = {
|
||||
originalFileName: data.fileInfo.fileDetails[0].originalFileName,
|
||||
@@ -229,21 +115,37 @@
|
||||
displayAttachFile(fileInfo);
|
||||
}
|
||||
},
|
||||
error: function (e) { alert(e.responseText); }
|
||||
error: function (e) {
|
||||
alert(e.responseText);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$(document).ready(function () {
|
||||
var returnUrl = getReturnUrlForReturn();
|
||||
var key = "${param.id}";
|
||||
isDetail = key != "" && key != "null";
|
||||
|
||||
if (isDetail) {
|
||||
$("#title").append(" 수정");
|
||||
buttonControl(true);
|
||||
} else {
|
||||
$("#title").append(" 등록");
|
||||
$("#btn_modify").html('<i class="material-icons">save</i> <%= localeMessage.getString("button.register") %>');
|
||||
buttonControl(false);
|
||||
}
|
||||
|
||||
init();
|
||||
|
||||
initSummernote('#contents', { placeholder: '여기에 내용을 입력하세요.', height: 300 });
|
||||
// Summernote 에디터 초기화 (이미지 붙여넣기 data-uri 방식, 리사이징 지원)
|
||||
initSummernote('#contents', {
|
||||
placeholder: '여기에 내용을 입력하세요.',
|
||||
height: 300
|
||||
});
|
||||
|
||||
$('#noticeType').on('change', toggleIncidentFields);
|
||||
|
||||
$('#btn_addAffectedApi').click(openApiPopup);
|
||||
$('#btn_removeAffectedApi').click(removeSelectedAffectedApis);
|
||||
if (key) {
|
||||
detail(key);
|
||||
}
|
||||
|
||||
$('#fileInput').change(function(e) {
|
||||
var file = e.target.files[0];
|
||||
@@ -251,66 +153,51 @@
|
||||
fileInfo = {
|
||||
originalFileName: file.name.split('.').slice(0, -1).join('.'),
|
||||
fileExtension: file.name.split('.').pop(),
|
||||
file: file, size: file.size
|
||||
file: file,
|
||||
size: file.size
|
||||
};
|
||||
displayAttachFile(fileInfo);
|
||||
}
|
||||
this.value = '';
|
||||
this.value = ''; // 입력 필드 초기화
|
||||
});
|
||||
|
||||
$('#addFile').click(function() {
|
||||
$('#fileInput').click();
|
||||
});
|
||||
|
||||
$('#addFile').click(function() { $('#fileInput').click(); });
|
||||
|
||||
$("#btn_modify").click(function () {
|
||||
if (!checkRequired("ajaxForm")) return;
|
||||
|
||||
var noticeType = $('#noticeType').val();
|
||||
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;
|
||||
if (confirm("<%= localeMessage.getString("common.checkSave")%>") != true)
|
||||
return;
|
||||
|
||||
var formData = new FormData($("#ajaxForm")[0]);
|
||||
|
||||
formData.set("useYn", $("#useYn").is(":checked") ? "Y" : "N");
|
||||
formData.set("fixYn", $("#fixYn").is(":checked") ? "Y" : "N");
|
||||
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");
|
||||
|
||||
// 파일 처리
|
||||
if (fileInfo && fileInfo.file) {
|
||||
formData.set("files", fileInfo.file);
|
||||
formData.set("fileName", fileInfo.originalFileName + '.' + fileInfo.fileExtension);
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
type: "POST", url: url, data: formData,
|
||||
processData: false, contentType: false,
|
||||
success: function () {
|
||||
type: "POST",
|
||||
url: url,
|
||||
data: formData,
|
||||
processData: false,
|
||||
contentType: false,
|
||||
success: function (json) {
|
||||
alert("<%= localeMessage.getString("common.saveMsg") %>");
|
||||
goNav(returnUrl);
|
||||
},
|
||||
error: function (e) { alert(e.responseText); }
|
||||
error: function (e) {
|
||||
alert(e.responseText);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -318,18 +205,25 @@
|
||||
if (confirm("<%= localeMessage.getString("common.confirmMsg")%>")) {
|
||||
var postData = $('#ajaxForm').serializeArray();
|
||||
postData.push({name: "cmd", value: "DELETE"});
|
||||
|
||||
$.ajax({
|
||||
type: "POST", url: url, data: postData,
|
||||
success: function () {
|
||||
type: "POST",
|
||||
url: url,
|
||||
data: postData,
|
||||
success: function (args) {
|
||||
alert("<%= localeMessage.getString("common.deleteMsg") %>");
|
||||
goNav(returnUrl);
|
||||
},
|
||||
error: function (e) { alert(e.responseText); }
|
||||
error: function (e) {
|
||||
alert(e.responseText);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
$("#btn_previous").click(function () { goNav(returnUrl); });
|
||||
$("#btn_previous").click(function () {
|
||||
goNav(returnUrl);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
@@ -339,7 +233,7 @@
|
||||
<ul class="path">
|
||||
<li><a href="#">${rmsMenuPath}</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div><!-- end content_top -->
|
||||
<div class="content_middle">
|
||||
<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>
|
||||
@@ -350,87 +244,29 @@
|
||||
<form id="ajaxForm" enctype="multipart/form-data" accept-charset="UTF-8">
|
||||
<input type="hidden" name="id" id="id">
|
||||
<table class="table_row" cellspacing="0">
|
||||
<colgroup>
|
||||
<col style="width: 10%"/>
|
||||
<col style="width: 40%"/>
|
||||
<col style="width: 10%"/>
|
||||
<col style="width: 40%"/>
|
||||
</colgroup>
|
||||
<tr>
|
||||
<th>게시유형 <font color="red">*</font></th>
|
||||
<td colspan="3">
|
||||
<div class="select-style">
|
||||
<select name="noticeType" id="noticeType"></select>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>제목 <font color="red">*</font></th>
|
||||
<td colspan="3"><input type="text" id="noticeSubject" name="noticeSubject" style="width:100%" data-required data-warning="제목을 입력하여 주십시오."/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>사용여부</th>
|
||||
<th style="width:20%;">사용여부</th>
|
||||
<td>
|
||||
<input type="checkbox" name="useYn" id="useYn" value="Y" ${portalNoticeUI.useYn eq 'Y' ? 'checked' : ''}> 사용
|
||||
</td>
|
||||
<th>상단고정</th>
|
||||
<td>
|
||||
<input type="checkbox" name="fixYn" id="fixYn" value="Y" ${portalNoticeUI.fixYn eq 'Y' ? 'checked' : ''}> 고정
|
||||
</td>
|
||||
<input type="checkbox" name="useYn" id="useYn" value="Y" ${portalNoticeUI.useYn eq 'Y' ? 'checked' : ''}> 사용</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>본문 <font color="red">*</font></th>
|
||||
<td colspan="3">
|
||||
<th style="width:20%;">제목 <font color="red">*</font></th>
|
||||
<td><input type="text" id="noticeSubject" name="noticeSubject" style="width:100%" data-required data-warning="제목을 입력하여 주십시오."/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th style="width:20%;">본문 <font color="red">*</font></th>
|
||||
<td>
|
||||
<textarea id="contents" name="noticeDetail" style="width:100%;height:300px" data-required data-warning="본문을 입력하여 주십시오."></textarea>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- ───── 장애/점검 전용 영역 ───── -->
|
||||
<tr class="incident-row" style="display:none;">
|
||||
<th>시작 <font color="red">*</font></th>
|
||||
<tr>
|
||||
<th style="width:20%;">첨부파일</th>
|
||||
<td>
|
||||
<input type="datetime-local" id="startedAt" name="startedAt" style="width:90%">
|
||||
</td>
|
||||
<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 style="margin: 5px 0px; display: inline-block;">
|
||||
<input type="file" id="fileInput" name="files" style="display:none;" />
|
||||
<button type="button" id="addFile" class="cssbtn smallBtn">파일 선택</button>
|
||||
</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>
|
||||
<div id="attachFiles" style="margin: 5px 0px; display: inline-block;"></div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
@@ -438,4 +274,4 @@
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
@@ -312,9 +312,7 @@ $(document).ready(function() {
|
||||
<div class="content_middle" id="content_middle">
|
||||
<div class="search_wrap">
|
||||
<button type="button" class="cssbtn" id="btn_new" level="W"><i class="material-icons">add</i> <%= localeMessage.getString("button.new") %></button>
|
||||
<c:if test="${hardDeleteEnabled}">
|
||||
<button type="button" class="cssbtn" id="btn_delete_selected" level="W" style="background-color: #dc3545; border-color: #dc3545; color: white;"><i class="material-icons">delete_forever</i> 선택 삭제</button>
|
||||
</c:if>
|
||||
<button type="button" class="cssbtn" id="btn_search" level="R"><i class="material-icons">search</i> <%= localeMessage.getString("button.search") %></button>
|
||||
</div>
|
||||
<%--법인정보관리--%>
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
|
||||
<%@ page import="java.io.*"%>
|
||||
<%@ page import="com.eactive.eai.rms.common.util.CommonUtil"%>
|
||||
<%@ page import="com.eactive.eai.rms.common.context.MonitoringContext"%>
|
||||
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
|
||||
<%@ taglib uri="http://www.springframework.org/tags" prefix="spring"%>
|
||||
<%@ include file="/jsp/common/include/localemessage.jsp" %>
|
||||
@@ -9,9 +7,6 @@
|
||||
response.setHeader("Pragma", "No-cache");
|
||||
response.setHeader("Cache-Control", "no-cache");
|
||||
response.setHeader("Expires", "0");
|
||||
|
||||
MonitoringContext monitoringContext = (MonitoringContext)CommonUtil.getBean(request, "monitoringContext");
|
||||
String reverseProxyUrl = monitoringContext.getStringProperty(MonitoringContext.RMS_WEBHOOK_REVERSE_PROXY_URL, "");
|
||||
%>
|
||||
<%!
|
||||
public String getRequiredLabel(String label) {
|
||||
@@ -138,15 +133,10 @@
|
||||
$("#btn_delete").hide();
|
||||
}
|
||||
|
||||
$("select[name=approvalStatus]").val(portalOrgData.approvalStatus);
|
||||
// 승인상태는 읽기전용(수정 불가) - 값은 그대로 제출되도록 disabled 대신 잠금 처리
|
||||
$("select[name=approvalStatus]")
|
||||
.css({'pointer-events':'none', 'background-color':'#eee'})
|
||||
.attr('tabindex', '-1');
|
||||
$("#approvalStatus").val(portalOrgData.approvalStatus);
|
||||
$("#compRegNo").val(formatCompanyRegNo(portalOrgData.compRegNo));
|
||||
$("#corpRegNo").val(formatCorpRegNo(portalOrgData.corpRegNo));
|
||||
$("#createdDate").inputmask("9999-99-99 99:99:99", {'autoUnmask': true});
|
||||
$("#reverseProxyPath").val(portalOrgData.reverseProxyPath);
|
||||
|
||||
// 전화번호 처리
|
||||
if (portalOrgData.scPhoneNumber) {
|
||||
@@ -414,6 +404,47 @@
|
||||
downloadFile(fileId, '1'); // fileSn은 '1'로 가정
|
||||
});
|
||||
|
||||
// OBP 파트너코드 조회 버튼 클릭
|
||||
$("#btn_query_obp").click(function() {
|
||||
var compRegNo = $("#compRegNo").val();
|
||||
if (!compRegNo) {
|
||||
jSuites.notification({ name: '알림', message: '사업자등록번호를 먼저 입력해주세요.', error: true });
|
||||
$("#compRegNo").focus();
|
||||
return;
|
||||
}
|
||||
|
||||
// 하이픈 제거
|
||||
compRegNo = compRegNo.replace(/-/g, '');
|
||||
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: url,
|
||||
dataType: "json",
|
||||
data: {
|
||||
cmd: 'QUERY_OBP_PARTNER_CODE',
|
||||
compRegNo: compRegNo
|
||||
},
|
||||
beforeSend: function() {
|
||||
$("#btn_query_obp").prop("disabled", true).html('<i class="material-icons">hourglass_empty</i> 조회중...');
|
||||
},
|
||||
success: function(json) {
|
||||
if (json.success) {
|
||||
$("#orgCode").val(json.partnerCode);
|
||||
jSuites.notification({ name: '성공', message: '파트너코드가 조회되었습니다: ' + json.partnerCode });
|
||||
} else {
|
||||
jSuites.notification({ name: '실패', message: json.message, error: true });
|
||||
}
|
||||
},
|
||||
error: function(e) {
|
||||
jSuites.notification({ name: '오류', message: '파트너코드 조회 중 오류가 발생했습니다.', error: true });
|
||||
console.error(e.responseText);
|
||||
},
|
||||
complete: function() {
|
||||
$("#btn_query_obp").prop("disabled", false).html('<i class="material-icons">search</i> 파트너코드 조회');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$("#btn_modify").click(function(){
|
||||
if (!checkRequired("ajaxForm")) return;
|
||||
|
||||
@@ -484,7 +515,8 @@
|
||||
}
|
||||
}
|
||||
|
||||
// 저장 확인/사유 입력은 유효성 검사 및 FormData 구성 후 처리 (하단 submit 분기)
|
||||
if (confirm("<%= localeMessage.getString("common.checkSave")%>") !== true)
|
||||
return;
|
||||
|
||||
// FormData 생성 전에 분리된 전화번호 필드 제거
|
||||
$("#scPhonePrefix, #scPhoneMiddle, #scPhoneLast, #orgPhonePrefix, #orgPhoneMiddle, #orgPhoneLast").removeAttr('name');
|
||||
@@ -519,47 +551,23 @@
|
||||
}
|
||||
|
||||
|
||||
// cmd 및 사유(auditReason) 첨부 후 전송
|
||||
function submitOrg(cmd, auditReason) {
|
||||
formData.append("cmd", cmd);
|
||||
// 감사로그(AUDIT_LOG) 발화를 위한 systemCode. 법인/가입자 리포지토리는 고정 EMS 스키마라 스키마 전환 영향 없음
|
||||
formData.append("serviceType", "APIGW");
|
||||
if (auditReason) {
|
||||
formData.append("auditReason", auditReason);
|
||||
// cmd 파라미터 추가
|
||||
formData.append("cmd", isDetail ? "UPDATE" : "INSERT");
|
||||
|
||||
$.ajax({
|
||||
type : "POST",
|
||||
url:url,
|
||||
data: formData,
|
||||
processData: false,
|
||||
contentType: false,
|
||||
success:function(json){
|
||||
alert("<%= localeMessage.getString("common.saveMsg") %>");
|
||||
goNav(returnUrl);
|
||||
},
|
||||
error:function(e){
|
||||
alert(e.responseText);
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
type : "POST",
|
||||
url:url,
|
||||
data: formData,
|
||||
processData: false,
|
||||
contentType: false,
|
||||
success:function(json){
|
||||
alert("<%= localeMessage.getString("common.saveMsg") %>");
|
||||
goNav(returnUrl);
|
||||
},
|
||||
error:function(e){
|
||||
alert(e.responseText);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (isDetail) {
|
||||
// 수정: 상태를 REMOVED로 바꾼 경우 삭제(cmd=DELETE)로 감사 기록 + 익명화
|
||||
var targetCmd = ($("#orgStatus").val() === 'REMOVED') ? "DELETE" : "UPDATE";
|
||||
var isRemove = (targetCmd === "DELETE");
|
||||
showReasonPrompt(
|
||||
isRemove ? "해당 법인을 삭제(탈퇴) 처리합니다. 사유를 입력해 주세요." : "법인 정보를 수정합니다. 사유를 입력해 주세요.",
|
||||
{
|
||||
title: isRemove ? "삭제 사유" : "수정 사유",
|
||||
onConfirm: function(reason) { submitOrg(targetCmd, reason); }
|
||||
}
|
||||
);
|
||||
} else {
|
||||
// 신규 등록: 사유 불필요
|
||||
if (confirm("<%= localeMessage.getString("common.checkSave")%>") !== true) return;
|
||||
submitOrg("INSERT", null);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$("#btn_previous").click(function() {
|
||||
@@ -568,28 +576,25 @@
|
||||
|
||||
$("#btn_delete").click(function(){
|
||||
|
||||
showReasonPrompt("해당 법인을 삭제 처리합니다. 사유를 입력해 주세요.", {
|
||||
title: "삭제 사유",
|
||||
onConfirm: function(reason) {
|
||||
var postData = $('#ajaxForm').serializeArray();
|
||||
postData.push({name: "cmd" , value:"DELETE"});
|
||||
postData.push({name: "serviceType", value: "APIGW"});
|
||||
postData.push({name: "auditReason", value: reason});
|
||||
if(confirm("<%= localeMessage.getString("common.confirmMsg")%>")){
|
||||
var postData = $('#ajaxForm').serializeArray();
|
||||
postData.push({name: "cmd" , value:"DELETE"});
|
||||
|
||||
$.ajax({
|
||||
type : "POST",
|
||||
url:url,
|
||||
data:postData,
|
||||
success:function(args){
|
||||
alert("<%= localeMessage.getString("common.deleteMsg") %>");
|
||||
goNav(returnUrl);
|
||||
},
|
||||
error:function(e){
|
||||
alert(e.responseText);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
$.ajax({
|
||||
type : "POST",
|
||||
url:url,
|
||||
data:postData,
|
||||
success:function(args){
|
||||
alert("<%= localeMessage.getString("common.deleteMsg") %>");
|
||||
goNav(returnUrl);
|
||||
},
|
||||
error:function(e){
|
||||
alert(e.responseText);
|
||||
}
|
||||
});
|
||||
}else{
|
||||
return false;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
@@ -625,7 +630,10 @@
|
||||
<tr>
|
||||
<th style="width:15%;"><%= localeMessage.getString("portalOrg.orgCode") %></th>
|
||||
<td colspan="3" style="width:35%;">
|
||||
<input type="text" name="orgCode" id="orgCode"/>
|
||||
<div style="display: flex; align-items: center;">
|
||||
<input type="text" name="orgCode" id="orgCode" style="width: 200px; margin-right: 10px;"/>
|
||||
<button type="button" class="cssbtn smallBtn" id="btn_query_obp"><i class="material-icons">search</i> 파트너코드 조회</button>
|
||||
</div>
|
||||
</td>
|
||||
<th><%= localeMessage.getString("portalOrg.orgIndustryType") %></th> <%--업종--%>
|
||||
<td><input type="text" name="orgIndustryType" id="orgIndustryType" maxlength="50" /></td>
|
||||
@@ -730,16 +738,6 @@
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><%= localeMessage.getString("portalOrg.reverseProxyPath") %></th>
|
||||
<td colspan="3">
|
||||
<%=reverseProxyUrl %>/<input type="text" name="reverseProxyPath" id="reverseProxyPath" style="width:100px; "/>
|
||||
</td>
|
||||
<th></th>
|
||||
<td>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr><%--사업자등록증--%>
|
||||
<th><%= localeMessage.getString("portalOrg.compRegFile") %></th>
|
||||
<td colspan="5" style="padding-top: 5px; padding-bottom: 5px;">
|
||||
|
||||
@@ -26,38 +26,7 @@ function formatFile(cellvalue, options, rowObject) {
|
||||
return cellvalue + icon;
|
||||
}
|
||||
|
||||
function deleteSelectedRows() {
|
||||
var selectedIds = $("#grid").jqGrid('getGridParam', 'selarrrow');
|
||||
|
||||
if (selectedIds.length === 0) {
|
||||
alert('항목을 선택하세요.');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!confirm('선택된 ' + selectedIds.length + '개 항목을 삭제하시겠습니까?')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: url,
|
||||
dataType: "json",
|
||||
data: {
|
||||
cmd: 'DELETE_BULK',
|
||||
ids: selectedIds.join(',')
|
||||
},
|
||||
success: function(data) {
|
||||
alert('삭제되었습니다.');
|
||||
$("#grid").jqGrid('clearGridData', true);
|
||||
$("#grid").trigger("reloadGrid");
|
||||
},
|
||||
error: function(e) {
|
||||
alert('삭제 중 오류가 발생했습니다.');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
$(document).ready(function() {
|
||||
$('#grid').jqGrid({
|
||||
datatype:"json",
|
||||
mtype: 'POST',
|
||||
@@ -76,7 +45,7 @@ $(document).ready(function() {
|
||||
],
|
||||
jsonReader: {
|
||||
repeatitems:false
|
||||
},
|
||||
},
|
||||
pager : $('#pager'),
|
||||
page : '${param.page}',
|
||||
rowNum : '${rmsDefaultRowNum}',
|
||||
@@ -85,8 +54,6 @@ $(document).ready(function() {
|
||||
autowidth: true,
|
||||
viewrecords: true,
|
||||
rowList : eval('[${rmsDefaultRowList}]'),
|
||||
multiselect: true,
|
||||
multiboxonly: true,
|
||||
ondblClickRow: function(rowId) {
|
||||
var rowData = $(this).getRowData(rowId);
|
||||
var id = rowData['id'];
|
||||
@@ -127,14 +94,10 @@ $(document).ready(function() {
|
||||
resizeJqGridWidth('grid','content_middle','1000');
|
||||
|
||||
$("#btn_search").click(function(){
|
||||
var postData = getSearchForJqgrid("cmd","LIST"); //jqgrid에서는 object 로
|
||||
var postData = getSearchForJqgrid("cmd","LIST"); //jqgrid에서는 object 로
|
||||
$("#grid").setGridParam({ url:url,postData: postData ,page:1 }).trigger("reloadGrid");
|
||||
});
|
||||
|
||||
$("#btn_delete_selected").click(function(){
|
||||
deleteSelectedRows();
|
||||
});
|
||||
|
||||
$("input[name^=search]").keydown(function(key){
|
||||
if (key.keyCode == 13){
|
||||
$("#btn_search").click();
|
||||
@@ -157,9 +120,8 @@ $(document).ready(function() {
|
||||
<div class="content_middle" id="content_middle">
|
||||
<div class="search_wrap">
|
||||
<button type="button" class="cssbtn" id="btn_search" level="R" ><i class="material-icons">search</i> <%= localeMessage.getString("button.search") %></button>
|
||||
<button type="button" class="cssbtn" id="btn_delete_selected" level="W" style="background-color: #dc3545; border-color: #dc3545; color: white;"><i class="material-icons">delete_forever</i> 선택 삭제</button>
|
||||
</div>
|
||||
<div class="title">피드백/개선요청 신청 목록</div>
|
||||
<div class="title">사업제휴 신청 목록</div>
|
||||
<form id="ajaxForm" onsubmit="return false;">
|
||||
<table class="search_condition" cellspacing=0;>
|
||||
<tbody>
|
||||
|
||||
@@ -92,27 +92,6 @@ $(document).ready(function() {
|
||||
goNav(returnUrl);//LIST로 이동
|
||||
});
|
||||
|
||||
$("#btn_delete").click(function(){
|
||||
if (!confirm('선택한 항목을 삭제하시겠습니까?')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: url,
|
||||
dataType: "json",
|
||||
data: { cmd: 'DELETE', id: key },
|
||||
success: function(data) {
|
||||
alert('삭제되었습니다.');
|
||||
var returnUrl = getReturnUrlForReturn();
|
||||
goNav(returnUrl);//LIST로 이동
|
||||
},
|
||||
error: function(e) {
|
||||
alert('삭제 중 오류가 발생했습니다.');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$("#btn_unmask").click(function () {
|
||||
const reason = prompt("마스킹 해제 사유를 입력하세요:");
|
||||
|
||||
@@ -155,10 +134,9 @@ $(document).ready(function() {
|
||||
<div class="content_middle">
|
||||
<div class="search_wrap">
|
||||
<button type="button" class="cssbtn" id="btn_unmask" level="W" status="DETAIL"><i class="material-icons">lock_open</i> 마스킹해제</button>
|
||||
<button type="button" class="cssbtn" id="btn_delete" level="W" status="DETAIL" style="background-color: #dc3545; border-color: #dc3545; color: white;"><i class="material-icons">delete_forever</i> 삭제</button>
|
||||
<button type="button" class="cssbtn" id="btn_previous" level="R" status="DETAIL,NEW"><i class="material-icons">arrow_back</i> <%= localeMessage.getString("button.previous") %></button>
|
||||
</div>
|
||||
<div class="title">피드백/개선요청 신청 상세</div>
|
||||
<div class="title">사업제휴 신청 상세</div>
|
||||
|
||||
<table id="grid" ></table>
|
||||
<div id="pager"></div>
|
||||
|
||||
@@ -86,11 +86,9 @@
|
||||
editurl: "clientArray",
|
||||
colNames: ['<%= localeMessage.getString("propertyDetail.propertyKey") %>',
|
||||
'<%= localeMessage.getString("propertyDetail.propertyValue") %>',
|
||||
'설명',
|
||||
'<%= localeMessage.getString("propertyDetail.delYn") %>'],
|
||||
colModel: [{name: 'PRPTYNAME', width: 50, align: 'left', editable: true},
|
||||
{name: 'PRPTY2VAL', width: 150, align: 'left', editable: true},
|
||||
{name: 'PRPTYDESC', width: 200, align: 'left', editable: true, edittype: 'textarea', editoptions: {rows: 2}},
|
||||
{name: 'PRPTY2VAL', width: 200, align: 'left', editable: true},
|
||||
{
|
||||
name: 'DELETEYN',
|
||||
width: 20,
|
||||
@@ -260,7 +258,6 @@
|
||||
var data = new Object();
|
||||
data["PRPTYNAME"] = $("input[name=prptyName]").val();
|
||||
data["PRPTY2VAL"] = $("input[name=prpty2Val]").val();
|
||||
data["PRPTYDESC"] = $("input[name=prptyDesc]").val();
|
||||
|
||||
var rows = $("#grid")[0].rows;
|
||||
var index = Number($(rows[rows.length - 1]).attr("id"));
|
||||
@@ -343,11 +340,6 @@
|
||||
<td colspan="3"><input type="text" name="prpty2Val"/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>설명</th>
|
||||
<td colspan="3"><input type="text" name="prptyDesc"/>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<!-- grid -->
|
||||
<table id="grid"></table>
|
||||
|
||||
@@ -29,16 +29,6 @@
|
||||
// 원본 행 데이터를 ID로 조회하기 위한 맵
|
||||
var gridRowDataMap = {};
|
||||
|
||||
// 휴대폰번호 중복 조회 모드 여부
|
||||
var isDupView = false;
|
||||
|
||||
// 현재 검색조건 + 중복 조회 모드 플래그를 합쳐 LIST 요청 파라미터를 생성
|
||||
function buildListPostData() {
|
||||
var postData = getSearchForJqgrid("cmd", "LIST");
|
||||
postData.onlyDuplicateMobile = isDupView;
|
||||
return postData;
|
||||
}
|
||||
|
||||
/* 선택된 사용자 삭제 - 상태에 따라 Soft/Hard Delete 자동 분기 */
|
||||
function deleteSelectedUsers() {
|
||||
var selectedIds = $("#grid").jqGrid('getGridParam', 'selarrrow');
|
||||
@@ -176,13 +166,6 @@
|
||||
|
||||
$('#grid').jqGrid('setColProp', 'approvalStatus', {editoptions: {value: select_approvalStatus}});
|
||||
$('#grid').jqGrid('setColProp', 'userStatus', {editoptions: {value: select_userStatus}});
|
||||
|
||||
// 휴대폰번호 중복 허용안함(기능 ON) + 중복 사용자 존재 시 배너 노출
|
||||
if (json.mobileDuplicateCheckEnabled && json.mobileDuplicateUserCount > 0) {
|
||||
$("#dupMobileCount").text(json.mobileDuplicateUserCount);
|
||||
$("#dupMobileBanner").show();
|
||||
}
|
||||
|
||||
$('#grid').trigger('reloadGrid');
|
||||
|
||||
},
|
||||
@@ -193,7 +176,7 @@
|
||||
}
|
||||
|
||||
$(document).ready(function () {
|
||||
var gridPostData = buildListPostData();
|
||||
var gridPostData = getSearchForJqgrid("cmd", "LIST");
|
||||
|
||||
$('#grid').jqGrid({
|
||||
datatype: "json",
|
||||
@@ -205,7 +188,7 @@
|
||||
'<%= localeMessage.getString("portalUser.orgName") %>',
|
||||
'<%= localeMessage.getString("portalUser.name") %>',
|
||||
'<%= localeMessage.getString("portalUser.userId") %>',
|
||||
'<%= localeMessage.getString("portalUser.mobileNumber") %>',
|
||||
<%--'<%= localeMessage.getString("portalUser.mobile") %>',--%>
|
||||
'<%= localeMessage.getString("portalUser.roleName") %>',
|
||||
'<%= localeMessage.getString("portalUser.userStatus") %>',
|
||||
'<%= localeMessage.getString("portalUser.createOn") %>',
|
||||
@@ -218,7 +201,7 @@
|
||||
{name: 'portalOrgUIs.orgName', align: 'center', width: "120"},
|
||||
{name: 'userName', align: 'center', width: "80" },
|
||||
{name: 'loginId', align: 'center', width: "150" },
|
||||
{name: 'mobileNumber', align: 'center', width: "110", hidden: true},
|
||||
// {name: 'mobileNumber', align: 'center', width: "100" },
|
||||
{name: 'roleCodeDescription', align: 'center', width: "80"},
|
||||
{name: 'userStatusDescription', align: 'center', width: "50"},
|
||||
{name: 'createdDate', align: 'center', width: "100", formatter: timeStampFormat},
|
||||
@@ -291,20 +274,9 @@
|
||||
init();
|
||||
|
||||
$("#btn_search").click(function () {
|
||||
$("#grid").setGridParam({url: url, postData: buildListPostData(), page: 1}).trigger("reloadGrid");
|
||||
});
|
||||
var postData = getSearchForJqgrid("cmd", "LIST");
|
||||
|
||||
// 중복 휴대폰번호 사용자만 보기 / 전체 보기 토글
|
||||
$("#btn_dup_toggle").click(function () {
|
||||
isDupView = !isDupView;
|
||||
if (isDupView) {
|
||||
$(this).text("전체 보기");
|
||||
$("#grid").jqGrid('showCol', 'mobileNumber');
|
||||
} else {
|
||||
$(this).text("중복만 보기");
|
||||
$("#grid").jqGrid('hideCol', 'mobileNumber');
|
||||
}
|
||||
$("#grid").setGridParam({url: url, postData: buildListPostData(), page: 1}).trigger("reloadGrid");
|
||||
$("#grid").setGridParam({url: url, postData: postData, page: 1}).trigger("reloadGrid");
|
||||
});
|
||||
|
||||
$("#btn_new").click(function () {
|
||||
@@ -347,11 +319,9 @@
|
||||
<button type="button" class="cssbtn" id="btn_new" level="W"><i
|
||||
class="material-icons">add</i> <%= localeMessage.getString("button.new") %>
|
||||
</button>
|
||||
<c:if test="${hardDeleteEnabled}">
|
||||
<button type="button" class="cssbtn" id="btn_delete_selected" level="W" style="background-color: #dc3545; border-color: #dc3545; color: white;"><i
|
||||
class="material-icons">delete_forever</i> 선택 삭제
|
||||
</button>
|
||||
</c:if>
|
||||
<button type="button" class="cssbtn" id="btn_search" level="R"><i
|
||||
class="material-icons">search</i> <%= localeMessage.getString("button.search") %>
|
||||
</button>
|
||||
@@ -364,11 +334,6 @@
|
||||
</div>
|
||||
<% } %>
|
||||
|
||||
<div id="dupMobileBanner" style="display:none; background:#f8d7da; border:1px solid #dc3545; padding:8px 15px; margin:5px 0; border-radius:4px; color:#721c24;">
|
||||
<strong>⚠ 중복 휴대폰번호 사용자 <span id="dupMobileCount">0</span>건 발견</strong>
|
||||
<button type="button" class="cssbtn" id="btn_dup_toggle" style="margin-left:10px;">중복만 보기</button>
|
||||
</div>
|
||||
|
||||
<table class="search_condition" cellspacing=0;>
|
||||
<tbody>
|
||||
<tr>
|
||||
@@ -400,16 +365,15 @@
|
||||
<td><input type="text" name="searchUserName"></td>
|
||||
|
||||
<th style="width:10%; min-width:100px;">
|
||||
<%= localeMessage.getString("portalUser.userId") %><span style="color:#dc3545;">(*)</span>
|
||||
<%= localeMessage.getString("portalUser.userId") %>
|
||||
</th>
|
||||
<td><input type="text" name="searchUserId" value="${param.searchUserId}"></td>
|
||||
<th style="width:10%; min-width:100px;"><%= localeMessage.getString("portalUser.mobileNumber") %><span style="color:#dc3545;">(*)</span>
|
||||
<th style="width:10%; min-width:100px;"><%= localeMessage.getString("portalUser.mobileNumber") %>
|
||||
</th>
|
||||
<td><input type="text" name="searchMobileNumber" value="${param.searchMobileNumber}"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div style="color:#dc3545; font-size:12px; margin:4px 2px;">(*) 암호화 항목 · 부분검색 불가(전체 일치)</div>
|
||||
|
||||
<table id="grid"></table>
|
||||
<div id="pager"></div>
|
||||
|
||||
@@ -165,10 +165,6 @@
|
||||
$("#userStatus").val(data.userStatus);
|
||||
$("#roleCode").val(data.roleCode).trigger('change');
|
||||
$("#approvalStatus").val(data.approvalStatus);
|
||||
// 승인상태는 읽기전용(수정 불가) - 값은 그대로 제출되도록 disabled 대신 잠금 처리
|
||||
$("#approvalStatus")
|
||||
.css({'pointer-events':'none', 'background-color':'#eee'})
|
||||
.attr('tabindex', '-1');
|
||||
|
||||
// 탈퇴 상태 처리
|
||||
if (originalUserStatus === STATUS.REMOVED) {
|
||||
@@ -492,49 +488,29 @@
|
||||
formData.push({name: 'mobileNumber', value: mobileNumber});
|
||||
}
|
||||
|
||||
// cmd 및 사유(auditReason) 첨부 후 전송
|
||||
function submitUser(cmd, auditReason) {
|
||||
formData.push({ name: 'cmd', value: cmd });
|
||||
// 감사로그(AUDIT_LOG) 발화를 위한 systemCode. 가입자 리포지토리는 고정 EMS 스키마라 스키마 전환 영향 없음
|
||||
formData.push({ name: 'serviceType', value: 'APIGW' });
|
||||
if (auditReason) {
|
||||
formData.push({ name: 'auditReason', value: auditReason });
|
||||
formData.push({
|
||||
name: 'cmd',
|
||||
value: isDetail ? "UPDATE" : "INSERT"
|
||||
});
|
||||
|
||||
if (!confirm("<%= localeMessage.getString("common.checkSave")%>")) return;
|
||||
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: url,
|
||||
data: formData,
|
||||
success: function (json) {
|
||||
if (json.status === "fail") {
|
||||
alert(json.errorMsg || "저장에 실패했습니다.");
|
||||
return;
|
||||
}
|
||||
alert("<%= localeMessage.getString("common.saveMsg") %>");
|
||||
goNav(returnUrl);
|
||||
},
|
||||
error: function (e) {
|
||||
alert(e.responseText);
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: url,
|
||||
data: formData,
|
||||
success: function (json) {
|
||||
if (json.status === "fail") {
|
||||
alert(json.errorMsg || "저장에 실패했습니다.");
|
||||
return;
|
||||
}
|
||||
alert("<%= localeMessage.getString("common.saveMsg") %>");
|
||||
goNav(returnUrl);
|
||||
},
|
||||
error: function (e) {
|
||||
alert(e.responseText);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (isDetail) {
|
||||
// 수정: 상태를 REMOVED로 바꾼 경우 삭제(cmd=DELETE)로 감사 기록 + 익명화
|
||||
var targetCmd = ($("#userStatus").val() === 'REMOVED') ? "DELETE" : "UPDATE";
|
||||
var isRemove = (targetCmd === "DELETE");
|
||||
showReasonPrompt(
|
||||
isRemove ? "해당 가입자를 삭제(탈퇴) 처리합니다. 사유를 입력해 주세요." : "가입자 정보를 수정합니다. 사유를 입력해 주세요.",
|
||||
{
|
||||
title: isRemove ? "삭제 사유" : "수정 사유",
|
||||
onConfirm: function(reason) { submitUser(targetCmd, reason); }
|
||||
}
|
||||
);
|
||||
} else {
|
||||
// 신규 등록: 사유 불필요
|
||||
if (!confirm("<%= localeMessage.getString("common.checkSave")%>")) return;
|
||||
submitUser("INSERT", null);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$("#btn_previous").click(function () {
|
||||
@@ -542,26 +518,21 @@
|
||||
});
|
||||
|
||||
$("#btn_delete").click(function () {
|
||||
showReasonPrompt("해당 가입자를 삭제 처리합니다. 사유를 입력해 주세요.", {
|
||||
title: "삭제 사유",
|
||||
onConfirm: function(reason) {
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: url,
|
||||
data: {
|
||||
cmd: "DELETE",
|
||||
id: $('input[name=id]').val(),
|
||||
serviceType: "APIGW",
|
||||
auditReason: reason
|
||||
},
|
||||
success: function () {
|
||||
alert("<%= localeMessage.getString("common.deleteMsg") %>");
|
||||
goNav(returnUrl);
|
||||
},
|
||||
error: function (e) {
|
||||
alert(e.responseText);
|
||||
}
|
||||
});
|
||||
if (!confirm("<%= localeMessage.getString("common.confirmMsg")%>")) return;
|
||||
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: url,
|
||||
data: {
|
||||
cmd: "DELETE",
|
||||
id: $('input[name=id]').val()
|
||||
},
|
||||
success: function () {
|
||||
alert("<%= localeMessage.getString("common.deleteMsg") %>");
|
||||
goNav(returnUrl);
|
||||
},
|
||||
error: function (e) {
|
||||
alert(e.responseText);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,242 +0,0 @@
|
||||
<%@ page language="java" contentType="text/html; charset=utf-8" %>
|
||||
<%@ page import="java.io.*" %>
|
||||
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
|
||||
<%@ taglib uri="http://www.springframework.org/tags" prefix="spring" %>
|
||||
<%@ include file="/jsp/common/include/localemessage.jsp" %>
|
||||
<%
|
||||
response.setHeader("Pragma", "No-cache");
|
||||
response.setHeader("Cache-Control", "no-cache");
|
||||
response.setHeader("Expires", "0");
|
||||
%>
|
||||
<html>
|
||||
<head>
|
||||
<title></title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<jsp:include page="/jsp/common/include/css.jsp"/>
|
||||
<jsp:include page="/jsp/common/include/script.jsp"/>
|
||||
<style>
|
||||
.url-cell { display:inline-block; max-width:380px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; vertical-align:middle; }
|
||||
.succ-y { color:#1a73e8; font-weight:bold; }
|
||||
.succ-n { color:#d93025; font-weight:bold; }
|
||||
</style>
|
||||
<script language="javascript">
|
||||
var url = '<c:url value="/onl/apim/webhook/webhookSendLogMan.json" />';
|
||||
var url_view = '<c:url value="/onl/apim/webhook/webhookSendLogMan.view" />';
|
||||
var combo;
|
||||
|
||||
function escapeHtmlJs(s) { return $('<div>').text(s == null ? '' : s).html(); }
|
||||
function escapeAttr(s) { return escapeHtmlJs(s).replace(/"/g, '"'); }
|
||||
|
||||
function formatTargetUrl(cellvalue, options, rowObject) {
|
||||
var v = cellvalue || '';
|
||||
return '<span class="url-cell" title="' + escapeAttr(v) + '">' + escapeHtmlJs(v) + '</span>';
|
||||
}
|
||||
|
||||
// 이벤트유형 코드 → 모니터링 공통코드(EVENT_TYPE) 코드명
|
||||
function formatEventType(cellvalue, options, rowObject) {
|
||||
if (!combo || !combo.eventTypeList) {
|
||||
return escapeHtmlJs(cellvalue);
|
||||
}
|
||||
for (var i = 0; i < combo.eventTypeList.length; i++) {
|
||||
if (combo.eventTypeList[i].CODE == cellvalue) {
|
||||
return escapeHtmlJs(combo.eventTypeList[i].NAME);
|
||||
}
|
||||
}
|
||||
return escapeHtmlJs(cellvalue);
|
||||
}
|
||||
|
||||
function formatSuccess(cellvalue, options, rowObject) {
|
||||
if (cellvalue === true) return '<span class="succ-y">성공</span>';
|
||||
if (cellvalue === false) return '<span class="succ-n">실패</span>';
|
||||
return '';
|
||||
}
|
||||
|
||||
|
||||
function init() {
|
||||
|
||||
$("#startDatepicker").datepicker().inputmask("yyyy-mm-dd",{'autoUnmask':true});
|
||||
$("#endDatepicker").datepicker().inputmask("yyyy-mm-dd",{'autoUnmask':true});
|
||||
|
||||
var today = getToday();
|
||||
|
||||
$("input[name=searchStartYYYYMMDD]").val(today.substring(0,6)+"01");
|
||||
$("input[name=searchEndYYYYMMDD]").val(today);
|
||||
$("input[name=searchStartDate]").val(today.substring(0,6)+"01");
|
||||
$("input[name=searchEndDate]").val(today);
|
||||
|
||||
$.ajax({
|
||||
type: "POST", url: url, dataType: "json",
|
||||
data: {cmd: 'LIST_INIT_COMBO'},
|
||||
success: function (json) {
|
||||
combo = json;
|
||||
new makeOptions("CODE", "NAME").setObj($("select[name=searchEventType]"))
|
||||
.setNoValueInclude(true).setNoValue("", "<%=localeMessage.getString("combo.all")%>")
|
||||
.setData(json.eventTypeList).rendering();
|
||||
|
||||
// 뒤로가기 등으로 이전 검색조건 파라미터가 있으면 기본값을 덮어씀
|
||||
putSelectFromParam();
|
||||
|
||||
// 콤보(이벤트유형) 로드 후 그리드 생성 — formatEventType 경합 방지
|
||||
buildGrid();
|
||||
},
|
||||
error: function (e) { alert(e.responseText); }
|
||||
});
|
||||
}
|
||||
|
||||
function buildGrid() {
|
||||
$('#grid').jqGrid({
|
||||
datatype: "json",
|
||||
mtype: 'POST',
|
||||
url: url,
|
||||
postData: getSearchForJqgrid("cmd", "LIST"),
|
||||
colNames: ['id', 'No.', '기관명', '이벤트유형', 'Target URL', 'Proxy URL', 'Status', '성공여부', '발송서버', '발송일시'],
|
||||
colModel: [
|
||||
{name: 'id', align: 'center', key: true, hidden: true},
|
||||
{name: 'rowNum', align: 'center', width: 45, sortable: false},
|
||||
{name: 'orgName', align: 'left', width: 140},
|
||||
{name: 'eventType', align: 'center', width: 120, formatter: formatEventType},
|
||||
{name: 'targetUrl', align: 'left', width: 300, formatter: formatTargetUrl},
|
||||
{name: 'proxyUrl', align: 'left', width: 300, formatter: formatTargetUrl},
|
||||
{name: 'statusCode', align: 'center', width: 60},
|
||||
{name: 'success', align: 'center', width: 70, formatter: formatSuccess},
|
||||
{name: 'sendBy', align: 'center', width: 100},
|
||||
{name: 'sentAt', align: 'center', width: 140}
|
||||
],
|
||||
jsonReader: {repeatitems: false},
|
||||
pager: $('#pager'),
|
||||
page: '${param.page}',
|
||||
rowNum: '${rmsDefaultRowNum}',
|
||||
autoheight: true,
|
||||
height: $("#container").height(),
|
||||
autowidth: true,
|
||||
viewrecords: true,
|
||||
rowList: eval('[${rmsDefaultRowList}]'),
|
||||
ondblClickRow: function (rowId) {
|
||||
var rowData = $(this).getRowData(rowId);
|
||||
var id = rowData['id'];
|
||||
var url2 = url_view + '?cmd=DETAIL';
|
||||
url2 += '&page=' + $(this).getGridParam("page");
|
||||
url2 += '&returnUrl=' + getReturnUrl();
|
||||
url2 += '&menuId=' + '${param.menuId}';
|
||||
url2 += '&id=' + id;
|
||||
url2 += '&' + getSearchUrl();
|
||||
goNav(url2);
|
||||
},
|
||||
loadComplete: function () {
|
||||
var page = $(this).getGridParam('page');
|
||||
var rowNum = $(this).getGridParam('rowNum');
|
||||
var rows = $(this).getDataIDs();
|
||||
var colModel = $(this).getGridParam("colModel");
|
||||
for (var i = 0; i < rows.length; i++) {
|
||||
var number = ((page - 1) * rowNum + i) + 1;
|
||||
$(this).setCell(rows[i], 'rowNum', number);
|
||||
}
|
||||
// 서버 고정 정렬(등록일시 역순) — 컬럼 정렬 비활성
|
||||
for (var i = 0; i < colModel.length; i++) {
|
||||
$(this).setColProp(colModel[i].name, {sortable: false});
|
||||
}
|
||||
},
|
||||
loadError: function (jqXHR, textStatus, errorThrown) {
|
||||
var location = '<%=request.getContextPath()%>/';
|
||||
comloadError(jqXHR, textStatus, errorThrown, location);
|
||||
}
|
||||
});
|
||||
resizeJqGridWidth('grid', 'content_middle', '1000');
|
||||
}
|
||||
|
||||
$(document).ready(function () {
|
||||
init();
|
||||
|
||||
$("#btn_search").click(function () {
|
||||
var start = $("input[name=searchStartYYYYMMDD]").val().replace(/-/gi, "");
|
||||
var end = $("input[name=searchEndYYYYMMDD]").val().replace(/-/gi, "");
|
||||
|
||||
if (start && start > getToday()) {
|
||||
alert("시작일이 오늘 이후입니다. 시작일을 확인해주세요.");
|
||||
$("input[name=searchStartYYYYMMDD]").focus();
|
||||
return false;
|
||||
}
|
||||
if (start && end && start > end) {
|
||||
alert("조회기간 종료일은 시작일보다 커야합니다.");
|
||||
$("input[name=searchEndYYYYMMDD]").focus();
|
||||
return false;
|
||||
}
|
||||
$("input[name=searchStartDate]").val(start);
|
||||
$("input[name=searchEndDate]").val(end);
|
||||
|
||||
var postData = getSearchForJqgrid("cmd", "LIST");
|
||||
$("#grid").setGridParam({url: url, postData: postData, page: 1}).trigger("reloadGrid");
|
||||
});
|
||||
|
||||
$("select[name^=search]").change(function () { $("#btn_search").click(); });
|
||||
$("input[name^=search]").keydown(function (key) {
|
||||
if (key.keyCode == 13) { $("#btn_search").click(); }
|
||||
});
|
||||
|
||||
|
||||
|
||||
buttonControl();
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="right_box">
|
||||
<div class="content_top">
|
||||
<ul class="path"><li><a href="#">${rmsMenuPath}</a></li></ul>
|
||||
</div><!-- end content_top -->
|
||||
<div class="content_middle" id="content_middle">
|
||||
<div class="search_wrap">
|
||||
<button type="button" class="cssbtn" id="btn_search" level="R"><i class="material-icons">search</i> <%= localeMessage.getString("button.search") %></button>
|
||||
</div>
|
||||
<div class="title">웹훅 발송 로그<span class="tooltip">외부로 발송된 웹훅 이력을 조회합니다.</span></div>
|
||||
<form id="ajaxForm" onsubmit="return false;">
|
||||
<table class="search_condition" cellspacing=0;>
|
||||
<colgroup>
|
||||
<col style="width:120px;"><col style="width:200px;">
|
||||
<col style="width:120px;"><col style="width:200px;">
|
||||
<col style="width:120px;"><col style="width:200px;">
|
||||
</colgroup>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>기관명</th>
|
||||
<td><input type="text" name="searchOrgName" autocomplete="off"></td>
|
||||
<th>이벤트유형</th>
|
||||
<td>
|
||||
<div class="select-style">
|
||||
<select name="searchEventType"></select>
|
||||
</div>
|
||||
</td>
|
||||
<th>성공여부</th>
|
||||
<td>
|
||||
<div class="select-style">
|
||||
<select name="searchSuccess">
|
||||
<option value="">전체</option>
|
||||
<option value="Y">성공</option>
|
||||
<option value="N">실패</option>
|
||||
</select>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Target URL</th>
|
||||
<td><input type="text" name="searchTargetUrl" autocomplete="off" style="width:95%;"></td>
|
||||
<th>발송기간</th>
|
||||
<td>
|
||||
<input type="text" name="searchStartYYYYMMDD" id="startDatepicker" readonly="readonly" value="" size="10" style="width:40%; border:1px solid #ebebec;">
|
||||
~
|
||||
<input type="text" name="searchEndYYYYMMDD" value="" id="endDatepicker" size="10" readonly="readonly" style="width:40%; border:1px solid #ebebec;">
|
||||
<input type="hidden" name="searchStartDate" value="">
|
||||
<input type="hidden" name="searchEndDate" value="">
|
||||
</td>
|
||||
<th></th>
|
||||
<td></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</form>
|
||||
<table id="grid"></table>
|
||||
<div id="pager"></div>
|
||||
</div><!-- end content_middle -->
|
||||
</div><!-- end right_box -->
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,137 +0,0 @@
|
||||
<%@ page language="java" contentType="text/html; charset=utf-8" %>
|
||||
<%@ page import="java.io.*" %>
|
||||
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
|
||||
<%@ taglib uri="http://www.springframework.org/tags" prefix="spring" %>
|
||||
<%@ include file="/jsp/common/include/localemessage.jsp" %>
|
||||
<%
|
||||
response.setHeader("Pragma", "No-cache");
|
||||
response.setHeader("Cache-Control", "no-cache");
|
||||
response.setHeader("Expires", "0");
|
||||
%>
|
||||
<html>
|
||||
<head>
|
||||
<title></title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<jsp:include page="/jsp/common/include/css.jsp"/>
|
||||
<jsp:include page="/jsp/common/include/script.jsp"/>
|
||||
<style>
|
||||
.succ-y { color:#1a73e8; font-weight:bold; }
|
||||
.succ-n { color:#d93025; font-weight:bold; }
|
||||
.pre-wrap { white-space:pre-wrap; word-break:break-all; min-height:60px; line-height:1.5; }
|
||||
</style>
|
||||
<script language="javascript">
|
||||
var url = '<c:url value="/onl/apim/webhook/webhookSendLogMan.json" />';
|
||||
var url_view = '<c:url value="/onl/apim/webhook/webhookSendLogMan.view" />';
|
||||
var key = '${param.id}';
|
||||
|
||||
function escapeHtmlJs(s) { return $('<div>').text(s == null ? '' : s).html(); }
|
||||
|
||||
function detail() {
|
||||
if (!key) return;
|
||||
$.ajax({
|
||||
type: "POST", url: url, dataType: "json",
|
||||
data: {cmd: 'DETAIL', id: key},
|
||||
success: function (data) {
|
||||
$('#id').text(data.id != null ? data.id : '');
|
||||
$('#orgName').text(data.orgName || '');
|
||||
$('#eventType').text(data.eventTypeName || data.eventType || '')
|
||||
.attr('title', data.eventType || '');
|
||||
$('#targetUrl').text(data.targetUrl || '');
|
||||
$('#proxyUrl').text(data.proxyUrl || '');
|
||||
$('#statusCode').text(data.statusCode != null ? data.statusCode : '');
|
||||
$('#retryCount').text(data.retryCount != null ? data.retryCount : '');
|
||||
$('#sendBy').text(data.sendBy || '');
|
||||
$('#sentAt').text(data.sentAt || '');
|
||||
$('#signature').text(data.signature || '');
|
||||
$('#errorMessage').text(data.errorMessage || '');
|
||||
$('#payload').text(data.payload || '');
|
||||
$('#responseBody').text(data.responseBody || '');
|
||||
|
||||
if (data.success === true) {
|
||||
$('#success').html('<span class="succ-y">성공</span>');
|
||||
} else if (data.success === false) {
|
||||
$('#success').html('<span class="succ-n">실패</span>');
|
||||
} else {
|
||||
$('#success').text('');
|
||||
}
|
||||
},
|
||||
error: function (e) { alert(e.responseText); }
|
||||
});
|
||||
}
|
||||
|
||||
$(document).ready(function () {
|
||||
var returnUrl = getReturnUrlForReturn();
|
||||
|
||||
buttonControl();
|
||||
detail();
|
||||
|
||||
$('#btn_previous').click(function () { goNav(returnUrl); });
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="right_box">
|
||||
<div class="content_top">
|
||||
<ul class="path"><li><a href="#">${rmsMenuPath}</a></li></ul>
|
||||
</div>
|
||||
<div class="content_middle">
|
||||
<div class="search_wrap">
|
||||
<button type="button" class="cssbtn" id="btn_previous" level="R" status="DETAIL,NEW"><i class="material-icons">arrow_back</i> <%= localeMessage.getString("button.previous") %></button>
|
||||
</div>
|
||||
<div class="title" id="title">웹훅 발송 로그 상세</div>
|
||||
<table class="table_row" cellspacing="0">
|
||||
<colgroup>
|
||||
<col style="width:12%"/><col style="width:38%"/>
|
||||
<col style="width:12%"/><col style="width:38%"/>
|
||||
</colgroup>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<td><span id="id"></span></td>
|
||||
<th>기관명</th>
|
||||
<td><span id="orgName"></span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>이벤트유형</th>
|
||||
<td><span id="eventType"></span></td>
|
||||
<th>Status Code</th>
|
||||
<td><span id="statusCode"></span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Target URL</th>
|
||||
<td><span id="targetUrl"></span></td>
|
||||
<th>Proxy URL</th>
|
||||
<td><span id="proxyUrl"></span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>성공여부</th>
|
||||
<td><span id="success"></span></td>
|
||||
<th>재시도횟수</th>
|
||||
<td><span id="retryCount"></span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>발송서버</th>
|
||||
<td><span id="sendBy"></span></td>
|
||||
<th>발송일시</th>
|
||||
<td><span id="sentAt"></span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Signature</th>
|
||||
<td colspan="3"><span id="signature"></span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Payload</th>
|
||||
<td colspan="3"><div id="payload" class="pre-wrap"></div></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Response Body</th>
|
||||
<td colspan="3"><div id="responseBody" class="pre-wrap"></div></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>에러 메세지</th>
|
||||
<td colspan="3"><div id="errorMessage" class="pre-wrap"></div></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -82,54 +82,6 @@
|
||||
</style>
|
||||
<jsp:include page="/jsp/common/include/script.jsp"/>
|
||||
<script language="javascript">
|
||||
function executeAggregationHourly() {
|
||||
var targetDate = $("#targetHour").val().replace(/-/g, "");
|
||||
var resultDiv = $("#hourlyResult");
|
||||
|
||||
// 날짜 입력 검증
|
||||
if (!targetDate || targetDate.trim() === '') {
|
||||
alert('대상 날짜를 입력하세요. (예: 20250120)');
|
||||
$("#targetHour").focus();
|
||||
return;
|
||||
}
|
||||
|
||||
resultDiv.hide();
|
||||
$("#btn_execute_hourly").prop("disabled", true).text("실행중...");
|
||||
|
||||
$.ajax({
|
||||
url: '<c:url value="/onl/kjb/statistics/apiStatsAggregationMan.json"/>',
|
||||
type: 'POST',
|
||||
data: {
|
||||
cmd: 'AGGREGATION_HOUR',
|
||||
targetDate: targetDate,
|
||||
serviceType: '${param.serviceType}'
|
||||
},
|
||||
success: function(response) {
|
||||
resultDiv.removeClass("error").addClass("success");
|
||||
var message = response.message + "\n";
|
||||
message += "대상 날짜: " + response.targetDate + "\n";
|
||||
message += "처리 건수: " + response.processedCount;
|
||||
resultDiv.find("pre").text(message);
|
||||
resultDiv.show();
|
||||
},
|
||||
error: function(xhr) {
|
||||
resultDiv.removeClass("success").addClass("error");
|
||||
var message = "집계 실행 실패\n";
|
||||
try {
|
||||
var error = JSON.parse(xhr.responseText);
|
||||
message += error.message;
|
||||
} catch(e) {
|
||||
message += xhr.responseText || "알 수 없는 오류가 발생했습니다.";
|
||||
}
|
||||
resultDiv.find("pre").text(message);
|
||||
resultDiv.show();
|
||||
},
|
||||
complete: function() {
|
||||
$("#btn_execute_hourly").prop("disabled", false).text("집계 실행");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function executeHourlyToDaily() {
|
||||
var targetDate = $("#targetDate").val().replace(/-/g, "");
|
||||
var resultDiv = $("#dailyResult");
|
||||
@@ -289,7 +241,6 @@
|
||||
var year = yesterday.getFullYear();
|
||||
var month = String(yesterday.getMonth() + 1).padStart(2, '0');
|
||||
var day = String(yesterday.getDate()).padStart(2, '0');
|
||||
$("#targetHour").val(year + month + day);
|
||||
$("#targetDate").val(year + month + day);
|
||||
|
||||
// 전월
|
||||
@@ -304,12 +255,6 @@
|
||||
$("#targetYear").val(lastYear);
|
||||
|
||||
// 버튼 이벤트
|
||||
$("#btn_execute_hourly").click(function() {
|
||||
if (confirm("시간별 통계 집계를 실행하시겠습니까?")) {
|
||||
executeAggregationHourly();
|
||||
}
|
||||
});
|
||||
|
||||
$("#btn_execute_daily").click(function() {
|
||||
if (confirm("시간별→일별 통계 집계를 실행하시겠습니까?")) {
|
||||
executeHourlyToDaily();
|
||||
@@ -352,26 +297,9 @@
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- 거래로그 → 시간별 집계 -->
|
||||
<div class="aggregation-section">
|
||||
<h3>1. 거래로그 → 시간별 통계 집계</h3>
|
||||
<div class="aggregation-form">
|
||||
<label>대상 날짜:</label>
|
||||
<input type="text" id="targetHour" placeholder="yyyyMMdd" maxlength="8">
|
||||
<span style="color: #666; font-size: 12px;">(예: 20250120)</span>
|
||||
<button type="button" class="cssbtn" id="btn_execute_hourly" level="W">
|
||||
<i class="material-icons">play_arrow</i> 집계 실행
|
||||
</button>
|
||||
</div>
|
||||
<div id="hourlyResult" class="result-area">
|
||||
<strong>실행 결과:</strong>
|
||||
<pre></pre>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 시간별 → 일별 집계 -->
|
||||
<div class="aggregation-section">
|
||||
<h3>2. 시간별 → 일별 통계 집계</h3>
|
||||
<h3>1. 시간별 → 일별 통계 집계</h3>
|
||||
<div class="aggregation-form">
|
||||
<label>대상 날짜:</label>
|
||||
<input type="text" id="targetDate" placeholder="yyyyMMdd" maxlength="8">
|
||||
@@ -388,7 +316,7 @@
|
||||
|
||||
<!-- 일별 → 월별 집계 -->
|
||||
<div class="aggregation-section">
|
||||
<h3>3. 일별 → 월별 통계 집계</h3>
|
||||
<h3>2. 일별 → 월별 통계 집계</h3>
|
||||
<div class="aggregation-form">
|
||||
<label>대상 월:</label>
|
||||
<input type="text" id="targetMonth" placeholder="yyyyMM" maxlength="6">
|
||||
@@ -405,7 +333,7 @@
|
||||
|
||||
<!-- 월별 → 연별 집계 -->
|
||||
<div class="aggregation-section">
|
||||
<h3>4. 월별 → 연별 통계 집계</h3>
|
||||
<h3>3. 월별 → 연별 통계 집계</h3>
|
||||
<div class="aggregation-form">
|
||||
<label>대상 연도:</label>
|
||||
<input type="text" id="targetYear" placeholder="yyyy" maxlength="4">
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
var url = '<c:url value="/onl/kjb/statistics/apiStatsDayMan.json"/>';
|
||||
var url_view = '<c:url value="/onl/kjb/statistics/apiStatsDayMan.view"/>';
|
||||
|
||||
var totalDonutChart, callChart;
|
||||
var totalDonutChart, seq900DonutChart, callChart, respChart;
|
||||
|
||||
function numberFormatter(cellvalue, options, rowObject) {
|
||||
if (cellvalue == null || cellvalue == '') return '0';
|
||||
@@ -54,7 +54,9 @@
|
||||
|
||||
function initCharts() {
|
||||
totalDonutChart = echarts.init(document.getElementById('totalDonutChart'));
|
||||
seq900DonutChart = echarts.init(document.getElementById('seq900DonutChart'));
|
||||
callChart = echarts.init(document.getElementById('callChart'));
|
||||
respChart = echarts.init(document.getElementById('respChart'));
|
||||
|
||||
// 총건수 도넛 차트
|
||||
var totalDonutOption = {
|
||||
@@ -71,7 +73,7 @@
|
||||
legend: {
|
||||
orient: 'horizontal',
|
||||
bottom: 10,
|
||||
data: ['성공', 'Timeout', '시스템오류']
|
||||
data: ['성공', 'Timeout', '시스템오류', '업무오류']
|
||||
},
|
||||
series: [{
|
||||
name: '총건수',
|
||||
@@ -96,7 +98,8 @@
|
||||
data: [
|
||||
{ value: 0, name: '성공', itemStyle: { color: '#91CC75' } },
|
||||
{ value: 0, name: 'Timeout', itemStyle: { color: '#FAC858' } },
|
||||
{ value: 0, name: '시스템오류', itemStyle: { color: '#EE6666' } }
|
||||
{ value: 0, name: '시스템오류', itemStyle: { color: '#EE6666' } },
|
||||
{ value: 0, name: '업무오류', itemStyle: { color: '#FC8452' } }
|
||||
]
|
||||
}],
|
||||
graphic: [{
|
||||
@@ -115,7 +118,64 @@
|
||||
|
||||
totalDonutChart.setOption(totalDonutOption);
|
||||
|
||||
|
||||
// Seq900 도넛 차트
|
||||
var seq900DonutOption = {
|
||||
title: {
|
||||
text: 'Seq900 에러 분포',
|
||||
left: 'center',
|
||||
top: 10,
|
||||
textStyle: { fontSize: 14 }
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
formatter: '{b}: {c} ({d}%)'
|
||||
},
|
||||
legend: {
|
||||
orient: 'horizontal',
|
||||
bottom: 10,
|
||||
data: ['Timeout', '시스템오류', '업무오류']
|
||||
},
|
||||
series: [{
|
||||
name: 'Seq900 에러',
|
||||
type: 'pie',
|
||||
radius: ['40%', '70%'],
|
||||
center: ['50%', '50%'],
|
||||
avoidLabelOverlap: true,
|
||||
label: {
|
||||
show: true,
|
||||
formatter: '{b}: {c}'
|
||||
},
|
||||
emphasis: {
|
||||
label: {
|
||||
show: true,
|
||||
fontSize: 16,
|
||||
fontWeight: 'bold'
|
||||
}
|
||||
},
|
||||
labelLine: {
|
||||
show: true
|
||||
},
|
||||
data: [
|
||||
{ value: 0, name: 'Timeout', itemStyle: { color: '#FAC858' } },
|
||||
{ value: 0, name: '시스템오류', itemStyle: { color: '#EE6666' } },
|
||||
{ value: 0, name: '업무오류', itemStyle: { color: '#FC8452' } }
|
||||
]
|
||||
}],
|
||||
graphic: [{
|
||||
type: 'text',
|
||||
left: 'center',
|
||||
top: 'center',
|
||||
style: {
|
||||
text: '0',
|
||||
textAlign: 'center',
|
||||
fill: '#333',
|
||||
fontSize: 24,
|
||||
fontWeight: 'bold'
|
||||
}
|
||||
}]
|
||||
};
|
||||
|
||||
seq900DonutChart.setOption(seq900DonutOption);
|
||||
|
||||
// 호출량 차트
|
||||
var callOption = {
|
||||
@@ -131,13 +191,32 @@
|
||||
series: [
|
||||
{ name: '성공', type: 'line', stack: 'Total', smooth: true, areaStyle: { opacity: 0.5 }, itemStyle: { color: '#5470C6' }, data: [] },
|
||||
{ name: 'Timeout', type: 'line', stack: 'Total', smooth: true, areaStyle: { opacity: 0.5 }, itemStyle: { color: '#FAC858' }, data: [] },
|
||||
{ name: '시스템오류', type: 'line', stack: 'Total', smooth: true, areaStyle: { opacity: 0.5 }, itemStyle: { color: '#EE6666' }, data: [] }
|
||||
{ name: '시스템오류', type: 'line', stack: 'Total', smooth: true, areaStyle: { opacity: 0.5 }, itemStyle: { color: '#EE6666' }, data: [] },
|
||||
{ name: '업무오류', type: 'line', stack: 'Total', smooth: true, areaStyle: { opacity: 0.5 }, itemStyle: { color: '#FC8452' }, data: [] }
|
||||
]
|
||||
};
|
||||
|
||||
callChart.setOption(callOption);
|
||||
|
||||
|
||||
// 응답시간 차트
|
||||
var respOption = {
|
||||
title: { text: '응답시간 추이 (ms)', left: 'center', textStyle: { fontSize: 14 } },
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: { type: 'cross', label: { backgroundColor: '#6a7985' } }
|
||||
},
|
||||
legend: { data: ['P95', 'P50', '평균'], bottom: 0 },
|
||||
grid: { left: '3%', right: '4%', bottom: '15%', top: '15%', containLabel: true },
|
||||
xAxis: { type: 'category', boundaryGap: false, data: [] },
|
||||
yAxis: { type: 'value' },
|
||||
series: [
|
||||
{ name: 'P95', type: 'line', smooth: true, itemStyle: { color: '#EE6666' }, lineStyle: { type: 'dashed' }, data: [] },
|
||||
{ name: 'P50', type: 'line', smooth: true, itemStyle: { color: '#5470C6' }, data: [] },
|
||||
{ name: '평균', type: 'line', smooth: true, itemStyle: { color: '#91CC75' }, data: [] }
|
||||
]
|
||||
};
|
||||
|
||||
respChart.setOption(respOption);
|
||||
}
|
||||
|
||||
function updateCharts(data) {
|
||||
@@ -189,7 +268,8 @@
|
||||
data: [
|
||||
{ value: totalSuccess, name: '성공' },
|
||||
{ value: totalTimeout, name: 'Timeout' },
|
||||
{ value: totalSystemErr, name: '시스템오류' }
|
||||
{ value: totalSystemErr, name: '시스템오류' },
|
||||
{ value: totalBizErr, name: '업무오류' }
|
||||
]
|
||||
}],
|
||||
graphic: [{
|
||||
@@ -199,6 +279,22 @@
|
||||
}]
|
||||
});
|
||||
|
||||
// Seq900 도넛 차트 업데이트
|
||||
var seq900Total = seq900Timeout + seq900SystemErr + seq900BizErr;
|
||||
seq900DonutChart.setOption({
|
||||
series: [{
|
||||
data: [
|
||||
{ value: seq900Timeout, name: 'Timeout' },
|
||||
{ value: seq900SystemErr, name: '시스템오류' },
|
||||
{ value: seq900BizErr, name: '업무오류' }
|
||||
]
|
||||
}],
|
||||
graphic: [{
|
||||
style: {
|
||||
text: seq900Total.toLocaleString()
|
||||
}
|
||||
}]
|
||||
});
|
||||
|
||||
// 호출량 차트 업데이트
|
||||
callChart.setOption({
|
||||
@@ -206,13 +302,24 @@
|
||||
series: [
|
||||
{ data: successData },
|
||||
{ data: timeoutData },
|
||||
{ data: systemErrData }
|
||||
{ data: systemErrData },
|
||||
{ data: bizErrData }
|
||||
]
|
||||
});
|
||||
|
||||
// 응답시간 차트 업데이트
|
||||
respChart.setOption({
|
||||
xAxis: { data: times.map(function(t) { return t.substring(6, 8) + '일'; }) },
|
||||
series: [
|
||||
{ data: p95RespData },
|
||||
{ data: p50RespData },
|
||||
{ data: avgRespData }
|
||||
]
|
||||
});
|
||||
|
||||
// 드릴다운을 위해 원본 데이터 저장
|
||||
callChart.rawData = data;
|
||||
respChart.rawData = data;
|
||||
}
|
||||
|
||||
function fetchChartData() {
|
||||
@@ -428,7 +535,8 @@
|
||||
postData: gridPostData,
|
||||
colNames: [
|
||||
'API명',
|
||||
'총건수', '성공', 'Timeout', '시스템오류',
|
||||
'총건수', '성공', 'Timeout', '시스템오류', '업무오류',
|
||||
'Seq900 Timeout', 'Seq900 시스템오류', 'Seq900 업무오류',
|
||||
'평균응답(ms)', '최소응답(ms)', '최대응답(ms)'
|
||||
],
|
||||
colModel: [
|
||||
@@ -437,6 +545,10 @@
|
||||
{ name: 'successCnt', align: 'right', width: '80', formatter: numberFormatter, sortable: false },
|
||||
{ name: 'timeoutCnt', align: 'right', width: '80', formatter: numberFormatter, sortable: false },
|
||||
{ name: 'systemErrCnt', align: 'right', width: '80', formatter: numberFormatter, sortable: false },
|
||||
{ name: 'bizErrCnt', align: 'right', width: '80', formatter: numberFormatter, sortable: false },
|
||||
{ name: 'seq900TimeoutCnt', align: 'right', width: '100', formatter: numberFormatter, sortable: false },
|
||||
{ name: 'seq900SystemErrCnt', align: 'right', width: '120', formatter: numberFormatter, sortable: false },
|
||||
{ name: 'seq900BizErrCnt', align: 'right', width: '100', formatter: numberFormatter, sortable: false },
|
||||
{ name: 'avgRespTime', align: 'right', width: '90', formatter: decimalFormatter, sortable: false },
|
||||
{ name: 'minRespTime', align: 'right', width: '90', formatter: decimalFormatter, sortable: false },
|
||||
{ name: 'maxRespTime', align: 'right', width: '90', formatter: decimalFormatter, sortable: false }
|
||||
@@ -468,8 +580,9 @@
|
||||
colNames: [
|
||||
'통계시간', 'API명', '인스턴스', '업무구분', '클라이언트ID',
|
||||
'Inbound Adapter', 'Outbound Adapter',
|
||||
'총건수', '성공', 'Timeout', '시스템오류',
|
||||
'평균응답(ms)', '최소응답(ms)', '최대응답(ms)'
|
||||
'총건수', '성공', 'Timeout', '시스템오류', '업무오류',
|
||||
'Seq900 Timeout', 'Seq900 시스템오류', 'Seq900 업무오류',
|
||||
'평균응답(ms)', '최소응답(ms)', '최대응답(ms)', 'P50(ms)', 'P95(ms)'
|
||||
],
|
||||
colModel: [
|
||||
{ name: 'statTime', align: 'center', width: '120', sortable: false },
|
||||
@@ -483,9 +596,15 @@
|
||||
{ name: 'successCnt', align: 'right', width: '70', formatter: numberFormatter, sortable: false },
|
||||
{ name: 'timeoutCnt', align: 'right', width: '70', formatter: numberFormatter, sortable: false },
|
||||
{ name: 'systemErrCnt', align: 'right', width: '70', formatter: numberFormatter, sortable: false },
|
||||
{ name: 'bizErrCnt', align: 'right', width: '70', formatter: numberFormatter, sortable: false },
|
||||
{ name: 'seq900TimeoutCnt', align: 'right', width: '70', formatter: numberFormatter, sortable: false },
|
||||
{ name: 'seq900SystemErrCnt', align: 'right', width: '70', formatter: numberFormatter, sortable: false },
|
||||
{ name: 'seq900BizErrCnt', align: 'right', width: '70', formatter: numberFormatter, sortable: false },
|
||||
{ name: 'avgRespTime', align: 'right', width: '80', formatter: decimalFormatter, sortable: false },
|
||||
{ name: 'minRespTime', align: 'right', width: '80', formatter: decimalFormatter, sortable: false },
|
||||
{ name: 'maxRespTime', align: 'right', width: '80', formatter: decimalFormatter, sortable: false }
|
||||
{ name: 'maxRespTime', align: 'right', width: '80', formatter: decimalFormatter, sortable: false },
|
||||
{ name: 'p50RespTime', align: 'right', width: '70', formatter: decimalFormatter, sortable: false },
|
||||
{ name: 'p95RespTime', align: 'right', width: '70', formatter: decimalFormatter, sortable: false }
|
||||
],
|
||||
jsonReader: { repeatitems: false },
|
||||
pager: $('#pager'),
|
||||
@@ -566,7 +685,9 @@
|
||||
// 윈도우 리사이즈 시 차트 리사이즈
|
||||
$(window).resize(function() {
|
||||
if (totalDonutChart) totalDonutChart.resize();
|
||||
if (seq900DonutChart) seq900DonutChart.resize();
|
||||
if (callChart) callChart.resize();
|
||||
if (respChart) respChart.resize();
|
||||
});
|
||||
|
||||
buttonControl();
|
||||
@@ -591,7 +712,7 @@
|
||||
<tbody>
|
||||
<tr>
|
||||
<th style="width:100px;">조회기간</th>
|
||||
<td colspan="5">
|
||||
<td colspan="3">
|
||||
<input type="text" name="searchStartDateTime" value="${param.searchStartDateTime}" style="width:100px;">
|
||||
~
|
||||
<input type="text" name="searchEndDateTime" value="${param.searchEndDateTime}" style="width:100px;">
|
||||
@@ -603,20 +724,22 @@
|
||||
<td>
|
||||
<input type="text" name="searchApiName" value="${param.searchApiName}">
|
||||
</td>
|
||||
<th style="width:100px;">업무구분</th>
|
||||
<td>
|
||||
<input type="text" name="searchBizDivCode" value="${param.searchBizDivCode}">
|
||||
</td>
|
||||
<th style="width:100px;">인스턴스</th>
|
||||
<td>
|
||||
<input type="text" name="searchGwInstanceId" value="${param.searchGwInstanceId}">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th style="width:100px;">업무구분</th>
|
||||
<td>
|
||||
<input type="text" name="searchBizDivCode" value="${param.searchBizDivCode}">
|
||||
</td>
|
||||
<th style="width:100px;">클라이언트ID</th>
|
||||
<td>
|
||||
<input type="text" name="searchClientId" value="${param.searchClientId}">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th style="width:100px;">Inbound Adapter</th>
|
||||
<td>
|
||||
<input type="text" name="searchInboundAdapter" value="${param.searchInboundAdapter}">
|
||||
@@ -631,7 +754,12 @@
|
||||
<!-- 도넛 차트 (상단 50%씩) -->
|
||||
<div class="chart-container">
|
||||
<div id="totalDonutChart" class="chart"></div>
|
||||
<div id="seq900DonutChart" class="chart"></div>
|
||||
</div>
|
||||
<!-- 호출량 및 응답시간 차트 (하단 50%씩) -->
|
||||
<div class="chart-container">
|
||||
<div id="callChart" class="chart"></div>
|
||||
<div id="respChart" class="chart"></div>
|
||||
</div>
|
||||
|
||||
<!-- 요약 그리드 -->
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
var url_view = '<c:url value="/onl/kjb/statistics/apiStatsHourMan.view"/>';
|
||||
var url_minute_view = '<c:url value="/onl/kjb/statistics/apiStatsMinuteMan.view"/>';
|
||||
|
||||
var totalDonutChart, callChart;
|
||||
var totalDonutChart, seq900DonutChart, callChart, respChart;
|
||||
|
||||
function numberFormatter(cellvalue, options, rowObject) {
|
||||
if (cellvalue == null || cellvalue == '') return '0';
|
||||
@@ -55,7 +55,9 @@
|
||||
|
||||
function initCharts() {
|
||||
totalDonutChart = echarts.init(document.getElementById('totalDonutChart'));
|
||||
seq900DonutChart = echarts.init(document.getElementById('seq900DonutChart'));
|
||||
callChart = echarts.init(document.getElementById('callChart'));
|
||||
respChart = echarts.init(document.getElementById('respChart'));
|
||||
|
||||
// 총건수 도넛 차트
|
||||
var totalDonutOption = {
|
||||
@@ -72,7 +74,7 @@
|
||||
legend: {
|
||||
orient: 'horizontal',
|
||||
bottom: 10,
|
||||
data: ['성공', 'Timeout', '시스템오류']
|
||||
data: ['성공', 'Timeout', '시스템오류', '업무오류']
|
||||
},
|
||||
series: [{
|
||||
name: '총건수',
|
||||
@@ -97,7 +99,8 @@
|
||||
data: [
|
||||
{ value: 0, name: '성공', itemStyle: { color: '#91CC75' } },
|
||||
{ value: 0, name: 'Timeout', itemStyle: { color: '#FAC858' } },
|
||||
{ value: 0, name: '시스템오류', itemStyle: { color: '#EE6666' } }
|
||||
{ value: 0, name: '시스템오류', itemStyle: { color: '#EE6666' } },
|
||||
{ value: 0, name: '업무오류', itemStyle: { color: '#FC8452' } }
|
||||
]
|
||||
}],
|
||||
graphic: [{
|
||||
@@ -116,7 +119,64 @@
|
||||
|
||||
totalDonutChart.setOption(totalDonutOption);
|
||||
|
||||
|
||||
// Seq900 도넛 차트
|
||||
var seq900DonutOption = {
|
||||
title: {
|
||||
text: 'Seq900 에러 분포',
|
||||
left: 'center',
|
||||
top: 10,
|
||||
textStyle: { fontSize: 14 }
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
formatter: '{b}: {c} ({d}%)'
|
||||
},
|
||||
legend: {
|
||||
orient: 'horizontal',
|
||||
bottom: 10,
|
||||
data: ['Timeout', '시스템오류', '업무오류']
|
||||
},
|
||||
series: [{
|
||||
name: 'Seq900 에러',
|
||||
type: 'pie',
|
||||
radius: ['40%', '70%'],
|
||||
center: ['50%', '50%'],
|
||||
avoidLabelOverlap: true,
|
||||
label: {
|
||||
show: true,
|
||||
formatter: '{b}: {c}'
|
||||
},
|
||||
emphasis: {
|
||||
label: {
|
||||
show: true,
|
||||
fontSize: 16,
|
||||
fontWeight: 'bold'
|
||||
}
|
||||
},
|
||||
labelLine: {
|
||||
show: true
|
||||
},
|
||||
data: [
|
||||
{ value: 0, name: 'Timeout', itemStyle: { color: '#FAC858' } },
|
||||
{ value: 0, name: '시스템오류', itemStyle: { color: '#EE6666' } },
|
||||
{ value: 0, name: '업무오류', itemStyle: { color: '#FC8452' } }
|
||||
]
|
||||
}],
|
||||
graphic: [{
|
||||
type: 'text',
|
||||
left: 'center',
|
||||
top: 'center',
|
||||
style: {
|
||||
text: '0',
|
||||
textAlign: 'center',
|
||||
fill: '#333',
|
||||
fontSize: 24,
|
||||
fontWeight: 'bold'
|
||||
}
|
||||
}]
|
||||
};
|
||||
|
||||
seq900DonutChart.setOption(seq900DonutOption);
|
||||
|
||||
// 호출량 차트
|
||||
var callOption = {
|
||||
@@ -125,24 +185,47 @@
|
||||
trigger: 'axis',
|
||||
axisPointer: { type: 'cross', label: { backgroundColor: '#6a7985' } }
|
||||
},
|
||||
legend: { data: ['성공', 'Timeout', '시스템오류'], bottom: 0 },
|
||||
legend: { data: ['성공', 'Timeout', '시스템오류', '업무오류'], bottom: 0 },
|
||||
grid: { left: '3%', right: '4%', bottom: '15%', top: '15%', containLabel: true },
|
||||
xAxis: { type: 'category', boundaryGap: false, data: [] },
|
||||
yAxis: { type: 'value', minInterval: 1 },
|
||||
series: [
|
||||
{ name: '성공', type: 'line', stack: 'Total', smooth: true, areaStyle: { opacity: 0.5 }, itemStyle: { color: '#5470C6' }, data: [] },
|
||||
{ name: 'Timeout', type: 'line', stack: 'Total', smooth: true, areaStyle: { opacity: 0.5 }, itemStyle: { color: '#FAC858' }, data: [] },
|
||||
{ name: '시스템오류', type: 'line', stack: 'Total', smooth: true, areaStyle: { opacity: 0.5 }, itemStyle: { color: '#EE6666' }, data: [] }
|
||||
{ name: '시스템오류', type: 'line', stack: 'Total', smooth: true, areaStyle: { opacity: 0.5 }, itemStyle: { color: '#EE6666' }, data: [] },
|
||||
{ name: '업무오류', type: 'line', stack: 'Total', smooth: true, areaStyle: { opacity: 0.5 }, itemStyle: { color: '#FC8452' }, data: [] }
|
||||
]
|
||||
};
|
||||
|
||||
callChart.setOption(callOption);
|
||||
|
||||
// 응답시간 차트
|
||||
var respOption = {
|
||||
title: { text: '응답시간 추이 (ms)', left: 'center', textStyle: { fontSize: 14 } },
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: { type: 'cross', label: { backgroundColor: '#6a7985' } }
|
||||
},
|
||||
legend: { data: ['P95', 'P50', '평균'], bottom: 0 },
|
||||
grid: { left: '3%', right: '4%', bottom: '15%', top: '15%', containLabel: true },
|
||||
xAxis: { type: 'category', boundaryGap: false, data: [] },
|
||||
yAxis: { type: 'value' },
|
||||
series: [
|
||||
{ name: 'P95', type: 'line', smooth: true, itemStyle: { color: '#EE6666' }, lineStyle: { type: 'dashed' }, data: [] },
|
||||
{ name: 'P50', type: 'line', smooth: true, itemStyle: { color: '#5470C6' }, data: [] },
|
||||
{ name: '평균', type: 'line', smooth: true, itemStyle: { color: '#91CC75' }, data: [] }
|
||||
]
|
||||
};
|
||||
|
||||
respChart.setOption(respOption);
|
||||
|
||||
// 드릴다운 이벤트 (시간 클릭 시 분단위 화면으로 이동)
|
||||
callChart.on('click', function(params) {
|
||||
drillDownToMinute(callChart, params.dataIndex);
|
||||
});
|
||||
respChart.on('click', function(params) {
|
||||
drillDownToMinute(respChart, params.dataIndex);
|
||||
});
|
||||
}
|
||||
|
||||
function drillDownToMinute(chart, dataIndex) {
|
||||
@@ -225,7 +308,8 @@
|
||||
data: [
|
||||
{ value: totalSuccess, name: '성공' },
|
||||
{ value: totalTimeout, name: 'Timeout' },
|
||||
{ value: totalSystemErr, name: '시스템오류' }
|
||||
{ value: totalSystemErr, name: '시스템오류' },
|
||||
{ value: totalBizErr, name: '업무오류' }
|
||||
]
|
||||
}],
|
||||
graphic: [{
|
||||
@@ -235,6 +319,22 @@
|
||||
}]
|
||||
});
|
||||
|
||||
// Seq900 도넛 차트 업데이트
|
||||
var seq900Total = seq900Timeout + seq900SystemErr + seq900BizErr;
|
||||
seq900DonutChart.setOption({
|
||||
series: [{
|
||||
data: [
|
||||
{ value: seq900Timeout, name: 'Timeout' },
|
||||
{ value: seq900SystemErr, name: '시스템오류' },
|
||||
{ value: seq900BizErr, name: '업무오류' }
|
||||
]
|
||||
}],
|
||||
graphic: [{
|
||||
style: {
|
||||
text: seq900Total.toLocaleString()
|
||||
}
|
||||
}]
|
||||
});
|
||||
|
||||
// 호출량 차트 업데이트
|
||||
callChart.setOption({
|
||||
@@ -242,13 +342,24 @@
|
||||
series: [
|
||||
{ data: successData },
|
||||
{ data: timeoutData },
|
||||
{ data: systemErrData }
|
||||
{ data: systemErrData },
|
||||
{ data: bizErrData }
|
||||
]
|
||||
});
|
||||
|
||||
// 응답시간 차트 업데이트
|
||||
respChart.setOption({
|
||||
xAxis: { data: times.map(function(t) { return t.substring(8, 10) + '시'; }) },
|
||||
series: [
|
||||
{ data: p95RespData },
|
||||
{ data: p50RespData },
|
||||
{ data: avgRespData }
|
||||
]
|
||||
});
|
||||
|
||||
// 드릴다운을 위해 원본 데이터 저장
|
||||
callChart.rawData = data;
|
||||
respChart.rawData = data;
|
||||
}
|
||||
|
||||
function fetchChartData() {
|
||||
@@ -430,7 +541,8 @@
|
||||
postData: gridPostData,
|
||||
colNames: [
|
||||
'API명',
|
||||
'총건수', '성공', 'Timeout', '시스템오류',
|
||||
'총건수', '성공', 'Timeout', '시스템오류', '업무오류',
|
||||
'Seq900 Timeout', 'Seq900 시스템오류', 'Seq900 업무오류',
|
||||
'평균응답(ms)', '최소응답(ms)', '최대응답(ms)'
|
||||
],
|
||||
colModel: [
|
||||
@@ -439,6 +551,10 @@
|
||||
{ name: 'successCnt', align: 'right', width: '80', formatter: numberFormatter, sortable: false },
|
||||
{ name: 'timeoutCnt', align: 'right', width: '80', formatter: numberFormatter, sortable: false },
|
||||
{ name: 'systemErrCnt', align: 'right', width: '80', formatter: numberFormatter, sortable: false },
|
||||
{ name: 'bizErrCnt', align: 'right', width: '80', formatter: numberFormatter, sortable: false },
|
||||
{ name: 'seq900TimeoutCnt', align: 'right', width: '100', formatter: numberFormatter, sortable: false },
|
||||
{ name: 'seq900SystemErrCnt', align: 'right', width: '120', formatter: numberFormatter, sortable: false },
|
||||
{ name: 'seq900BizErrCnt', align: 'right', width: '100', formatter: numberFormatter, sortable: false },
|
||||
{ name: 'avgRespTime', align: 'right', width: '90', formatter: decimalFormatter, sortable: false },
|
||||
{ name: 'minRespTime', align: 'right', width: '90', formatter: decimalFormatter, sortable: false },
|
||||
{ name: 'maxRespTime', align: 'right', width: '90', formatter: decimalFormatter, sortable: false }
|
||||
@@ -470,8 +586,9 @@
|
||||
colNames: [
|
||||
'통계시간', 'API명', '인스턴스', '업무구분', '클라이언트ID',
|
||||
'Inbound Adapter', 'Outbound Adapter',
|
||||
'총건수', '성공', 'Timeout', '시스템오류',
|
||||
'평균응답(ms)', '최소응답(ms)', '최대응답(ms)'
|
||||
'총건수', '성공', 'Timeout', '시스템오류', '업무오류',
|
||||
'Seq900 Timeout', 'Seq900 시스템오류', 'Seq900 업무오류',
|
||||
'평균응답(ms)', '최소응답(ms)', '최대응답(ms)', 'P50(ms)', 'P95(ms)'
|
||||
],
|
||||
colModel: [
|
||||
{ name: 'statTime', align: 'center', width: '120', sortable: false },
|
||||
@@ -485,9 +602,15 @@
|
||||
{ name: 'successCnt', align: 'right', width: '70', formatter: numberFormatter, sortable: false },
|
||||
{ name: 'timeoutCnt', align: 'right', width: '70', formatter: numberFormatter, sortable: false },
|
||||
{ name: 'systemErrCnt', align: 'right', width: '70', formatter: numberFormatter, sortable: false },
|
||||
{ name: 'bizErrCnt', align: 'right', width: '70', formatter: numberFormatter, sortable: false },
|
||||
{ name: 'seq900TimeoutCnt', align: 'right', width: '70', formatter: numberFormatter, sortable: false },
|
||||
{ name: 'seq900SystemErrCnt', align: 'right', width: '70', formatter: numberFormatter, sortable: false },
|
||||
{ name: 'seq900BizErrCnt', align: 'right', width: '70', formatter: numberFormatter, sortable: false },
|
||||
{ name: 'avgRespTime', align: 'right', width: '80', formatter: decimalFormatter, sortable: false },
|
||||
{ name: 'minRespTime', align: 'right', width: '80', formatter: decimalFormatter, sortable: false },
|
||||
{ name: 'maxRespTime', align: 'right', width: '80', formatter: decimalFormatter, sortable: false }
|
||||
{ name: 'maxRespTime', align: 'right', width: '80', formatter: decimalFormatter, sortable: false },
|
||||
{ name: 'p50RespTime', align: 'right', width: '70', formatter: decimalFormatter, sortable: false },
|
||||
{ name: 'p95RespTime', align: 'right', width: '70', formatter: decimalFormatter, sortable: false }
|
||||
],
|
||||
jsonReader: { repeatitems: false },
|
||||
pager: $('#pager'),
|
||||
@@ -546,7 +669,9 @@
|
||||
// 윈도우 리사이즈 시 차트 리사이즈
|
||||
$(window).resize(function() {
|
||||
if (totalDonutChart) totalDonutChart.resize();
|
||||
if (seq900DonutChart) seq900DonutChart.resize();
|
||||
if (callChart) callChart.resize();
|
||||
if (respChart) respChart.resize();
|
||||
});
|
||||
|
||||
buttonControl();
|
||||
@@ -611,7 +736,12 @@
|
||||
<!-- 도넛 차트 (상단 50%씩) -->
|
||||
<div class="chart-container">
|
||||
<div id="totalDonutChart" class="chart"></div>
|
||||
<div id="seq900DonutChart" class="chart"></div>
|
||||
</div>
|
||||
<!-- 호출량 및 응답시간 차트 (하단 50%씩) -->
|
||||
<div class="chart-container">
|
||||
<div id="callChart" class="chart"></div>
|
||||
<div id="respChart" class="chart"></div>
|
||||
</div>
|
||||
|
||||
<!-- 요약 그리드 -->
|
||||
@@ -622,7 +752,7 @@
|
||||
<i class="material-icons">file_download</i> Excel 다운로드 (요약)
|
||||
</button>
|
||||
</div>
|
||||
<table id="gridSummary"></table>
|
||||
<table id="gridSummary"></table>
|
||||
<div id="pagerSummary"></div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
%>
|
||||
<html>
|
||||
<head>
|
||||
<title>대시보드</title>
|
||||
<title>분별 통계 조회</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<jsp:include page="/jsp/common/include/css.jsp"/>
|
||||
<style>
|
||||
@@ -33,7 +33,7 @@
|
||||
var url = '<c:url value="/onl/kjb/statistics/apiStatsMinuteMan.json"/>';
|
||||
var url_view = '<c:url value="/onl/kjb/statistics/apiStatsMinuteMan.view"/>';
|
||||
|
||||
var totalDonutChart, callChart;
|
||||
var totalDonutChart, seq900DonutChart, callChart, respChart;
|
||||
|
||||
function numberFormatter(cellvalue, options, rowObject) {
|
||||
if (cellvalue == null || cellvalue == '') return '0';
|
||||
@@ -47,7 +47,9 @@
|
||||
|
||||
function initCharts() {
|
||||
totalDonutChart = echarts.init(document.getElementById('totalDonutChart'));
|
||||
seq900DonutChart = echarts.init(document.getElementById('seq900DonutChart'));
|
||||
callChart = echarts.init(document.getElementById('callChart'));
|
||||
respChart = echarts.init(document.getElementById('respChart'));
|
||||
|
||||
// 총건수 도넛 차트
|
||||
var totalDonutOption = {
|
||||
@@ -64,7 +66,7 @@
|
||||
legend: {
|
||||
orient: 'horizontal',
|
||||
bottom: 10,
|
||||
data: ['성공', 'Timeout', '시스템오류']
|
||||
data: ['성공', 'Timeout', '시스템오류', '업무오류']
|
||||
},
|
||||
series: [{
|
||||
name: '총건수',
|
||||
@@ -89,7 +91,8 @@
|
||||
data: [
|
||||
{ value: 0, name: '성공', itemStyle: { color: '#91CC75' } },
|
||||
{ value: 0, name: 'Timeout', itemStyle: { color: '#FAC858' } },
|
||||
{ value: 0, name: '시스템오류', itemStyle: { color: '#EE6666' } }
|
||||
{ value: 0, name: '시스템오류', itemStyle: { color: '#EE6666' } },
|
||||
{ value: 0, name: '업무오류', itemStyle: { color: '#FC8452' } }
|
||||
]
|
||||
}],
|
||||
graphic: [{
|
||||
@@ -108,7 +111,64 @@
|
||||
|
||||
totalDonutChart.setOption(totalDonutOption);
|
||||
|
||||
// Seq900 도넛 차트
|
||||
var seq900DonutOption = {
|
||||
title: {
|
||||
text: 'Seq900 에러 분포',
|
||||
left: 'center',
|
||||
top: 10,
|
||||
textStyle: { fontSize: 14 }
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
formatter: '{b}: {c} ({d}%)'
|
||||
},
|
||||
legend: {
|
||||
orient: 'horizontal',
|
||||
bottom: 10,
|
||||
data: ['Timeout', '시스템오류', '업무오류']
|
||||
},
|
||||
series: [{
|
||||
name: 'Seq900 에러',
|
||||
type: 'pie',
|
||||
radius: ['40%', '70%'],
|
||||
center: ['50%', '50%'],
|
||||
avoidLabelOverlap: true,
|
||||
label: {
|
||||
show: true,
|
||||
formatter: '{b}: {c}'
|
||||
},
|
||||
emphasis: {
|
||||
label: {
|
||||
show: true,
|
||||
fontSize: 16,
|
||||
fontWeight: 'bold'
|
||||
}
|
||||
},
|
||||
labelLine: {
|
||||
show: true
|
||||
},
|
||||
data: [
|
||||
{ value: 0, name: 'Timeout', itemStyle: { color: '#FAC858' } },
|
||||
{ value: 0, name: '시스템오류', itemStyle: { color: '#EE6666' } },
|
||||
{ value: 0, name: '업무오류', itemStyle: { color: '#FC8452' } }
|
||||
]
|
||||
}],
|
||||
graphic: [{
|
||||
type: 'text',
|
||||
left: 'center',
|
||||
top: 'center',
|
||||
style: {
|
||||
text: '0',
|
||||
textAlign: 'center',
|
||||
fill: '#333',
|
||||
fontSize: 24,
|
||||
fontWeight: 'bold'
|
||||
}
|
||||
}]
|
||||
};
|
||||
|
||||
seq900DonutChart.setOption(seq900DonutOption);
|
||||
|
||||
// 호출량 차트
|
||||
var callOption = {
|
||||
@@ -117,7 +177,7 @@
|
||||
trigger: 'axis',
|
||||
axisPointer: { type: 'cross', label: { backgroundColor: '#6a7985' } }
|
||||
},
|
||||
legend: { data: ['성공', 'Timeout', '시스템오류'], bottom: 0 },
|
||||
legend: { data: ['성공', 'Timeout', '시스템오류', '업무오류'], bottom: 0 },
|
||||
grid: { left: '3%', right: '4%', bottom: '15%', top: '15%', containLabel: true },
|
||||
xAxis: { type: 'category', boundaryGap: false, data: [] },
|
||||
yAxis: { type: 'value', minInterval: 1 },
|
||||
@@ -128,12 +188,34 @@
|
||||
series: [
|
||||
{ name: '성공', type: 'line', stack: 'Total', smooth: true, areaStyle: { opacity: 0.5 }, itemStyle: { color: '#5470C6' }, data: [], showSymbol: false },
|
||||
{ name: 'Timeout', type: 'line', stack: 'Total', smooth: true, areaStyle: { opacity: 0.5 }, itemStyle: { color: '#FAC858' }, data: [], showSymbol: false },
|
||||
{ name: '시스템오류', type: 'line', stack: 'Total', smooth: true, areaStyle: { opacity: 0.5 }, itemStyle: { color: '#EE6666' }, data: [], showSymbol: false }
|
||||
{ name: '시스템오류', type: 'line', stack: 'Total', smooth: true, areaStyle: { opacity: 0.5 }, itemStyle: { color: '#EE6666' }, data: [], showSymbol: false },
|
||||
{ name: '업무오류', type: 'line', stack: 'Total', smooth: true, areaStyle: { opacity: 0.5 }, itemStyle: { color: '#FC8452' }, data: [], showSymbol: false }
|
||||
]
|
||||
};
|
||||
|
||||
var respOption = {
|
||||
title: { text: '응답시간 추이 (ms)', left: 'center', textStyle: { fontSize: 14 } },
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: { type: 'cross', label: { backgroundColor: '#6a7985' } }
|
||||
},
|
||||
legend: { data: ['P95', 'P50', '평균'], bottom: 0 },
|
||||
grid: { left: '3%', right: '4%', bottom: '15%', top: '15%', containLabel: true },
|
||||
xAxis: { type: 'category', boundaryGap: false, data: [] },
|
||||
yAxis: { type: 'value' },
|
||||
dataZoom: [
|
||||
{ type: 'inside', start: 0, end: 100 },
|
||||
{ start: 0, end: 100 }
|
||||
],
|
||||
series: [
|
||||
{ name: 'P95', type: 'line', smooth: true, itemStyle: { color: '#EE6666' }, lineStyle: { type: 'dashed' }, data: [], showSymbol: false },
|
||||
{ name: 'P50', type: 'line', smooth: true, itemStyle: { color: '#5470C6' }, data: [], showSymbol: false },
|
||||
{ name: '평균', type: 'line', smooth: true, itemStyle: { color: '#91CC75' }, data: [], showSymbol: false }
|
||||
]
|
||||
};
|
||||
|
||||
callChart.setOption(callOption);
|
||||
respChart.setOption(respOption);
|
||||
}
|
||||
|
||||
function updateCharts(data) {
|
||||
@@ -187,7 +269,8 @@
|
||||
data: [
|
||||
{ value: totalSuccess, name: '성공' },
|
||||
{ value: totalTimeout, name: 'Timeout' },
|
||||
{ value: totalSystemErr, name: '시스템오류' }
|
||||
{ value: totalSystemErr, name: '시스템오류' },
|
||||
{ value: totalBizErr, name: '업무오류' }
|
||||
]
|
||||
}],
|
||||
graphic: [{
|
||||
@@ -197,6 +280,22 @@
|
||||
}]
|
||||
});
|
||||
|
||||
// Seq900 도넛 차트 업데이트
|
||||
var seq900Total = seq900Timeout + seq900SystemErr + seq900BizErr;
|
||||
seq900DonutChart.setOption({
|
||||
series: [{
|
||||
data: [
|
||||
{ value: seq900Timeout, name: 'Timeout' },
|
||||
{ value: seq900SystemErr, name: '시스템오류' },
|
||||
{ value: seq900BizErr, name: '업무오류' }
|
||||
]
|
||||
}],
|
||||
graphic: [{
|
||||
style: {
|
||||
text: seq900Total.toLocaleString()
|
||||
}
|
||||
}]
|
||||
});
|
||||
|
||||
// 호출량 차트 업데이트
|
||||
callChart.setOption({
|
||||
@@ -204,10 +303,20 @@
|
||||
series: [
|
||||
{ data: successData },
|
||||
{ data: timeoutData },
|
||||
{ data: systemErrData }
|
||||
{ data: systemErrData },
|
||||
{ data: bizErrData }
|
||||
]
|
||||
});
|
||||
|
||||
// 응답시간 차트 업데이트
|
||||
respChart.setOption({
|
||||
xAxis: { data: times },
|
||||
series: [
|
||||
{ data: p95RespData },
|
||||
{ data: p50RespData },
|
||||
{ data: avgRespData }
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
// 조회 기간 검증 및 조정 (최대 1시간)
|
||||
@@ -443,7 +552,8 @@
|
||||
postData: gridPostData,
|
||||
colNames: [
|
||||
'API명',
|
||||
'총건수', '성공', 'Timeout', '시스템오류',
|
||||
'총건수', '성공', 'Timeout', '시스템오류', '업무오류',
|
||||
'Seq900 Timeout', 'Seq900 시스템오류', 'Seq900 업무오류',
|
||||
'평균응답(ms)', '최소응답(ms)', '최대응답(ms)'
|
||||
],
|
||||
colModel: [
|
||||
@@ -452,6 +562,10 @@
|
||||
{ name: 'successCnt', align: 'right', width: '80', formatter: numberFormatter, sortable: false },
|
||||
{ name: 'timeoutCnt', align: 'right', width: '80', formatter: numberFormatter, sortable: false },
|
||||
{ name: 'systemErrCnt', align: 'right', width: '80', formatter: numberFormatter, sortable: false },
|
||||
{ name: 'bizErrCnt', align: 'right', width: '80', formatter: numberFormatter, sortable: false },
|
||||
{ name: 'seq900TimeoutCnt', align: 'right', width: '100', formatter: numberFormatter, sortable: false },
|
||||
{ name: 'seq900SystemErrCnt', align: 'right', width: '120', formatter: numberFormatter, sortable: false },
|
||||
{ name: 'seq900BizErrCnt', align: 'right', width: '100', formatter: numberFormatter, sortable: false },
|
||||
{ name: 'avgRespTime', align: 'right', width: '90', formatter: decimalFormatter, sortable: false },
|
||||
{ name: 'minRespTime', align: 'right', width: '90', formatter: decimalFormatter, sortable: false },
|
||||
{ name: 'maxRespTime', align: 'right', width: '90', formatter: decimalFormatter, sortable: false }
|
||||
@@ -483,8 +597,9 @@
|
||||
colNames: [
|
||||
'통계시간', 'API명', '인스턴스', '업무구분', '클라이언트ID',
|
||||
'Inbound Adapter', 'Outbound Adapter',
|
||||
'총건수', '성공', 'Timeout', '시스템오류',
|
||||
'평균응답(ms)', '최소응답(ms)', '최대응답(ms)'
|
||||
'총건수', '성공', 'Timeout', '시스템오류', '업무오류',
|
||||
'Seq900 Timeout', 'Seq900 시스템오류', 'Seq900 업무오류',
|
||||
'평균응답(ms)', '최소응답(ms)', '최대응답(ms)', 'P50(ms)', 'P95(ms)'
|
||||
],
|
||||
colModel: [
|
||||
{ name: 'statTime', align: 'center', width: '120', sortable: false },
|
||||
@@ -498,9 +613,15 @@
|
||||
{ name: 'successCnt', align: 'right', width: '70', formatter: numberFormatter, sortable: false },
|
||||
{ name: 'timeoutCnt', align: 'right', width: '70', formatter: numberFormatter, sortable: false },
|
||||
{ name: 'systemErrCnt', align: 'right', width: '70', formatter: numberFormatter, sortable: false },
|
||||
{ name: 'bizErrCnt', align: 'right', width: '70', formatter: numberFormatter, sortable: false },
|
||||
{ name: 'seq900TimeoutCnt', align: 'right', width: '70', formatter: numberFormatter, sortable: false },
|
||||
{ name: 'seq900SystemErrCnt', align: 'right', width: '70', formatter: numberFormatter, sortable: false },
|
||||
{ name: 'seq900BizErrCnt', align: 'right', width: '70', formatter: numberFormatter, sortable: false },
|
||||
{ name: 'avgRespTime', align: 'right', width: '80', formatter: decimalFormatter, sortable: false },
|
||||
{ name: 'minRespTime', align: 'right', width: '80', formatter: decimalFormatter, sortable: false },
|
||||
{ name: 'maxRespTime', align: 'right', width: '80', formatter: decimalFormatter, sortable: false }
|
||||
{ name: 'maxRespTime', align: 'right', width: '80', formatter: decimalFormatter, sortable: false },
|
||||
{ name: 'p50RespTime', align: 'right', width: '70', formatter: decimalFormatter, sortable: false },
|
||||
{ name: 'p95RespTime', align: 'right', width: '70', formatter: decimalFormatter, sortable: false }
|
||||
],
|
||||
jsonReader: { repeatitems: false },
|
||||
pager: $('#pager'),
|
||||
@@ -525,26 +646,35 @@
|
||||
$("input[name=searchStartDate], input[name=searchEndDate]").inputmask("9999-99-99", { 'autoUnmask': true });
|
||||
$("input[name=searchStartTime], input[name=searchEndTime]").inputmask("99:99", { 'autoUnmask': true });
|
||||
|
||||
// 기본값 설정 (현재 시간(분) 기준 1시간 전 ~ 현재 시간(분))
|
||||
// 기본값 설정 (현재 시간 기준 1시간 전 정각 ~ 현재 정각)
|
||||
var now = new Date();
|
||||
var oneHourAgo = new Date(now.getTime() - 60 * 60 * 1000);
|
||||
var endHour = now.getHours();
|
||||
var startHour = endHour - 1;
|
||||
var startDate, endDate;
|
||||
|
||||
function formatDate(d) {
|
||||
return d.getFullYear() + '-' +
|
||||
String(d.getMonth() + 1).padStart(2, '0') + '-' +
|
||||
String(d.getDate()).padStart(2, '0');
|
||||
}
|
||||
function formatTime(d) {
|
||||
return String(d.getHours()).padStart(2, '0') + ':' + String(d.getMinutes()).padStart(2, '0');
|
||||
if (startHour < 0) {
|
||||
startHour = 23;
|
||||
var yesterday = new Date(now);
|
||||
yesterday.setDate(yesterday.getDate() - 1);
|
||||
startDate = yesterday.getFullYear() + '-' +
|
||||
String(yesterday.getMonth() + 1).padStart(2, '0') + '-' +
|
||||
String(yesterday.getDate()).padStart(2, '0');
|
||||
endDate = now.getFullYear() + '-' +
|
||||
String(now.getMonth() + 1).padStart(2, '0') + '-' +
|
||||
String(now.getDate()).padStart(2, '0');
|
||||
} else {
|
||||
var today = getToday();
|
||||
startDate = today;
|
||||
endDate = today;
|
||||
}
|
||||
|
||||
if (!$("input[name=searchStartDate]").val()) {
|
||||
$("input[name=searchStartDate]").val(formatDate(oneHourAgo));
|
||||
$("input[name=searchStartTime]").val(formatTime(oneHourAgo));
|
||||
$("input[name=searchStartDate]").val(startDate);
|
||||
$("input[name=searchStartTime]").val(String(startHour).padStart(2, '0') + ':00');
|
||||
}
|
||||
if (!$("input[name=searchEndDate]").val()) {
|
||||
$("input[name=searchEndDate]").val(formatDate(now));
|
||||
$("input[name=searchEndTime]").val(formatTime(now));
|
||||
$("input[name=searchEndDate]").val(endDate);
|
||||
$("input[name=searchEndTime]").val(String(endHour).padStart(2, '0') + ':00');
|
||||
}
|
||||
|
||||
initCharts();
|
||||
@@ -581,7 +711,9 @@
|
||||
// 윈도우 리사이즈 시 차트 리사이즈
|
||||
$(window).resize(function() {
|
||||
if (totalDonutChart) totalDonutChart.resize();
|
||||
if (seq900DonutChart) seq900DonutChart.resize();
|
||||
if (callChart) callChart.resize();
|
||||
if (respChart) respChart.resize();
|
||||
});
|
||||
|
||||
buttonControl();
|
||||
@@ -601,7 +733,7 @@
|
||||
<i class="material-icons">search</i> <%= localeMessage.getString("button.search") %>
|
||||
</button>
|
||||
</div>
|
||||
<div class="title" id="title">대시보드</div>
|
||||
<div class="title" id="title">API 분단위 통계</div>
|
||||
<table class="search_condition" cellspacing="0">
|
||||
<tbody>
|
||||
<tr>
|
||||
@@ -615,28 +747,72 @@
|
||||
<span style="color:#888; font-size:12px; margin-left:10px;">(최대 1시간, 초과시 자동 조정)</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tr>
|
||||
<th style="width:100px;">API명</th>
|
||||
<td>
|
||||
<input type="text" name="searchApiName" value="${param.searchApiName}">
|
||||
</td>
|
||||
<th style="width:100px;">인스턴스</th>
|
||||
<td>
|
||||
<input type="text" name="searchGwInstanceId" value="${param.searchGwInstanceId}">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th style="width:100px;">업무구분</th>
|
||||
<td>
|
||||
<input type="text" name="searchBizDivCode" value="${param.searchBizDivCode}">
|
||||
</td>
|
||||
<th style="width:100px;">클라이언트ID</th>
|
||||
<td>
|
||||
<input type="text" name="searchClientId" value="${param.searchClientId}">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th style="width:100px;">Inbound Adapter</th>
|
||||
<td>
|
||||
<input type="text" name="searchInboundAdapter" value="${param.searchInboundAdapter}">
|
||||
</td>
|
||||
<th style="width:100px;">Outbound Adapter</th>
|
||||
<td>
|
||||
<input type="text" name="searchOutboundAdapter" value="${param.searchOutboundAdapter}">
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<!-- 도넛 차트 (상단 50%씩) -->
|
||||
<div class="chart-container">
|
||||
<div id="totalDonutChart" class="chart"></div>
|
||||
<div id="callChart" class="chart"></div>
|
||||
<div id="seq900DonutChart" class="chart"></div>
|
||||
</div>
|
||||
<!-- 호출량 및 응답시간 차트 (하단 50%씩) -->
|
||||
<div class="chart-container">
|
||||
<div id="callChart" class="chart"></div>
|
||||
<div id="respChart" class="chart"></div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- 요약 그리드 -->
|
||||
<div style="margin-top: 20px;">
|
||||
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px;">
|
||||
<div class="title" style="margin: 0;">API별 사용현황</div>
|
||||
<div class="title" style="margin: 0;">API명별 요약 통계</div>
|
||||
<button type="button" class="cssbtn" id="btn_excel_export_summary" level="R">
|
||||
<i class="material-icons">file_download</i> Excel 다운로드
|
||||
<i class="material-icons">file_download</i> Excel 다운로드 (요약)
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<table id="gridSummary"></table>
|
||||
<div id="pagerSummary"></div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- 상세 그리드 -->
|
||||
<div style="margin-top: 20px;">
|
||||
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px;">
|
||||
<div class="title" style="margin: 0;">상세 통계</div>
|
||||
<button type="button" class="cssbtn" id="btn_excel_export" level="R">
|
||||
<i class="material-icons">file_download</i> Excel 다운로드 (상세)
|
||||
</button>
|
||||
</div>
|
||||
<table id="grid"></table>
|
||||
<div id="pager"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
var url = '<c:url value="/onl/kjb/statistics/apiStatsMonthMan.json"/>';
|
||||
var url_view = '<c:url value="/onl/kjb/statistics/apiStatsMonthMan.view"/>';
|
||||
|
||||
var totalDonutChart, callChart;
|
||||
var totalDonutChart, seq900DonutChart, callChart, respChart;
|
||||
|
||||
function numberFormatter(cellvalue, options, rowObject) {
|
||||
if (cellvalue == null || cellvalue == '') return '0';
|
||||
@@ -54,7 +54,9 @@
|
||||
|
||||
function initCharts() {
|
||||
totalDonutChart = echarts.init(document.getElementById('totalDonutChart'));
|
||||
seq900DonutChart = echarts.init(document.getElementById('seq900DonutChart'));
|
||||
callChart = echarts.init(document.getElementById('callChart'));
|
||||
respChart = echarts.init(document.getElementById('respChart'));
|
||||
|
||||
// 총건수 도넛 차트
|
||||
var totalDonutOption = {
|
||||
@@ -71,7 +73,7 @@
|
||||
legend: {
|
||||
orient: 'horizontal',
|
||||
bottom: 10,
|
||||
data: ['성공', 'Timeout', '시스템오류']
|
||||
data: ['성공', 'Timeout', '시스템오류', '업무오류']
|
||||
},
|
||||
series: [{
|
||||
name: '총건수',
|
||||
@@ -96,7 +98,8 @@
|
||||
data: [
|
||||
{ value: 0, name: '성공', itemStyle: { color: '#91CC75' } },
|
||||
{ value: 0, name: 'Timeout', itemStyle: { color: '#FAC858' } },
|
||||
{ value: 0, name: '시스템오류', itemStyle: { color: '#EE6666' } }
|
||||
{ value: 0, name: '시스템오류', itemStyle: { color: '#EE6666' } },
|
||||
{ value: 0, name: '업무오류', itemStyle: { color: '#FC8452' } }
|
||||
]
|
||||
}],
|
||||
graphic: [{
|
||||
@@ -115,7 +118,64 @@
|
||||
|
||||
totalDonutChart.setOption(totalDonutOption);
|
||||
|
||||
|
||||
// Seq900 도넛 차트
|
||||
var seq900DonutOption = {
|
||||
title: {
|
||||
text: 'Seq900 에러 분포',
|
||||
left: 'center',
|
||||
top: 10,
|
||||
textStyle: { fontSize: 14 }
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
formatter: '{b}: {c} ({d}%)'
|
||||
},
|
||||
legend: {
|
||||
orient: 'horizontal',
|
||||
bottom: 10,
|
||||
data: ['Timeout', '시스템오류', '업무오류']
|
||||
},
|
||||
series: [{
|
||||
name: 'Seq900 에러',
|
||||
type: 'pie',
|
||||
radius: ['40%', '70%'],
|
||||
center: ['50%', '50%'],
|
||||
avoidLabelOverlap: true,
|
||||
label: {
|
||||
show: true,
|
||||
formatter: '{b}: {c}'
|
||||
},
|
||||
emphasis: {
|
||||
label: {
|
||||
show: true,
|
||||
fontSize: 16,
|
||||
fontWeight: 'bold'
|
||||
}
|
||||
},
|
||||
labelLine: {
|
||||
show: true
|
||||
},
|
||||
data: [
|
||||
{ value: 0, name: 'Timeout', itemStyle: { color: '#FAC858' } },
|
||||
{ value: 0, name: '시스템오류', itemStyle: { color: '#EE6666' } },
|
||||
{ value: 0, name: '업무오류', itemStyle: { color: '#FC8452' } }
|
||||
]
|
||||
}],
|
||||
graphic: [{
|
||||
type: 'text',
|
||||
left: 'center',
|
||||
top: 'center',
|
||||
style: {
|
||||
text: '0',
|
||||
textAlign: 'center',
|
||||
fill: '#333',
|
||||
fontSize: 24,
|
||||
fontWeight: 'bold'
|
||||
}
|
||||
}]
|
||||
};
|
||||
|
||||
seq900DonutChart.setOption(seq900DonutOption);
|
||||
|
||||
// 호출량 차트
|
||||
var callOption = {
|
||||
@@ -124,18 +184,39 @@
|
||||
trigger: 'axis',
|
||||
axisPointer: { type: 'cross', label: { backgroundColor: '#6a7985' } }
|
||||
},
|
||||
legend: { data: ['성공', 'Timeout', '시스템오류'], bottom: 0 },
|
||||
legend: { data: ['성공', 'Timeout', '시스템오류', '업무오류'], bottom: 0 },
|
||||
grid: { left: '3%', right: '4%', bottom: '15%', top: '15%', containLabel: true },
|
||||
xAxis: { type: 'category', boundaryGap: false, data: [] },
|
||||
yAxis: { type: 'value', minInterval: 1 },
|
||||
series: [
|
||||
{ name: '성공', type: 'line', stack: 'Total', smooth: true, areaStyle: { opacity: 0.5 }, itemStyle: { color: '#5470C6' }, data: [] },
|
||||
{ name: 'Timeout', type: 'line', stack: 'Total', smooth: true, areaStyle: { opacity: 0.5 }, itemStyle: { color: '#FAC858' }, data: [] },
|
||||
{ name: '시스템오류', type: 'line', stack: 'Total', smooth: true, areaStyle: { opacity: 0.5 }, itemStyle: { color: '#EE6666' }, data: [] }
|
||||
{ name: '시스템오류', type: 'line', stack: 'Total', smooth: true, areaStyle: { opacity: 0.5 }, itemStyle: { color: '#EE6666' }, data: [] },
|
||||
{ name: '업무오류', type: 'line', stack: 'Total', smooth: true, areaStyle: { opacity: 0.5 }, itemStyle: { color: '#FC8452' }, data: [] }
|
||||
]
|
||||
};
|
||||
|
||||
callChart.setOption(callOption);
|
||||
|
||||
// 응답시간 차트
|
||||
var respOption = {
|
||||
title: { text: '응답시간 추이 (ms)', left: 'center', textStyle: { fontSize: 14 } },
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: { type: 'cross', label: { backgroundColor: '#6a7985' } }
|
||||
},
|
||||
legend: { data: ['P95', 'P50', '평균'], bottom: 0 },
|
||||
grid: { left: '3%', right: '4%', bottom: '15%', top: '15%', containLabel: true },
|
||||
xAxis: { type: 'category', boundaryGap: false, data: [] },
|
||||
yAxis: { type: 'value' },
|
||||
series: [
|
||||
{ name: 'P95', type: 'line', smooth: true, itemStyle: { color: '#EE6666' }, lineStyle: { type: 'dashed' }, data: [] },
|
||||
{ name: 'P50', type: 'line', smooth: true, itemStyle: { color: '#5470C6' }, data: [] },
|
||||
{ name: '평균', type: 'line', smooth: true, itemStyle: { color: '#91CC75' }, data: [] }
|
||||
]
|
||||
};
|
||||
|
||||
respChart.setOption(respOption);
|
||||
}
|
||||
|
||||
function updateCharts(data) {
|
||||
@@ -187,7 +268,8 @@
|
||||
data: [
|
||||
{ value: totalSuccess, name: '성공' },
|
||||
{ value: totalTimeout, name: 'Timeout' },
|
||||
{ value: totalSystemErr, name: '시스템오류' }
|
||||
{ value: totalSystemErr, name: '시스템오류' },
|
||||
{ value: totalBizErr, name: '업무오류' }
|
||||
]
|
||||
}],
|
||||
graphic: [{
|
||||
@@ -197,7 +279,22 @@
|
||||
}]
|
||||
});
|
||||
|
||||
|
||||
// Seq900 도넛 차트 업데이트
|
||||
var seq900Total = seq900Timeout + seq900SystemErr + seq900BizErr;
|
||||
seq900DonutChart.setOption({
|
||||
series: [{
|
||||
data: [
|
||||
{ value: seq900Timeout, name: 'Timeout' },
|
||||
{ value: seq900SystemErr, name: '시스템오류' },
|
||||
{ value: seq900BizErr, name: '업무오류' }
|
||||
]
|
||||
}],
|
||||
graphic: [{
|
||||
style: {
|
||||
text: seq900Total.toLocaleString()
|
||||
}
|
||||
}]
|
||||
});
|
||||
|
||||
// 호출량 차트 업데이트
|
||||
callChart.setOption({
|
||||
@@ -210,10 +307,19 @@
|
||||
]
|
||||
});
|
||||
|
||||
|
||||
// 응답시간 차트 업데이트
|
||||
respChart.setOption({
|
||||
xAxis: { data: times.map(function(t) { return t.substring(4, 6) + '월'; }) },
|
||||
series: [
|
||||
{ data: p95RespData },
|
||||
{ data: p50RespData },
|
||||
{ data: avgRespData }
|
||||
]
|
||||
});
|
||||
|
||||
// 드릴다운을 위해 원본 데이터 저장
|
||||
callChart.rawData = data;
|
||||
respChart.rawData = data;
|
||||
}
|
||||
|
||||
function fetchChartData() {
|
||||
@@ -433,7 +539,8 @@
|
||||
postData: gridPostData,
|
||||
colNames: [
|
||||
'API명',
|
||||
'총건수', '성공', 'Timeout', '시스템오류',
|
||||
'총건수', '성공', 'Timeout', '시스템오류', '업무오류',
|
||||
'Seq900 Timeout', 'Seq900 시스템오류', 'Seq900 업무오류',
|
||||
'평균응답(ms)', '최소응답(ms)', '최대응답(ms)'
|
||||
],
|
||||
colModel: [
|
||||
@@ -442,6 +549,10 @@
|
||||
{ name: 'successCnt', align: 'right', width: '80', formatter: numberFormatter, sortable: false },
|
||||
{ name: 'timeoutCnt', align: 'right', width: '80', formatter: numberFormatter, sortable: false },
|
||||
{ name: 'systemErrCnt', align: 'right', width: '80', formatter: numberFormatter, sortable: false },
|
||||
{ name: 'bizErrCnt', align: 'right', width: '80', formatter: numberFormatter, sortable: false },
|
||||
{ name: 'seq900TimeoutCnt', align: 'right', width: '100', formatter: numberFormatter, sortable: false },
|
||||
{ name: 'seq900SystemErrCnt', align: 'right', width: '120', formatter: numberFormatter, sortable: false },
|
||||
{ name: 'seq900BizErrCnt', align: 'right', width: '100', formatter: numberFormatter, sortable: false },
|
||||
{ name: 'avgRespTime', align: 'right', width: '90', formatter: decimalFormatter, sortable: false },
|
||||
{ name: 'minRespTime', align: 'right', width: '90', formatter: decimalFormatter, sortable: false },
|
||||
{ name: 'maxRespTime', align: 'right', width: '90', formatter: decimalFormatter, sortable: false }
|
||||
@@ -473,8 +584,9 @@
|
||||
colNames: [
|
||||
'통계시간', 'API명', '인스턴스', '업무구분', '클라이언트ID',
|
||||
'Inbound Adapter', 'Outbound Adapter',
|
||||
'총건수', '성공', 'Timeout', '시스템오류',
|
||||
'평균응답(ms)', '최소응답(ms)', '최대응답(ms)'
|
||||
'총건수', '성공', 'Timeout', '시스템오류', '업무오류',
|
||||
'Seq900 Timeout', 'Seq900 시스템오류', 'Seq900 업무오류',
|
||||
'평균응답(ms)', '최소응답(ms)', '최대응답(ms)', 'P50(ms)', 'P95(ms)'
|
||||
],
|
||||
colModel: [
|
||||
{ name: 'statTime', align: 'center', width: '120', sortable: false },
|
||||
@@ -488,9 +600,15 @@
|
||||
{ name: 'successCnt', align: 'right', width: '70', formatter: numberFormatter, sortable: false },
|
||||
{ name: 'timeoutCnt', align: 'right', width: '70', formatter: numberFormatter, sortable: false },
|
||||
{ name: 'systemErrCnt', align: 'right', width: '70', formatter: numberFormatter, sortable: false },
|
||||
{ name: 'bizErrCnt', align: 'right', width: '70', formatter: numberFormatter, sortable: false },
|
||||
{ name: 'seq900TimeoutCnt', align: 'right', width: '70', formatter: numberFormatter, sortable: false },
|
||||
{ name: 'seq900SystemErrCnt', align: 'right', width: '70', formatter: numberFormatter, sortable: false },
|
||||
{ name: 'seq900BizErrCnt', align: 'right', width: '70', formatter: numberFormatter, sortable: false },
|
||||
{ name: 'avgRespTime', align: 'right', width: '80', formatter: decimalFormatter, sortable: false },
|
||||
{ name: 'minRespTime', align: 'right', width: '80', formatter: decimalFormatter, sortable: false },
|
||||
{ name: 'maxRespTime', align: 'right', width: '80', formatter: decimalFormatter, sortable: false }
|
||||
{ name: 'maxRespTime', align: 'right', width: '80', formatter: decimalFormatter, sortable: false },
|
||||
{ name: 'p50RespTime', align: 'right', width: '70', formatter: decimalFormatter, sortable: false },
|
||||
{ name: 'p95RespTime', align: 'right', width: '70', formatter: decimalFormatter, sortable: false }
|
||||
],
|
||||
jsonReader: { repeatitems: false },
|
||||
pager: $('#pager'),
|
||||
@@ -557,7 +675,9 @@
|
||||
// 윈도우 리사이즈 시 차트 리사이즈
|
||||
$(window).resize(function() {
|
||||
if (totalDonutChart) totalDonutChart.resize();
|
||||
if (seq900DonutChart) seq900DonutChart.resize();
|
||||
if (callChart) callChart.resize();
|
||||
if (respChart) respChart.resize();
|
||||
});
|
||||
|
||||
buttonControl();
|
||||
@@ -624,9 +744,13 @@
|
||||
<!-- 도넛 차트 (상단 50%씩) -->
|
||||
<div class="chart-container">
|
||||
<div id="totalDonutChart" class="chart"></div>
|
||||
<div id="callChart" class="chart"></div>
|
||||
<div id="seq900DonutChart" class="chart"></div>
|
||||
</div>
|
||||
<!-- 호출량 및 응답시간 차트 (하단 50%씩) -->
|
||||
<div class="chart-container">
|
||||
<div id="callChart" class="chart"></div>
|
||||
<div id="respChart" class="chart"></div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- 요약 그리드 -->
|
||||
<div style="margin-top: 20px;">
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
var url = '<c:url value="/onl/kjb/statistics/apiStatsYearMan.json"/>';
|
||||
var url_view = '<c:url value="/onl/kjb/statistics/apiStatsYearMan.view"/>';
|
||||
|
||||
var totalDonutChart, callChart;
|
||||
var totalDonutChart, seq900DonutChart, callChart, respChart;
|
||||
|
||||
function numberFormatter(cellvalue, options, rowObject) {
|
||||
if (cellvalue == null || cellvalue == '') return '0';
|
||||
@@ -54,7 +54,9 @@
|
||||
|
||||
function initCharts() {
|
||||
totalDonutChart = echarts.init(document.getElementById('totalDonutChart'));
|
||||
seq900DonutChart = echarts.init(document.getElementById('seq900DonutChart'));
|
||||
callChart = echarts.init(document.getElementById('callChart'));
|
||||
respChart = echarts.init(document.getElementById('respChart'));
|
||||
|
||||
// 총건수 도넛 차트
|
||||
var totalDonutOption = {
|
||||
@@ -71,7 +73,7 @@
|
||||
legend: {
|
||||
orient: 'horizontal',
|
||||
bottom: 10,
|
||||
data: ['성공', 'Timeout', '시스템오류']
|
||||
data: ['성공', 'Timeout', '시스템오류', '업무오류']
|
||||
},
|
||||
series: [{
|
||||
name: '총건수',
|
||||
@@ -96,7 +98,8 @@
|
||||
data: [
|
||||
{ value: 0, name: '성공', itemStyle: { color: '#91CC75' } },
|
||||
{ value: 0, name: 'Timeout', itemStyle: { color: '#FAC858' } },
|
||||
{ value: 0, name: '시스템오류', itemStyle: { color: '#EE6666' } }
|
||||
{ value: 0, name: '시스템오류', itemStyle: { color: '#EE6666' } },
|
||||
{ value: 0, name: '업무오류', itemStyle: { color: '#FC8452' } }
|
||||
]
|
||||
}],
|
||||
graphic: [{
|
||||
@@ -115,7 +118,64 @@
|
||||
|
||||
totalDonutChart.setOption(totalDonutOption);
|
||||
|
||||
|
||||
// Seq900 도넛 차트
|
||||
var seq900DonutOption = {
|
||||
title: {
|
||||
text: 'Seq900 에러 분포',
|
||||
left: 'center',
|
||||
top: 10,
|
||||
textStyle: { fontSize: 14 }
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
formatter: '{b}: {c} ({d}%)'
|
||||
},
|
||||
legend: {
|
||||
orient: 'horizontal',
|
||||
bottom: 10,
|
||||
data: ['Timeout', '시스템오류', '업무오류']
|
||||
},
|
||||
series: [{
|
||||
name: 'Seq900 에러',
|
||||
type: 'pie',
|
||||
radius: ['40%', '70%'],
|
||||
center: ['50%', '50%'],
|
||||
avoidLabelOverlap: true,
|
||||
label: {
|
||||
show: true,
|
||||
formatter: '{b}: {c}'
|
||||
},
|
||||
emphasis: {
|
||||
label: {
|
||||
show: true,
|
||||
fontSize: 16,
|
||||
fontWeight: 'bold'
|
||||
}
|
||||
},
|
||||
labelLine: {
|
||||
show: true
|
||||
},
|
||||
data: [
|
||||
{ value: 0, name: 'Timeout', itemStyle: { color: '#FAC858' } },
|
||||
{ value: 0, name: '시스템오류', itemStyle: { color: '#EE6666' } },
|
||||
{ value: 0, name: '업무오류', itemStyle: { color: '#FC8452' } }
|
||||
]
|
||||
}],
|
||||
graphic: [{
|
||||
type: 'text',
|
||||
left: 'center',
|
||||
top: 'center',
|
||||
style: {
|
||||
text: '0',
|
||||
textAlign: 'center',
|
||||
fill: '#333',
|
||||
fontSize: 24,
|
||||
fontWeight: 'bold'
|
||||
}
|
||||
}]
|
||||
};
|
||||
|
||||
seq900DonutChart.setOption(seq900DonutOption);
|
||||
|
||||
// 호출량 차트
|
||||
var callOption = {
|
||||
@@ -124,20 +184,39 @@
|
||||
trigger: 'axis',
|
||||
axisPointer: { type: 'cross', label: { backgroundColor: '#6a7985' } }
|
||||
},
|
||||
legend: { data: ['성공', 'Timeout', '시스템오류'], bottom: 0 },
|
||||
legend: { data: ['성공', 'Timeout', '시스템오류', '업무오류'], bottom: 0 },
|
||||
grid: { left: '3%', right: '4%', bottom: '15%', top: '15%', containLabel: true },
|
||||
xAxis: { type: 'category', boundaryGap: false, data: [] },
|
||||
yAxis: { type: 'value', minInterval: 1 },
|
||||
series: [
|
||||
{ name: '성공', type: 'line', stack: 'Total', smooth: true, areaStyle: { opacity: 0.5 }, itemStyle: { color: '#5470C6' }, data: [] },
|
||||
{ name: 'Timeout', type: 'line', stack: 'Total', smooth: true, areaStyle: { opacity: 0.5 }, itemStyle: { color: '#FAC858' }, data: [] },
|
||||
{ name: '시스템오류', type: 'line', stack: 'Total', smooth: true, areaStyle: { opacity: 0.5 }, itemStyle: { color: '#EE6666' }, data: [] }
|
||||
{ name: '시스템오류', type: 'line', stack: 'Total', smooth: true, areaStyle: { opacity: 0.5 }, itemStyle: { color: '#EE6666' }, data: [] },
|
||||
{ name: '업무오류', type: 'line', stack: 'Total', smooth: true, areaStyle: { opacity: 0.5 }, itemStyle: { color: '#FC8452' }, data: [] }
|
||||
]
|
||||
};
|
||||
|
||||
callChart.setOption(callOption);
|
||||
|
||||
|
||||
// 응답시간 차트
|
||||
var respOption = {
|
||||
title: { text: '응답시간 추이 (ms)', left: 'center', textStyle: { fontSize: 14 } },
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: { type: 'cross', label: { backgroundColor: '#6a7985' } }
|
||||
},
|
||||
legend: { data: ['P95', 'P50', '평균'], bottom: 0 },
|
||||
grid: { left: '3%', right: '4%', bottom: '15%', top: '15%', containLabel: true },
|
||||
xAxis: { type: 'category', boundaryGap: false, data: [] },
|
||||
yAxis: { type: 'value' },
|
||||
series: [
|
||||
{ name: 'P95', type: 'line', smooth: true, itemStyle: { color: '#EE6666' }, lineStyle: { type: 'dashed' }, data: [] },
|
||||
{ name: 'P50', type: 'line', smooth: true, itemStyle: { color: '#5470C6' }, data: [] },
|
||||
{ name: '평균', type: 'line', smooth: true, itemStyle: { color: '#91CC75' }, data: [] }
|
||||
]
|
||||
};
|
||||
|
||||
respChart.setOption(respOption);
|
||||
}
|
||||
|
||||
function updateCharts(data) {
|
||||
@@ -189,7 +268,8 @@
|
||||
data: [
|
||||
{ value: totalSuccess, name: '성공' },
|
||||
{ value: totalTimeout, name: 'Timeout' },
|
||||
{ value: totalSystemErr, name: '시스템오류' }
|
||||
{ value: totalSystemErr, name: '시스템오류' },
|
||||
{ value: totalBizErr, name: '업무오류' }
|
||||
]
|
||||
}],
|
||||
graphic: [{
|
||||
@@ -199,7 +279,22 @@
|
||||
}]
|
||||
});
|
||||
|
||||
|
||||
// Seq900 도넛 차트 업데이트
|
||||
var seq900Total = seq900Timeout + seq900SystemErr + seq900BizErr;
|
||||
seq900DonutChart.setOption({
|
||||
series: [{
|
||||
data: [
|
||||
{ value: seq900Timeout, name: 'Timeout' },
|
||||
{ value: seq900SystemErr, name: '시스템오류' },
|
||||
{ value: seq900BizErr, name: '업무오류' }
|
||||
]
|
||||
}],
|
||||
graphic: [{
|
||||
style: {
|
||||
text: seq900Total.toLocaleString()
|
||||
}
|
||||
}]
|
||||
});
|
||||
|
||||
// 호출량 차트 업데이트
|
||||
callChart.setOption({
|
||||
@@ -212,10 +307,19 @@
|
||||
]
|
||||
});
|
||||
|
||||
|
||||
// 응답시간 차트 업데이트
|
||||
respChart.setOption({
|
||||
xAxis: { data: times.map(function(t) { return t.substring(0, 4) + '년'; }) },
|
||||
series: [
|
||||
{ data: p95RespData },
|
||||
{ data: p50RespData },
|
||||
{ data: avgRespData }
|
||||
]
|
||||
});
|
||||
|
||||
// 드릴다운을 위해 원본 데이터 저장
|
||||
callChart.rawData = data;
|
||||
respChart.rawData = data;
|
||||
}
|
||||
|
||||
function fetchChartData() {
|
||||
@@ -409,7 +513,8 @@
|
||||
postData: gridPostData,
|
||||
colNames: [
|
||||
'API명',
|
||||
'총건수', '성공', 'Timeout', '시스템오류',
|
||||
'총건수', '성공', 'Timeout', '시스템오류', '업무오류',
|
||||
'Seq900 Timeout', 'Seq900 시스템오류', 'Seq900 업무오류',
|
||||
'평균응답(ms)', '최소응답(ms)', '최대응답(ms)'
|
||||
],
|
||||
colModel: [
|
||||
@@ -418,6 +523,10 @@
|
||||
{ name: 'successCnt', align: 'right', width: '80', formatter: numberFormatter, sortable: false },
|
||||
{ name: 'timeoutCnt', align: 'right', width: '80', formatter: numberFormatter, sortable: false },
|
||||
{ name: 'systemErrCnt', align: 'right', width: '80', formatter: numberFormatter, sortable: false },
|
||||
{ name: 'bizErrCnt', align: 'right', width: '80', formatter: numberFormatter, sortable: false },
|
||||
{ name: 'seq900TimeoutCnt', align: 'right', width: '100', formatter: numberFormatter, sortable: false },
|
||||
{ name: 'seq900SystemErrCnt', align: 'right', width: '120', formatter: numberFormatter, sortable: false },
|
||||
{ name: 'seq900BizErrCnt', align: 'right', width: '100', formatter: numberFormatter, sortable: false },
|
||||
{ name: 'avgRespTime', align: 'right', width: '90', formatter: decimalFormatter, sortable: false },
|
||||
{ name: 'minRespTime', align: 'right', width: '90', formatter: decimalFormatter, sortable: false },
|
||||
{ name: 'maxRespTime', align: 'right', width: '90', formatter: decimalFormatter, sortable: false }
|
||||
@@ -449,8 +558,9 @@
|
||||
colNames: [
|
||||
'통계시간', 'API명', '인스턴스', '업무구분', '클라이언트ID',
|
||||
'Inbound Adapter', 'Outbound Adapter',
|
||||
'총건수', '성공', 'Timeout', '시스템오류',
|
||||
'평균응답(ms)', '최소응답(ms)', '최대응답(ms)'
|
||||
'총건수', '성공', 'Timeout', '시스템오류', '업무오류',
|
||||
'Seq900 Timeout', 'Seq900 시스템오류', 'Seq900 업무오류',
|
||||
'평균응답(ms)', '최소응답(ms)', '최대응답(ms)', 'P50(ms)', 'P95(ms)'
|
||||
],
|
||||
colModel: [
|
||||
{ name: 'statTime', align: 'center', width: '120', sortable: false },
|
||||
@@ -464,9 +574,15 @@
|
||||
{ name: 'successCnt', align: 'right', width: '70', formatter: numberFormatter, sortable: false },
|
||||
{ name: 'timeoutCnt', align: 'right', width: '70', formatter: numberFormatter, sortable: false },
|
||||
{ name: 'systemErrCnt', align: 'right', width: '70', formatter: numberFormatter, sortable: false },
|
||||
{ name: 'bizErrCnt', align: 'right', width: '70', formatter: numberFormatter, sortable: false },
|
||||
{ name: 'seq900TimeoutCnt', align: 'right', width: '70', formatter: numberFormatter, sortable: false },
|
||||
{ name: 'seq900SystemErrCnt', align: 'right', width: '70', formatter: numberFormatter, sortable: false },
|
||||
{ name: 'seq900BizErrCnt', align: 'right', width: '70', formatter: numberFormatter, sortable: false },
|
||||
{ name: 'avgRespTime', align: 'right', width: '80', formatter: decimalFormatter, sortable: false },
|
||||
{ name: 'minRespTime', align: 'right', width: '80', formatter: decimalFormatter, sortable: false },
|
||||
{ name: 'maxRespTime', align: 'right', width: '80', formatter: decimalFormatter, sortable: false }
|
||||
{ name: 'maxRespTime', align: 'right', width: '80', formatter: decimalFormatter, sortable: false },
|
||||
{ name: 'p50RespTime', align: 'right', width: '70', formatter: decimalFormatter, sortable: false },
|
||||
{ name: 'p95RespTime', align: 'right', width: '70', formatter: decimalFormatter, sortable: false }
|
||||
],
|
||||
jsonReader: { repeatitems: false },
|
||||
pager: $('#pager'),
|
||||
@@ -528,7 +644,9 @@
|
||||
// 윈도우 리사이즈 시 차트 리사이즈
|
||||
$(window).resize(function() {
|
||||
if (totalDonutChart) totalDonutChart.resize();
|
||||
if (seq900DonutChart) seq900DonutChart.resize();
|
||||
if (callChart) callChart.resize();
|
||||
if (respChart) respChart.resize();
|
||||
});
|
||||
|
||||
buttonControl();
|
||||
@@ -594,7 +712,12 @@
|
||||
<!-- 도넛 차트 (상단 50%씩) -->
|
||||
<div class="chart-container">
|
||||
<div id="totalDonutChart" class="chart"></div>
|
||||
<div id="seq900DonutChart" class="chart"></div>
|
||||
</div>
|
||||
<!-- 호출량 및 응답시간 차트 (하단 50%씩) -->
|
||||
<div class="chart-container">
|
||||
<div id="callChart" class="chart"></div>
|
||||
<div id="respChart" class="chart"></div>
|
||||
</div>
|
||||
|
||||
<!-- 요약 그리드 -->
|
||||
|
||||
@@ -1,314 +0,0 @@
|
||||
<%@ page language="java" contentType="text/html; charset=utf-8"%>
|
||||
<%@ page import="java.io.*"%>
|
||||
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
|
||||
<%@ include file="/jsp/common/include/localemessage.jsp" %>
|
||||
<%
|
||||
response.setHeader("Pragma", "No-cache");
|
||||
response.setHeader("Cache-Control", "no-cache");
|
||||
response.setHeader("Expires", "0");
|
||||
request.setCharacterEncoding("UTF-8");
|
||||
%>
|
||||
<html>
|
||||
<head>
|
||||
<title>대시보드</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<jsp:include page="/jsp/common/include/css.jsp"/>
|
||||
<jsp:include page="/jsp/common/include/script.jsp"/>
|
||||
<script src="<c:url value="/addon/echarts/echarts.min.js"/>"></script>
|
||||
<script language="javascript">
|
||||
var url = '<c:url value="/onl/kjb/statistics/apiUseStatsMan.json"/>';
|
||||
var url_view = '<c:url value="/onl/kjb/statistics/apiUseStatsMan.view"/>';
|
||||
|
||||
|
||||
function numberFormatter(cellvalue, options, rowObject) {
|
||||
if (cellvalue == null || cellvalue == '') return '0';
|
||||
return Number(cellvalue).toLocaleString();
|
||||
}
|
||||
|
||||
function decimalFormatter(cellvalue, options, rowObject) {
|
||||
if (cellvalue == null || cellvalue == '') return '0';
|
||||
return (cellvalue).toFixed(2);
|
||||
}
|
||||
|
||||
function getPostData() {
|
||||
var postData = {}
|
||||
if (arguments.length == 2) {
|
||||
postData[arguments[0]] = arguments[1];
|
||||
}
|
||||
|
||||
var searchStartDate = $("input[name=searchStartDate]").val().replace(/-/g, "");
|
||||
var searchEndDate = $("input[name=searchEndDate]").val().replace(/-/g, "");
|
||||
|
||||
if (searchStartDate && searchEndDate) {
|
||||
var start = new Date(searchStartDate.substring(0, 4), parseInt(searchStartDate.substring(4, 6)) - 1, searchStartDate.substring(6, 8));
|
||||
var end = new Date(searchEndDate.substring(0, 4), parseInt(searchEndDate.substring(4, 6)) - 1, searchEndDate.substring(6, 8));
|
||||
var diffDays = Math.ceil((end - start) / (1000 * 60 * 60 * 24));
|
||||
|
||||
if (diffDays > 31) {
|
||||
alert('조회 기간은 최대 31일까지 가능합니다.');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
postData.searchType = $('input[name="searchType"]:checked').val();
|
||||
postData.searchOrgName = $('#searchOrgName').val();
|
||||
postData.searchApiName = $('#searchApiName').val();
|
||||
postData.searchStartDateTime = searchStartDate;
|
||||
postData.searchEndDateTime = searchEndDate;
|
||||
|
||||
return postData;
|
||||
}
|
||||
|
||||
|
||||
function search() {
|
||||
var postData = getPostData("cmd", "LIST");
|
||||
if (postData) {
|
||||
$("#grid").setGridParam({ url: url, postData: postData, page: 1 }).trigger("reloadGrid");
|
||||
}
|
||||
}
|
||||
|
||||
function exportToExcel() {
|
||||
|
||||
var postData = getPostData("cmd", "EXCEL_EXPORT");
|
||||
if (!postData) {
|
||||
return;
|
||||
}
|
||||
postData.serviceType = '${param.serviceType}';
|
||||
|
||||
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open('POST', url, true);
|
||||
xhr.responseType = 'blob';
|
||||
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
|
||||
|
||||
xhr.onload = function() {
|
||||
console.log('[Excel Export] Response received - Status:', xhr.status);
|
||||
|
||||
if (xhr.status === 200) {
|
||||
var blob = xhr.response;
|
||||
var contentType = xhr.getResponseHeader('Content-Type');
|
||||
console.log('[Excel Export] Blob size:', blob.size, 'Content-Type:', contentType);
|
||||
|
||||
// JSON 에러 응답인지 확인
|
||||
if (contentType && contentType.indexOf('application/json') !== -1) {
|
||||
var reader = new FileReader();
|
||||
reader.onload = function() {
|
||||
try {
|
||||
var errorObj = JSON.parse(reader.result);
|
||||
alert(errorObj.message || 'Excel 파일 생성에 실패했습니다.');
|
||||
} catch (e) {
|
||||
alert('Excel 파일 생성에 실패했습니다.');
|
||||
}
|
||||
};
|
||||
reader.readAsText(blob);
|
||||
return;
|
||||
}
|
||||
|
||||
// Blob 크기가 0이면 에러
|
||||
if (blob.size === 0) {
|
||||
alert('Excel 파일 생성에 실패했습니다. (빈 응답)');
|
||||
return;
|
||||
}
|
||||
|
||||
// 파일명 추출
|
||||
var filename = 'api_stats.xlsx';
|
||||
var disposition = xhr.getResponseHeader('Content-Disposition');
|
||||
console.log('[Excel Export] Content-Disposition:', disposition);
|
||||
|
||||
if (disposition && disposition.indexOf('filename=') !== -1) {
|
||||
var filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/;
|
||||
var matches = filenameRegex.exec(disposition);
|
||||
if (matches != null && matches[1]) {
|
||||
filename = matches[1].replace(/['"]/g, '');
|
||||
filename = decodeURIComponent(filename);
|
||||
}
|
||||
}
|
||||
|
||||
console.log('[Excel Export] Downloading as:', filename);
|
||||
|
||||
// 파일 다운로드
|
||||
var link = document.createElement('a');
|
||||
link.href = window.URL.createObjectURL(blob);
|
||||
link.download = filename;
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
window.URL.revokeObjectURL(link.href);
|
||||
|
||||
console.log('[Excel Export] Download triggered');
|
||||
|
||||
} else if (xhr.status === 204) {
|
||||
alert('조회된 데이터가 없습니다.');
|
||||
} else {
|
||||
// 에러 응답 처리
|
||||
var reader = new FileReader();
|
||||
reader.onload = function() {
|
||||
var message = 'Excel 다운로드 중 오류가 발생했습니다.';
|
||||
console.log('reader.result', reader.result)
|
||||
try {
|
||||
var errorObj = JSON.parse(reader.result);
|
||||
if (errorObj.message) message = errorObj.message;
|
||||
} catch (e) {
|
||||
console.error('[Excel Export] Error parsing error response:', e);
|
||||
}
|
||||
alert(message);
|
||||
};
|
||||
reader.readAsText(xhr.response);
|
||||
}
|
||||
};
|
||||
|
||||
xhr.onerror = function() {
|
||||
console.error('[Excel Export] Network error');
|
||||
alert('네트워크 오류가 발생했습니다.');
|
||||
};
|
||||
|
||||
// Form data 생성
|
||||
var formData = [];
|
||||
for (var key in postData) {
|
||||
if (postData.hasOwnProperty(key) && postData[key] != null && postData[key] !== '') {
|
||||
formData.push(encodeURIComponent(key) + '=' + encodeURIComponent(postData[key]));
|
||||
}
|
||||
}
|
||||
|
||||
xhr.send(formData.join('&'));
|
||||
}
|
||||
|
||||
function list() {
|
||||
var gridPostData = getPostData("cmd", "LIST");
|
||||
|
||||
$('#grid').jqGrid({
|
||||
datatype: "json",
|
||||
mtype: 'POST',
|
||||
postData: gridPostData,
|
||||
colNames: [
|
||||
'구분',
|
||||
'총건수', '성공', '성공율(%)', '실패율(%)', 'Timeout', '시스템오류',
|
||||
'평균응답(ms)', '최소응답(ms)', '최대응답(ms)'
|
||||
],
|
||||
colModel: [
|
||||
{ name: 'orgName', align: 'left', width: '250', sortable: false },
|
||||
{ name: 'totalCnt', align: 'right', width: '80', formatter: numberFormatter, sortable: false },
|
||||
{ name: 'successCnt', align: 'right', width: '80', formatter: numberFormatter, sortable: false },
|
||||
{ name: 'successRate', align: 'right', width: '80', formatter: decimalFormatter, sortable: false },
|
||||
{ name: 'failRate', align: 'right', width: '80', formatter: decimalFormatter, sortable: false },
|
||||
{ name: 'timeoutCnt', align: 'right', width: '80', formatter: numberFormatter, sortable: false },
|
||||
{ name: 'systemErrCnt', align: 'right', width: '80', formatter: numberFormatter, sortable: false },
|
||||
{ name: 'avgRespTime', align: 'right', width: '90', formatter: numberFormatter, sortable: false },
|
||||
{ name: 'minRespTime', align: 'right', width: '90', formatter: numberFormatter, sortable: false },
|
||||
{ name: 'maxRespTime', align: 'right', width: '90', formatter: numberFormatter, sortable: false }
|
||||
],
|
||||
jsonReader: { repeatitems: false },
|
||||
pager: $('#pager'),
|
||||
page: '${param.page}',
|
||||
rowNum: '${rmsDefaultRowNum}',
|
||||
autoheight: true,
|
||||
height: 'auto',
|
||||
autowidth: true,
|
||||
viewrecords: true,
|
||||
rowList: eval('[${rmsDefaultRowList}]'),
|
||||
loadComplete: function(d) {
|
||||
var colModel = $(this).getGridParam("colModel");
|
||||
for (var i = 0; i < colModel.length; i++) {
|
||||
$(this).setColProp(colModel[i].name, { sortable: false });
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
$(document).ready(function() {
|
||||
// 날짜/시간 입력 마스크
|
||||
$("input[name=searchStartDate], input[name=searchEndDate]").inputmask("9999-99-99", { 'autoUnmask': true });
|
||||
$("input[name=searchStartDate], input[name=searchEndDate]").datepicker();
|
||||
|
||||
var today = getToday();
|
||||
var startDate = today.substring(0,6)+"01";
|
||||
var endDate = today;
|
||||
|
||||
|
||||
if (!$("input[name=searchStartDate]").val()) {
|
||||
$("input[name=searchStartDate]").val(startDate);
|
||||
}
|
||||
if (!$("input[name=searchEndDate]").val()) {
|
||||
$("input[name=searchEndDate]").val(endDate);
|
||||
}
|
||||
|
||||
list();
|
||||
search();
|
||||
|
||||
resizeJqGridWidth('grid', 'content_middle', '1200');
|
||||
|
||||
$("#btn_search").click(function() {
|
||||
search();
|
||||
});
|
||||
|
||||
$("#btn_excel").click(function() {
|
||||
exportToExcel();
|
||||
});
|
||||
|
||||
$("input[name^=search]").keydown(function(key) {
|
||||
if (key.keyCode == 13) {
|
||||
$("#btn_search").click();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
buttonControl();
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="right_box">
|
||||
<div class="content_top">
|
||||
<ul class="path">
|
||||
<li><a href="#">${rmsMenuPath}</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="content_middle" id="content_middle">
|
||||
<div class="search_wrap">
|
||||
<button type="button" class="cssbtn" id="btn_excel" level="R" style="display: inline-block;"><i class="material-icons">table_view</i> 엑셀</button>
|
||||
<button type="button" class="cssbtn" id="btn_search" level="R" style="display: inline-block;">
|
||||
<i class="material-icons">search</i> <%= localeMessage.getString("button.search") %>
|
||||
</button>
|
||||
</div>
|
||||
<div class="title" id="title">API 사용현황</div>
|
||||
<table class="search_condition" cellspacing="0">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th style="width:120px;">조회기간</th>
|
||||
<td colspan="5">
|
||||
<input type="text" name="searchStartDate" id="searchStartDate" value="${param.searchStartDate}" style="width:100px;">
|
||||
~
|
||||
<input type="text" name="searchEndDate" id="searchEndDate" value="${param.searchEndDate}" style="width:100px;">
|
||||
<span style="color:#888; font-size:12px; margin-left:10px;">(최대 31일)</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th style="width:120px;">조회구분</th>
|
||||
<td>
|
||||
<input type="radio" name="searchType" id="searchTypeORG" value="ORG" checked="checked"><label for="searchTypeORG">제휴사</label></input>
|
||||
<input type="radio" name="searchType" id="searchTypeAPI" value="API"><label for="searchTypeAPI">API</label></input>
|
||||
<input type="radio" name="searchType" id="searchTypeDATE" value="DATE"><label for="searchTypeDATE">사용일</label></input>
|
||||
</td>
|
||||
<th style="width:120px;">제휴사명</th>
|
||||
<td>
|
||||
<input type="text" name="searchOrgName" id="searchOrgName" value="${param.searchOrgName}">
|
||||
</td>
|
||||
<th style="width:120px;">API명</th>
|
||||
<td>
|
||||
<input type="text" name="searchApiName" id="searchApiName" value="${param.searchApiName}">
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div>
|
||||
<table id="grid"></table>
|
||||
<div id="pager"></div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -77,17 +77,13 @@
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
/* iframe 모달(showModal) 콘텐츠 패딩 제거 — iframe 이 다이얼로그 폭을 최대한 사용해 내부 가로스크롤 방지 */
|
||||
.ui-dialog .ui-dialog-content { padding: 0 !important; }
|
||||
|
||||
|
||||
</style>
|
||||
<script language="javascript" >
|
||||
var isDetail = false;
|
||||
var url ='<c:url value="/onl/transaction/apim/apiInterfaceMan.json" />';
|
||||
const url_excel ='<c:url value="/onl/transaction/apim/apiInterfaceMan.excel" />';
|
||||
var url_spec = '<c:url value="/onl/transaction/apim/apiSpecMan.view"/>';
|
||||
var url_openapi_spec = '<c:url value="/onl/transaction/apim/djbApiSpecMan.view"/>';
|
||||
var returnUrl ;
|
||||
let headerValues = [];
|
||||
let headerNames = [];
|
||||
@@ -302,7 +298,7 @@
|
||||
if(!isStandardMessage){
|
||||
$(adapterSettings.adapterUrlId).text(adapterInfo.urlPath);
|
||||
} else {
|
||||
//$(adapterSettings.methodSelectId).val('');
|
||||
$(adapterSettings.methodSelectId).val('');
|
||||
$(adapterSettings.restPathInputId).val('');
|
||||
$(adapterSettings.adapterUrlId).text('');
|
||||
}
|
||||
@@ -415,7 +411,7 @@
|
||||
function detail(url,key){
|
||||
jsonUrl = url;
|
||||
if (!isDetail)return;
|
||||
// $("input[name='btnRadioSyncAsync']").attr('disabled', true);
|
||||
$("input[name='btnRadioSyncAsync']").attr('disabled', true);
|
||||
$.ajax({
|
||||
type : "POST",
|
||||
url:url,
|
||||
@@ -490,7 +486,6 @@
|
||||
}
|
||||
|
||||
if(data.errResponseTransform != null && data.errResponseTransform != '' && data.errTransformYn == 'Y') {
|
||||
$('#toggleTransformYn').bootstrapToggle('on');
|
||||
$('#toggleErrTransformYn').bootstrapToggle('on');
|
||||
$('#rowErrResponseTransform').show();
|
||||
} else {
|
||||
@@ -876,10 +871,6 @@
|
||||
});
|
||||
|
||||
var postData = $('#ajaxForm').serializeArray();
|
||||
|
||||
console.log('modify', postData);
|
||||
console.log('method', $('#inboundHttpMethod').val());
|
||||
|
||||
if (isDetail){
|
||||
postData.push({ name: "cmd" , value:"UPDATE"});
|
||||
}else{
|
||||
@@ -971,13 +962,11 @@
|
||||
headerNames = messageKeyList;
|
||||
isHeaderRouting = true;
|
||||
initHeaders(headerNames);
|
||||
//$('#slideInboundRoutingRule').carousel(0);
|
||||
$('#collapseInboundRoutingInfo').collapse('show')
|
||||
$('#slideInboundRoutingRule').carousel(0);
|
||||
setHeaderRoutingLabel();
|
||||
}else{
|
||||
isHeaderRouting = false;
|
||||
$('#collapseInboundRoutingInfo').collapse('hide')
|
||||
//$('#slideInboundRoutingRule').carousel(1);
|
||||
$('#slideInboundRoutingRule').carousel(1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -997,33 +986,6 @@
|
||||
showModal(popupUrl, args, 1200, 800);
|
||||
}
|
||||
|
||||
// v2: OpenAPI Spec — 기본은 모달(iframe), Shift+클릭은 새 창.
|
||||
// 컨텍스트는 전부 query string 으로 전달(dialogArguments 미사용).
|
||||
function showOpenApiSpecPopup(e) {
|
||||
var eaiSvcName = getFullSvcName();
|
||||
var eaiSvcDesc = $('#eaiSvcDesc').val();
|
||||
var apipath = $('#apiFullPath').val(); // METHOD|URL ('|' 포함 → 인코딩 필수)
|
||||
var contentType = $('#contentType').val();
|
||||
var baseUrl = url_openapi_spec
|
||||
+ '?cmd=DETAIL'
|
||||
+ '&eaiSvcName=' + encodeURIComponent(eaiSvcName)
|
||||
+ '&apipath=' + encodeURIComponent(apipath)
|
||||
+ '&eaiSvcDesc=' + encodeURIComponent(eaiSvcDesc)
|
||||
+ '&contentType=' + encodeURIComponent(contentType);
|
||||
if (e && e.shiftKey) {
|
||||
// Shift+클릭: 새 창. window.open 은 serviceType/menuId 자동 부착 안 되므로 직접 붙임.
|
||||
window.open(urlAddServiceType(baseUrl + '&menuId=' + encodeURIComponent(getMenuId())), '_blank');
|
||||
} else {
|
||||
// 기본: 모달 + iframe. showModal 이 serviceType/menuId/pop 을 자동 부착.
|
||||
// 콘텐츠 고정폭 1600 + 다이얼로그 chrome(패딩/보더 ~40px) 때문에 iframe 내부폭이 1600 미만이면 가로스크롤 발생.
|
||||
// 뷰포트 내에서 최대한 크게 열어(캡 1720) iframe 내부폭이 1600 이상 확보되게 한다.
|
||||
var vw = $(window).width(), vh = $(window).height();
|
||||
var modalW = Math.min(vw - 12, 1720);
|
||||
var modalH = Math.min(vh - 20, 900);
|
||||
showModal(baseUrl, { title: 'OpenAPI Spec' }, modalW, modalH);
|
||||
}
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
makeValidate();
|
||||
// var bootstrapButton = $.fn.button.noConflict()
|
||||
@@ -1038,14 +1000,9 @@
|
||||
}
|
||||
init(url,key,detail);
|
||||
|
||||
// v1: 기존 API 스펙 (모달) — 기능 비교용 복원
|
||||
$("#btn_api_spec").click(function() {
|
||||
showApiSpecPopup();
|
||||
});
|
||||
// v2: 신규 OpenAPI Spec (새 탭)
|
||||
$("#btn_openapi_spec").click(function(e) {
|
||||
showOpenApiSpecPopup(e);
|
||||
});
|
||||
|
||||
$("#btn_modify").click(modifyApi);
|
||||
$("#btn_delete").click(function(){
|
||||
@@ -1544,10 +1501,7 @@
|
||||
<button type="button" class="cssbtn" id="btn_json_export" level="R" status="DETAIL,NEW"><i class="material-icons">download</i> <%=localeMessage.getString("button.exportJson")%></button></button>
|
||||
<button type="button" class="cssbtn" id="btn_delete" level="W" status="DETAIL"><i class="material-icons">delete</i> <%= localeMessage.getString("button.delete") %></button>
|
||||
<button type="button" class="cssbtn" id="btn_modify" level="R" status="DETAIL,NEW"><i class="material-icons">save</i> <%= localeMessage.getString("button.modify") %></button>
|
||||
<!-- v1: 기존 API 스펙 (모달) — 기능 비교용 복원 -->
|
||||
<button type="button" class="cssbtn" id="btn_api_spec" level="R" status="DETAIL,NEW"><i class="material-icons">description</i> API 스펙(삭제예정)</button>
|
||||
<!-- v2: 신규 OpenAPI Spec (새 탭) -->
|
||||
<button type="button" class="cssbtn" id="btn_openapi_spec" level="R" status="DETAIL,NEW"><i class="material-icons">api</i> OpenAPI Spec</button>
|
||||
<button type="button" class="cssbtn" id="btn_api_spec" level="R" status="DETAIL,NEW"><i class="material-icons">description</i> API 스펙</button>
|
||||
<button type="button" class="cssbtn" id="btn_previous" level="R" status="DETAIL,NEW"><i class="material-icons">arrow_back</i> <%= localeMessage.getString("button.previous") %></button>
|
||||
</div>
|
||||
<div class="title" id="title" style="font-size:1.4em">${rmsMenuName}</div>
|
||||
@@ -1618,7 +1572,7 @@
|
||||
<fieldset class="groupbox-border">
|
||||
<legend class="groupbox-border">INTERFACE TYPE</legend>
|
||||
<div class="row">
|
||||
<!-- <div class="form-group col-md-3">
|
||||
<div class="form-group col-md-3">
|
||||
<label for="eaiBzwkDstcd"><span class="material-icons-outlined">sync</span> Sync/Async 타입</label><br>
|
||||
<div class="btn-group" id="btnGroupSyncAsync" role="group">
|
||||
<input type="radio"
|
||||
@@ -1661,19 +1615,19 @@
|
||||
<i class="bi bi-share-fill"></i> S > A
|
||||
</label>
|
||||
</div>
|
||||
</div> -->
|
||||
</div>
|
||||
<div class="form-group col-md-3">
|
||||
<label for="eaiBzwkDstcd">요청/응답 구분</label><br>
|
||||
<div class="btn-group" role="group" aria-label="Basic radio toggle button group"
|
||||
x-effect="if (apiInterface.syncAsyncType !== 'async') apiInterface.requestType = 'S'">
|
||||
<input type="radio" class="btn-check" name="btnRadioReqRes" id="btnTypeRequest"
|
||||
x-model="apiInterface.requestType"
|
||||
value="S" :disabled="$store.formState.isReqResDisabled">
|
||||
value="S" :disabled="apiInterface.syncAsyncType !== 'async' || $store.formState.isReqResDisabled">
|
||||
<label class="btn btn-outline-primary" for="btnTypeRequest"><i class="bi bi-arrow-bar-right"></i> 요청</label>
|
||||
|
||||
<input type="radio" class="btn-check" name="btnRadioReqRes" id="btnTypeResponse"
|
||||
x-model="apiInterface.requestType"
|
||||
value="R" :disabled="$store.formState.isReqResDisabled">
|
||||
value="R" :disabled="apiInterface.syncAsyncType !== 'async' || $store.formState.isReqResDisabled">
|
||||
<label class="btn btn-outline-primary" for="btnTypeResponse"><i class="bi bi-arrow-bar-left"></i> 응답</label>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1730,7 +1684,7 @@
|
||||
<option value="none" selected>NONE</option>
|
||||
<option value="oauth">OAUTH</option>
|
||||
<option value="api_key">API_KEY</option>
|
||||
<!-- <option value="ca">CA</option> -->
|
||||
<option value="ca">CA</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1817,34 +1771,36 @@
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group col-md-8">
|
||||
<div id="slideInboundRoutingRule">
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<label for="inboundRestPath">
|
||||
<span class="material-icons">link</span>
|
||||
수신 REST PATH(URL)
|
||||
<div id="slideInboundRoutingRule" class="carousel slide" data-ride="carousel" data-interval="false">
|
||||
<div class="carousel-inner" style="margin-bottom:-20px">
|
||||
<div class="carousel-item">
|
||||
<label>
|
||||
<span class="material-icons justify-content-md-start">http</span>
|
||||
수신 라우팅 정보
|
||||
</label>
|
||||
<input type="text" class="form-control" id="inboundRestPath"
|
||||
name="inboundRestPath" disabled>
|
||||
<div class="d-grid gap-2">
|
||||
<button type="button" id="headerRoutingButton" class="btn btn-primary"
|
||||
data-bs-toggle="modal" data-bs-target="#routingModal">
|
||||
수정
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="carousel-item active">
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<label for="inboundRestPath">
|
||||
<span class="material-icons">link</span>
|
||||
수신 REST PATH(URL)
|
||||
</label>
|
||||
<input type="text" class="form-control" id="inboundRestPath"
|
||||
name="inboundRestPath" disabled>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="collapse mt-2" id="collapseInboundRoutingInfo">
|
||||
<div class="form-group col-md-12">
|
||||
<label>
|
||||
<span class="material-icons justify-content-md-start">http</span>
|
||||
수신 라우팅 정보
|
||||
</label>
|
||||
<div class="d-grid gap-2">
|
||||
<button type="button" id="headerRoutingButton" class="btn btn-primary"
|
||||
data-bs-toggle="modal" data-bs-target="#routingModal">
|
||||
수정
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="inboundResponseMethodPathRow"
|
||||
x-show="showInboundResponse">
|
||||
<div id="inboundResponseRestPart" class="row">
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
</style>
|
||||
<script language="javascript">
|
||||
var url = '<c:url value="/onl/transaction/apim/apiSpecMan.json" />';
|
||||
var url_org ='<c:url value="/onl/transaction/apim/apiSpecMan.view" />';
|
||||
var url_org ='<c:url value="/onl/transaction/apim/apiSpecManPopup.view" />';
|
||||
var isDetail = false;
|
||||
var sampleRequestEditor, sampleResponseEditor, testbedSpecEditor;
|
||||
|
||||
|
||||
@@ -1,132 +0,0 @@
|
||||
<%@ page language="java" contentType="text/html; charset=utf-8"%>
|
||||
<%@ page import="java.io.*"%>
|
||||
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
|
||||
<%@ include file="/jsp/common/include/localemessage.jsp" %>
|
||||
<%
|
||||
response.setHeader("Pragma", "No-cache");
|
||||
response.setHeader("Cache-Control", "no-cache");
|
||||
response.setHeader("Expires", "0");
|
||||
%>
|
||||
<html>
|
||||
<head>
|
||||
<title>API 그룹 선택</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<jsp:include page="/jsp/common/include/css_custom.jsp"/>
|
||||
<jsp:include page="/jsp/common/include/css.jsp"/>
|
||||
<jsp:include page="/jsp/common/include/script.jsp"/>
|
||||
<script language="javascript">
|
||||
var url = '<c:url value="/onl/apim/apigroup/apiGroupMan.json" />';
|
||||
|
||||
function search() {
|
||||
var postData = getSearchForJqgrid("cmd", "LIST");
|
||||
$("#grid").setGridParam({url: url, postData: postData, page: 1}).trigger("reloadGrid");
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
var urlParams = new URLSearchParams(window.location.search);
|
||||
var selectedGroupIds = urlParams.get("selectedGroupIds");
|
||||
|
||||
if (selectedGroupIds) {
|
||||
selectedGroupIds = selectedGroupIds.split(','); // 콤마로 구분된 ID들을 배열로 변환
|
||||
} else {
|
||||
selectedGroupIds = [];
|
||||
}
|
||||
|
||||
$('#grid').jqGrid({
|
||||
datatype: "json",
|
||||
mtype: 'POST',
|
||||
url: url,
|
||||
postData: getSearchForJqgrid("cmd", "LIST"),
|
||||
colNames: ['API 그룹 ID', 'API 그룹 명', '설명'],
|
||||
colModel: [
|
||||
{name: 'id', hidden: true},
|
||||
{name: 'groupName', align: 'center', width: "180"},
|
||||
{name: 'groupDesc', align: 'left', width: "200"}
|
||||
],
|
||||
jsonReader: {
|
||||
repeatitems: false
|
||||
},
|
||||
pager: $('#pager'),
|
||||
page: '${param.page}',
|
||||
rowNum: '${rmsDefaultRowNum}',
|
||||
autoheight: true,
|
||||
height: 'auto',
|
||||
width: 410,
|
||||
autowidth: false,
|
||||
viewrecords: true,
|
||||
shrinkToFit: false,
|
||||
multiselect: true,
|
||||
multiboxonly: false,
|
||||
loadComplete: function(data) {
|
||||
var $grid = $(this);
|
||||
$.each($grid.getDataIDs(), function(_, id) {
|
||||
var rowData = $grid.getRowData(id);
|
||||
if ($.inArray(rowData.id, selectedGroupIds) !== -1) {
|
||||
$grid.jqGrid('setSelection', id, true);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
$("#btn_search").click(search);
|
||||
|
||||
$("#btn_save").click(function() {
|
||||
var selectedRows = $('#grid').getGridParam('selarrrow');
|
||||
|
||||
var selectedGroups = [];
|
||||
if (selectedRows.length > 0) {
|
||||
selectedGroups = selectedRows.map(function(rowid) {
|
||||
var rowData = $('#grid').getRowData(rowid);
|
||||
return {
|
||||
id: rowData.id,
|
||||
groupName: rowData.groupName
|
||||
};
|
||||
});
|
||||
}
|
||||
// 부모 창의 selectApiGroup 함수 호출
|
||||
if (window.parent && typeof window.parent.selectApiGroup === "function") {
|
||||
window.parent.selectApiGroup(selectedGroups);
|
||||
window.close();
|
||||
}
|
||||
});
|
||||
|
||||
$("#btn_close").click(function () {
|
||||
window.close();
|
||||
});
|
||||
|
||||
// 검색어 입력 후 엔터 키 처리
|
||||
$("input[name=searchGroupName]").keydown(function(key) {
|
||||
if (key.keyCode == 13) {
|
||||
search();
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="popup_box">
|
||||
<div class="search_wrap">
|
||||
<button type="button" class="cssbtn" id="btn_save" level="W">
|
||||
<i class="material-icons">save</i> <%= localeMessage.getString("button.save") %>
|
||||
</button>
|
||||
<button type="button" class="cssbtn" id="btn_search" level="R">
|
||||
<i class="material-icons">search</i> <%= localeMessage.getString("button.search") %>
|
||||
</button>
|
||||
<button type="button" class="cssbtn" id="btn_close" level="R" status="DETAIL"><i
|
||||
class="material-icons">close</i> <%= localeMessage.getString("button.close") %>
|
||||
</button>
|
||||
</div>
|
||||
<div class="title"></div>
|
||||
<table class="search_condition" cellspacing="0">
|
||||
<tr>
|
||||
<th style="width:80px;">API 그룹 명</th>
|
||||
<td>
|
||||
<input type="text" name="searchGroupName" value="${param.searchGroupName}">
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<table id="grid"></table>
|
||||
<div id="pager"></div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,173 +0,0 @@
|
||||
<%@ page language="java" contentType="text/html; charset=utf-8" %>
|
||||
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
|
||||
<%
|
||||
response.setHeader("Pragma", "No-cache");
|
||||
response.setHeader("Cache-Control", "no-cache");
|
||||
response.setHeader("Expires", "0");
|
||||
%>
|
||||
<!doctype html>
|
||||
<html lang="ko">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=1600" />
|
||||
<title>OpenAPI Spec 에디터</title>
|
||||
|
||||
<%-- Tailwind (벤더된 Play CDN JS, 오프라인) --%>
|
||||
<script src="<c:url value='/plugins/openapi/tailwind.js'/>"></script>
|
||||
<script>
|
||||
tailwind.config = { theme: { extend: { colors: { brand: {
|
||||
50:'#eff6ff', 100:'#dbeafe', 500:'#3b82f6', 600:'#2563eb', 700:'#1d4ed8'
|
||||
} } } } };
|
||||
</script>
|
||||
|
||||
<%-- Swagger UI 5 (벤더) --%>
|
||||
<link rel="stylesheet" href="<c:url value='/plugins/swaggerUI/swagger-ui.css'/>" />
|
||||
<script src="<c:url value='/plugins/swaggerUI/swagger-ui-bundle.js'/>"></script>
|
||||
<script src="<c:url value='/plugins/swaggerUI/swagger-ui-standalone-preset.js'/>"></script>
|
||||
<script src="<c:url value='/plugins/swaggerUI/djb-swagger-i18n.js'/>" charset="UTF-8"></script>
|
||||
|
||||
<%-- js-yaml (벤더) --%>
|
||||
<script src="<c:url value='/plugins/openapi/js-yaml.min.js'/>"></script>
|
||||
|
||||
<%-- CodeMirror (YAML 편집기 — Monaco 대체, admin addon) --%>
|
||||
<link rel="stylesheet" href="<c:url value='/addon/codemirror/lib/codemirror.css'/>" />
|
||||
<script src="<c:url value='/addon/codemirror/lib/codemirror.js'/>"></script>
|
||||
<script src="<c:url value='/addon/codemirror/mode/yaml/yaml.js'/>"></script>
|
||||
<script src="<c:url value='/addon/codemirror/mode/javascript/javascript.js'/>"></script>
|
||||
|
||||
<%-- jQuery + Summernote(lite, 무-Bootstrap) — 설명 리치 에디터 --%>
|
||||
<link rel="stylesheet" href="<c:url value='/addon/summernote/summernote-lite.css'/>" />
|
||||
<%-- 개발자포탈과 동일한 에디터 콘텐츠 스타일(.editor-content, Noto Sans KR 16px) --%>
|
||||
<link rel="stylesheet" href="<c:url value='/js/djb/apispec/editor-content.css'/>" />
|
||||
<script src="<c:url value='/js/jquery-1.12.1.min.js'/>"></script>
|
||||
<script src="<c:url value='/addon/summernote/summernote-lite.min.js'/>"></script>
|
||||
<script src="<c:url value='/addon/summernote/lang/summernote-ko-KR.js'/>"></script>
|
||||
|
||||
<link rel="stylesheet" href="<c:url value='/js/djb/apispec/styles.css'/>" />
|
||||
<style>
|
||||
/* 페이지 자체 세로 스크롤 제거 — 뷰포트 고정, 내부 패널만 스크롤 */
|
||||
html, body { height: 100%; margin: 0; overflow-x: auto; overflow-y: hidden; }
|
||||
#djb-wrap { height: 100vh; display: flex; flex-direction: column; }
|
||||
#djb-wrap > header, #djb-wrap > nav { flex: 0 0 auto; }
|
||||
#body { flex: 1 1 auto; min-height: 0 !important; overflow: hidden; }
|
||||
#form-panel { display: flex; flex-direction: column; min-height: 0; overflow: hidden; }
|
||||
#form-content { flex: 1 1 auto; min-height: 0; overflow-y: auto; }
|
||||
#preview-panel { display: flex; flex-direction: column; min-height: 0; overflow: hidden; }
|
||||
#preview-swagger, #preview-monaco { flex: 1 1 auto; min-height: 0; height: auto !important; overflow: auto; }
|
||||
#preview-monaco .CodeMirror { height: 100%; }
|
||||
/* 패널 내부 sticky 는 flex 행으로 (스크롤 컨테이너는 form-content / preview) */
|
||||
#form-panel > .sticky, #preview-panel > .sticky { position: static !important; }
|
||||
</style>
|
||||
|
||||
<script>
|
||||
window.DJB_CTX = {
|
||||
eaiSvcName: "${param.eaiSvcName}",
|
||||
apiName: "${param.eaiSvcDesc}",
|
||||
detailUrl: "<c:url value='/onl/transaction/apim/djbApiSpecMan.json'/>?cmd=DETAIL_SPEC&serviceType=APIGW",
|
||||
saveUrl: "<c:url value='/onl/transaction/apim/djbApiSpecMan.json'/>?cmd=SAVE&serviceType=APIGW",
|
||||
swaggerBase: "<c:url value='/plugins/swaggerUI/'/>",
|
||||
gwAddress: "${gwAddress}",
|
||||
orgPopupUrl: "<c:url value='/onl/transaction/apim/apiSpecMan.view'/>",
|
||||
groupPopupUrl: "<c:url value='/onl/transaction/apim/djbApiSpecMan.view'/>?cmd=GROUP_POPUP",
|
||||
jsonUrl: "<c:url value='/onl/transaction/apim/djbApiSpecMan.json'/>"
|
||||
};
|
||||
</script>
|
||||
</head>
|
||||
<body class="bg-slate-50 text-slate-800 antialiased" style="min-width:1600px;">
|
||||
|
||||
<div id="djb-wrap" class="mx-auto" style="width:1600px;">
|
||||
|
||||
<!-- ===== Header ===== -->
|
||||
<header class="h-14 flex items-center justify-between px-6 bg-white border-b border-slate-200 sticky top-0 z-30">
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="font-semibold text-slate-900">OpenAPI Spec 에디터</div>
|
||||
<span class="text-slate-300">|</span>
|
||||
<span class="text-sm text-slate-500">인터페이스:</span>
|
||||
<span class="text-sm font-medium text-slate-800" id="hdr-project-name">${param.eaiSvcName}</span>
|
||||
<span class="ml-2 px-2 py-0.5 text-[11px] rounded-full bg-slate-100 text-slate-600 border border-slate-200" id="hdr-version">v1.0.0</span>
|
||||
<span id="hdr-source" class="hidden ml-1 px-2 py-0.5 text-[11px] rounded-full bg-emerald-50 text-emerald-700 border border-emerald-200"></span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<button id="btn-newwin" class="hidden px-3 py-1.5 text-sm rounded border border-slate-300 bg-white hover:bg-slate-50">새창으로 띄우기</button>
|
||||
<button id="btn-regen" class="px-3 py-1.5 text-sm rounded border border-slate-300 bg-white hover:bg-slate-50">자동 생성(초기화)</button>
|
||||
<button id="btn-save" class="px-3 py-1.5 text-sm rounded bg-emerald-600 text-white hover:bg-emerald-700">저장</button>
|
||||
<div class="relative">
|
||||
<button id="btn-export" class="px-3 py-1.5 text-sm rounded bg-brand-600 text-white hover:bg-brand-700">내보내기 ▾</button>
|
||||
<div id="export-menu" class="hidden absolute right-0 mt-1 w-44 bg-white border border-slate-200 rounded shadow-lg z-40">
|
||||
<button data-export="yaml" class="block w-full text-left px-3 py-2 text-sm hover:bg-slate-50">YAML 다운로드</button>
|
||||
<button data-export="json" class="block w-full text-left px-3 py-2 text-sm hover:bg-slate-50">JSON 다운로드</button>
|
||||
</div>
|
||||
</div>
|
||||
<button id="btn-close" class="px-3 py-1.5 text-sm rounded border border-slate-300 bg-white hover:bg-slate-50">닫기</button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- ===== Stepper ===== -->
|
||||
<nav class="bg-white border-b border-slate-200 sticky z-20" style="top:56px;">
|
||||
<div class="h-1 bg-slate-100">
|
||||
<div id="stepper-progress" class="h-1 bg-brand-500 transition-all duration-300" style="width:16.66%"></div>
|
||||
</div>
|
||||
<ol id="stepper" class="grid grid-cols-5 px-2"></ol>
|
||||
</nav>
|
||||
|
||||
<!-- ===== Body ===== -->
|
||||
<main id="body" class="flex" style="min-height: calc(100vh - 56px - 72px);">
|
||||
<section id="form-panel" class="bg-white border-r border-slate-200 transition-all duration-300" style="width:960px;">
|
||||
<div class="flex items-center justify-between px-6 py-3 border-b border-slate-100 sticky bg-white z-10" style="top:128px;">
|
||||
<div class="flex items-baseline gap-3">
|
||||
<span class="text-xs uppercase tracking-wide text-slate-400">Step <span id="form-step-num">1</span> / 5</span>
|
||||
<h2 class="text-base font-semibold text-slate-800" id="form-step-title">기본정보</h2>
|
||||
</div>
|
||||
<button id="btn-toggle-preview-from-form" class="hidden text-xs px-2 py-1 rounded border border-slate-200 text-slate-600 hover:bg-slate-50">▶ 미리보기 다시 열기</button>
|
||||
</div>
|
||||
<div id="form-content" class="px-6 py-5"></div>
|
||||
<div class="sticky bottom-0 bg-white border-t border-slate-200 px-6 py-3 flex items-center justify-between">
|
||||
<button id="btn-prev" class="px-4 py-2 text-sm rounded border border-slate-300 bg-white hover:bg-slate-50 disabled:opacity-40 disabled:cursor-not-allowed">◀ 이전</button>
|
||||
<div class="text-xs text-slate-500" id="footer-hint">필수 항목을 채우고 다음 단계로 이동하세요</div>
|
||||
<button id="btn-next" class="px-4 py-2 text-sm rounded bg-brand-600 text-white hover:bg-brand-700">다음 ▶</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<aside id="preview-panel" class="bg-slate-50 transition-all duration-300" style="width:640px;">
|
||||
<div class="flex items-center justify-between px-4 py-2 border-b border-slate-200 bg-white sticky z-10" style="top:128px;">
|
||||
<div class="flex items-center gap-1">
|
||||
<button data-ptab="swagger" class="preview-tab px-3 py-1.5 text-sm rounded-t border-b-2 border-brand-600 text-brand-700 font-medium">Swagger UI</button>
|
||||
<button data-ptab="yaml-ro" class="preview-tab px-3 py-1.5 text-sm rounded-t border-b-2 border-transparent text-slate-600 hover:text-slate-800">YAML 보기</button>
|
||||
<button data-ptab="yaml-edit" class="preview-tab px-3 py-1.5 text-sm rounded-t border-b-2 border-transparent text-slate-600 hover:text-slate-800">스펙 편집</button>
|
||||
</div>
|
||||
<button id="btn-toggle-preview" class="text-xs px-2 py-1 rounded border border-slate-200 text-slate-600 hover:bg-slate-100">패널 숨기기 ▶</button>
|
||||
</div>
|
||||
<div id="yaml-edit-bar" class="hidden flex items-center justify-between bg-amber-50 border-b border-amber-200 px-4 py-2 text-xs">
|
||||
<span class="text-amber-800">YAML 을 직접 편집한 뒤 [Form 에 적용] 을 눌러 양식에 동기화하세요.</span>
|
||||
<div class="flex items-center gap-2">
|
||||
<button id="btn-yaml-revert" class="px-2 py-1 rounded border border-amber-300 bg-white hover:bg-amber-100">변경 취소</button>
|
||||
<button id="btn-yaml-apply" class="px-2 py-1 rounded bg-amber-600 text-white hover:bg-amber-700">Form 에 적용</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="preview-swagger" class="overflow-auto" style="height: calc(100vh - 56px - 72px - 41px);"></div>
|
||||
<div id="preview-monaco" class="hidden" style="height: calc(100vh - 56px - 72px - 41px);"></div>
|
||||
</aside>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<div id="toast" class="fixed bottom-5 left-1/2 -translate-x-1/2 hidden px-4 py-2 rounded bg-slate-900 text-white text-sm shadow-lg z-50"></div>
|
||||
|
||||
<!-- Security scheme modal -->
|
||||
<div id="sec-modal" class="hidden fixed inset-0 z-50 bg-slate-900/40 flex items-center justify-center">
|
||||
<div class="bg-white rounded-lg shadow-xl w-[640px] max-h-[80vh] overflow-auto">
|
||||
<div class="flex items-center justify-between px-5 py-3 border-b border-slate-200">
|
||||
<h3 class="font-semibold text-slate-800">보안 스킴 추가</h3>
|
||||
<button class="text-slate-400 hover:text-slate-700" data-sec-close>✕</button>
|
||||
</div>
|
||||
<div class="p-5" id="sec-modal-body"></div>
|
||||
<div class="px-5 py-3 border-t border-slate-200 flex justify-end gap-2 bg-slate-50">
|
||||
<button class="px-3 py-1.5 text-sm rounded border border-slate-300 bg-white" data-sec-close>취소</button>
|
||||
<button id="btn-sec-save" class="px-3 py-1.5 text-sm rounded bg-brand-600 text-white">추가</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="<c:url value='/js/djb/apispec/sample-data.js'/>"></script>
|
||||
<script src="<c:url value='/js/djb/apispec/app.js'/>"></script>
|
||||
</body>
|
||||
</html>
|
||||
-2
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,122 +0,0 @@
|
||||
/*!
|
||||
* djb-swagger-i18n.js
|
||||
* Swagger UI 영문 라벨을 한글로 치환하는 i18n 패치 (방법 B - DOM patch)
|
||||
* 배포 경로 권장: static/plugins/swaggerUI/djb-swagger-i18n.js
|
||||
* index.html 의 swagger-initializer.js 직전 또는 직후에 <script> 로 로드
|
||||
*
|
||||
* ?lang=en 쿼리스트링이 있으면 패치 비활성화 (원문 보기)
|
||||
* 매핑 카탈로그는 01-i18n-mapping/i18n-strings.csv 와 동기 유지
|
||||
*/
|
||||
(function (window) {
|
||||
'use strict';
|
||||
|
||||
// ── 비활성화 옵션 ──
|
||||
if (/[?&]lang=en\b/.test(window.location.search)) return;
|
||||
|
||||
// ── 매핑 (key 는 디버깅용, 실제 비교는 EN 문자열 일치) ──
|
||||
var I18N_MAP = {
|
||||
// P0 — OAuth2 모달 본문
|
||||
'auth.modal.title': { en: 'Available authorizations', ko: '사용 가능한 인증' },
|
||||
'auth.scopes.description': {
|
||||
en: 'Scopes are used to grant an application different levels of access to data on behalf of the end user. Each API may declare one or more scopes.',
|
||||
ko: '스코프는 최종 사용자를 대신하여 애플리케이션이 데이터에 접근할 수 있는 권한 수준을 부여합니다. 각 API는 하나 이상의 스코프를 선언할 수 있습니다.'
|
||||
},
|
||||
'auth.scopes.swagger_grant': {
|
||||
en: 'API requires the following scopes. Select which ones you want to grant to Swagger UI.',
|
||||
ko: '이 API 는 아래 스코프가 필요합니다. Swagger UI 에 부여할 스코프를 선택하세요.'
|
||||
},
|
||||
'auth.flow.clientCredentials': { en: 'OAuth2 client credentials flow', ko: 'OAuth2 클라이언트 자격증명 흐름' },
|
||||
'auth.flow.implicit': { en: 'OAuth2 implicit flow', ko: 'OAuth2 암묵적 흐름' },
|
||||
'auth.flow.password': { en: 'OAuth2 password flow', ko: 'OAuth2 비밀번호 흐름' },
|
||||
'auth.flow.authorizationCode': { en: 'OAuth2 authorization code flow', ko: 'OAuth2 인가 코드 흐름' },
|
||||
'auth.field.tokenUrl': { en: 'Token URL:', ko: '토큰 URL:' },
|
||||
'auth.field.flow': { en: 'Flow:', ko: '흐름:' },
|
||||
'auth.field.clientId': { en: 'client_id:', ko: '클라이언트 ID (client_id):' },
|
||||
'auth.field.clientSecret': { en: 'client_secret:',ko: '클라이언트 시크릿 (client_secret):' },
|
||||
'auth.field.scopes': { en: 'Scopes:', ko: '스코프:' },
|
||||
'auth.action.selectAll': { en: 'select all', ko: '모두 선택' },
|
||||
'auth.action.selectNone': { en: 'select none', ko: '선택 해제' },
|
||||
'auth.action.authorize': { en: 'Authorize', ko: '인증' },
|
||||
'auth.action.close': { en: 'Close', ko: '닫기' },
|
||||
'auth.action.logout': { en: 'Logout', ko: '로그아웃' },
|
||||
'auth.modal.authorized': { en: 'authorized', ko: '인증됨' },
|
||||
|
||||
// P1 — 기타 인증 흐름
|
||||
'auth.scheme.apiKey': { en: 'API key', ko: 'API 키' },
|
||||
'auth.field.name': { en: 'name:', ko: '이름:' },
|
||||
'auth.field.in': { en: 'in:', ko: '위치:' },
|
||||
'auth.field.value': { en: 'Value:', ko: '값:' },
|
||||
'basic.field.username': { en: 'Username:',ko: '사용자 ID:' },
|
||||
'basic.field.password': { en: 'Password:',ko: '비밀번호:' }
|
||||
// P2 (오퍼레이션/응답) 는 필요 시 추가
|
||||
};
|
||||
|
||||
// 빠른 lookup 을 위해 EN → KO 인덱스 사전 생성
|
||||
var EN_TO_KO = Object.create(null);
|
||||
Object.keys(I18N_MAP).forEach(function (k) {
|
||||
var e = I18N_MAP[k];
|
||||
EN_TO_KO[e.en] = e.ko;
|
||||
});
|
||||
|
||||
// ── 치환 대상 노드 식별 + 치환 ──
|
||||
function translateNode(node) {
|
||||
if (!node) return;
|
||||
if (node.nodeType === Node.TEXT_NODE) {
|
||||
var raw = node.nodeValue;
|
||||
if (!raw) return;
|
||||
var trimmed = raw.trim();
|
||||
if (!trimmed) return;
|
||||
// 1) 완전 일치 (가장 안전)
|
||||
if (EN_TO_KO[trimmed]) {
|
||||
node.nodeValue = raw.replace(trimmed, EN_TO_KO[trimmed]);
|
||||
return;
|
||||
}
|
||||
// 2) 라벨 colon 패턴: "Token URL:" 같이 끝이 ":" 인 짧은 라벨
|
||||
// (불필요 — 1번 완전 일치로 대부분 커버)
|
||||
return;
|
||||
}
|
||||
if (node.nodeType !== Node.ELEMENT_NODE) return;
|
||||
if (node.tagName === 'SCRIPT' || node.tagName === 'STYLE') return;
|
||||
|
||||
// input placeholder / button title 도 일부 케이스에 대비
|
||||
if (node.tagName === 'INPUT' && node.placeholder && EN_TO_KO[node.placeholder.trim()]) {
|
||||
node.placeholder = EN_TO_KO[node.placeholder.trim()];
|
||||
}
|
||||
|
||||
var i, child = node.childNodes, len = child.length;
|
||||
for (i = 0; i < len; i++) translateNode(child[i]);
|
||||
}
|
||||
|
||||
function translateAll() {
|
||||
var root = document.getElementById('swagger-ui') || document.body;
|
||||
translateNode(root);
|
||||
}
|
||||
|
||||
// ── MutationObserver — SwaggerUI 가 동적으로 DOM 을 다시 그릴 때마다 재적용 ──
|
||||
var observer = new MutationObserver(function (mutations) {
|
||||
for (var i = 0; i < mutations.length; i++) {
|
||||
var m = mutations[i];
|
||||
if (m.addedNodes && m.addedNodes.length) {
|
||||
for (var j = 0; j < m.addedNodes.length; j++) translateNode(m.addedNodes[j]);
|
||||
}
|
||||
if (m.type === 'characterData') {
|
||||
translateNode(m.target);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
function start() {
|
||||
translateAll();
|
||||
observer.observe(document.body, {
|
||||
childList: true,
|
||||
subtree: true,
|
||||
characterData: true
|
||||
});
|
||||
}
|
||||
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', start);
|
||||
} else {
|
||||
start();
|
||||
}
|
||||
})(window);
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+26
-22
@@ -4,6 +4,7 @@ plugins {
|
||||
id 'eclipse-wtp'
|
||||
id 'idea'
|
||||
id 'war'
|
||||
id 'com.diffplug.eclipse.apt' version '3.41.1'
|
||||
}
|
||||
|
||||
group 'com.eactive'
|
||||
@@ -14,7 +15,7 @@ def quartzVersion = "2.2.1"
|
||||
def queryDslVersion = "5.0.0"
|
||||
def hibernateVersion = "5.6.15.Final"
|
||||
|
||||
def nexusUrl = "https://nexus.eactive.synology.me:8090"
|
||||
def reposiliteUrl = "http://localhost:8080"
|
||||
//def useOnJboss = false
|
||||
|
||||
def generatedJavaDir = "$buildDir/generated/java"
|
||||
@@ -22,8 +23,12 @@ def generatedJavaDir = "$buildDir/generated/java"
|
||||
allprojects {
|
||||
repositories {
|
||||
maven {
|
||||
url "${nexusUrl}/repository/maven-public/"
|
||||
url "${reposiliteUrl}/private"
|
||||
allowInsecureProtocol = true
|
||||
credentials {
|
||||
username = reposiliteUser
|
||||
password = reposilitePassword
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -48,6 +53,10 @@ compileJava {
|
||||
options.encoding = 'UTF-8'
|
||||
options.compilerArgs = ['-parameters']
|
||||
options.generatedSourceOutputDirectory = project.file(generatedJavaDir)
|
||||
|
||||
aptOptions {
|
||||
processorArgs = [ 'querydsl.generatedAnnotationClass' : 'com.querydsl.core.annotations.Generated' ]
|
||||
}
|
||||
}
|
||||
|
||||
war {
|
||||
@@ -84,11 +93,10 @@ dependencies {
|
||||
implementation project(':elink-online-emsclient')
|
||||
implementation project(':elink-portal-common')
|
||||
//implementation project(':kjb-safedb')
|
||||
//implementation project(":eapim-admin-djb")
|
||||
implementation project(":eapim-admin-djb")
|
||||
|
||||
/* Custom Libs (damo-manager.jar 는 Tomcat lib 가 런타임 제공 → compileOnly, WAR 미포함) */
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar'], exclude: ['damo-manager.jar'])
|
||||
compileOnly files('libs/damo-manager.jar')
|
||||
/* Custom Libs */
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
|
||||
//implementation "org.dom4j:dom4j:2.1.3"
|
||||
//implementation "com.rabbitmq:amqp-client:3.6.6"
|
||||
@@ -121,7 +129,7 @@ dependencies {
|
||||
implementation "org.snmp4j:snmp4j:1.10.1"
|
||||
implementation "com.googlecode.json-simple:json-simple:1.1.1"
|
||||
|
||||
implementation "io.netty:netty-all:4.1.94.Final"
|
||||
implementation "io.netty:netty-all:4.1.0.Final"
|
||||
|
||||
compileOnly "org.apache.mina:mina-filter-ssl:1.1.6"
|
||||
compileOnly "org.apache.mina:mina-core:1.1.6"
|
||||
@@ -197,13 +205,14 @@ dependencies {
|
||||
}
|
||||
implementation 'software.amazon.awssdk:sso:2.20.142'
|
||||
implementation 'software.amazon.awssdk:sts:2.20.142'
|
||||
|
||||
|
||||
implementation ('io.kubernetes:client-java:18.0.1') {
|
||||
exclude group: 'org.slf4j', module: 'slf4j-api'
|
||||
exclude group: 'org.slf4j', module: 'logback-classic'
|
||||
}
|
||||
|
||||
implementation group: 'commons-net', name: 'commons-net', version: '3.5'
|
||||
|
||||
// JDK 8 의 rt.jar 에는 org.w3c.dom.ElementTraversal 이 없어 xercesImpl 가 NCDFE 를 일으킴.
|
||||
// xml-apis 1.4.01 에 그 클래스가 포함됨. 직접 의존성으로 묶어 WAR 에 패키징되도록 함.
|
||||
implementation 'xml-apis:xml-apis:1.4.01'
|
||||
|
||||
testRuntimeOnly 'com.h2database:h2:2.1.214'
|
||||
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1'
|
||||
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1'
|
||||
@@ -223,11 +232,6 @@ configurations.all {
|
||||
cacheDynamicVersionsFor 10, 'minutes'
|
||||
// Do not cache changing modules
|
||||
cacheChangingModulesFor 0, 'seconds'
|
||||
|
||||
// JDK 8 의 rt.jar 에는 org.w3c.dom.ElementTraversal 이 없음.
|
||||
// 일부 transitive 가 끌어오는 xml-apis:1.0.b2 (2002, DOM L2) 는 이 클래스를 포함하지 않아
|
||||
// xercesImpl 가 NoClassDefFoundError 를 일으킴 → 1.4.01 로 강제 통일.
|
||||
force 'xml-apis:xml-apis:1.4.01'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -252,11 +256,11 @@ eclipse {
|
||||
}
|
||||
synchronizationTasks settingEclipseEncoding, initDirs
|
||||
jdt {
|
||||
// apt {
|
||||
// // generated 된 패스 경로를 .setting/org.eclipse.jdt.apt.core.prefs 의 org.eclipse.jdt.apt.genSrcDir에 적용한다.
|
||||
// // project > properties > Java Compiler > Annoation Processing 화면에서 확인 가능하다.
|
||||
// genSrcDir = file(generatedJavaDir)
|
||||
// }
|
||||
apt {
|
||||
// generated 된 패스 경로를 .setting/org.eclipse.jdt.apt.core.prefs 의 org.eclipse.jdt.apt.genSrcDir에 적용한다.
|
||||
// project > properties > Java Compiler > Annoation Processing 화면에서 확인 가능하다.
|
||||
genSrcDir = file(generatedJavaDir)
|
||||
}
|
||||
}
|
||||
project {
|
||||
if (!natures.contains('org.eclipse.buildship.core.gradleprojectnature')) {
|
||||
|
||||
+34
-61
@@ -1,9 +1,9 @@
|
||||
plugins {
|
||||
id 'java-library'
|
||||
id 'eclipse'
|
||||
id 'eclipse-wtp'
|
||||
id 'idea'
|
||||
id 'war'
|
||||
id 'project-report'
|
||||
id 'checkstyle'
|
||||
}
|
||||
|
||||
group 'com.eactive'
|
||||
@@ -14,19 +14,17 @@ def quartzVersion = "2.2.1"
|
||||
def queryDslVersion = "5.0.0"
|
||||
def hibernateVersion = "5.6.15.Final"
|
||||
|
||||
def nexusUrl = "https://nexus.eactive.synology.me:8090"
|
||||
/*def nexusUrl = "https://nexus.eactive.synology.me:8090"*/
|
||||
//def useOnJboss = false
|
||||
|
||||
def generatedJavaDir = "$buildDir/generated/java"
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
maven {
|
||||
url "${nexusUrl}/repository/maven-public/"
|
||||
allowInsecureProtocol = true
|
||||
}
|
||||
}
|
||||
}
|
||||
/*repositories {
|
||||
maven {
|
||||
url "${nexusUrl}/repository/maven-public/"
|
||||
allowInsecureProtocol = true
|
||||
}
|
||||
}*/
|
||||
|
||||
project.webAppDirName = 'WebContent'
|
||||
|
||||
@@ -36,6 +34,10 @@ java {
|
||||
}
|
||||
}
|
||||
|
||||
checkstyle {
|
||||
toolVersion = '8.45.1'
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
java {
|
||||
@@ -46,7 +48,10 @@ sourceSets {
|
||||
|
||||
compileJava {
|
||||
options.encoding = 'UTF-8'
|
||||
options.compilerArgs = ['-parameters']
|
||||
options.compilerArgs = [
|
||||
'-parameters',
|
||||
'-Aquerydsl.generatedAnnotationClass=com.querydsl.core.annotations.Generated'
|
||||
]
|
||||
options.generatedSourceOutputDirectory = project.file(generatedJavaDir)
|
||||
}
|
||||
|
||||
@@ -62,6 +67,7 @@ war {
|
||||
// rootSpec.exclude '**/persistence.xml'
|
||||
exclude '**/context.xml'
|
||||
exclude '**/context-*.xml'
|
||||
exclude '**/context_*.xml'
|
||||
archiveFileName = "eapim-admin.war"
|
||||
duplicatesStrategy = DuplicatesStrategy.WARN
|
||||
}
|
||||
@@ -83,12 +89,11 @@ dependencies {
|
||||
implementation project(':elink-online-common')
|
||||
implementation project(':elink-online-emsclient')
|
||||
implementation project(':elink-portal-common')
|
||||
//implementation project(':kjb-safedb')
|
||||
implementation project(":eapim-admin-djb")
|
||||
implementation project(':kjb-safedb')
|
||||
implementation project(":eapim-admin-kjb")
|
||||
|
||||
/* Custom Libs (damo-manager.jar 는 Tomcat lib 가 런타임 제공 → compileOnly, WAR 미포함) */
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar'], exclude: ['damo-manager.jar'])
|
||||
compileOnly files('libs/damo-manager.jar')
|
||||
/* Custom Libs */
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
|
||||
//implementation "org.dom4j:dom4j:2.1.3"
|
||||
//implementation "com.rabbitmq:amqp-client:3.6.6"
|
||||
@@ -121,7 +126,7 @@ dependencies {
|
||||
implementation "org.snmp4j:snmp4j:1.10.1"
|
||||
implementation "com.googlecode.json-simple:json-simple:1.1.1"
|
||||
|
||||
implementation "io.netty:netty-all:4.1.94.Final"
|
||||
implementation "io.netty:netty-all:4.1.0.Final"
|
||||
|
||||
compileOnly "org.apache.mina:mina-filter-ssl:1.1.6"
|
||||
compileOnly "org.apache.mina:mina-core:1.1.6"
|
||||
@@ -198,11 +203,12 @@ dependencies {
|
||||
implementation 'software.amazon.awssdk:sso:2.20.142'
|
||||
implementation 'software.amazon.awssdk:sts:2.20.142'
|
||||
|
||||
implementation group: 'commons-net', name: 'commons-net', version: '3.5'
|
||||
implementation ('io.kubernetes:client-java:18.0.1') {
|
||||
exclude group: 'org.slf4j', module: 'slf4j-api'
|
||||
exclude group: 'org.slf4j', module: 'logback-classic'
|
||||
}
|
||||
|
||||
// JDK 8 의 rt.jar 에는 org.w3c.dom.ElementTraversal 이 없어 xercesImpl 가 NCDFE 를 일으킴.
|
||||
// xml-apis 1.4.01 에 그 클래스가 포함됨. 직접 의존성으로 묶어 WAR 에 패키징되도록 함.
|
||||
implementation 'xml-apis:xml-apis:1.4.01'
|
||||
implementation group: 'commons-net', name: 'commons-net', version: '3.5'
|
||||
|
||||
testRuntimeOnly 'com.h2database:h2:2.1.214'
|
||||
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1'
|
||||
@@ -223,51 +229,18 @@ configurations.all {
|
||||
cacheDynamicVersionsFor 10, 'minutes'
|
||||
// Do not cache changing modules
|
||||
cacheChangingModulesFor 0, 'seconds'
|
||||
|
||||
// JDK 8 의 rt.jar 에는 org.w3c.dom.ElementTraversal 이 없음.
|
||||
// 일부 transitive 가 끌어오는 xml-apis:1.0.b2 (2002, DOM L2) 는 이 클래스를 포함하지 않아
|
||||
// xercesImpl 가 NoClassDefFoundError 를 일으킴 → 1.4.01 로 강제 통일.
|
||||
force 'xml-apis:xml-apis:1.4.01'
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
task settingEclipseEncoding {
|
||||
if (project.file('.settings').exists()) {
|
||||
File f = file('.settings/org.eclipse.core.resources.prefs')
|
||||
f.write('eclipse.preferences.version=1\n')
|
||||
f.append('encoding/<project>=utf-8')
|
||||
}
|
||||
}
|
||||
|
||||
task initDirs() {
|
||||
file(generatedJavaDir).mkdirs()
|
||||
}
|
||||
|
||||
eclipse {
|
||||
wtp {
|
||||
component {
|
||||
contextPath = 'monitoring'
|
||||
}
|
||||
}
|
||||
synchronizationTasks settingEclipseEncoding, initDirs
|
||||
jdt {
|
||||
apt {
|
||||
// generated 된 패스 경로를 .setting/org.eclipse.jdt.apt.core.prefs 의 org.eclipse.jdt.apt.genSrcDir에 적용한다.
|
||||
// project > properties > Java Compiler > Annoation Processing 화면에서 확인 가능하다.
|
||||
genSrcDir = file(generatedJavaDir)
|
||||
}
|
||||
}
|
||||
project {
|
||||
if (!natures.contains('org.eclipse.buildship.core.gradleprojectnature')) {
|
||||
natures.add('org.eclipse.buildship.core.gradleprojectnature')
|
||||
buildCommand 'org.eclipse.buildship.core.gradleprojectbuilder'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tasks.withType(JavaCompile) {
|
||||
options.fork = true // 컴파일을 별도 프로세스로 분리
|
||||
options.forkOptions.memoryMaximumSize = '4g' // 컴파일러에 2GB 할당 (필요시 4g로 증량)
|
||||
options.encoding = 'UTF-8'
|
||||
}
|
||||
tasks.withType(Checkstyle) {
|
||||
reports {
|
||||
xml.required = false
|
||||
html.required = true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
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()
|
||||
@@ -1,51 +0,0 @@
|
||||
# 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
|
||||
+4
-1
@@ -1,4 +1,7 @@
|
||||
org.gradle.jvmargs=-Xmx4g -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError
|
||||
org.gradle.parallel=true
|
||||
org.gradle.caching=true
|
||||
org.gradle.configureondemand=true
|
||||
org.gradle.configureondemand=true
|
||||
|
||||
reposiliteUser=admin
|
||||
reposilitePassword=BFoZsHrQU4lmJf9rruDdcsPgE0gltKfRdTjAx04IgvFhrd5q07QwMFZqeLHFv+5n
|
||||
+12
-12
@@ -1,4 +1,4 @@
|
||||
CREATE TABLE EMSAPP.API_HISTORY
|
||||
CREATE TABLE EMSADM.API_HISTORY
|
||||
(
|
||||
API_ID VARCHAR2(30),
|
||||
SEQ NUMBER(22),
|
||||
@@ -9,23 +9,23 @@ CREATE TABLE EMSAPP.API_HISTORY
|
||||
LOB (SAVEDDATA) STORE AS SECUREFILE (TABLESPACE TS_AGW_D01)
|
||||
TABLESPACE TS_EMS_D01;
|
||||
|
||||
COMMENT ON TABLE EMSAPP.API_HISTORY IS 'API 변경이력 테이블';
|
||||
COMMENT ON COLUMN EMSAPP.API_HISTORY.API_ID IS 'API ID(TSEAIHE01.EAISVCNAME)';
|
||||
COMMENT ON COLUMN EMSAPP.API_HISTORY.SEQ IS 'Sequence 값';
|
||||
COMMENT ON COLUMN EMSAPP.API_HISTORY.SAVEDDATA IS '변경저장 정보';
|
||||
COMMENT ON COLUMN EMSAPP.API_HISTORY.USERID IS '사용자ID';
|
||||
COMMENT ON COLUMN EMSAPP.API_HISTORY.SAVEDDT IS '저장일시';
|
||||
COMMENT ON TABLE EMSADM.API_HISTORY IS 'API 변경이력 테이블';
|
||||
COMMENT ON COLUMN EMSADM.API_HISTORY.API_ID IS 'API ID(TSEAIHE01.EAISVCNAME)';
|
||||
COMMENT ON COLUMN EMSADM.API_HISTORY.SEQ IS 'Sequence 값';
|
||||
COMMENT ON COLUMN EMSADM.API_HISTORY.SAVEDDATA IS '변경저장 정보';
|
||||
COMMENT ON COLUMN EMSADM.API_HISTORY.USERID IS '사용자ID';
|
||||
COMMENT ON COLUMN EMSADM.API_HISTORY.SAVEDDT IS '저장일시';
|
||||
|
||||
CREATE UNIQUE INDEX EMSAPP.PK_API_HISTORY ON EMSAPP.API_HISTORY
|
||||
CREATE UNIQUE INDEX EMSADM.PK_API_HISTORY ON EMSADM.API_HISTORY
|
||||
(API_ID, SEQ)
|
||||
TABLESPACE TS_EMS_I01;
|
||||
|
||||
ALTER TABLE EMSAPP.API_HISTORY ADD (
|
||||
ALTER TABLE EMSADM.API_HISTORY ADD (
|
||||
CONSTRAINT PK_API_HISTORY
|
||||
PRIMARY KEY
|
||||
(API_ID, SEQ)
|
||||
USING INDEX EMSAPP.PK_API_HISTORY
|
||||
USING INDEX EMSADM.PK_API_HISTORY
|
||||
ENABLE VALIDATE);
|
||||
|
||||
GRANT DELETE, INSERT, SELECT, UPDATE ON EMSAPP.API_HISTORY TO RL_EMS_ALL;
|
||||
GRANT SELECT ON EMSAPP.API_HISTORY TO RL_EMS_SEL;
|
||||
GRANT DELETE, INSERT, SELECT, UPDATE ON EMSADM.API_HISTORY TO RL_EMS_ALL;
|
||||
GRANT SELECT ON EMSADM.API_HISTORY TO RL_EMS_SEL;
|
||||
|
||||
+30
-30
@@ -39,37 +39,37 @@
|
||||
|
||||
```sql
|
||||
-- 개발 서버용
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.eai_batch.url', 'http://172.31.32.111:10230/BATAppWeb/EaiBatCall');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.eai.batch.connection_timeout', '5');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.eai.batch.response_timeout', '5');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.eai.batch.timeout', '10');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.eai.batch.charset', 'euc-kr');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.eai_batch.send_dir', '/Data/eapim/portal/sendmail/snd');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.eai_batch.backup_dir', '/Data/eapim/portal/sendmail/bak');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.eai_batch.error_dir', '/Data/eapim/portal/sendmail/bak');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.eai_batch.retention_date', '60');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.eai_batch.interface_id', 'UIEFF00001UAG');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.host_url', 'http://172.31.35.144:9021');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.api_key', '7cca6d58-e4f7-474d-9edd-525806bc99ff');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.connection_timeout', '5');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.response_timeout', '5');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.timeout', '10');
|
||||
insert into EMSADM.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 EMSADM.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 EMSADM.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 EMSADM.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 EMSADM.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 EMSADM.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 EMSADM.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 EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.timeout', '10');
|
||||
|
||||
|
||||
-- autogen template
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.eai.batch.url', '');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.eai.batch.connection_timeout', '5');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.eai.batch.response_timeout', '5');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.eai.batch.timeout', '10');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.eai.batch.charset', 'euc-kr');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.eai_batch.send_dir', '');
|
||||
insert into EMSAPP.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.error_dir', '');
|
||||
insert into EMSAPP.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.retention_date', '60');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.host', '');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.api_key', '');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.connection_timeout', '5');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.response_timeout', '5');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.timeout', '10');
|
||||
insert into EMSADM.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 EMSADM.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 EMSADM.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 EMSADM.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 EMSADM.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 EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.host', '');
|
||||
insert into EMSADM.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 EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.response_timeout', '5');
|
||||
insert into EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.timeout', '10');
|
||||
```
|
||||
@@ -163,13 +163,13 @@ SMS 인증번호 발송 - authCode: 123456
|
||||
|
||||
```sql
|
||||
-- SMS 인증 설정 초기화
|
||||
INSERT INTO EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL)
|
||||
INSERT INTO EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL)
|
||||
VALUES ('Monitoring', 'kjb.sms_auth.enabled', 'true');
|
||||
|
||||
INSERT INTO EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL)
|
||||
INSERT INTO EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL)
|
||||
VALUES ('Monitoring', 'kjb.sms_auth.mode', 'real');
|
||||
|
||||
INSERT INTO EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL)
|
||||
INSERT INTO EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL)
|
||||
VALUES ('Monitoring', 'kjb.sms_auth.fixed_value', '654321');
|
||||
```
|
||||
|
||||
@@ -187,7 +187,7 @@ SMS 발송을 위해 UMS 관련 프로퍼티가 설정되어 있어야 합니다
|
||||
### Q: SMS 인증을 일시적으로 비활성화하려면?
|
||||
|
||||
```sql
|
||||
UPDATE EMSAPP.TSEAIRM24
|
||||
UPDATE EMSADM.TSEAIRM24
|
||||
SET PRPTY2VAL = 'false'
|
||||
WHERE PRPTYGROUPNAME = 'Monitoring' AND PRPTYNAME = 'kjb.sms_auth.enabled';
|
||||
```
|
||||
|
||||
Binary file not shown.
@@ -9,7 +9,7 @@
|
||||
## 기술 스택
|
||||
|
||||
- Spring Framework 5.3.27
|
||||
- Java 17
|
||||
- Java 8
|
||||
- Gradle 8.7
|
||||
- Oracle 19c
|
||||
- Spring Data JPA 2.5.2
|
||||
@@ -23,61 +23,251 @@
|
||||
|
||||
### 사전 요구사항
|
||||
|
||||
- JDK 17 이상
|
||||
- JDK 8 이상
|
||||
- Gradle 8.7
|
||||
- Git
|
||||
- Oracle 19c (또는 개발 환경에 따라 접근 가능한 DB)
|
||||
|
||||
### 환경별 설정 파일
|
||||
### Git Bash에서 초기 세팅
|
||||
|
||||
```bash
|
||||
# ====================================
|
||||
# 1. 변수 설정 (프로젝트 경로 및 브랜치)
|
||||
# ====================================
|
||||
export WORKSPACE_DIR="/c/eactive/workspaces/kjb-eapim"
|
||||
export PROJECT_BRANCH="jenkins_with_weblogic"
|
||||
|
||||
# Git Repository URL 설정
|
||||
# 기본: ssh://git@192.168.240.178:18081/eapim
|
||||
# 대체: https://git.eactive.synology.me:8090/kjb-eapim
|
||||
export GIT_REPO_BASE="ssh://git@192.168.240.178:18081/eapim"
|
||||
# export GIT_REPO_BASE="https://git.eactive.synology.me:8090/kjb-eapim"
|
||||
|
||||
# Gradle 명령어 설정
|
||||
# 일반 환경: gradle
|
||||
# msi-gf63 환경: /c/eactive/workspaces/shell-scripts/kjb-gradle.sh
|
||||
export GRADLE_CMD="gradle"
|
||||
# export GRADLE_CMD="/c/eactive/workspaces/shell-scripts/kjb-gradle.sh"
|
||||
|
||||
# ====================================
|
||||
# 2. 작업 디렉토리 생성
|
||||
# ====================================
|
||||
mkdir -p $WORKSPACE_DIR
|
||||
cd $WORKSPACE_DIR
|
||||
|
||||
# ====================================
|
||||
# 3. eapim-admin 프로젝트 클론
|
||||
# ====================================
|
||||
git clone ${GIT_REPO_BASE}/eapim-admin.git
|
||||
cd eapim-admin
|
||||
|
||||
# 프로젝트 브랜치로 체크아웃
|
||||
git checkout $PROJECT_BRANCH
|
||||
|
||||
# ====================================
|
||||
# 4. eapim-online 프로젝트 클론 (코어 모듈 포함)
|
||||
# ====================================
|
||||
cd $WORKSPACE_DIR
|
||||
git clone ${GIT_REPO_BASE}/eapim-online.git
|
||||
cd eapim-online
|
||||
git checkout $PROJECT_BRANCH
|
||||
|
||||
# ====================================
|
||||
# 5. 의존 모듈 클론
|
||||
# ====================================
|
||||
cd $WORKSPACE_DIR
|
||||
|
||||
# elink-portal-common 클론
|
||||
git clone ${GIT_REPO_BASE}/elink-portal-common.git
|
||||
cd elink-portal-common
|
||||
git checkout master # 또는 필요한 브랜치
|
||||
|
||||
# kjb-safedb 클론
|
||||
cd $WORKSPACE_DIR
|
||||
git clone ${GIT_REPO_BASE}/kjb-safedb.git
|
||||
cd kjb-safedb
|
||||
git checkout master # 또는 필요한 브랜치
|
||||
|
||||
# ====================================
|
||||
# 6. 최종 폴더 구조 확인
|
||||
# ====================================
|
||||
# kjb-eapim/
|
||||
# ├── eapim-admin/ # Admin 관리 콘솔 (현재 프로젝트)
|
||||
# ├── eapim-online/ # Online 게이트웨이 코어 모듈
|
||||
# │ ├── elink-online-core/
|
||||
# │ ├── elink-online-core-jpa/
|
||||
# │ ├── elink-online-transformer/
|
||||
# │ ├── elink-online-common/
|
||||
# │ ├── elink-online-emsclient/
|
||||
# │ └── elink-online-adapter/
|
||||
# ├── elink-portal-common/ # 포털 공통 컴포넌트 (JPA 엔티티)
|
||||
# └── kjb-safedb/ # SafeDB 암호화 라이브러리
|
||||
|
||||
# ====================================
|
||||
# 7. eapim-admin 프로젝트로 이동 및 디펜던시 다운로드
|
||||
# ====================================
|
||||
cd $WORKSPACE_DIR/eapim-admin
|
||||
|
||||
# Gradle 디펜던시 다운로드
|
||||
$GRADLE_CMD dependencies
|
||||
```
|
||||
|
||||
### Windows CMD에서 초기 세팅
|
||||
|
||||
```cmd
|
||||
REM ====================================
|
||||
REM 1. 변수 설정 (프로젝트 경로 및 브랜치)
|
||||
REM ====================================
|
||||
set WORKSPACE_DIR=C:\eactive\workspaces\kjb-eapim
|
||||
set PROJECT_BRANCH=jenkins_with_weblogic
|
||||
|
||||
REM Git Repository URL 설정
|
||||
REM 기본: ssh://git@192.168.240.178:18081/eapim
|
||||
REM 대체: https://git.eactive.synology.me:8090/kjb-eapim
|
||||
set GIT_REPO_BASE=ssh://git@192.168.240.178:18081/eapim
|
||||
REM set GIT_REPO_BASE=https://git.eactive.synology.me:8090/kjb-eapim
|
||||
|
||||
REM Gradle 명령어 설정
|
||||
REM 일반 환경: gradle
|
||||
REM msi-gf63 환경: C:\eactive\workspaces\shell-scripts\kjb-gradle.sh
|
||||
set GRADLE_CMD=gradle
|
||||
REM set GRADLE_CMD=C:\eactive\workspaces\shell-scripts\kjb-gradle.sh
|
||||
|
||||
REM ====================================
|
||||
REM 2. 작업 디렉토리 생성
|
||||
REM ====================================
|
||||
if not exist "%WORKSPACE_DIR%" mkdir "%WORKSPACE_DIR%"
|
||||
cd /d "%WORKSPACE_DIR%"
|
||||
|
||||
REM ====================================
|
||||
REM 3. eapim-admin 프로젝트 클론
|
||||
REM ====================================
|
||||
git clone %GIT_REPO_BASE%/eapim-admin.git
|
||||
cd eapim-admin
|
||||
|
||||
REM 프로젝트 브랜치로 체크아웃
|
||||
git checkout %PROJECT_BRANCH%
|
||||
|
||||
REM ====================================
|
||||
REM 4. eapim-online 프로젝트 클론 (코어 모듈 포함)
|
||||
REM ====================================
|
||||
cd /d "%WORKSPACE_DIR%"
|
||||
git clone %GIT_REPO_BASE%/eapim-online.git
|
||||
cd eapim-online
|
||||
git checkout %PROJECT_BRANCH%
|
||||
|
||||
REM ====================================
|
||||
REM 5. 의존 모듈 클론
|
||||
REM ====================================
|
||||
cd /d "%WORKSPACE_DIR%"
|
||||
|
||||
REM elink-portal-common 클론
|
||||
git clone %GIT_REPO_BASE%/elink-portal-common.git
|
||||
cd elink-portal-common
|
||||
git checkout master
|
||||
|
||||
REM kjb-safedb 클론
|
||||
cd /d "%WORKSPACE_DIR%"
|
||||
git clone %GIT_REPO_BASE%/kjb-safedb.git
|
||||
cd kjb-safedb
|
||||
git checkout master
|
||||
|
||||
REM ====================================
|
||||
REM 6. 최종 폴더 구조 확인
|
||||
REM ====================================
|
||||
REM kjb-eapim\
|
||||
REM ├── eapim-admin\ # Admin 관리 콘솔 (현재 프로젝트)
|
||||
REM ├── eapim-online\ # Online 게이트웨이 코어 모듈
|
||||
REM │ ├── elink-online-core\
|
||||
REM │ ├── elink-online-core-jpa\
|
||||
REM │ ├── elink-online-transformer\
|
||||
REM │ ├── elink-online-common\
|
||||
REM │ ├── elink-online-emsclient\
|
||||
REM │ └── elink-online-adapter\
|
||||
REM ├── elink-portal-common\ # 포털 공통 컴포넌트 (JPA 엔티티)
|
||||
REM └── kjb-safedb\ # SafeDB 암호화 라이브러리
|
||||
|
||||
REM ====================================
|
||||
REM 7. eapim-admin 프로젝트로 이동 및 디펜던시 다운로드
|
||||
REM ====================================
|
||||
cd /d "%WORKSPACE_DIR%\eapim-admin"
|
||||
|
||||
REM Gradle 디펜던시 다운로드
|
||||
%GRADLE_CMD% dependencies
|
||||
```
|
||||
|
||||
### 환경별 설정 파일
|
||||
|
||||
프로젝트 클론 후 필요한 환경 설정을 수정하세요:
|
||||
|
||||
- `WebContent/WEB-INF/properties/env.D.properties` - 개발 환경
|
||||
- `WebContent/WEB-INF/properties/env.T.properties` - 테스트/스테이징 환경
|
||||
- `WebContent/WEB-INF/properties/env.P.properties` - 운영 환경
|
||||
|
||||
|
||||
### 주의사항
|
||||
|
||||
- **gradlew 사용 금지**: 반드시 시스템에 설치된 gradle을 직접 사용하거나 환경에 맞는 스크립트를 사용하세요
|
||||
- **Gradle 명령어 변수화**: 위의 `GRADLE_CMD` 변수를 설정하면 환경별로 다른 gradle 명령어 사용 가능
|
||||
- **SafeDB**: 암호화 기능이 필요한 경우 `-Dkjb_safedb.mode=fake` 설정 (개발 환경) 또는 실제 SafeDB 라이브러리 설치
|
||||
- **멀티 모듈 구조**: eapim-online의 여러 모듈들을 참조하므로 반드시 eapim-online도 클론해야 합니다
|
||||
|
||||
|
||||
## 빌드 및 실행
|
||||
|
||||
### Gradle 명령어
|
||||
### Gradle 명령어 설정
|
||||
|
||||
프로젝트 루트의 Gradle wrapper(`./gradlew`) 를 사용합니다.
|
||||
사용 환경에 따라 적절한 gradle 명령어를 설정하세요:
|
||||
|
||||
**Git Bash:**
|
||||
```bash
|
||||
# 일반 환경
|
||||
export GRADLE_CMD="gradle"
|
||||
|
||||
# msi-gf63 환경
|
||||
export GRADLE_CMD="/c/eactive/workspaces/shell-scripts/kjb-gradle.sh"
|
||||
```
|
||||
|
||||
**Windows CMD:**
|
||||
```cmd
|
||||
REM 일반 환경
|
||||
set GRADLE_CMD=gradle
|
||||
|
||||
REM msi-gf63 환경
|
||||
set GRADLE_CMD=C:\eactive\workspaces\shell-scripts\kjb-gradle.sh
|
||||
```
|
||||
|
||||
### 빌드 및 실행 명령어
|
||||
|
||||
```bash
|
||||
# 표준 빌드
|
||||
./gradlew build
|
||||
$GRADLE_CMD build
|
||||
|
||||
# Weblogic 배포용 빌드 (테스트 제외)
|
||||
./gradlew build -x test -Pprofile=weblogic
|
||||
$GRADLE_CMD build -x test -Pprofile=weblogic
|
||||
|
||||
# WAR 파일 빌드[ems_stdout.log](../../logs/emsSvr-RinjaeMA/ems_stdout.log)
|
||||
./gradlew war
|
||||
# WAR 파일 빌드
|
||||
$GRADLE_CMD war
|
||||
|
||||
# 클린 빌드
|
||||
./gradlew clean build
|
||||
$GRADLE_CMD clean build
|
||||
|
||||
# 테스트 실행
|
||||
./gradlew test
|
||||
$GRADLE_CMD test
|
||||
|
||||
# 패키징 없이 클래스만 컴파일
|
||||
./gradlew classes
|
||||
$GRADLE_CMD classes
|
||||
|
||||
# QueryDSL Q-classes 및 기타 애노테이션 생성
|
||||
./gradlew compileJava
|
||||
$GRADLE_CMD compileJava
|
||||
|
||||
# 의존성 트리 보기
|
||||
./gradlew dependencies
|
||||
$GRADLE_CMD dependencies
|
||||
|
||||
# 사용 가능한 모든 태스크 목록
|
||||
./gradlew tasks --all
|
||||
$GRADLE_CMD tasks --all
|
||||
```
|
||||
|
||||
**주의**: gradlew 사용 금지 - offline gradle 단독 실행
|
||||
|
||||
## 프로젝트 구조
|
||||
|
||||
### 전체 프로젝트 구조
|
||||
@@ -238,7 +428,7 @@ WebContent/
|
||||
```
|
||||
-Deai.datasource.type=DEV
|
||||
-Dinst.Name=emsSvr11
|
||||
-Deai.tableowner=EMSAPP
|
||||
-Deai.tableowner=EMSADM
|
||||
-Deai.systemmode=D
|
||||
-Dfile.encoding=utf-8
|
||||
-DLOGBACK_LOG_LEVEL=info
|
||||
@@ -248,7 +438,7 @@ WebContent/
|
||||
```
|
||||
-Deai.datasource.type=DEV
|
||||
-Dinst.Name=emsSvr99
|
||||
-Deai.tableowner=EMSAPP
|
||||
-Deai.tableowner=EMSADM
|
||||
-Deai.systemmode=D
|
||||
-Dfile.encoding=utf-8
|
||||
-Dlogin.mode=db
|
||||
@@ -387,82 +577,82 @@ gradle test --tests "com.eactive.eai.rms.*"
|
||||
```sql
|
||||
-- 로컬 개발용 (외부)
|
||||
-- EAI, UMS 주소 미기입시 내부적으론 호출 시도 하지 않고 warning 로그만 남기며 정상 처리
|
||||
--insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.eai_batch.url', '');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.eai.batch.connection_timeout', '5');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.eai.batch.response_timeout', '5');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.eai.batch.timeout', '10');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.eai.batch.charset', 'euc-kr');
|
||||
--insert into EMSADM.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 EMSADM.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 EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.eai.batch.charset', 'euc-kr');
|
||||
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.eai_batch.send_dir', '/Data/eapim/portal/sendmail/snd');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.eai_batch.backup_dir', '/Data/eapim/portal/sendmail/bak');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.eai_batch.error_dir', '/Data/eapim/portal/sendmail/bak');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.eai_batch.retention_date', '60');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.eai_batch.interface_id', 'UIEFF00001UAG');
|
||||
insert into EMSADM.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 EMSADM.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 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.host_url', '');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.api_key', '7cca6d58-e4f7-474d-9edd-525806bc99ff');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.connection_timeout', '5');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.response_timeout', '5');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.timeout', '10');
|
||||
-- insert into EMSADM.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 EMSADM.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 EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.timeout', '10');
|
||||
|
||||
--insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.obp.host_url', '');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.obp.trust_system', 'APIMPT-0002-QVBJTVBULTAwMDI=');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.obp.commission_uri', '/api/billing/findBillingForCondition');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.obp.commission_print_uri', '/api/billing/billing/findBillingForCondition/print');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.obp.query_partnercode_uri', '/api/customer/findCustomerByBusinessManRegistrationNo/%s');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.obp.create_partnercode_uri', '/not-yet-implement/%s');
|
||||
--insert into EMSADM.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 EMSADM.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 EMSADM.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');
|
||||
commit;
|
||||
|
||||
|
||||
-- 은행 개발 서버용
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.eai_batch.url', 'http://172.31.32.111:10230/BATAppWeb/EaiBatCall');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.eai.batch.connection_timeout', '5');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.eai.batch.response_timeout', '5');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.eai.batch.timeout', '10');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.eai.batch.charset', 'euc-kr');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.eai_batch.send_dir', '/Data/eapim/portal/sendmail/snd');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.eai_batch.backup_dir', '/Data/eapim/portal/sendmail/bak');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.eai_batch.error_dir', '/Data/eapim/portal/sendmail/bak');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.eai_batch.retention_date', '60');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.eai_batch.interface_id', 'UIEFF00001UAG');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.host_url', 'http://172.31.35.144:9021');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.api_key', '7cca6d58-e4f7-474d-9edd-525806bc99ff');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.connection_timeout', '5');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.response_timeout', '5');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.timeout', '10');
|
||||
insert into EMSADM.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 EMSADM.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 EMSADM.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 EMSADM.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 EMSADM.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 EMSADM.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 EMSADM.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 EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.timeout', '10');
|
||||
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.obp.host_url', 'http://192.168.246.15:8181');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.obp.trust_system', 'APIMPT-0002-QVBJTVBULTAwMDI=');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.obp.commission_uri', '/api/billing/findBillingForCondition');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.obp.commission_print_uri', '/api/billing/billing/findBillingForCondition/print');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.obp.query_partnercode_uri', '/api/customer/findCustomerByBusinessManRegistrationNo/%s');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.obp.create_partnercode_uri', '/not-yet-implement/%s');
|
||||
insert into EMSADM.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 EMSADM.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 EMSADM.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');
|
||||
commit;
|
||||
|
||||
|
||||
-- 은행 운영 서버용
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.eai_batch.url', 'http://172.21.1.40:10860/BATAppWeb/EaiBatCall');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.eai.batch.connection_timeout', '5');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.eai.batch.response_timeout', '5');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.eai.batch.timeout', '10');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.eai.batch.charset', 'euc-kr');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.eai_batch.send_dir', '/Data/eapim/portal/sendmail/snd');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.eai_batch.backup_dir', '/Data/eapim/portal/sendmail/bak');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.eai_batch.error_dir', '/Data/eapim/portal/sendmail/bak');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.eai_batch.retention_date', '60');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.eai_batch.interface_id', 'UIEFF00001UAG');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.host_url', 'http://172.24.128.66:9021');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.api_key', '7cca6d58-e4f7-474d-9edd-525806bc99ff');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.connection_timeout', '5');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.response_timeout', '5');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.timeout', '10');
|
||||
insert into EMSADM.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 EMSADM.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 EMSADM.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 EMSADM.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 EMSADM.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 EMSADM.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 EMSADM.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 EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.ums.timeout', '10');
|
||||
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.obp.host_url', 'http://192.168.246.15:8181');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.obp.trust_system', 'APIMPT-0002-QVBJTVBULTAwMDI=');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.obp.commission_uri', '/api/billing/findBillingForCondition');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.obp.commission_print_uri', '/api/billing/billing/findBillingForCondition/print');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.obp.query_partnercode_uri', '/api/customer/findCustomerByBusinessManRegistrationNo/%s');
|
||||
insert into EMSAPP.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.obp.create_partnercode_uri', '/not-yet-implement/%s');
|
||||
insert into EMSADM.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 EMSADM.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 EMSADM.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');
|
||||
commit;
|
||||
```
|
||||
## 라이선스
|
||||
|
||||
+2
-2
@@ -7,7 +7,7 @@ include 'elink-online-common'
|
||||
include 'elink-online-emsclient'
|
||||
include 'elink-portal-common'
|
||||
//include 'kjb-safedb'
|
||||
//include 'eapim-admin-djb'
|
||||
include 'eapim-admin-djb'
|
||||
|
||||
project (':elink-online-core-jpa').projectDir = new File(settingsDir, "../eapim-online/elink-online-core-jpa")
|
||||
project (':elink-online-core').projectDir = new File(settingsDir, "../eapim-online/elink-online-core")
|
||||
@@ -16,4 +16,4 @@ project (':elink-online-common').projectDir = new File(settingsDir, "../eapim-on
|
||||
project (':elink-online-emsclient').projectDir = new File(settingsDir, "../eapim-online/elink-online-emsclient")
|
||||
project (':elink-portal-common').projectDir = new File(settingsDir, "../elink-portal-common")
|
||||
//project (':kjb-safedb').projectDir = new File(settingsDir, "../kjb-safedb")
|
||||
//project (':eapim-admin-djb').projectDir = new File(settingsDir, "../eapim-admin-djb")
|
||||
project (':eapim-admin-djb').projectDir = new File(settingsDir, "../eapim-admin-djb")
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user