init
This commit is contained in:
+231
@@ -0,0 +1,231 @@
|
||||
# Package Files #
|
||||
*.war
|
||||
src/main/generated
|
||||
|
||||
# Eclipse #
|
||||
.metadata
|
||||
bin/
|
||||
tmp/
|
||||
*.tmp
|
||||
*.bak
|
||||
*.swp
|
||||
*~.nib
|
||||
local.properties
|
||||
.settings/
|
||||
.loadpath
|
||||
.recommenders
|
||||
|
||||
.classpath
|
||||
.project
|
||||
|
||||
# External tool builders
|
||||
.externalToolBuilders/
|
||||
|
||||
# Locally stored "Eclipse launch configurations"
|
||||
*.launch
|
||||
|
||||
# PyDev specific (Python IDE for Eclipse)
|
||||
*.pydevproject
|
||||
|
||||
# CDT-specific (C/C++ Development Tooling)
|
||||
.cproject
|
||||
|
||||
# CDT- autotools
|
||||
.autotools
|
||||
|
||||
# Java annotation processor (APT)
|
||||
.factorypath
|
||||
|
||||
# PDT-specific (PHP Development Tools)
|
||||
.buildpath
|
||||
|
||||
# sbteclipse plugin
|
||||
.target
|
||||
|
||||
# Tern plugin
|
||||
.tern-project
|
||||
|
||||
# TeXlipse plugin
|
||||
.texlipse
|
||||
|
||||
# STS (Spring Tool Suite)
|
||||
.springBeans
|
||||
|
||||
# Code Recommenders
|
||||
.recommenders/
|
||||
|
||||
# Annotation Processing
|
||||
.apt_generated/
|
||||
.apt_generated_test/
|
||||
|
||||
# Scala IDE specific (Scala & Java development for Eclipse)
|
||||
.cache-main
|
||||
.scala_dependencies
|
||||
.worksheet
|
||||
|
||||
# Uncomment this line if you wish to ignore the project description file.
|
||||
# Typically, this file would be tracked if it contains build/dependency configurations:
|
||||
#.project
|
||||
|
||||
### Intellij ###
|
||||
.idea
|
||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
|
||||
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||
|
||||
# User-specific stuff
|
||||
.idea/**/workspace.xml
|
||||
.idea/**/tasks.xml
|
||||
.idea/**/usage.statistics.xml
|
||||
.idea/**/dictionaries
|
||||
.idea/**/shelf
|
||||
|
||||
# Generated files
|
||||
.idea/**/contentModel.xml
|
||||
|
||||
# Sensitive or high-churn files
|
||||
.idea/**/dataSources/
|
||||
.idea/**/dataSources.ids
|
||||
.idea/**/dataSources.local.xml
|
||||
.idea/**/sqlDataSources.xml
|
||||
.idea/**/dynamic.xml
|
||||
.idea/**/uiDesigner.xml
|
||||
.idea/**/dbnavigator.xml
|
||||
|
||||
# Gradle
|
||||
.idea/**/gradle.xml
|
||||
.idea/**/libraries
|
||||
|
||||
# Gradle and Maven with auto-import
|
||||
# When using Gradle or Maven with auto-import, you should exclude module files,
|
||||
# since they will be recreated, and may cause churn. Uncomment if using
|
||||
# auto-import.
|
||||
# .idea/modules.xml
|
||||
# .idea/*.iml
|
||||
# .idea/modules
|
||||
# *.iml
|
||||
# *.ipr
|
||||
|
||||
# CMake
|
||||
cmake-build-*/
|
||||
|
||||
# Mongo Explorer plugin
|
||||
.idea/**/mongoSettings.xml
|
||||
|
||||
# File-based project format
|
||||
*.iws
|
||||
|
||||
# IntelliJ
|
||||
out/
|
||||
|
||||
# mpeltonen/sbt-idea plugin
|
||||
.idea_modules/
|
||||
|
||||
# JIRA plugin
|
||||
atlassian-ide-plugin.xml
|
||||
|
||||
# Cursive Clojure plugin
|
||||
.idea/replstate.xml
|
||||
|
||||
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||
com_crashlytics_export_strings.xml
|
||||
crashlytics.properties
|
||||
crashlytics-build.properties
|
||||
fabric.properties
|
||||
|
||||
# Editor-based Rest Client
|
||||
.idea/httpRequests
|
||||
|
||||
# Android studio 3.1+ serialized cache file
|
||||
.idea/caches/build_file_checksums.ser
|
||||
|
||||
### Intellij Patch ###
|
||||
# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
|
||||
|
||||
# *.iml
|
||||
# modules.xml
|
||||
# .idea/misc.xml
|
||||
# *.ipr
|
||||
|
||||
# Sonarlint plugin
|
||||
.idea/**/sonarlint/
|
||||
|
||||
# SonarQube Plugin
|
||||
.idea/**/sonarIssues.xml
|
||||
|
||||
# Markdown Navigator plugin
|
||||
.idea/**/markdown-navigator.xml
|
||||
.idea/**/markdown-navigator/
|
||||
|
||||
### Java ###
|
||||
# Compiled class file
|
||||
*.class
|
||||
|
||||
# Log file
|
||||
*.log
|
||||
|
||||
# BlueJ files
|
||||
*.ctxt
|
||||
|
||||
# Mobile Tools for Java (J2ME)
|
||||
.mtj.tmp/
|
||||
|
||||
# Package Files #
|
||||
*.war
|
||||
*.nar
|
||||
*.ear
|
||||
*.zip
|
||||
*.tar.gz
|
||||
*.rar
|
||||
|
||||
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
||||
hs_err_pid*
|
||||
|
||||
### macOS ###
|
||||
# General
|
||||
.DS_Store
|
||||
.AppleDouble
|
||||
.LSOverride
|
||||
|
||||
# Icon must end with two \r
|
||||
Icon
|
||||
|
||||
# Thumbnails
|
||||
._*
|
||||
|
||||
# Files that might appear in the root of a volume
|
||||
.DocumentRevisions-V100
|
||||
.fseventsd
|
||||
.Spotlight-V100
|
||||
.TemporaryItems
|
||||
.Trashes
|
||||
.VolumeIcon.icns
|
||||
.com.apple.timemachine.donotpresent
|
||||
|
||||
# Directories potentially created on remote AFP share
|
||||
.AppleDB
|
||||
.AppleDesktop
|
||||
Network Trash Folder
|
||||
Temporary Items
|
||||
.apdisk
|
||||
|
||||
### Gradle ###
|
||||
.gradle
|
||||
build/
|
||||
|
||||
# Ignore Gradle GUI config
|
||||
gradle-app.setting
|
||||
|
||||
# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
|
||||
!gradle-wrapper.jar
|
||||
|
||||
# Cache of project
|
||||
.gradletasknamecache
|
||||
|
||||
# # Work around https://youtrack.jetbrains.com/issue/IDEA-116898
|
||||
# gradle/wrapper/gradle-wrapper.properties
|
||||
|
||||
### Gradle Patch ###
|
||||
**/build/
|
||||
|
||||
# End of https://www.gitignore.io/api/java,macos,gradle,intellij
|
||||
/EAISIMWeb/
|
||||
@@ -0,0 +1,3 @@
|
||||
# elink-portal-common
|
||||
eLink 개발자 포탈, 관리자 포탈(EMS)의 공통 자원을 관리하는 프로젝트<br>
|
||||
공통 entity, service, repository 등으로 구성한다.
|
||||
+189
@@ -0,0 +1,189 @@
|
||||
plugins {
|
||||
id 'java-library'
|
||||
id 'maven-publish'
|
||||
id 'eclipse-wtp'
|
||||
id 'idea'
|
||||
id 'com.diffplug.eclipse.apt' version '3.41.1'
|
||||
id 'net.researchgate.release' version '3.0.2'
|
||||
}
|
||||
idea {
|
||||
module {
|
||||
downloadSources = true
|
||||
}
|
||||
}
|
||||
|
||||
def nexusUrl = "https://nexus.eactive.synology.me:8090"
|
||||
|
||||
def springVersion = "5.3.27"
|
||||
//def quartzVersion = "2.2.1"
|
||||
def queryDslVersion = "5.0.0"
|
||||
def hibernateVersion = "5.6.15.Final"
|
||||
|
||||
def generatedJavaDir = "$buildDir/generated/java"
|
||||
def generatedTestJavaDir = "$buildDir/generated-test/java"
|
||||
|
||||
repositories {
|
||||
maven {
|
||||
url "${nexusUrl}/repository/maven-public/"
|
||||
allowInsecureProtocol = true
|
||||
}
|
||||
}
|
||||
|
||||
java {
|
||||
toolchain {
|
||||
languageVersion = JavaLanguageVersion.of(8)
|
||||
}
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
java {
|
||||
srcDirs = [
|
||||
'src/main/java',
|
||||
generatedJavaDir
|
||||
]
|
||||
}
|
||||
resources {
|
||||
srcDirs = ['src/main/resources']
|
||||
}
|
||||
}
|
||||
test {
|
||||
java {
|
||||
srcDirs = [
|
||||
'src/test/java',
|
||||
generatedTestJavaDir
|
||||
]
|
||||
}
|
||||
resources {
|
||||
srcDirs = ['src/test/resources']
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
compileJava {
|
||||
options.annotationProcessorGeneratedSourcesDirectory = project.file(generatedJavaDir)
|
||||
}
|
||||
|
||||
compileTestJava {
|
||||
options.annotationProcessorGeneratedSourcesDirectory = project.file(generatedTestJavaDir)
|
||||
}
|
||||
|
||||
tasks.withType(JavaCompile) {
|
||||
options.encoding = 'UTF-8'
|
||||
options.compilerArgs += [
|
||||
'-Amapstruct.verbose=false'
|
||||
]
|
||||
aptOptions {
|
||||
processorArgs = ['querydsl.generatedAnnotationClass': 'com.querydsl.core.annotations.Generated']
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
api "com.querydsl:querydsl-jpa:${queryDslVersion}"
|
||||
api "com.querydsl:querydsl-apt:${queryDslVersion}"
|
||||
api "org.mapstruct:mapstruct:1.5.5.Final", "org.projectlombok:lombok:1.18.28"
|
||||
|
||||
// project > properties > Java Compiler > Annoation Processing
|
||||
annotationProcessor "org.projectlombok:lombok:1.18.28", "org.projectlombok:lombok-mapstruct-binding:0.2.0", "org.mapstruct:mapstruct-processor:1.5.5.Final"
|
||||
annotationProcessor "com.querydsl:querydsl-apt:${queryDslVersion}:jpa", "jakarta.persistence:jakarta.persistence-api:2.2.3", "jakarta.annotation:jakarta.annotation-api:1.3.5"
|
||||
|
||||
// implementation files(rootProject.file('libs/elink-common-data-4.5.jar'));
|
||||
implementation 'com.eactive.elink.common:elink-common-data:4.5.5'
|
||||
|
||||
api 'org.springframework.data:spring-data-jpa:2.7.17'
|
||||
|
||||
api "org.hibernate:hibernate-core:${hibernateVersion}"
|
||||
api "org.hibernate:hibernate-entitymanager:${hibernateVersion}"
|
||||
api "org.hibernate:hibernate-ehcache:${hibernateVersion}"
|
||||
api "org.hibernate:hibernate-validator:5.4.3.Final"
|
||||
api "org.hibernate:hibernate-envers:${hibernateVersion}"
|
||||
|
||||
api 'org.springframework.data:spring-data-envers:2.7.17'
|
||||
|
||||
api "org.springframework:spring-aspects:${springVersion}"
|
||||
|
||||
api group: 'org.apache.commons', name: 'commons-lang3', version: '3.9'
|
||||
|
||||
api "org.springframework:spring-web:${springVersion}"
|
||||
api group: 'commons-io', name: 'commons-io', version: '2.11.0'
|
||||
|
||||
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'
|
||||
testRuntimeOnly 'org.junit.platform:junit-platform-launcher:1.8.2'
|
||||
}
|
||||
|
||||
test {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
|
||||
task sourcesJar(type: Jar, dependsOn: classes) {
|
||||
archiveClassifier = 'sources'
|
||||
from sourceSets.main.allSource
|
||||
}
|
||||
|
||||
publishing {
|
||||
repositories {
|
||||
maven {
|
||||
name = 'eactiveRepository'
|
||||
def releasesRepoUrl = "${nexusUrl}/repository/maven-releases/"
|
||||
def snapshotsRepoUrl = "${nexusUrl}/repository/maven-snapshots/"
|
||||
url = version.endsWith('SNAPSHOT') ? snapshotsRepoUrl : releasesRepoUrl
|
||||
credentials(PasswordCredentials)
|
||||
}
|
||||
}
|
||||
|
||||
publications {
|
||||
mavenJava(MavenPublication) {
|
||||
from components.java
|
||||
pom {
|
||||
description = "eLink Core JPA module"
|
||||
licenses {
|
||||
license {
|
||||
name = "Commercial License"
|
||||
url = "http://eactive.co.kr"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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() {
|
||||
if (!project.file(generatedJavaDir).exists()) {
|
||||
file(generatedJavaDir).mkdirs()
|
||||
}
|
||||
|
||||
if (!project.file(generatedTestJavaDir).exists()) {
|
||||
file(generatedTestJavaDir).mkdirs()
|
||||
}
|
||||
}
|
||||
|
||||
eclipse {
|
||||
synchronizationTasks settingEclipseEncoding, initDirs
|
||||
jdt {
|
||||
apt {
|
||||
genSrcDir = file(generatedJavaDir)
|
||||
genTestSrcDir = file(generatedTestJavaDir)
|
||||
}
|
||||
}
|
||||
project {
|
||||
if (!natures.contains('org.eclipse.buildship.core.gradleprojectnature')) {
|
||||
natures.add('org.eclipse.buildship.core.gradleprojectnature')
|
||||
buildCommand 'org.eclipse.buildship.core.gradleprojectbuilder'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tasks.named('eclipse').configure {
|
||||
finalizedBy 'compileJava', 'compileTestJava'
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
group=com.eactive
|
||||
version=4.5.1-SNAPSHOT
|
||||
Vendored
BIN
Binary file not shown.
+6
@@ -0,0 +1,6 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
networkTimeout=10000
|
||||
@@ -0,0 +1,234 @@
|
||||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Copyright © 2015-2021 the original authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
#
|
||||
# Gradle start up script for POSIX generated by Gradle.
|
||||
#
|
||||
# Important for running:
|
||||
#
|
||||
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
|
||||
# noncompliant, but you have some other compliant shell such as ksh or
|
||||
# bash, then to run this script, type that shell name before the whole
|
||||
# command line, like:
|
||||
#
|
||||
# ksh Gradle
|
||||
#
|
||||
# Busybox and similar reduced shells will NOT work, because this script
|
||||
# requires all of these POSIX shell features:
|
||||
# * functions;
|
||||
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
|
||||
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
|
||||
# * compound commands having a testable exit status, especially «case»;
|
||||
# * various built-in commands including «command», «set», and «ulimit».
|
||||
#
|
||||
# Important for patching:
|
||||
#
|
||||
# (2) This script targets any POSIX shell, so it avoids extensions provided
|
||||
# by Bash, Ksh, etc; in particular arrays are avoided.
|
||||
#
|
||||
# The "traditional" practice of packing multiple parameters into a
|
||||
# space-separated string is a well documented source of bugs and security
|
||||
# problems, so this is (mostly) avoided, by progressively accumulating
|
||||
# options in "$@", and eventually passing that to Java.
|
||||
#
|
||||
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
|
||||
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
|
||||
# see the in-line comments for details.
|
||||
#
|
||||
# There are tweaks for specific operating systems such as AIX, CygWin,
|
||||
# Darwin, MinGW, and NonStop.
|
||||
#
|
||||
# (3) This script is generated from the Groovy template
|
||||
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# within the Gradle project.
|
||||
#
|
||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
|
||||
# Resolve links: $0 may be a link
|
||||
app_path=$0
|
||||
|
||||
# Need this for daisy-chained symlinks.
|
||||
while
|
||||
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
|
||||
[ -h "$app_path" ]
|
||||
do
|
||||
ls=$( ls -ld "$app_path" )
|
||||
link=${ls#*' -> '}
|
||||
case $link in #(
|
||||
/*) app_path=$link ;; #(
|
||||
*) app_path=$APP_HOME$link ;;
|
||||
esac
|
||||
done
|
||||
|
||||
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=${0##*/}
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD=maximum
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
} >&2
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
} >&2
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "$( uname )" in #(
|
||||
CYGWIN* ) cygwin=true ;; #(
|
||||
Darwin* ) darwin=true ;; #(
|
||||
MSYS* | MINGW* ) msys=true ;; #(
|
||||
NONSTOP* ) nonstop=true ;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD=$JAVA_HOME/jre/sh/java
|
||||
else
|
||||
JAVACMD=$JAVA_HOME/bin/java
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD=java
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
case $MAX_FD in #(
|
||||
max*)
|
||||
MAX_FD=$( ulimit -H -n ) ||
|
||||
warn "Could not query maximum file descriptor limit"
|
||||
esac
|
||||
case $MAX_FD in #(
|
||||
'' | soft) :;; #(
|
||||
*)
|
||||
ulimit -n "$MAX_FD" ||
|
||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||
esac
|
||||
fi
|
||||
|
||||
# Collect all arguments for the java command, stacking in reverse order:
|
||||
# * args from the command line
|
||||
# * the main class name
|
||||
# * -classpath
|
||||
# * -D...appname settings
|
||||
# * --module-path (only if needed)
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
|
||||
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if "$cygwin" || "$msys" ; then
|
||||
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
|
||||
|
||||
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
for arg do
|
||||
if
|
||||
case $arg in #(
|
||||
-*) false ;; # don't mess with options #(
|
||||
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
|
||||
[ -e "$t" ] ;; #(
|
||||
*) false ;;
|
||||
esac
|
||||
then
|
||||
arg=$( cygpath --path --ignore --mixed "$arg" )
|
||||
fi
|
||||
# Roll the args list around exactly as many times as the number of
|
||||
# args, so each arg winds up back in the position where it started, but
|
||||
# possibly modified.
|
||||
#
|
||||
# NB: a `for` loop captures its iteration list before it begins, so
|
||||
# changing the positional parameters here affects neither the number of
|
||||
# iterations, nor the values presented in `arg`.
|
||||
shift # remove old arg
|
||||
set -- "$@" "$arg" # push replacement arg
|
||||
done
|
||||
fi
|
||||
|
||||
# Collect all arguments for the java command;
|
||||
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
|
||||
# shell script including quotes and variable substitutions, so put them in
|
||||
# double quotes to make sure that they get re-expanded; and
|
||||
# * put everything else in single quotes, so that it's not re-expanded.
|
||||
|
||||
set -- \
|
||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||
-classpath "$CLASSPATH" \
|
||||
org.gradle.wrapper.GradleWrapperMain \
|
||||
"$@"
|
||||
|
||||
# Use "xargs" to parse quoted args.
|
||||
#
|
||||
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||
#
|
||||
# In Bash we could simply go:
|
||||
#
|
||||
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
|
||||
# set -- "${ARGS[@]}" "$@"
|
||||
#
|
||||
# but POSIX shell has neither arrays nor command substitution, so instead we
|
||||
# post-process each arg (as a line of input to sed) to backslash-escape any
|
||||
# character that might be a shell metacharacter, then use eval to reverse
|
||||
# that process (while maintaining the separation between arguments), and wrap
|
||||
# the whole thing up as a single "set" statement.
|
||||
#
|
||||
# This will of course break if any of these variables contains a newline or
|
||||
# an unmatched quote.
|
||||
#
|
||||
|
||||
eval "set -- $(
|
||||
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
|
||||
xargs -n1 |
|
||||
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
|
||||
tr '\n' ' '
|
||||
)" '"$@"'
|
||||
|
||||
exec "$JAVACMD" "$@"
|
||||
Vendored
+89
@@ -0,0 +1,89 @@
|
||||
@rem
|
||||
@rem Copyright 2015 the original author or authors.
|
||||
@rem
|
||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@rem you may not use this file except in compliance with the License.
|
||||
@rem You may obtain a copy of the License at
|
||||
@rem
|
||||
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||
@rem
|
||||
@rem Unless required by applicable law or agreed to in writing, software
|
||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.eactive.apim.portal.agreements.entity;
|
||||
|
||||
// 약관, 개인정보처리방침, 개인정보수집동의서 유형을 정의하는 Enum
|
||||
public enum AgreementType {
|
||||
TERMS_OF_USE("TERMS_OF_USE", "이용약관"),
|
||||
PRIVACY_POLICY("PRIVACY_POLICY", "개인정보처리방침"),
|
||||
PRIVACY_COLLECT_IND("PRIVACY_COLLECT_IND", "개인용 개인정보수집동의서"),
|
||||
PRIVACY_COLLECT_ORG("PRIVACY_COLLECT_ORG", "법인용 개인정보수집동의서");
|
||||
|
||||
private final String code;
|
||||
private final String description;
|
||||
|
||||
AgreementType(String code, String description) {
|
||||
this.code = code;
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public static AgreementType fromCode(String code) {
|
||||
for (AgreementType type : AgreementType.values()) {
|
||||
if (type.getCode().equals(code)) {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("Unknown agreement type code: " + code);
|
||||
}
|
||||
|
||||
public boolean isPrivacyCollect() {
|
||||
return this == PRIVACY_COLLECT_IND || this == PRIVACY_COLLECT_ORG;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package com.eactive.apim.portal.agreements.entity;
|
||||
|
||||
import com.eactive.apim.portal.common.entity.Auditable;
|
||||
import com.eactive.eai.data.converter.LocalDateTimeToStringConverter14;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.hibernate.annotations.Comment;
|
||||
|
||||
import javax.persistence.*;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* PORTAL_TERMS 이용약관 관리
|
||||
*
|
||||
* @author Sungpil Hyun
|
||||
* @version 1.0
|
||||
*/
|
||||
@Data
|
||||
@Entity
|
||||
@Table(name = "ptl_terms")
|
||||
@IdClass(AgreementsId.class)
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class Agreements extends Auditable {
|
||||
|
||||
public Agreements() {
|
||||
}
|
||||
|
||||
public Agreements(AgreementType agreementsType) {
|
||||
this.agreementsType = agreementsType;
|
||||
this.status = AgreementsStatus.TEMP;
|
||||
}
|
||||
|
||||
// 약관 유형
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Id
|
||||
@Column(name = "terms_type", nullable = false)
|
||||
private AgreementType agreementsType;
|
||||
|
||||
@Id
|
||||
@Column(name = "terms_version", nullable = false)
|
||||
private int revision;
|
||||
|
||||
@Column(name = "term_code", length = 255)
|
||||
@Comment("약관 코드")
|
||||
private String termCode;
|
||||
|
||||
@Column(name = "terms_name", nullable = false)
|
||||
private String name;
|
||||
|
||||
@Column(name = "terms_detail", nullable = false)
|
||||
private String contents;
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "terms_status", nullable = false)
|
||||
private AgreementsStatus status;
|
||||
|
||||
@Column(name = "publish_date")
|
||||
@Convert(converter = LocalDateTimeToStringConverter14.class)
|
||||
private LocalDateTime publishedOn;
|
||||
|
||||
@Column(name = "SCHEDULED_ON")
|
||||
@Convert(converter = LocalDateTimeToStringConverter14.class)
|
||||
private LocalDateTime scheduledOn;
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.eactive.apim.portal.agreements.entity;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
public class AgreementsId implements Serializable {
|
||||
|
||||
public AgreementsId() {
|
||||
}
|
||||
|
||||
public AgreementsId(AgreementType agreementType, int revision) {
|
||||
this.agreementsType = agreementType;
|
||||
this.revision = revision;
|
||||
}
|
||||
|
||||
private AgreementType agreementsType;
|
||||
|
||||
private int revision;
|
||||
|
||||
public AgreementType getAgreementsType() {
|
||||
return agreementsType;
|
||||
}
|
||||
|
||||
public void setAgreementsType(AgreementType agreementsType) {
|
||||
this.agreementsType = agreementsType;
|
||||
}
|
||||
|
||||
public int getRevision() {
|
||||
return revision;
|
||||
}
|
||||
|
||||
public void setRevision(int revision) {
|
||||
this.revision = revision;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.eactive.apim.portal.agreements.entity;
|
||||
|
||||
public enum AgreementsStatus {
|
||||
TEMP, //신규
|
||||
DRAFT, //임시저장
|
||||
PUBLISHED, //발행
|
||||
ARCHIVED, //보관
|
||||
PENDING_REVIEW, //검토 대기
|
||||
SCHEDULED //발행 예정
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.eactive.apim.portal.agreements.repository;
|
||||
|
||||
import com.eactive.apim.portal.agreements.entity.AgreementType;
|
||||
import com.eactive.apim.portal.agreements.entity.Agreements;
|
||||
import com.eactive.apim.portal.agreements.entity.AgreementsId;
|
||||
import com.eactive.apim.portal.agreements.entity.AgreementsStatus;
|
||||
import com.eactive.eai.rms.data.EMSDataSource;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
@Repository
|
||||
@EMSDataSource
|
||||
public interface AgreementsRepository extends JpaRepository<Agreements, AgreementsId>, JpaSpecificationExecutor<Agreements> {
|
||||
|
||||
|
||||
List<Agreements> findAllByAgreementsTypeOrderByRevisionDesc(AgreementType agreementsType);
|
||||
|
||||
List<Agreements> findAllByAgreementsTypeAndStatus(AgreementType agreementsType, AgreementsStatus status);
|
||||
|
||||
List<Agreements> findAllByAgreementsTypeOrderByPublishedOnDesc(AgreementType agreementsType);
|
||||
|
||||
@Query("SELECT a FROM Agreements a " + "WHERE a.agreementsType = :type " +
|
||||
"AND a.publishedOn <= :date " + "ORDER BY a.publishedOn DESC, a.revision DESC")
|
||||
List<Agreements> findLastesBeforeDate(@Param("type") AgreementType agreementsType,
|
||||
@Param("date") LocalDateTime date);
|
||||
|
||||
@Query("SELECT a FROM Agreements a " + "WHERE a.agreementsType = :type " +
|
||||
"AND a.publishedOn > :date " + "ORDER BY a.publishedOn ASC, a.revision ASC")
|
||||
List<Agreements> findEarliesAfterDate(@Param("type") AgreementType agreementsType,
|
||||
@Param("date") LocalDateTime date);
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
package com.eactive.apim.portal.agreements.service;
|
||||
|
||||
import com.eactive.apim.portal.agreements.entity.AgreementType;
|
||||
import com.eactive.apim.portal.agreements.entity.Agreements;
|
||||
import com.eactive.apim.portal.agreements.entity.AgreementsId;
|
||||
import com.eactive.apim.portal.agreements.entity.AgreementsStatus;
|
||||
import com.eactive.apim.portal.agreements.repository.AgreementsRepository;
|
||||
import com.eactive.apim.portal.common.exception.NotFoundException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
@Service
|
||||
@Transactional
|
||||
public class AgreementsService {
|
||||
|
||||
private static final String NOT_FOUND_MESSAGE = "해당 버전의 약관이 존재하지 않습니다.";
|
||||
private static final String CANNOT_MODIFY_MESSAGE = "임시저장 상태가 아닌 약관은 수정할 수 없습니다.";
|
||||
private static final String CANNOT_PUBLISH_MESSAGE = "발행할 수 없는 상태입니다.";
|
||||
private static final String CANNOT_DELETE_MESSAGE = "임시저장 상태가 아닌 약관은 삭제할 수 없습니다.";
|
||||
private static final String NOT_FOUND_BY_DATE_MESSAGE = "해당 시행일자의 약관이 존재하지 않습니다.";
|
||||
|
||||
private final AgreementsRepository agreementsRepository;
|
||||
|
||||
@Autowired
|
||||
public AgreementsService(AgreementsRepository agreementsRepository) {
|
||||
this.agreementsRepository = agreementsRepository;
|
||||
}
|
||||
|
||||
public Agreements save(Agreements agreements) {
|
||||
if (agreements.getRevision() == 0) {
|
||||
agreements.setRevision(getNextRevision(agreements.getAgreementsType()));
|
||||
agreements.setStatus(AgreementsStatus.DRAFT);
|
||||
agreements.setTermCode(agreements.getAgreementsType().getCode() + agreements.getRevision());
|
||||
} else {
|
||||
AgreementsId id = new AgreementsId(agreements.getAgreementsType(), agreements.getRevision());
|
||||
Agreements existing = agreementsRepository.findById(id)
|
||||
.orElseThrow(() -> new NotFoundException(NOT_FOUND_MESSAGE));
|
||||
if (!AgreementsStatus.DRAFT.equals(existing.getStatus())) {
|
||||
throw new IllegalStateException(CANNOT_MODIFY_MESSAGE);
|
||||
}
|
||||
existing.setName(agreements.getName());
|
||||
existing.setContents(agreements.getContents());
|
||||
agreements = existing;
|
||||
}
|
||||
return agreementsRepository.save(agreements);
|
||||
}
|
||||
|
||||
|
||||
private int getNextRevision(AgreementType agreementType) {
|
||||
return agreementsRepository.findAllByAgreementsTypeOrderByRevisionDesc(agreementType)
|
||||
.stream()
|
||||
.findFirst()
|
||||
.map(a -> a.getRevision() + 1)
|
||||
.orElse(1);
|
||||
}
|
||||
|
||||
public void publish(AgreementType agreementType, int revision) {
|
||||
AgreementsId id = new AgreementsId(agreementType, revision);
|
||||
Agreements current = agreementsRepository.findById(id)
|
||||
.orElseThrow(() -> new NotFoundException(NOT_FOUND_MESSAGE));
|
||||
|
||||
agreementsRepository.findAllByAgreementsTypeAndStatus(agreementType, AgreementsStatus.PUBLISHED)
|
||||
.forEach(agreements -> {
|
||||
agreements.setStatus(AgreementsStatus.ARCHIVED);
|
||||
agreementsRepository.save(agreements);
|
||||
});
|
||||
|
||||
if (current.getStatus().equals(AgreementsStatus.DRAFT) ||
|
||||
current.getStatus().equals(AgreementsStatus.PENDING_REVIEW) ||
|
||||
current.getStatus().equals(AgreementsStatus.SCHEDULED)) {
|
||||
current.setStatus(AgreementsStatus.PUBLISHED);
|
||||
current.setPublishedOn(LocalDateTime.now());
|
||||
agreementsRepository.save(current);
|
||||
} else {
|
||||
throw new IllegalStateException(CANNOT_PUBLISH_MESSAGE);
|
||||
}
|
||||
}
|
||||
|
||||
public Optional<Agreements> findLastesBeforeDate(AgreementType agreementType, LocalDateTime date) {
|
||||
List<Agreements> agreements = agreementsRepository.findLastesBeforeDate(agreementType, date);
|
||||
return agreements.isEmpty() ? Optional.empty() : Optional.of(agreements.get(0));
|
||||
}
|
||||
|
||||
public Optional<Agreements> findEarliesAfterDate(String agreementTypeCode, LocalDateTime date) {
|
||||
AgreementType agreementType = AgreementType.fromCode(agreementTypeCode);
|
||||
List<Agreements> agreements = agreementsRepository.findEarliesAfterDate(agreementType, date);
|
||||
return agreements.isEmpty() ? Optional.empty() : Optional.of(agreements.get(0));
|
||||
}
|
||||
|
||||
public void delete(String agreementType, Integer revision) {
|
||||
AgreementsId id = new AgreementsId(AgreementType.fromCode(agreementType), revision);
|
||||
Agreements agreements = agreementsRepository.findById(id)
|
||||
.orElseThrow(() -> new NotFoundException(NOT_FOUND_MESSAGE));
|
||||
if (agreements.getStatus().equals(AgreementsStatus.DRAFT)) {
|
||||
agreementsRepository.delete(agreements);
|
||||
} else {
|
||||
throw new IllegalStateException(CANNOT_DELETE_MESSAGE);
|
||||
}
|
||||
}
|
||||
|
||||
public Agreements findById(String agreementType, int revision) {
|
||||
AgreementsId id = new AgreementsId(AgreementType.fromCode(agreementType), revision);
|
||||
return agreementsRepository.findById(id).orElseThrow(() -> new NotFoundException(NOT_FOUND_MESSAGE));
|
||||
}
|
||||
|
||||
public List<Agreements> findAllAgreementsSortPublishedOn(AgreementType agreementType) {
|
||||
List<Agreements> agreements = agreementsRepository.findAllByAgreementsTypeOrderByPublishedOnDesc(agreementType);
|
||||
if(agreements.isEmpty()) {
|
||||
throw new IllegalStateException(NOT_FOUND_BY_DATE_MESSAGE);
|
||||
}
|
||||
return agreements;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
package com.eactive.apim.portal.apispec.entity;
|
||||
|
||||
import com.eactive.eai.data.converter.LocalDateTimeToStringConverter17;
|
||||
import com.eactive.eai.data.entity.AbstractEntity;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NonNull;
|
||||
import org.springframework.data.annotation.CreatedBy;
|
||||
import org.springframework.data.annotation.CreatedDate;
|
||||
import org.springframework.data.annotation.LastModifiedBy;
|
||||
import org.springframework.data.annotation.LastModifiedDate;
|
||||
|
||||
import javax.persistence.*;
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
@Entity
|
||||
@Table(name = "ptl_api_spec_info")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
public class ApiSpecInfo extends AbstractEntity<String> implements Serializable, com.eactive.eai.data.Data {
|
||||
|
||||
@Id
|
||||
@Column(length = 30, name = "api_id")
|
||||
private String apiId;
|
||||
|
||||
@Column(name = "api_name")
|
||||
private String apiName;
|
||||
|
||||
@Column(name = "api_simple_description")
|
||||
private String apiSimpleDescription; //간단 설명
|
||||
|
||||
@Column(name = "api_url")
|
||||
private String apiUrl;
|
||||
|
||||
@Column(name = "api_method")
|
||||
private String apiMethod;
|
||||
|
||||
@Column(name = "api_content_type")
|
||||
private String apiContentType;
|
||||
|
||||
@Lob
|
||||
@Column(name = "api_request_spec", columnDefinition = "TEXT")
|
||||
private String apiRequestSpec;
|
||||
|
||||
@Lob
|
||||
@Column(name = "api_response_spec", columnDefinition = "TEXT")
|
||||
private String apiResponseSpec;
|
||||
|
||||
@Lob
|
||||
@Column(name = "sample_request", columnDefinition = "TEXT")
|
||||
private String sampleRequest; //요청 메시지 예제
|
||||
|
||||
@Lob
|
||||
@Column(name = "sample_response", columnDefinition = "TEXT")
|
||||
private String sampleResponse; //응답 메시지 예제
|
||||
|
||||
@Column(name = "response_type")
|
||||
private String responseType; //sample, mock
|
||||
|
||||
@Column(name = "mock_url")
|
||||
private String mockUrl;
|
||||
|
||||
@Column(name = "file_id")
|
||||
private String fileId;
|
||||
|
||||
@Lob
|
||||
@Column(name = "description", columnDefinition = "TEXT")
|
||||
private String description; //설명
|
||||
|
||||
@Lob
|
||||
@Column(name = "testbed_spec", columnDefinition = "TEXT")
|
||||
private String testbedSpec;
|
||||
|
||||
@Column(name = "display_yn", length = 1)
|
||||
private String displayYn;
|
||||
|
||||
@Column(name = "display_org")
|
||||
private String displayOrg;
|
||||
|
||||
@Column(name = "display_role_code")
|
||||
private String displayRoleCode;
|
||||
|
||||
@CreatedBy
|
||||
@Column(name = "created_by", length = 50)
|
||||
private String createdBy;
|
||||
|
||||
@CreatedDate
|
||||
@Column(length = 17, name = "created_date")
|
||||
@Convert(converter = LocalDateTimeToStringConverter17.class)
|
||||
private LocalDateTime createdDate;
|
||||
|
||||
@LastModifiedBy
|
||||
@Column(name = "last_modified_by", length = 50)
|
||||
private String lastModifiedBy;
|
||||
|
||||
@LastModifiedDate
|
||||
@Column(length = 17, name = "last_modified_date")
|
||||
@Convert(converter = LocalDateTimeToStringConverter17.class)
|
||||
private LocalDateTime lastModifiedDate;
|
||||
|
||||
@Override
|
||||
public @NonNull String getId() {
|
||||
return apiId;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.eactive.apim.portal.apispec.repository;
|
||||
|
||||
import com.eactive.apim.portal.apispec.entity.ApiSpecInfo;
|
||||
import com.eactive.eai.rms.data.EMSDataSource;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
|
||||
@EMSDataSource
|
||||
public interface ApiSpecInfoRepository extends JpaRepository<ApiSpecInfo, String>, JpaSpecificationExecutor<ApiSpecInfo> {
|
||||
|
||||
List<ApiSpecInfo> findAllByDisplayYn(String displayYn);
|
||||
|
||||
List<ApiSpecInfo> findAllByDisplayYnAndApiIdIn(String displayYn, List<String> apiIds);
|
||||
|
||||
@Query("SELECT a FROM ApiSpecInfo a WHERE a.displayYn = 'Y' AND (" +
|
||||
"LOWER(a.apiName) LIKE LOWER(CONCAT('%', :keyword, '%')) OR " +
|
||||
"LOWER(a.apiSimpleDescription) LIKE LOWER(CONCAT('%', :keyword, '%')))")
|
||||
List<ApiSpecInfo> searchByKeyword(@Param("keyword") String keyword);
|
||||
|
||||
|
||||
Optional<ApiSpecInfo> findApiSpecInfoByApiUrlAndApiMethodAndDisplayYn(String apiUrl, String apiMethod, String displayYn);
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package com.eactive.apim.portal.apispec.service;
|
||||
|
||||
import com.eactive.apim.portal.apispec.entity.ApiSpecInfo;
|
||||
import com.eactive.apim.portal.apispec.repository.ApiSpecInfoRepository;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
@Transactional
|
||||
@RequiredArgsConstructor
|
||||
public class ApiSpecInfoService {
|
||||
|
||||
private final ApiSpecInfoRepository apiSpecInfoRepository;
|
||||
|
||||
public Optional<ApiSpecInfo> findById(String id) {
|
||||
return apiSpecInfoRepository.findById(id);
|
||||
}
|
||||
|
||||
public Optional<ApiSpecInfo> findByUrlAndMethod(String url, String method) {
|
||||
return apiSpecInfoRepository.findApiSpecInfoByApiUrlAndApiMethodAndDisplayYn(url, method, "Y");
|
||||
}
|
||||
|
||||
//2. create
|
||||
public ApiSpecInfo create(ApiSpecInfo apiSpecInfo) {
|
||||
return apiSpecInfoRepository.save(apiSpecInfo);
|
||||
}
|
||||
|
||||
public ApiSpecInfo updateById(String id, ApiSpecInfo updatedApiSpecInfo) {
|
||||
Optional<ApiSpecInfo> existingApiSpecInfo = apiSpecInfoRepository.findById(id);
|
||||
if (existingApiSpecInfo.isPresent()) {
|
||||
ApiSpecInfo apiSpecInfoToUpdate = existingApiSpecInfo.get();
|
||||
apiSpecInfoToUpdate.setApiName(updatedApiSpecInfo.getApiName());
|
||||
apiSpecInfoToUpdate.setApiSimpleDescription(updatedApiSpecInfo.getApiSimpleDescription());
|
||||
apiSpecInfoToUpdate.setApiUrl(updatedApiSpecInfo.getApiUrl());
|
||||
apiSpecInfoToUpdate.setApiMethod(updatedApiSpecInfo.getApiMethod());
|
||||
apiSpecInfoToUpdate.setApiContentType(updatedApiSpecInfo.getApiContentType());
|
||||
apiSpecInfoToUpdate.setApiRequestSpec(updatedApiSpecInfo.getApiRequestSpec());
|
||||
apiSpecInfoToUpdate.setApiResponseSpec(updatedApiSpecInfo.getApiResponseSpec());
|
||||
apiSpecInfoToUpdate.setSampleRequest(updatedApiSpecInfo.getSampleRequest());
|
||||
apiSpecInfoToUpdate.setSampleResponse(updatedApiSpecInfo.getSampleResponse());
|
||||
apiSpecInfoToUpdate.setResponseType(updatedApiSpecInfo.getResponseType());
|
||||
apiSpecInfoToUpdate.setMockUrl(updatedApiSpecInfo.getMockUrl());
|
||||
apiSpecInfoToUpdate.setFileId(updatedApiSpecInfo.getFileId());
|
||||
apiSpecInfoToUpdate.setDescription(updatedApiSpecInfo.getDescription());
|
||||
apiSpecInfoToUpdate.setTestbedSpec(updatedApiSpecInfo.getTestbedSpec());
|
||||
apiSpecInfoToUpdate.setDisplayYn(updatedApiSpecInfo.getDisplayYn());
|
||||
apiSpecInfoToUpdate.setDisplayRoleCode(updatedApiSpecInfo.getDisplayRoleCode());
|
||||
apiSpecInfoToUpdate.setDisplayOrg(updatedApiSpecInfo.getDisplayOrg());
|
||||
return apiSpecInfoRepository.save(apiSpecInfoToUpdate);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void deleteById(String id) {
|
||||
apiSpecInfoRepository.deleteById(id);
|
||||
}
|
||||
|
||||
public List<ApiSpecInfo> findPublicApis() {
|
||||
return apiSpecInfoRepository.findAllByDisplayYn("Y");
|
||||
}
|
||||
|
||||
public List<ApiSpecInfo> findApiSpecsByApiIds(List<String> apiIds) {
|
||||
return apiSpecInfoRepository.findAllByDisplayYnAndApiIdIn("Y", apiIds);
|
||||
}
|
||||
|
||||
public List<ApiSpecInfo> searchApis(String keyword) {
|
||||
if (keyword == null) keyword = "";
|
||||
return apiSpecInfoRepository.searchByKeyword(keyword);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
package com.eactive.apim.portal.app.entity;
|
||||
|
||||
import com.eactive.apim.portal.apispec.entity.ApiSpecInfo;
|
||||
import com.eactive.eai.data.converter.LocalDateTimeToStringConverter14;
|
||||
import com.eactive.eai.data.entity.AbstractEntity;
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Convert;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.JoinTable;
|
||||
import javax.persistence.ManyToMany;
|
||||
import javax.persistence.Table;
|
||||
import lombok.Data;
|
||||
import lombok.NonNull;
|
||||
import org.hibernate.annotations.Comment;
|
||||
import org.hibernate.annotations.LazyCollection;
|
||||
import org.hibernate.annotations.LazyCollectionOption;
|
||||
import org.springframework.data.annotation.LastModifiedBy;
|
||||
import org.springframework.data.annotation.LastModifiedDate;
|
||||
|
||||
@Entity
|
||||
@Data
|
||||
@Table(name = "ptl_credential")
|
||||
@org.hibernate.annotations.Table(appliesTo = "ptl_credential", comment = "환경 인증 정보")
|
||||
public class Credential extends AbstractEntity<String> implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Id
|
||||
@Column(unique = true, nullable = false, length = 256)
|
||||
@Comment("client id")
|
||||
private String clientid;
|
||||
|
||||
@Column(length = 11)
|
||||
@Comment("Access Token 유효 기간(초)")
|
||||
private String accesstokenvalidityseconds;
|
||||
|
||||
@Column(length = 256)
|
||||
@Comment("허용된 IP")
|
||||
private String allowedips;
|
||||
|
||||
@Column(length = 256)
|
||||
@Comment("권한")
|
||||
private String authorities;
|
||||
|
||||
@Column(length = 256)
|
||||
@Comment("자동 승인 스코프")
|
||||
private String autoapprove;
|
||||
|
||||
@Column(length = 40)
|
||||
@Comment("client명")
|
||||
private String clientname;
|
||||
|
||||
@Column(length = 512)
|
||||
@Comment("Client 비밀번호")
|
||||
private String clientsecret;
|
||||
|
||||
@Column
|
||||
@Comment("서버")
|
||||
private String server;
|
||||
|
||||
@Column(length = 256)
|
||||
@Comment("권한타입")
|
||||
private String granttypes;
|
||||
|
||||
@Column(length = 8)
|
||||
@Comment("수정인")
|
||||
@LastModifiedBy
|
||||
private String modifiedby;
|
||||
|
||||
@Column(length = 14)
|
||||
@Comment("수정일")
|
||||
@Convert(converter = LocalDateTimeToStringConverter14.class)
|
||||
@LastModifiedDate
|
||||
private LocalDateTime modifiedon;
|
||||
|
||||
// @Column(length = 256, name="uri")
|
||||
@Column(length = 256)
|
||||
@Comment("Redirect URI")
|
||||
private String redirecturi;
|
||||
|
||||
@Column(length = 11)
|
||||
@Comment("Refresh Token 유효 기간(초)")
|
||||
private String refreshtokenvalidityseconds;
|
||||
|
||||
@Column(length = 4000)
|
||||
@Comment("리소스 id")
|
||||
private String resourceids;
|
||||
|
||||
@Column(length = 1024)
|
||||
@Comment("scope")
|
||||
private String scope;
|
||||
|
||||
@Column(length = 4000)
|
||||
@Comment("암호key")
|
||||
private String securitykey;
|
||||
|
||||
@Column(length = 38)
|
||||
@Comment("기관ID")
|
||||
private String orgid;
|
||||
|
||||
@Comment("APP 상태")
|
||||
private String appstatus; //0: 차단, 1: 정상
|
||||
|
||||
@Column(length = 255)
|
||||
@Comment("기관이름")
|
||||
private String orgname;
|
||||
|
||||
@Comment("일별 토큰 제한 수 (0:무제한)")
|
||||
private int dailytokenlimit;
|
||||
|
||||
@Override
|
||||
public @NonNull String getId() {
|
||||
return clientid;
|
||||
}
|
||||
|
||||
@ManyToMany
|
||||
@LazyCollection(LazyCollectionOption.FALSE)
|
||||
@JoinTable(name = "ptl_credential_api", joinColumns = @JoinColumn(name = "client_id"), inverseJoinColumns = @JoinColumn(name = "eaisvcname"))
|
||||
private List<ApiSpecInfo> apiList = new ArrayList<>();
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
package com.eactive.apim.portal.app.entity;
|
||||
|
||||
import com.eactive.apim.portal.apispec.entity.ApiSpecInfo;
|
||||
import com.eactive.eai.data.converter.LocalDateTimeToStringConverter14;
|
||||
import com.eactive.eai.data.entity.AbstractEntity;
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Convert;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.JoinTable;
|
||||
import javax.persistence.ManyToMany;
|
||||
import javax.persistence.Table;
|
||||
import lombok.Data;
|
||||
import lombok.NonNull;
|
||||
import org.hibernate.annotations.Comment;
|
||||
import org.hibernate.annotations.LazyCollection;
|
||||
import org.hibernate.annotations.LazyCollectionOption;
|
||||
import org.springframework.data.annotation.LastModifiedBy;
|
||||
import org.springframework.data.annotation.LastModifiedDate;
|
||||
|
||||
@Entity
|
||||
@Data
|
||||
@Table(name = "ptl_prod_client")
|
||||
@org.hibernate.annotations.Table(appliesTo = "ptl_prod_client", comment = "사용자 정보")
|
||||
public class ProdClient extends AbstractEntity<String> implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Id
|
||||
@Column(unique = true, nullable = false, length = 256)
|
||||
@Comment("client id")
|
||||
private String clientid;
|
||||
|
||||
@Column(length = 11)
|
||||
@Comment("Access Token 유효 기간(초)")
|
||||
private String accesstokenvalidityseconds;
|
||||
|
||||
@Column(length = 256)
|
||||
@Comment("허용된 IP")
|
||||
private String allowedips;
|
||||
|
||||
@Column(length = 256)
|
||||
@Comment("권한")
|
||||
private String authorities;
|
||||
|
||||
@Column(length = 256)
|
||||
@Comment("자동 승인 스코프")
|
||||
private String autoapprove;
|
||||
|
||||
@Column(length = 40)
|
||||
@Comment("client명")
|
||||
private String clientname;
|
||||
|
||||
@Column(length = 512)
|
||||
@Comment("Client 비밀번호")
|
||||
private String clientsecret;
|
||||
|
||||
@Column(length = 256)
|
||||
@Comment("권한타입")
|
||||
private String granttypes;
|
||||
|
||||
@Column(length = 8)
|
||||
@Comment("수정인")
|
||||
@LastModifiedBy
|
||||
private String modifiedby;
|
||||
|
||||
@Column(length = 14)
|
||||
@Comment("수정일")
|
||||
@Convert(converter = LocalDateTimeToStringConverter14.class)
|
||||
@LastModifiedDate
|
||||
private LocalDateTime modifiedon;
|
||||
|
||||
@Column(length = 256)
|
||||
@Comment("Redirect URI")
|
||||
private String redirecturi;
|
||||
|
||||
@Column(length = 11)
|
||||
@Comment("Refresh Token 유효 기간(초)")
|
||||
private String refreshtokenvalidityseconds;
|
||||
|
||||
@Column(length = 4000)
|
||||
@Comment("리소스 id")
|
||||
private String resourceids;
|
||||
|
||||
@Column(length = 1024)
|
||||
@Comment("scope")
|
||||
private String scope;
|
||||
|
||||
@Column(length = 4000)
|
||||
@Comment("암호key")
|
||||
private String securitykey;
|
||||
|
||||
@Column(length = 38)
|
||||
@Comment("기관ID")
|
||||
private String orgid;
|
||||
|
||||
@Comment("APP 상태")
|
||||
private String appstatus; //0: 차단, 1: 정상
|
||||
|
||||
@Column(length = 255)
|
||||
@Comment("기관이름")
|
||||
private String orgname;
|
||||
|
||||
@Comment("일별 토큰 제한 수 (0:무제한)")
|
||||
private int dailytokenlimit;
|
||||
|
||||
@Override
|
||||
public @NonNull String getId() {
|
||||
return clientid;
|
||||
}
|
||||
|
||||
@ManyToMany
|
||||
@LazyCollection(LazyCollectionOption.FALSE)
|
||||
@JoinTable(name = "ptl_prod_client_api", joinColumns = @JoinColumn(name = "client_id"), inverseJoinColumns = @JoinColumn(name = "eaisvcname"))
|
||||
private List<ApiSpecInfo> apiList = new ArrayList<>();
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package com.eactive.apim.portal.app.event;
|
||||
|
||||
import com.eactive.apim.portal.template.service.MessageEventHandler;
|
||||
import com.eactive.apim.portal.template.service.MessageRecipient;
|
||||
import com.eactive.apim.portal.template.service.MessageSendEvent;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@Component
|
||||
public class ApikeyRegisterApprovedEvent implements MessageEventHandler {
|
||||
public static final String KEY = "apikey_register_approved";
|
||||
|
||||
@Override
|
||||
public boolean allowAdditionalRecipients() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageSendEvent createEvent(Object source, MessageRecipient recipient, Map<String, Object> params) {
|
||||
Map<String, String> requestParams = new HashMap<>();
|
||||
requestParams.put("apikeyName", (String) params.get("apiKeyName"));
|
||||
return new MessageSendEvent(source, KEY, recipient, requestParams);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getKey() {
|
||||
return KEY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayName() {
|
||||
return "API Key 사용 승인 완료";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "<div>사용 가능 변수: </div><br/>"
|
||||
+ "<div> - userName 수신자 이름 </div><br/>"
|
||||
+ "<div> - userId 수신자 ID (이메일) </div><br/>"
|
||||
+ "<div> - apiKeyName API Key 이름 </div><br/>";
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package com.eactive.apim.portal.app.event;
|
||||
|
||||
import com.eactive.apim.portal.template.service.MessageEventHandler;
|
||||
import com.eactive.apim.portal.template.service.MessageRecipient;
|
||||
import com.eactive.apim.portal.template.service.MessageSendEvent;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@Component
|
||||
public class ApikeyRegisterDeniedEvent implements MessageEventHandler {
|
||||
public static final String KEY = "apikey_register_denied";
|
||||
|
||||
@Override
|
||||
public boolean allowAdditionalRecipients() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageSendEvent createEvent(Object source, MessageRecipient recipient, Map<String, Object> params) {
|
||||
Map<String, String> requestParams = new HashMap<>();
|
||||
requestParams.put("apikeyName", (String) params.get("apiKeyName"));
|
||||
return new MessageSendEvent(source, KEY, recipient, requestParams);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getKey() {
|
||||
return KEY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayName() {
|
||||
return "API Key 사용 거절";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "<div>사용 가능 변수: </div><br/>"
|
||||
+ "<div> - userName 수신자 이름 </div><br/>"
|
||||
+ "<div> - userId 수신자 ID (이메일) </div><br/>"
|
||||
+ "<div> - apiKeyName API Key 이름 </div><br/>";
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package com.eactive.apim.portal.app.event;
|
||||
|
||||
import com.eactive.apim.portal.template.service.MessageEventHandler;
|
||||
import com.eactive.apim.portal.template.service.MessageRecipient;
|
||||
import com.eactive.apim.portal.template.service.MessageSendEvent;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@Component
|
||||
public class ApikeyRegisterRequestEvent implements MessageEventHandler {
|
||||
public static final String KEY = "apikey_register_request";
|
||||
|
||||
@Override
|
||||
public boolean allowAdditionalRecipients() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageSendEvent createEvent(Object source, MessageRecipient recipient, Map<String, Object> params) {
|
||||
Map<String, String> requestParams = new HashMap<>();
|
||||
requestParams.put("apikeyName", (String) params.get("apiKeyName"));
|
||||
requestParams.put("approvalName", (String) params.get("approvalName"));
|
||||
requestParams.put("requesterName", (String) params.get("requesterName"));
|
||||
return new MessageSendEvent(source, KEY, recipient, requestParams);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getKey() {
|
||||
return KEY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayName() {
|
||||
return "API Key 사용 승인 요청";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "<div>사용 가능 변수: </div><br/>"
|
||||
+ "<div> - userName 수신자 이름 </div><br/>"
|
||||
+ "<div> - userId 수신자 ID (이메일) </div><br/>"
|
||||
+ "<div> - apiKeyName API Key 이름 </div><br/>"
|
||||
+ "<div> - approvalName 승인 요청 제목 </div><br/>"
|
||||
+ "<div> - requesterName 요청 사용자 이름 </div><br/>";
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.eactive.apim.portal.app.repository;
|
||||
|
||||
import com.eactive.apim.portal.app.entity.Credential;
|
||||
import com.eactive.eai.data.jpa.BaseRepository;
|
||||
import com.eactive.eai.rms.data.EMSDataSource;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
|
||||
@EMSDataSource
|
||||
public interface CredentialRepository extends BaseRepository<Credential, String> {
|
||||
|
||||
Page<Credential> findAllByOrgidAndServer(Pageable pageable, String id, String server);
|
||||
|
||||
int countAllByOrgidAndServer(String orgid, String server);
|
||||
|
||||
List<Credential> findAllByOrgidAndServer(String orgid, String server);
|
||||
|
||||
Optional<Credential> findByClientidAndOrgidAndServer(String clientid, String orgid, String server);
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.eactive.apim.portal.app.repository;
|
||||
|
||||
import com.eactive.apim.portal.app.entity.ProdClient;
|
||||
import com.eactive.apim.portal.approval.entity.Approval;
|
||||
import com.eactive.apim.portal.approval.entity.ApprovalType;
|
||||
import com.eactive.eai.data.jpa.BaseRepository;
|
||||
import com.eactive.eai.rms.data.EMSDataSource;
|
||||
import java.util.Optional;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
|
||||
@EMSDataSource
|
||||
public interface ProdClientRepository extends BaseRepository<ProdClient, String> {
|
||||
|
||||
Page<ProdClient> findAllByOrgid(Pageable pageable, String id);
|
||||
|
||||
Optional<ProdClient> findByClientidAndOrgid(String clientid, String orgid);
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
package com.eactive.apim.portal.apprequest.entity;
|
||||
|
||||
import com.eactive.apim.portal.approval.entity.Approval;
|
||||
import com.eactive.apim.portal.approval.entity.ApprovalStatus;
|
||||
import com.eactive.apim.portal.portalorg.entity.PortalOrg;
|
||||
import com.eactive.eai.data.converter.LocalDateTimeToStringConverter17;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import javax.persistence.*;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.hibernate.annotations.CreationTimestamp;
|
||||
import org.hibernate.annotations.GenericGenerator;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode
|
||||
@Entity
|
||||
@Table(name = "ptl_app_request")
|
||||
public class AppRequest implements com.eactive.eai.data.Data {
|
||||
|
||||
private static final String CLIENT_SEPARATOR = "::";
|
||||
|
||||
@Id
|
||||
@GenericGenerator(name = "uuid-gen", strategy = "uuid2")
|
||||
@GeneratedValue(generator = "uuid-gen", strategy = GenerationType.IDENTITY)
|
||||
private String id;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "org_id")
|
||||
private PortalOrg org;
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
private ApprovalStatus status;
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
private AppRequestType type;
|
||||
|
||||
@OneToOne
|
||||
@JoinColumn(name = "approval_id")
|
||||
private Approval approval;
|
||||
|
||||
@Column(name = "api_list")
|
||||
private String apiList = ""; //comma separated api id list
|
||||
|
||||
@Column(name = "api_group_list")
|
||||
private String apiGroupList = ""; //comma separated api group id list
|
||||
|
||||
@Column(name = "prev_api_list")
|
||||
private String prevApiList = "";
|
||||
|
||||
@Column(name = "client_id")
|
||||
private String clientIds; //for modifying existing app
|
||||
|
||||
@Transient
|
||||
private String clientId;
|
||||
|
||||
@Transient
|
||||
private String refClient;
|
||||
|
||||
@Column(name = "client_name")
|
||||
private String clientName;
|
||||
|
||||
@Column(name = "reason")
|
||||
private String reason;
|
||||
|
||||
@Column(length = 17, name = "created_date")
|
||||
@Convert(converter = LocalDateTimeToStringConverter17.class)
|
||||
@CreationTimestamp
|
||||
private LocalDateTime createdDate;
|
||||
|
||||
|
||||
@PrePersist
|
||||
@PreUpdate
|
||||
private void beforeSave() {
|
||||
if (clientId == null && refClient == null) {
|
||||
this.clientIds = null;
|
||||
return;
|
||||
}
|
||||
|
||||
if (refClient != null && !refClient.isEmpty()) {
|
||||
this.clientIds = (this.clientId != null ? this.clientId : "") + CLIENT_SEPARATOR + refClient;
|
||||
} else if (clientId != null) {
|
||||
this.clientIds = clientId;
|
||||
}
|
||||
}
|
||||
|
||||
@PostLoad
|
||||
private void afterLoad() {
|
||||
if (this.clientIds == null) {
|
||||
this.clientId = null;
|
||||
this.refClient = null;
|
||||
return;
|
||||
}
|
||||
if (this.clientIds.contains(CLIENT_SEPARATOR)) {
|
||||
String[] parts = this.clientIds.split(CLIENT_SEPARATOR, 2);
|
||||
this.clientId = parts[0].isEmpty() ? null : parts[0];
|
||||
this.refClient = parts[1];
|
||||
} else {
|
||||
this.clientId = this.clientIds;
|
||||
this.refClient = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.eactive.apim.portal.apprequest.entity;
|
||||
|
||||
public enum AppRequestType {
|
||||
NEW("API키 신규"),
|
||||
MODIFY("API 변경"),
|
||||
DELETE("API키 삭제"),
|
||||
PROD_NEW("운영 API키 신규"),
|
||||
PROD_MODIFY("운영 API 변경"),
|
||||
PROD_DELETE("운영 API키 삭제");
|
||||
|
||||
private final String description;
|
||||
|
||||
AppRequestType(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public static AppRequestType fromString(String value) {
|
||||
try {
|
||||
return AppRequestType.valueOf(value.toUpperCase());
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new IllegalArgumentException("Invalid AppRequestType: " + value +
|
||||
". Allowed values are: " + java.util.Arrays.toString(AppRequestType.values()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.eactive.apim.portal.apprequest.repository;
|
||||
|
||||
import com.eactive.apim.portal.apprequest.entity.AppRequest;
|
||||
import com.eactive.apim.portal.apprequest.entity.AppRequestType;
|
||||
import com.eactive.apim.portal.approval.entity.Approval;
|
||||
import com.eactive.apim.portal.approval.entity.ApprovalStatus;
|
||||
import com.eactive.apim.portal.approval.statemachine.ApprovalState;
|
||||
import com.eactive.apim.portal.portalorg.entity.PortalOrg;
|
||||
import com.eactive.eai.data.jpa.BaseRepository;
|
||||
import com.eactive.eai.rms.data.EMSDataSource;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
|
||||
@EMSDataSource
|
||||
public interface AppRequestRepository extends BaseRepository<AppRequest, String> {
|
||||
|
||||
Page<AppRequest> findAllByOrgAndTypeIsIn(Pageable pageable, PortalOrg org, List<AppRequestType> types);
|
||||
|
||||
Optional<AppRequest> findAppRequestByApproval(Approval approval);
|
||||
|
||||
int countAllByOrgAndTypeIsInAndApproval_ApprovalStatusIn(PortalOrg org, List<AppRequestType> types, List<ApprovalState> approvalStates);
|
||||
|
||||
Optional<AppRequest> findByIdAndOrg(String id, PortalOrg org);
|
||||
|
||||
Optional<AppRequest> findByIdAndOrgAndTypeIsIn(String id, PortalOrg org, List<AppRequestType> types);
|
||||
|
||||
List<AppRequest> findAllByClientIdsContainsAndTypeIsIn(String clientId, List<AppRequestType> types);
|
||||
|
||||
List<AppRequest> findAllByOrgAndStatus(PortalOrg org, ApprovalStatus status);
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package com.eactive.apim.portal.approval.entity;
|
||||
|
||||
|
||||
import com.eactive.apim.portal.approval.statemachine.ApprovalState;
|
||||
import com.eactive.apim.portal.approval.statemachine.CreatedState;
|
||||
import com.eactive.apim.portal.portaluser.entity.PortalUser;
|
||||
import com.eactive.eai.data.converter.LocalDateTimeToStringConverter17;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
import org.hibernate.annotations.*;
|
||||
|
||||
import javax.persistence.CascadeType;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.*;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static javax.persistence.EnumType.STRING;
|
||||
|
||||
@Entity
|
||||
@Table(name = "PTL_APPROVAL")
|
||||
@TypeDef(name = "ApprovalState", typeClass = ApprovalStatusUserType.class, defaultForType = ApprovalState.class)
|
||||
@Data
|
||||
@ToString(exclude = "approvers")
|
||||
@EqualsAndHashCode(of = "id")
|
||||
public class Approval implements Serializable, com.eactive.eai.data.Data {
|
||||
|
||||
public Approval() {
|
||||
this.approvalStatus = new CreatedState();
|
||||
}
|
||||
|
||||
@Id
|
||||
@GenericGenerator(name = "uuid-gen", strategy = "uuid2")
|
||||
@GeneratedValue(generator = "uuid-gen", strategy = GenerationType.IDENTITY)
|
||||
private String id;
|
||||
|
||||
@Column(name = "approval_status", nullable = false)
|
||||
@Type(type = "ApprovalState")
|
||||
private ApprovalState approvalStatus; //승인 진행 상태 //statemachine 에 정의됨.
|
||||
|
||||
@Enumerated(value = STRING)
|
||||
@Column(name = "approval_type", nullable = false)
|
||||
private ApprovalType approvalType; //승인 유형: APP, USER
|
||||
|
||||
@Column(name = "target_id", nullable = false)
|
||||
private String targetId; //승인하는 대상 객체의 ID
|
||||
|
||||
@NotNull
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "REQUESTER_ID", nullable = false)
|
||||
private PortalUser requester;
|
||||
|
||||
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "approval")
|
||||
@LazyCollection(LazyCollectionOption.FALSE)
|
||||
private List<Approver> approvers = new ArrayList<>();
|
||||
|
||||
@Column(name = "approval_subject", nullable = false)
|
||||
private String approvalSubject; //제목
|
||||
|
||||
@Column(name = "approval_detail", columnDefinition = "TEXT")
|
||||
private String approvalDetail; //승인 요청 내용
|
||||
|
||||
@Column(length = 17, name = "created_date")
|
||||
@Convert(converter = LocalDateTimeToStringConverter17.class)
|
||||
@CreationTimestamp
|
||||
private LocalDateTime createdDate;
|
||||
|
||||
@Column(length = 17, name = "end_date")
|
||||
@Convert(converter = LocalDateTimeToStringConverter17.class)
|
||||
private LocalDateTime approvalDate;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.eactive.apim.portal.approval.entity;
|
||||
|
||||
public enum ApprovalStatus {
|
||||
CREATED("생성됨"),
|
||||
REQUESTED("요청됨"),
|
||||
PROCESSING("진행중"),
|
||||
CHECKING("확인"),
|
||||
CANCELED("취소됨"),
|
||||
DENIED("반려됨"),
|
||||
APPROVED("승인됨"),
|
||||
PENDING("대기중"),
|
||||
EXECUTION("후처리"),
|
||||
END("승인됨"),
|
||||
FAILED("배포실패");
|
||||
|
||||
private String description;
|
||||
|
||||
ApprovalStatus(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
package com.eactive.apim.portal.approval.entity;
|
||||
|
||||
import com.eactive.apim.portal.approval.statemachine.ApprovalState;
|
||||
import com.eactive.apim.portal.approval.statemachine.ApprovedState;
|
||||
import com.eactive.apim.portal.approval.statemachine.CanceledState;
|
||||
import com.eactive.apim.portal.approval.statemachine.CreatedState;
|
||||
import com.eactive.apim.portal.approval.statemachine.DeniedState;
|
||||
import com.eactive.apim.portal.approval.statemachine.DeployFailedState;
|
||||
import com.eactive.apim.portal.approval.statemachine.EndState;
|
||||
import com.eactive.apim.portal.approval.statemachine.ProcessingState;
|
||||
import com.eactive.apim.portal.approval.statemachine.RequestedState;
|
||||
import java.io.Serializable;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Types;
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.usertype.UserType;
|
||||
|
||||
public class ApprovalStatusUserType implements UserType {
|
||||
@Override
|
||||
public int[] sqlTypes() {
|
||||
return new int[]{Types.VARCHAR};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class returnedClass() {
|
||||
return ApprovalState.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object x, Object y) throws HibernateException {
|
||||
if (x == null) {
|
||||
return y == null;
|
||||
} else {
|
||||
if (y == null) {
|
||||
return false;
|
||||
} else {
|
||||
return x.toString().equalsIgnoreCase(y.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode(Object x) throws HibernateException {
|
||||
return x.hashCode();
|
||||
}
|
||||
|
||||
public static ApprovalState createState(String state) {
|
||||
switch (state) {
|
||||
case "APPROVED":
|
||||
return new ApprovedState();
|
||||
case "CANCELED":
|
||||
return new CanceledState();
|
||||
case "PROCESSING":
|
||||
return new ProcessingState();
|
||||
case "DENIED":
|
||||
return new DeniedState();
|
||||
case "REQUESTED":
|
||||
return new RequestedState();
|
||||
case "CREATED":
|
||||
return new CreatedState();
|
||||
case "END":
|
||||
return new EndState();
|
||||
case "FAILED":
|
||||
return new DeployFailedState();
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object nullSafeGet(ResultSet rs, String[] names, SharedSessionContractImplementor session, Object owner) throws HibernateException, SQLException {
|
||||
String value = rs.getString(names[0]);
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
return createState(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void nullSafeSet(PreparedStatement st, Object value, int index, SharedSessionContractImplementor session) throws HibernateException, SQLException {
|
||||
if (value == null) {
|
||||
st.setNull(index, Types.VARCHAR);
|
||||
} else {
|
||||
st.setString(index, value.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object deepCopy(Object value) throws HibernateException {
|
||||
if (value == null) return null;
|
||||
return createState(value.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMutable() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Serializable disassemble(Object value) throws HibernateException {
|
||||
return value.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object assemble(Serializable cached, Object owner) throws HibernateException {
|
||||
if (cached == null) {
|
||||
return null;
|
||||
}
|
||||
return createState(cached.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object replace(Object original, Object target, Object owner) throws HibernateException {
|
||||
if (original instanceof ApprovalState) {
|
||||
return deepCopy(original);
|
||||
} else {
|
||||
return original;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.eactive.apim.portal.approval.entity;
|
||||
|
||||
public enum ApprovalType {
|
||||
USER("사용자"),
|
||||
APP("APP");
|
||||
// ORG("기관");
|
||||
|
||||
private String description;
|
||||
|
||||
ApprovalType(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package com.eactive.apim.portal.approval.entity;
|
||||
|
||||
import com.eactive.apim.portal.user.entity.UserInfo;
|
||||
import com.eactive.eai.data.converter.LocalDateTimeToStringConverter17;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
|
||||
import javax.persistence.*;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static javax.persistence.EnumType.STRING;
|
||||
|
||||
@Entity
|
||||
@Table(name = "PTL_APPROVER")
|
||||
@Data
|
||||
@EqualsAndHashCode(of = "id")
|
||||
@ToString(exclude = "approval")
|
||||
public class Approver implements Serializable, com.eactive.eai.data.Data {
|
||||
|
||||
@EmbeddedId
|
||||
private ApproverId id;
|
||||
|
||||
@ManyToOne
|
||||
@MapsId("approval")
|
||||
@JoinColumn(name = "APPROVAL_ID", nullable = false)
|
||||
private Approval approval;
|
||||
|
||||
@NotNull
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "USER_ID", nullable = false)
|
||||
private UserInfo user;
|
||||
|
||||
@Enumerated(value = STRING)
|
||||
@Column(name = "approval_status")
|
||||
private ApproverStatus approverStatus;
|
||||
|
||||
@Column(name = "approval_message")
|
||||
private String approvalMessage;
|
||||
|
||||
@Column(length = 17, name = "approved_date")
|
||||
@Convert(converter = LocalDateTimeToStringConverter17.class)
|
||||
private LocalDateTime approvedDate;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.eactive.apim.portal.approval.entity;
|
||||
|
||||
import java.io.Serializable;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Embeddable;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Embeddable
|
||||
@Data
|
||||
@EqualsAndHashCode
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class ApproverId implements Serializable {
|
||||
|
||||
@Column(name = "approval_id")
|
||||
private String approval;
|
||||
|
||||
@Column(name = "approval_order")
|
||||
private Integer ordinal;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.eactive.apim.portal.approval.entity;
|
||||
|
||||
public enum ApproverStatus {
|
||||
|
||||
PENDING("대기중"),
|
||||
CURRENT("승인전"),
|
||||
APPROVED("승인됨"),
|
||||
DENIED("반려됨"),
|
||||
CANCELED("취소됨");
|
||||
|
||||
private final String description;
|
||||
|
||||
ApproverStatus(String description) { this.description = description; }
|
||||
|
||||
public String getDescription() { return description; }
|
||||
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.eactive.apim.portal.approval.repository;
|
||||
|
||||
import com.eactive.apim.portal.approval.entity.Approval;
|
||||
import com.eactive.apim.portal.approval.entity.ApprovalType;
|
||||
import com.eactive.eai.data.jpa.BaseRepository;
|
||||
import com.eactive.eai.rms.data.EMSDataSource;
|
||||
import java.util.Optional;
|
||||
|
||||
@EMSDataSource
|
||||
public interface ApprovalRepository extends BaseRepository<Approval, String> {
|
||||
|
||||
Optional<Approval> findApprovalByTargetIdAndApprovalType(String targetId, ApprovalType approvalType);
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.eactive.apim.portal.approval.service;
|
||||
|
||||
public class ApprovalDeployException extends Exception {
|
||||
|
||||
public ApprovalDeployException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.eactive.apim.portal.approval.service;
|
||||
|
||||
public class ApprovalException extends RuntimeException {
|
||||
|
||||
public ApprovalException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.eactive.apim.portal.approval.statemachine;
|
||||
|
||||
import com.eactive.apim.portal.approval.entity.Approval;
|
||||
|
||||
public interface ApprovalAuthorizer {
|
||||
|
||||
boolean isAuthorized(Approval approval, String approverId);
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.eactive.apim.portal.approval.statemachine;
|
||||
|
||||
public class ApprovalConstants {
|
||||
|
||||
public static final String APPROVER = "approverId";
|
||||
public static final Object MESSAGE = "message";
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.eactive.apim.portal.approval.statemachine;
|
||||
|
||||
public enum ApprovalEvent {
|
||||
BEGIN,
|
||||
APPROVE,
|
||||
CANCEL,
|
||||
DENY,
|
||||
END,
|
||||
REDEPLOY
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.eactive.apim.portal.approval.statemachine;
|
||||
|
||||
|
||||
import com.eactive.apim.portal.approval.entity.Approval;
|
||||
import com.eactive.apim.portal.approval.statemachine.listener.ApprovalListener;
|
||||
import com.eactive.apim.portal.approval.statemachine.listener.DefaultApprovalListener;
|
||||
import java.io.Serializable;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
public interface ApprovalState extends Serializable {
|
||||
|
||||
void handle(Approval approval, ApprovalEvent event, Map<String, Object> options);
|
||||
|
||||
default ApprovalListener getApprovalListener(Map<String, Object> options) {
|
||||
return (ApprovalListener) options.getOrDefault("listener", new DefaultApprovalListener());
|
||||
}
|
||||
|
||||
default ApprovalAuthorizer getApprovalAuthorizer(Map<String, Object> options) {
|
||||
return (ApprovalAuthorizer) options.getOrDefault("authorizer", new DefaultApprovalAuthorizer());
|
||||
}
|
||||
|
||||
|
||||
default boolean isAuthorizedApprover(Approval approval, String approverId, Map<String, Object> options) {
|
||||
return Optional.ofNullable(getApprovalAuthorizer(options))
|
||||
.map(authorizer -> authorizer.isAuthorized(approval, approverId))
|
||||
.orElse(false);
|
||||
}
|
||||
|
||||
String getDescription();
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.eactive.apim.portal.approval.statemachine;
|
||||
|
||||
import com.eactive.apim.portal.approval.entity.Approval;
|
||||
import java.util.Map;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Propagation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
@Transactional(propagation = Propagation.REQUIRED)
|
||||
public class ApprovalStateMachine {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(ApprovalStateMachine.class);
|
||||
|
||||
public void transition(Approval approval, ApprovalEvent event, Map<String, Object> options) {
|
||||
ApprovalState currentState = approval.getApprovalStatus();
|
||||
logger.info("Transitioning approval {} from {} state with event {}", approval.getId(), currentState, event);
|
||||
|
||||
try {
|
||||
currentState.handle(approval, event, options);
|
||||
logger.info("Transitioned approval {} to {} state", approval.getId(), approval.getApprovalStatus());
|
||||
|
||||
} catch (UnsupportedOperationException e) {
|
||||
logger.error("Invalid transition for approval {} from {} state with event {}", approval.getId(), currentState, event);
|
||||
throw new InvalidApprovalTransitionException("Invalid state transition", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package com.eactive.apim.portal.approval.statemachine;
|
||||
|
||||
|
||||
import com.eactive.apim.portal.approval.entity.Approval;
|
||||
import com.eactive.apim.portal.approval.entity.ApprovalStatus;
|
||||
import com.eactive.apim.portal.approval.service.ApprovalDeployException;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Map;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
public class ApprovedState implements ApprovalState {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(ApprovedState.class);
|
||||
|
||||
@Override
|
||||
@Transactional(noRollbackFor = ApprovalDeployException.class)
|
||||
public void handle(Approval approval, ApprovalEvent event, Map<String, Object> options) {
|
||||
if (event == ApprovalEvent.END) {
|
||||
handleEnd(approval, options);
|
||||
} else {
|
||||
logger.warn("Invalid event {} for ApprovedState in approval {}", event, approval.getId());
|
||||
throw new UnsupportedOperationException("Can only end from Approved state");
|
||||
}
|
||||
}
|
||||
|
||||
private void handleEnd(Approval approval, Map<String, Object> options) {
|
||||
approval.setApprovalDate(LocalDateTime.now());
|
||||
approval.setApprovalStatus(new EndState());
|
||||
|
||||
try {
|
||||
getApprovalListener(options).execute(approval, options);
|
||||
logger.info("Approval {} ended successfully", approval.getId());
|
||||
} catch (ApprovalDeployException e) {
|
||||
logger.error("Approval {} ended with error, {}", approval.getId(), e);
|
||||
approval.setApprovalStatus(new DeployFailedState());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return ApprovalStatus.APPROVED.getDescription();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return ApprovalStatus.APPROVED.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.eactive.apim.portal.approval.statemachine;
|
||||
|
||||
|
||||
import com.eactive.apim.portal.approval.entity.Approval;
|
||||
import com.eactive.apim.portal.approval.entity.ApprovalStatus;
|
||||
import java.util.Map;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class CanceledState implements ApprovalState {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(CanceledState.class);
|
||||
|
||||
@Override
|
||||
public void handle(Approval approval, ApprovalEvent event, Map<String, Object> options) {
|
||||
logger.warn("Invalid event {} for CanceledState in approval {}", event, approval.getId());
|
||||
throw new UnsupportedOperationException("No transitions allowed from Canceled state");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return ApprovalStatus.CANCELED.toString();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return ApprovalStatus.CANCELED.getDescription();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package com.eactive.apim.portal.approval.statemachine;
|
||||
|
||||
|
||||
import com.eactive.apim.portal.approval.entity.Approval;
|
||||
import com.eactive.apim.portal.approval.entity.ApprovalStatus;
|
||||
import com.eactive.apim.portal.approval.entity.Approver;
|
||||
import com.eactive.apim.portal.approval.entity.ApproverStatus;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class CreatedState implements ApprovalState {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(CreatedState.class);
|
||||
|
||||
@Override
|
||||
public void handle(Approval approval, ApprovalEvent event, Map<String, Object> options) {
|
||||
if (event == ApprovalEvent.BEGIN) {
|
||||
List<Approver> approvers = approval.getApprovers();
|
||||
if (approvers == null || approvers.isEmpty()) {
|
||||
logger.error("No approvers found for approval {}", approval.getId());
|
||||
throw new IllegalStateException("No approvers found.");
|
||||
}
|
||||
|
||||
approvers.forEach(approver -> approver.setApproverStatus(ApproverStatus.PENDING));
|
||||
approvers.get(0).setApproverStatus(ApproverStatus.CURRENT);
|
||||
|
||||
approval.setCreatedDate(LocalDateTime.now());
|
||||
approval.setApprovalStatus(new RequestedState());
|
||||
logger.info("Approval {} began successfully", approval.getId());
|
||||
} else {
|
||||
logger.warn("Invalid event {} for CreatedState in approval {}", event, approval.getId());
|
||||
throw new UnsupportedOperationException("Can only begin from Created state");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return ApprovalStatus.CREATED.getDescription();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return ApprovalStatus.CREATED.toString();
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
package com.eactive.apim.portal.approval.statemachine;
|
||||
|
||||
import com.eactive.apim.portal.approval.entity.Approval;
|
||||
|
||||
public class DefaultApprovalAuthorizer implements ApprovalAuthorizer {
|
||||
|
||||
@Override
|
||||
public boolean isAuthorized(Approval approval, String approverId) {
|
||||
//anyone can approve
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.eactive.apim.portal.approval.statemachine;
|
||||
|
||||
|
||||
import com.eactive.apim.portal.approval.entity.Approval;
|
||||
import com.eactive.apim.portal.approval.entity.ApprovalStatus;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Map;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class DeniedState implements ApprovalState {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(DeniedState.class);
|
||||
|
||||
@Override
|
||||
public void handle(Approval approval, ApprovalEvent event, Map<String, Object> options) {
|
||||
if (event == ApprovalEvent.END) {
|
||||
handleEnd(approval, options);
|
||||
} else {
|
||||
logger.warn("Invalid event {} for DeniedState in approval {}", event, approval.getId());
|
||||
throw new UnsupportedOperationException("Can only end from Denied state");
|
||||
}
|
||||
}
|
||||
|
||||
private void handleEnd(Approval approval, Map<String, Object> options) {
|
||||
approval.setApprovalDate(LocalDateTime.now());
|
||||
// approval.setApprovalStatus(new EndState());
|
||||
getApprovalListener(options).rollback(approval);
|
||||
logger.info("Approval {} ended (denied) and rolled back", approval.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return ApprovalStatus.DENIED.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return ApprovalStatus.DENIED.getDescription();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.eactive.apim.portal.approval.statemachine;
|
||||
|
||||
import com.eactive.apim.portal.approval.entity.Approval;
|
||||
import com.eactive.apim.portal.approval.entity.ApprovalStatus;
|
||||
import com.eactive.apim.portal.approval.service.ApprovalDeployException;
|
||||
import java.util.Map;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class DeployFailedState implements ApprovalState {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(DeployFailedState.class);
|
||||
|
||||
@Override
|
||||
public void handle(Approval approval, ApprovalEvent event, Map<String, Object> options) {
|
||||
if (event == ApprovalEvent.REDEPLOY) {
|
||||
try {
|
||||
getApprovalListener(options).execute(approval, options);
|
||||
approval.setApprovalStatus(new EndState());
|
||||
} catch (ApprovalDeployException e) {
|
||||
logger.error(e.getMessage(), e);
|
||||
approval.setApprovalStatus(new DeployFailedState());
|
||||
}
|
||||
} else {
|
||||
logger.warn("Invalid event {} for DeployFailedState in approval {}", event, approval.getId());
|
||||
throw new UnsupportedOperationException("Not supported yet.");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return ApprovalStatus.FAILED.toString();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return ApprovalStatus.FAILED.getDescription();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.eactive.apim.portal.approval.statemachine;
|
||||
|
||||
import com.eactive.apim.portal.approval.entity.Approval;
|
||||
import com.eactive.apim.portal.approval.entity.ApprovalStatus;
|
||||
import java.util.Map;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class EndState implements ApprovalState{
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(EndState.class);
|
||||
|
||||
@Override
|
||||
public void handle(Approval approval, ApprovalEvent event, Map<String, Object> options) {
|
||||
logger.warn("Invalid event {} for EndState in approval {}", event, approval.getId());
|
||||
throw new UnsupportedOperationException("No transitions allowed from End state");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return ApprovalStatus.END.toString();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return ApprovalStatus.END.getDescription();
|
||||
}
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
package com.eactive.apim.portal.approval.statemachine;
|
||||
|
||||
public class InvalidApprovalTransitionException extends RuntimeException {
|
||||
|
||||
public InvalidApprovalTransitionException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
package com.eactive.apim.portal.approval.statemachine;
|
||||
|
||||
|
||||
import com.eactive.apim.portal.approval.entity.Approval;
|
||||
import com.eactive.apim.portal.approval.entity.ApprovalStatus;
|
||||
import com.eactive.apim.portal.approval.entity.Approver;
|
||||
import com.eactive.apim.portal.approval.entity.ApproverStatus;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Map;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class ProcessingState implements ApprovalState {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(ProcessingState.class);
|
||||
|
||||
@Override
|
||||
public void handle(Approval approval, ApprovalEvent event, Map<String, Object> options) {
|
||||
switch (event) {
|
||||
case APPROVE:
|
||||
handleApprove(approval, options);
|
||||
break;
|
||||
case DENY:
|
||||
handleDeny(approval, options);
|
||||
break;
|
||||
default:
|
||||
logger.warn("Invalid event {} for ProcessingState in approval {}", event, approval.getId());
|
||||
throw new UnsupportedOperationException("Invalid event for Processing state");
|
||||
}
|
||||
}
|
||||
|
||||
private void updateApprovalStatus(Approval approval) {
|
||||
boolean allApproved = approval.getApprovers().stream()
|
||||
.allMatch(approver -> ApproverStatus.APPROVED.equals(approver.getApproverStatus()));
|
||||
|
||||
ApprovalState newStatus = allApproved
|
||||
? new ApprovedState()
|
||||
: new ProcessingState();
|
||||
approval.setApprovalStatus(newStatus);
|
||||
}
|
||||
|
||||
private void handleApprove(Approval approval, Map<String, Object> options) {
|
||||
String approverId = (String) options.getOrDefault(ApprovalConstants.APPROVER, "");
|
||||
if (isAuthorizedApprover(approval, approverId, options)) {
|
||||
boolean statusChanged = false;
|
||||
// Update current approver status and track if it changed
|
||||
for (Approver approver : approval.getApprovers()) {
|
||||
if (approver.getUser().getId().equals(approverId) && !ApproverStatus.APPROVED.equals(approver.getApproverStatus())) {
|
||||
approver.setApproverStatus(ApproverStatus.APPROVED);
|
||||
approver.setApprovedDate(LocalDateTime.now());
|
||||
statusChanged = true;
|
||||
break; // Found and updated the current approver
|
||||
}
|
||||
}
|
||||
|
||||
// If status changed, set next pending approver to CURRENT
|
||||
if (statusChanged) {
|
||||
boolean nextApproverFound = false;
|
||||
for (Approver approver : approval.getApprovers()) {
|
||||
if (ApproverStatus.PENDING.equals(approver.getApproverStatus()) && !nextApproverFound) {
|
||||
approver.setApproverStatus(ApproverStatus.CURRENT);
|
||||
nextApproverFound = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
updateApprovalStatus(approval);
|
||||
|
||||
logger.info("Approval {} approved by approver {} in Processing state", approval.getId(), approverId);
|
||||
} else {
|
||||
logger.warn("Unauthorized approval attempt for approval {} by approver {} in Processing state", approval.getId(), approverId);
|
||||
throw new UnauthorizedApprovalException("Unauthorized approver");
|
||||
}
|
||||
}
|
||||
|
||||
private void handleDeny(Approval approval, Map<String, Object> options) {
|
||||
String approverId = (String) options.getOrDefault(ApprovalConstants.APPROVER, "");
|
||||
if (isAuthorizedApprover(approval, approverId, options)) {
|
||||
approval.getApprovers().forEach(approver -> {
|
||||
if(approver.getUser().getId().equals(approverId)) {
|
||||
approver.setApproverStatus(ApproverStatus.DENIED);
|
||||
approver.setApprovedDate(LocalDateTime.now());
|
||||
String message = (String) options.getOrDefault("message", "");
|
||||
approver.setApprovalMessage(message);
|
||||
}
|
||||
});
|
||||
approval.setApprovalStatus(new DeniedState());
|
||||
logger.info("Approval {} denied by approver {} in Processing state", approval.getId(), approverId);
|
||||
} else {
|
||||
logger.warn("Unauthorized denial attempt for approval {} by approver {} in Processing state", approval.getId(), approverId);
|
||||
throw new UnauthorizedApprovalException("Unauthorized approver");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return ApprovalStatus.PROCESSING.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return ApprovalStatus.PROCESSING.getDescription();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
package com.eactive.apim.portal.approval.statemachine;
|
||||
|
||||
import com.eactive.apim.portal.approval.entity.Approval;
|
||||
import com.eactive.apim.portal.approval.entity.ApprovalStatus;
|
||||
import com.eactive.apim.portal.approval.entity.Approver;
|
||||
import com.eactive.apim.portal.approval.entity.ApproverStatus;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Map;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class RequestedState implements ApprovalState {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(RequestedState.class);
|
||||
|
||||
@Override
|
||||
public void handle(Approval approval, ApprovalEvent event, Map<String, Object> options) {
|
||||
switch (event) {
|
||||
case APPROVE:
|
||||
handleApprove(approval, options);
|
||||
break;
|
||||
case DENY:
|
||||
handleDeny(approval, options);
|
||||
break;
|
||||
case CANCEL:
|
||||
handleCancel(approval, options);
|
||||
break;
|
||||
default:
|
||||
logger.warn("Invalid event {} for RequestedState in approval {}", event, approval.getId());
|
||||
throw new UnsupportedOperationException("Invalid event for Requested state");
|
||||
}
|
||||
}
|
||||
|
||||
private void updateApprovalStatus(Approval approval) {
|
||||
boolean allApproved = approval.getApprovers().stream()
|
||||
.allMatch(approver -> ApproverStatus.APPROVED.equals(approver.getApproverStatus()));
|
||||
|
||||
ApprovalState newStatus = allApproved
|
||||
? new ApprovedState()
|
||||
: new ProcessingState();
|
||||
approval.setApprovalStatus(newStatus);
|
||||
}
|
||||
|
||||
private void handleApprove(Approval approval, Map<String, Object> options) {
|
||||
String approverId = (String) options.getOrDefault(ApprovalConstants.APPROVER, "");
|
||||
if (isAuthorizedApprover(approval, approverId, options)) {
|
||||
boolean statusChanged = false;
|
||||
|
||||
// Update current approver status and track if it changed
|
||||
for (Approver approver : approval.getApprovers()) {
|
||||
if (approver.getUser().getId().equals(approverId) && !ApproverStatus.APPROVED.equals(approver.getApproverStatus())) {
|
||||
approver.setApproverStatus(ApproverStatus.APPROVED);
|
||||
approver.setApprovedDate(LocalDateTime.now());
|
||||
statusChanged = true;
|
||||
break; // Found and updated the current approver
|
||||
}
|
||||
}
|
||||
|
||||
// If status changed, set next pending approver to CURRENT
|
||||
if (statusChanged) {
|
||||
boolean nextApproverFound = false;
|
||||
for (Approver approver : approval.getApprovers()) {
|
||||
if (ApproverStatus.PENDING.equals(approver.getApproverStatus()) && !nextApproverFound) {
|
||||
approver.setApproverStatus(ApproverStatus.CURRENT);
|
||||
nextApproverFound = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
updateApprovalStatus(approval);
|
||||
logger.info("Approval {} approved by approver {}", approval.getId(), approverId);
|
||||
} else {
|
||||
logger.warn("Unauthorized approval attempt for approval {} by approver {}", approval.getId(), approverId);
|
||||
throw new UnauthorizedApprovalException("Unauthorized approver");
|
||||
}
|
||||
}
|
||||
|
||||
private void handleDeny(Approval approval, Map<String, Object> options) {
|
||||
String approverId = (String) options.getOrDefault(ApprovalConstants.APPROVER, "");
|
||||
if (isAuthorizedApprover(approval, approverId, options)) {
|
||||
approval.getApprovers().forEach(approver -> {
|
||||
if (approver.getUser().getId().equals(approverId)) {
|
||||
approver.setApproverStatus(ApproverStatus.DENIED);
|
||||
approver.setApprovedDate(LocalDateTime.now());
|
||||
String message = (String) options.getOrDefault("message", "");
|
||||
approver.setApprovalMessage(message);
|
||||
}
|
||||
});
|
||||
approval.setApprovalStatus(new DeniedState());
|
||||
logger.info("Approval {} denied by approver {}", approval.getId(), approverId);
|
||||
} else {
|
||||
logger.warn("Unauthorized denial attempt for approval {} by approver {}", approval.getId(), approverId);
|
||||
throw new UnauthorizedApprovalException("Unauthorized approver");
|
||||
}
|
||||
}
|
||||
|
||||
private void handleCancel(Approval approval, Map<String, Object> options) {
|
||||
String approverId = (String) options.getOrDefault(ApprovalConstants.APPROVER, "");
|
||||
if (isAuthorizedApprover(approval, approverId, options)) {
|
||||
approval.getApprovers().forEach(approver -> {
|
||||
if (approver.getUser().getId().equals(approverId)) {
|
||||
approver.setApproverStatus(ApproverStatus.CANCELED);
|
||||
approver.setApprovedDate(LocalDateTime.now());
|
||||
}
|
||||
});
|
||||
approval.setApprovalDate(LocalDateTime.now());
|
||||
getApprovalListener(options).rollback(approval);
|
||||
approval.setApprovalStatus(new CanceledState());
|
||||
logger.info("Approval {} cancelled by approver {}", approval.getId(), approverId);
|
||||
} else {
|
||||
logger.warn("Unauthorized denial attempt for approval {} by approver {}", approval.getId(), approverId);
|
||||
throw new UnauthorizedApprovalException("Unauthorized approver");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return ApprovalStatus.REQUESTED.getDescription();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return ApprovalStatus.REQUESTED.toString();
|
||||
}
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
package com.eactive.apim.portal.approval.statemachine;
|
||||
|
||||
public class UnauthorizedApprovalException extends RuntimeException {
|
||||
|
||||
public UnauthorizedApprovalException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
package com.eactive.apim.portal.approval.statemachine.listener;
|
||||
|
||||
import com.eactive.apim.portal.approval.entity.Approval;
|
||||
import com.eactive.apim.portal.approval.service.ApprovalDeployException;
|
||||
import java.util.Map;
|
||||
|
||||
public interface ApprovalListener {
|
||||
|
||||
void execute(Approval approval, Map<String, Object> options) throws ApprovalDeployException;
|
||||
|
||||
void rollback(Approval approval);
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
package com.eactive.apim.portal.approval.statemachine.listener;
|
||||
|
||||
import com.eactive.apim.portal.approval.entity.Approval;
|
||||
import com.eactive.apim.portal.approval.service.ApprovalDeployException;
|
||||
import java.util.Map;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class DefaultApprovalListener implements ApprovalListener {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(DefaultApprovalListener.class);
|
||||
|
||||
@Override
|
||||
public void execute(Approval approval, Map<String, Object> options) throws ApprovalDeployException {
|
||||
logger.info("Executing approval listener for approval {}", approval.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rollback(Approval approval) {
|
||||
logger.info("Rolling back approval {}", approval.getId());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.eactive.apim.portal.approvalline.entity;
|
||||
|
||||
import com.eactive.apim.portal.common.entity.Auditable;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
import org.hibernate.annotations.Comment;
|
||||
import org.hibernate.annotations.GenericGenerator;
|
||||
|
||||
import javax.persistence.*;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(onlyExplicitlyIncluded = true, callSuper = true)
|
||||
|
||||
@Entity
|
||||
@Table(name = "ptl_approval_line")
|
||||
public class PortalApprovalLine extends Auditable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Id
|
||||
@Column(name = "id", length = 36, nullable = false)
|
||||
@GenericGenerator(name = "uuid-gen", strategy = "uuid2")
|
||||
@GeneratedValue(generator = "uuid-gen", strategy = GenerationType.IDENTITY)
|
||||
private String id;
|
||||
|
||||
@Column(name = "approval_type", length = 20, nullable = false)
|
||||
@Comment("승인타입")
|
||||
private String approvalType;
|
||||
|
||||
@Column(name = "approval_name", length = 50, nullable = false)
|
||||
@Comment("승인이름")
|
||||
private String approvalName;
|
||||
|
||||
@OneToMany(mappedBy = "approvalLine", cascade = CascadeType.ALL, orphanRemoval = true)
|
||||
@ToString.Exclude
|
||||
@OrderBy("approvalOrder ASC")
|
||||
private List<PortalApprovalLineUser> portalApprovalLineUsers;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package com.eactive.apim.portal.approvalline.entity;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.EmbeddedId;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import org.hibernate.annotations.Comment;
|
||||
|
||||
import com.eactive.apim.portal.user.entity.UserInfo;
|
||||
import com.eactive.eai.data.entity.AbstractEntity;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(onlyExplicitlyIncluded = true, callSuper = true)
|
||||
|
||||
@Entity
|
||||
@Table(name = "ptl_approval_line_user")
|
||||
public class PortalApprovalLineUser extends AbstractEntity<PortalApprovalLineUserId> implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@EmbeddedId
|
||||
private PortalApprovalLineUserId id;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "approval_line_id", insertable = false, updatable = false)
|
||||
@Comment("승인라인ID")
|
||||
@ToString.Exclude
|
||||
private PortalApprovalLine approvalLine;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "user_id", insertable = false, updatable = false)
|
||||
@Comment("사용자ID")
|
||||
@ToString.Exclude
|
||||
private UserInfo user;
|
||||
|
||||
@Column(name = "approval_order")
|
||||
@Comment("승인순서")
|
||||
private int approvalOrder;
|
||||
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
package com.eactive.apim.portal.approvalline.entity;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Embeddable;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Embeddable
|
||||
public class PortalApprovalLineUserId implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Column(name = "approval_line_id", nullable = false)
|
||||
private String approvalLineId;
|
||||
|
||||
@Column(name = "user_id", nullable = false)
|
||||
private String userId;
|
||||
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
package com.eactive.apim.portal.approvalline.repository;
|
||||
|
||||
import com.eactive.apim.portal.approvalline.entity.PortalApprovalLine;
|
||||
import com.eactive.eai.data.jpa.BaseRepository;
|
||||
import com.eactive.eai.rms.data.EMSDataSource;
|
||||
import java.util.Optional;
|
||||
|
||||
@EMSDataSource
|
||||
public interface PortalApprovalLineRepository extends BaseRepository<PortalApprovalLine, String> {
|
||||
|
||||
Optional<PortalApprovalLine> findPortalApprovalLineByApprovalType(String approvalType);
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
package com.eactive.apim.portal.approvalline.repository;
|
||||
|
||||
import com.eactive.apim.portal.approvalline.entity.PortalApprovalLineUser;
|
||||
import com.eactive.apim.portal.approvalline.entity.PortalApprovalLineUserId;
|
||||
import com.eactive.eai.data.jpa.BaseRepository;
|
||||
import com.eactive.eai.rms.data.EMSDataSource;
|
||||
|
||||
@EMSDataSource
|
||||
public interface PortalApprovalLineUserRepository extends BaseRepository<PortalApprovalLineUser, PortalApprovalLineUserId> {
|
||||
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
package com.eactive.apim.portal.approvalline.service;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import com.eactive.apim.portal.approvalline.entity.PortalApprovalLine;
|
||||
import com.eactive.apim.portal.approvalline.repository.PortalApprovalLineRepository;
|
||||
import com.eactive.eai.data.jpa.AbstractDataService;
|
||||
|
||||
@Service
|
||||
@Transactional(transactionManager = "transactionManagerForEMS")
|
||||
public class PortalApprovalLineService extends AbstractDataService<PortalApprovalLine, String, PortalApprovalLineRepository> {
|
||||
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
package com.eactive.apim.portal.approvalline.service;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import com.eactive.apim.portal.approvalline.entity.PortalApprovalLineUser;
|
||||
import com.eactive.apim.portal.approvalline.entity.PortalApprovalLineUserId;
|
||||
import com.eactive.apim.portal.approvalline.repository.PortalApprovalLineUserRepository;
|
||||
import com.eactive.eai.data.jpa.AbstractDataService;
|
||||
|
||||
@Service
|
||||
@Transactional(transactionManager = "transactionManagerForEMS")
|
||||
public class PortalApprovalLineUserService extends AbstractDataService<PortalApprovalLineUser, PortalApprovalLineUserId, PortalApprovalLineUserRepository> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.eactive.apim.portal.common.converter;
|
||||
|
||||
import javax.persistence.AttributeConverter;
|
||||
import javax.persistence.Converter;
|
||||
|
||||
@Converter
|
||||
public class BooleanYNConverter implements AttributeConverter<Boolean, String> {
|
||||
|
||||
@Override
|
||||
public String convertToDatabaseColumn(Boolean attribute) {
|
||||
return attribute != null && attribute ? "Y" : "N";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean convertToEntityAttribute(String dbData) {
|
||||
return "Y".equalsIgnoreCase(dbData);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.eactive.apim.portal.common.converter;
|
||||
|
||||
import com.eactive.apim.portal.common.entity.EnabledStatus;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
|
||||
public class EnabledStatusConverter implements Converter<Boolean, EnabledStatus> {
|
||||
|
||||
@Override
|
||||
public EnabledStatus convert(Boolean source) {
|
||||
if (source == null) return EnabledStatus.N;
|
||||
return source ? EnabledStatus.Y : EnabledStatus.N;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package com.eactive.apim.portal.common.entity;
|
||||
|
||||
import com.eactive.eai.data.converter.LocalDateTimeToStringConverter14;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.springframework.data.annotation.CreatedBy;
|
||||
import org.springframework.data.annotation.CreatedDate;
|
||||
import org.springframework.data.annotation.LastModifiedBy;
|
||||
import org.springframework.data.annotation.LastModifiedDate;
|
||||
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Convert;
|
||||
import javax.persistence.EntityListeners;
|
||||
import javax.persistence.MappedSuperclass;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@MappedSuperclass
|
||||
@EntityListeners(AuditingEntityListener.class)
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
public abstract class Auditable {
|
||||
|
||||
/**
|
||||
* 최초등록시점
|
||||
*/
|
||||
@Column(name = "CREATED_DATE")
|
||||
@Convert(converter = LocalDateTimeToStringConverter14.class)
|
||||
@CreatedDate
|
||||
private LocalDateTime createdDate;
|
||||
|
||||
/**
|
||||
* 최초등록자ID
|
||||
*/
|
||||
@Column(name = "created_by")
|
||||
@CreatedBy
|
||||
private String createdBy;
|
||||
|
||||
/**
|
||||
* 최종수정시점
|
||||
*/
|
||||
@Column(name = "LAST_MODIFIED_DATE")
|
||||
@Convert(converter = LocalDateTimeToStringConverter14.class)
|
||||
@LastModifiedDate
|
||||
private LocalDateTime lastModifiedDate;
|
||||
|
||||
/**
|
||||
* 최종수정자ID
|
||||
*/
|
||||
@Column(name = "LAST_MODIFIED_BY")
|
||||
@LastModifiedBy
|
||||
private String lastModifiedBy;
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.eactive.apim.portal.common.entity;
|
||||
|
||||
public enum EnabledStatus {
|
||||
Y("사용"),
|
||||
N("미사용");
|
||||
|
||||
private String description;
|
||||
|
||||
EnabledStatus(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package com.eactive.apim.portal.common.event;
|
||||
|
||||
import com.eactive.apim.portal.template.service.MessageEventHandler;
|
||||
import com.eactive.apim.portal.template.service.MessageRecipient;
|
||||
import com.eactive.apim.portal.template.service.MessageSendEvent;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@Component
|
||||
public class UnknownErrorEventHandler implements MessageEventHandler {
|
||||
|
||||
public static final String KEY = "unknown_error";
|
||||
|
||||
@Override
|
||||
public String getKey() {
|
||||
return KEY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean allowAdditionalRecipients() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayName() {
|
||||
return "알수 없는 에러 발생";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "<div>사용 가능 변수: </div><br/>"
|
||||
+ "<div> - userName 수신자 이름 </div><br/>"
|
||||
+ "<div> - userId 수신자 ID (이메일) </div><br/>"
|
||||
+ "<div> - errorMessage 에러 메시지 </div><br/>"
|
||||
+ "<div> - stackTrace Stack Trace </div><br/>"
|
||||
+ "<div> - requestURL URL </div><br/>"
|
||||
+ "<div> - requestParams 파라미터 </div><br/>";
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageSendEvent createEvent(Object source, MessageRecipient recipient, Map<String, Object> params) {
|
||||
Map<String, String> requestParams = new HashMap<>();
|
||||
requestParams.put("errorMessage", (String) params.get("errorMessage"));
|
||||
requestParams.put("stackTrace", (String) params.get("stackTrace"));
|
||||
requestParams.put("requestURL", (String) params.get("requestURL"));
|
||||
requestParams.put("requestParams", params.get("requestParams").toString());
|
||||
return new MessageSendEvent(source, KEY, recipient, requestParams);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.eactive.apim.portal.common.exception;
|
||||
|
||||
public class IntegrationException extends RuntimeException {
|
||||
public IntegrationException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.eactive.apim.portal.common.exception;
|
||||
|
||||
public class NotFoundException extends IllegalArgumentException {
|
||||
public NotFoundException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.eactive.apim.portal.common.exception;
|
||||
|
||||
public class UserNotFoundException extends RuntimeException {
|
||||
public UserNotFoundException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.eactive.apim.portal.common.exception;
|
||||
|
||||
public class UserPasswordResetException extends RuntimeException {
|
||||
public UserPasswordResetException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package com.eactive.apim.portal.file.entity;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import javax.persistence.*;
|
||||
|
||||
/**
|
||||
* PT_FILE_DETAIL 파일상세정보
|
||||
*
|
||||
* @author Sungpil Hyun
|
||||
* @version 1.0
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode
|
||||
@Entity
|
||||
@Table(name = "ptl_file_detail")
|
||||
@IdClass(FileDetailId.class)
|
||||
public class FileDetail {
|
||||
|
||||
/**
|
||||
* 첨부 파일 ID
|
||||
*/
|
||||
@Id
|
||||
@Column(name = "FILE_ID", nullable = false)
|
||||
private String fileId;
|
||||
|
||||
/**
|
||||
* 파일 순번
|
||||
*/
|
||||
@Id
|
||||
@Column(name = "FILE_SN", nullable = false)
|
||||
private Integer fileSn;
|
||||
|
||||
/**
|
||||
* 파일 내용
|
||||
*/
|
||||
@Column(name = "FILE_CN", nullable = true)
|
||||
private String fileContents;
|
||||
|
||||
/**
|
||||
* 파일 확장자
|
||||
*/
|
||||
@Column(name = "FILE_EXTENSION", nullable = false)
|
||||
private String fileExtension;
|
||||
|
||||
/**
|
||||
* 파일 크기
|
||||
*/
|
||||
@Column(name = "FILE_SIZE", nullable = true)
|
||||
private Integer fileSize;
|
||||
|
||||
/**
|
||||
* 원파일 이름
|
||||
*/
|
||||
@Column(name = "ORIGINAL_FILE_NAME", nullable = true)
|
||||
private String originalFileName;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package com.eactive.apim.portal.file.entity;
|
||||
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Embeddable;
|
||||
import java.io.Serializable;
|
||||
import java.util.Objects;
|
||||
|
||||
@Embeddable
|
||||
public class FileDetailId implements Serializable {
|
||||
|
||||
@Column(name = "FILE_ID", nullable = false)
|
||||
private String fileId;
|
||||
|
||||
@Column(name = "FILE_SN", nullable = false)
|
||||
private Integer fileSn;
|
||||
|
||||
public String getFileId() {
|
||||
return fileId;
|
||||
}
|
||||
|
||||
public void setFileId(String fileId) {
|
||||
this.fileId = fileId;
|
||||
}
|
||||
|
||||
public Integer getFileSn() {
|
||||
return fileSn;
|
||||
}
|
||||
|
||||
public void setFileSn(Integer fileSn) {
|
||||
this.fileSn = fileSn;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
FileDetailId that = (FileDetailId) o;
|
||||
return Objects.equals(fileId, that.fileId) && Objects.equals(fileSn, that.fileSn);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(fileId, fileSn);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package com.eactive.apim.portal.file.entity;
|
||||
|
||||
import com.eactive.apim.portal.common.entity.Auditable;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import javax.persistence.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* PT_FILE 파일속성
|
||||
*
|
||||
* @author Sungpil Hyun
|
||||
* @version 1.0
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Entity
|
||||
@Table(name = "ptl_file")
|
||||
public class FileInfo extends Auditable {
|
||||
|
||||
/**
|
||||
* 첨부파일ID
|
||||
*/
|
||||
@Id
|
||||
@Column(name = "FILE_ID", nullable = false)
|
||||
private String fileId;
|
||||
|
||||
/**
|
||||
* 사용여부
|
||||
*/
|
||||
@Column(name = "USE_YN", nullable = true)
|
||||
private String useYn;
|
||||
|
||||
@OneToMany(mappedBy = "fileId", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER)
|
||||
private List<FileDetail> fileDetails = new ArrayList<>();
|
||||
|
||||
|
||||
@Transient
|
||||
public String getFileDetailIds() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (FileDetail fileDetail : fileDetails) {
|
||||
sb.append(fileDetail.getFileSn()).append(",");
|
||||
}
|
||||
return StringUtils.removeEnd(sb.toString(), ",");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.eactive.apim.portal.file.exception;
|
||||
|
||||
public class InvalidFileException extends IllegalArgumentException {
|
||||
public InvalidFileException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.eactive.apim.portal.file.repository;
|
||||
|
||||
import com.eactive.apim.portal.file.entity.FileDetail;
|
||||
import com.eactive.apim.portal.file.entity.FileDetailId;
|
||||
import com.eactive.eai.rms.data.EMSDataSource;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
@Repository
|
||||
@EMSDataSource
|
||||
public interface FileDetailRepository extends JpaRepository<FileDetail, FileDetailId> {
|
||||
|
||||
|
||||
List<FileDetail> findAllByFileId(String fileId);
|
||||
|
||||
Optional<FileDetail> findFirstByFileIdAndFileSn(String fileId, Integer fileSn);
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.eactive.apim.portal.file.repository;
|
||||
|
||||
import com.eactive.apim.portal.file.entity.FileInfo;
|
||||
import com.eactive.eai.rms.data.EMSDataSource;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
@EMSDataSource
|
||||
public interface FileRepository extends JpaRepository<FileInfo, String> {
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.eactive.apim.portal.file.service;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Interface for binary content storage
|
||||
*/
|
||||
public interface BinaryStorage {
|
||||
/**
|
||||
* Store binary data and return content identifier
|
||||
*/
|
||||
String store(byte[] content) throws IOException;
|
||||
|
||||
/**
|
||||
* Retrieve binary data using content identifier
|
||||
*/
|
||||
byte[] retrieve(String contentId) throws IOException;
|
||||
|
||||
/**
|
||||
* Delete binary data using content identifier
|
||||
*/
|
||||
void delete(String contentId) throws IOException;
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package com.eactive.apim.portal.file.service;
|
||||
|
||||
import static com.eactive.apim.portal.file.service.StorageType.DATABASE;
|
||||
import static com.eactive.apim.portal.file.service.StorageType.FILESYSTEM;
|
||||
|
||||
import com.eactive.apim.portal.portalproperty.service.PortalPropertyService;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Map;
|
||||
import javax.annotation.PostConstruct;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class BinaryStorageFactory {
|
||||
|
||||
private static final String PORTAL_PROPERTY = "Portal";
|
||||
private final DatabaseBinaryStorage databaseStorage;
|
||||
private final FileSystemBinaryStorage filesystemStorage;
|
||||
private final PortalPropertyService portalPropertyService;
|
||||
|
||||
@PostConstruct
|
||||
public void validateConfiguration() {
|
||||
Map<String, String> properties = portalPropertyService.getPortalPropertiesAsMap(PORTAL_PROPERTY);
|
||||
String storageType = properties.getOrDefault("portal.file_storage.type", "DATABASE");
|
||||
if (StorageType.valueOf(storageType) == FILESYSTEM) {
|
||||
String path = properties.getOrDefault("portal.file_storage.path", "./files");
|
||||
Path basePath = Paths.get(path);
|
||||
if (!Files.exists(basePath)) {
|
||||
try {
|
||||
Files.createDirectories(basePath);
|
||||
} catch (IOException e) {
|
||||
throw new IllegalStateException("Could not create storage directory: " + basePath, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public BinaryStorage getStorage() {
|
||||
Map<String, String> properties = portalPropertyService.getPortalPropertiesAsMap(PORTAL_PROPERTY);
|
||||
String storageType = properties.getOrDefault("portal.file_storage.type", "DATABASE");
|
||||
|
||||
if (StorageType.valueOf(storageType) == FILESYSTEM) {
|
||||
return filesystemStorage;
|
||||
} else {
|
||||
return databaseStorage;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.eactive.apim.portal.file.service;
|
||||
|
||||
import java.io.IOException;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class BinaryStorageService {
|
||||
private final BinaryStorageFactory storageFactory;
|
||||
|
||||
public String store(byte[] content) throws IOException {
|
||||
return storageFactory.getStorage().store(content);
|
||||
}
|
||||
|
||||
public byte[] retrieve(String contentId) throws IOException {
|
||||
return storageFactory.getStorage().retrieve(contentId);
|
||||
}
|
||||
|
||||
public void delete(String contentId) throws IOException {
|
||||
storageFactory.getStorage().delete(contentId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.eactive.apim.portal.file.service;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Base64;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class DatabaseBinaryStorage implements BinaryStorage {
|
||||
|
||||
@Override
|
||||
public String store(byte[] content) {
|
||||
return Base64.getEncoder().encodeToString(content);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] retrieve(String contentId) throws IOException {
|
||||
try {
|
||||
return Base64.getDecoder().decode(contentId);
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new IOException("Failed to decode content", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(String contentId) {
|
||||
// No action needed - handled by database
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.eactive.apim.portal.file.service;
|
||||
|
||||
public class FileNotFoundException extends RuntimeException {
|
||||
public FileNotFoundException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,225 @@
|
||||
package com.eactive.apim.portal.file.service;
|
||||
|
||||
import com.eactive.apim.portal.file.entity.FileDetail;
|
||||
import com.eactive.apim.portal.file.entity.FileInfo;
|
||||
import com.eactive.apim.portal.file.repository.FileDetailRepository;
|
||||
import com.eactive.apim.portal.file.repository.FileRepository;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import com.eactive.apim.portal.file.exception.InvalidFileException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.*;
|
||||
|
||||
@Service("fileService")
|
||||
@Transactional
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class FileService {
|
||||
|
||||
private final FileRepository fileRepository;
|
||||
private final FileDetailRepository fileDetailRepository;
|
||||
private final BinaryStorageService binaryStorageService;
|
||||
private final FileTypeDetector fileTypeDetector;
|
||||
|
||||
/**
|
||||
* 특정 파일 ID를 기반으로 파일 정보를 검색합니다.
|
||||
*
|
||||
* @param fileId 파일 ID
|
||||
* @return 파일 정보
|
||||
*/
|
||||
public FileInfo findById(String fileId) {
|
||||
if (StringUtils.isEmpty(fileId)) {
|
||||
return null;
|
||||
}
|
||||
return fileRepository.findById(fileId).orElseThrow(() -> new FileNotFoundException("파일 정보가 없습니다."));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 특정 파일 ID와 일련 번호를 기반으로 파일 세부 정보를 얻습니다.
|
||||
*
|
||||
* @param fileId 파일 ID
|
||||
* @param fileSn 파일 일련 번호
|
||||
* @return 파일 세부 정보
|
||||
*/
|
||||
public FileDetail getFileDetailByIds(String fileId, int fileSn) {
|
||||
return fileDetailRepository.findFirstByFileIdAndFileSn(fileId, fileSn).orElseThrow(() -> new FileNotFoundException("파일 정보가 없습니다."));
|
||||
}
|
||||
|
||||
|
||||
public byte[] retrieveFileContent(FileDetail fileDetail) throws IOException {
|
||||
return binaryStorageService.retrieve(fileDetail.getFileContents());
|
||||
}
|
||||
|
||||
private void processAttachments(List<MultipartFile> attachFiles, FileInfo fileInfo, int lastFileDetailSn) throws IOException {
|
||||
for (MultipartFile file : attachFiles) {
|
||||
if (file.getSize() > 0) {
|
||||
lastFileDetailSn = addFileDetail(fileInfo, lastFileDetailSn, file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int addFileDetail(FileInfo fileInfo, int lastFileDetailSn, MultipartFile file) throws IOException {
|
||||
String type = fileTypeDetector.detectFileType(file.getBytes());
|
||||
log.debug("upload file type:{}", type);
|
||||
|
||||
if(type.equalsIgnoreCase("unknown")) {
|
||||
throw new InvalidFileException("허용된 파일 형식이 아닙니다. <br>문서파일과 이미지 파일만 등록 가능합니다. <br>(pdf, doc, docx, xls, xlsx, ppt, pptx, hwp, gif, jpg, png)");
|
||||
}
|
||||
|
||||
if (StringUtils.isNotBlank(file.getOriginalFilename()) && file.getSize() > 0) {
|
||||
int fileSn = lastFileDetailSn + 1;
|
||||
FileDetail fileDetail = new FileDetail();
|
||||
fileDetail.setFileId(fileInfo.getFileId());
|
||||
fileDetail.setFileSn(fileSn);
|
||||
fileDetail.setFileSize((int) file.getSize());
|
||||
fileDetail.setOriginalFileName(FilenameUtils.getBaseName(file.getOriginalFilename()));
|
||||
fileDetail.setFileExtension(type);
|
||||
String contentReference = binaryStorageService.store(file.getBytes());
|
||||
fileDetail.setFileContents(contentReference);
|
||||
fileInfo.getFileDetails().add(fileDetail);
|
||||
return fileSn;
|
||||
} else {
|
||||
return lastFileDetailSn;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 다중 파일을 생성합니다.
|
||||
*
|
||||
* @param attachFiles 첨부파일 리스트
|
||||
* @return 파일 정보
|
||||
* @throws IOException 파일 처리 중 오류 발생 시
|
||||
*/
|
||||
public FileInfo createFile(List<MultipartFile> attachFiles) throws IOException {
|
||||
|
||||
FileInfo fileInfo = new FileInfo();
|
||||
String id = UUID.randomUUID().toString();
|
||||
fileInfo.setFileId(id);
|
||||
fileInfo.setCreatedDate(LocalDateTime.now());
|
||||
fileInfo.setUseYn("Y");
|
||||
int lastFileDetailSn = 0;
|
||||
processAttachments(attachFiles, fileInfo, lastFileDetailSn);
|
||||
if (!fileInfo.getFileDetails().isEmpty()) {
|
||||
return fileRepository.save(fileInfo);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 기존 파일을 업데이트합니다.
|
||||
*
|
||||
* @param fileId 파일 ID
|
||||
* @param fileSn 파일 일련 번호 리스트
|
||||
* @param attachFiles 첨부파일 리스트
|
||||
* @return 파일 정보
|
||||
* @throws IOException 파일 처리 중 오류 발생 시
|
||||
*/
|
||||
public FileInfo updateFile(String fileId, List<Integer> fileSn, List<MultipartFile> attachFiles) throws IOException {
|
||||
FileInfo fileInfo = findById(fileId);
|
||||
List<FileDetail> fileDetails = fileInfo.getFileDetails();
|
||||
int lastFileDetailSn = fileDetails.isEmpty() ? 0 : fileDetails.get(fileDetails.size() - 1).getFileSn();
|
||||
if (fileSn != null) {
|
||||
fileDetails.removeIf(fileDetail -> !fileSn.contains(fileDetail.getFileSn()));
|
||||
}
|
||||
processAttachments(attachFiles, fileInfo, lastFileDetailSn);
|
||||
if (!fileInfo.getFileDetails().isEmpty()) {
|
||||
return fileRepository.save(fileInfo);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 특정 파일을 삭제합니다.
|
||||
*
|
||||
* @param fileId 파일 ID
|
||||
*/
|
||||
public void deleteFile(String fileId) {
|
||||
fileRepository.findById(fileId).ifPresent(file -> {
|
||||
file.getFileDetails().forEach(fileDetail -> {
|
||||
try {
|
||||
binaryStorageService.delete(fileDetail.getFileContents());
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
});
|
||||
fileRepository.deleteById(fileId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 단일 파일을 생성하거나 업데이트합니다.
|
||||
*
|
||||
* @param existingFileId 기존 파일 ID (존재할 경우)
|
||||
* @param file 멀티파트 파일
|
||||
* @param fileName 파일 이름
|
||||
* @return 파일 정보
|
||||
* @throws IOException 파일 처리 중 오류 발생 시
|
||||
*/
|
||||
public FileInfo createOrUpdateSingleFile(String existingFileId, MultipartFile file, String fileName) throws IOException {
|
||||
|
||||
// 신규 등록 또는 기존 파일 정보 조회
|
||||
FileInfo fileInfo = Optional.ofNullable(existingFileId)
|
||||
.filter(StringUtils::isNotBlank)
|
||||
.map(this::findById)
|
||||
.orElseGet(() -> {
|
||||
FileInfo newFile = new FileInfo();
|
||||
newFile.setFileId(UUID.randomUUID().toString());
|
||||
newFile.setUseYn("Y");
|
||||
return newFile;
|
||||
});
|
||||
|
||||
// 파일 상세 정보 생성 또는 업데이트
|
||||
FileDetail fileDetail = createFileDetail(fileInfo.getFileId(), 1, file, fileName);
|
||||
|
||||
// 기존 파일 상세 정보 제거 후 새로운 정보 추가
|
||||
fileInfo.getFileDetails().clear();
|
||||
fileInfo.getFileDetails().add(fileDetail);
|
||||
|
||||
return fileRepository.save(fileInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
* 파일 세부 정보를 생성합니다.
|
||||
*
|
||||
* @param fileId 파일 ID
|
||||
* @param fileSn 파일 일련 번호
|
||||
* @param file 멀티파트 파일
|
||||
* @param fileName 파일 이름
|
||||
* @return 파일 세부 정보
|
||||
* @throws IOException 파일 처리 중 오류 발생 시
|
||||
*/
|
||||
private FileDetail createFileDetail(String fileId, int fileSn, MultipartFile file, String fileName) throws IOException {
|
||||
String type = fileTypeDetector.detectFileType(file.getBytes());
|
||||
log.debug("upload file type:{}", type);
|
||||
|
||||
if(type.equalsIgnoreCase("unknown")) {
|
||||
throw new InvalidFileException("허용된 파일 형식이 아닙니다. <br>문서파일과 이미지 파일만 등록 가능합니다. <br>(pdf, doc, docx, xls, xlsx, ppt, pptx, hwp, gif, jpg, png)");
|
||||
}
|
||||
|
||||
FileDetail fileDetail = new FileDetail();
|
||||
fileDetail.setFileId(fileId);
|
||||
fileDetail.setFileSn(fileSn);
|
||||
fileDetail.setFileSize((int) file.getSize());
|
||||
fileDetail.setOriginalFileName(FilenameUtils.getBaseName(fileName));
|
||||
fileDetail.setFileExtension(FilenameUtils.getExtension(fileName));
|
||||
|
||||
String contentReference = binaryStorageService.store(file.getBytes());
|
||||
fileDetail.setFileContents(contentReference);
|
||||
|
||||
return fileDetail;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
package com.eactive.apim.portal.file.service;
|
||||
|
||||
import static com.eactive.apim.portal.file.service.StorageType.FILESYSTEM;
|
||||
|
||||
import com.eactive.apim.portal.portalproperty.service.PortalPropertyService;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.time.LocalDate;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import javax.annotation.PostConstruct;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class FileSystemBinaryStorage implements BinaryStorage {
|
||||
|
||||
private static final String PORTAL_PROPERTY = "Portal";
|
||||
private final PortalPropertyService portalPropertyService;
|
||||
|
||||
@PostConstruct
|
||||
public void init() throws IOException {
|
||||
Map<String, String> properties = portalPropertyService.getPortalPropertiesAsMap(PORTAL_PROPERTY);
|
||||
String storageType = properties.getOrDefault("portal.file_storage.type", "DATABASE");
|
||||
|
||||
if (StorageType.valueOf(storageType) == FILESYSTEM) {
|
||||
Path basePath = getBasePath();
|
||||
Files.createDirectories(basePath);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String store(byte[] content) throws IOException {
|
||||
String fileName = UUID.randomUUID().toString();
|
||||
|
||||
// Get fileType from ThreadLocal
|
||||
String fileType = Optional.ofNullable(FileTypeContext.getFileType())
|
||||
.filter(type -> !type.trim().isEmpty())
|
||||
.orElse("apim");
|
||||
|
||||
// Create full path: basePath/fileType/fileName
|
||||
Path filePath = getBasePath()
|
||||
.resolve(fileType)
|
||||
.resolve(fileName);
|
||||
|
||||
// Create directories if they don't exist
|
||||
Files.createDirectories(filePath.getParent());
|
||||
|
||||
// Write the file
|
||||
Files.write(filePath, content, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
|
||||
|
||||
return fileType + "/" + fileName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] retrieve(String contentId) throws IOException {
|
||||
Path filePath = getBasePath().resolve(contentId);
|
||||
if (!Files.exists(filePath)) {
|
||||
throw new FileNotFoundException("File not found: " + contentId);
|
||||
}
|
||||
return Files.readAllBytes(filePath);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(String contentId) throws IOException {
|
||||
if (contentId != null) {
|
||||
Path filePath = getBasePath().resolve(contentId);
|
||||
Files.deleteIfExists(filePath);
|
||||
}
|
||||
}
|
||||
|
||||
private Path getBasePath() {
|
||||
Map<String, String> properties = portalPropertyService.getPortalPropertiesAsMap(PORTAL_PROPERTY);
|
||||
String path = properties.getOrDefault("portal.file_storage.path", "./files");
|
||||
return Paths.get(path).toAbsolutePath();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.eactive.apim.portal.file.service;
|
||||
|
||||
public class FileTypeContext {
|
||||
private static final ThreadLocal<String> fileTypeHolder = new ThreadLocal<>();
|
||||
|
||||
public static void setFileType(String fileType) {
|
||||
fileTypeHolder.set(fileType);
|
||||
}
|
||||
|
||||
public static String getFileType() {
|
||||
return fileTypeHolder.get();
|
||||
}
|
||||
|
||||
public static void clear() {
|
||||
fileTypeHolder.remove();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,369 @@
|
||||
package com.eactive.apim.portal.file.service;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipInputStream;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 파일의 시그니처와 내용을 분석하여 파일 형식을 감지하는 클래스
|
||||
* 지원하는 파일 형식:
|
||||
* - 이미지: PNG, JPG, GIF(87a, 89a)
|
||||
* - 문서: PDF, HWP, HWPX
|
||||
* - Office: DOC, DOCX, XLS, XLSX, PPT, PPTX
|
||||
*/
|
||||
@Component
|
||||
public class FileTypeDetector {
|
||||
|
||||
private static final int BUFFER_SIZE = 8192;
|
||||
private static final int SMALL_BUFFER_SIZE = 4096;
|
||||
private static final int OLE_HEADER_SIZE = 512;
|
||||
private static final int MIN_FILE_SIZE = 8;
|
||||
|
||||
private static final byte[][] HWP_PATTERNS = {
|
||||
"HwpSummaryInformation".getBytes(StandardCharsets.US_ASCII),
|
||||
"FileHeader".getBytes(StandardCharsets.US_ASCII),
|
||||
"DocInfo".getBytes(StandardCharsets.US_ASCII),
|
||||
"BodyText".getBytes(StandardCharsets.US_ASCII),
|
||||
{0x48, 0x57, 0x50, 0x20, 0x44, 0x6F, 0x63, 0x75, 0x6D, 0x65, 0x6E, 0x74}, // "HWP Document"
|
||||
{0x48, 0x57, 0x50, 0x20, 0x46, 0x69, 0x6C, 0x65}, // "HWP File"
|
||||
};
|
||||
|
||||
private static final byte[][] WORD_PATTERNS = {
|
||||
"Word.Document".getBytes(StandardCharsets.US_ASCII),
|
||||
"MSWordDoc".getBytes(StandardCharsets.US_ASCII),
|
||||
{(byte) 0xEC, (byte) 0xA5, 0x40, (byte) 0xC0} // Word specific binary pattern
|
||||
};
|
||||
|
||||
private static final byte[][] EXCEL_PATTERNS = {
|
||||
"Excel.Sheet".getBytes(StandardCharsets.US_ASCII),
|
||||
"Workbook".getBytes(StandardCharsets.US_ASCII),
|
||||
{0x09, 0x08, 0x10, 0x00, 0x00, 0x06, 0x05, 0x00} // Excel specific binary pattern
|
||||
};
|
||||
|
||||
private static final byte[][] POWERPOINT_PATTERNS = {
|
||||
"PowerPoint".getBytes(StandardCharsets.US_ASCII),
|
||||
"PP97_DUALSTORAGE".getBytes(StandardCharsets.US_ASCII),
|
||||
{0x00, 0x6E, 0x1E, (byte) 0xF0} // PowerPoint specific binary pattern
|
||||
};
|
||||
|
||||
// 파일 시그니처 정의
|
||||
private static final Map<String, byte[]> FILE_SIGNATURES = new HashMap<>();
|
||||
|
||||
static {
|
||||
FILE_SIGNATURES.put("ooxml", new byte[]{0x50, 0x4B, 0x03, 0x04});
|
||||
FILE_SIGNATURES.put("ole2", new byte[]{(byte) 0xD0, (byte) 0xCF, 0x11, (byte) 0xE0, (byte) 0xA1, (byte) 0xB1, 0x1A, (byte) 0xE1});
|
||||
FILE_SIGNATURES.put("pdf", new byte[]{0x25, 0x50, 0x44, 0x46});
|
||||
FILE_SIGNATURES.put("png", new byte[]{(byte) 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A});
|
||||
FILE_SIGNATURES.put("jpg", new byte[]{(byte) 0xFF, (byte) 0xD8, (byte) 0xFF});
|
||||
FILE_SIGNATURES.put("gif_87a", new byte[]{0x47, 0x49, 0x46, 0x38, 0x37, 0x61});
|
||||
FILE_SIGNATURES.put("gif_89a", new byte[]{0x47, 0x49, 0x46, 0x38, 0x39, 0x61});
|
||||
FILE_SIGNATURES.put("hwp", new byte[]{(byte) 0xD0, (byte) 0xCF, 0x11, (byte) 0xE0, (byte) 0xA1, (byte) 0xB1, 0x1A, (byte) 0xE1});
|
||||
FILE_SIGNATURES.put("hwpx", new byte[]{0x50, 0x4B, 0x03, 0x04});
|
||||
}
|
||||
|
||||
/**
|
||||
* 바이트 배열로부터 파일 형식을 감지합니다.
|
||||
* @param bytes 검사할 파일의 바이트 배열
|
||||
* @return 파일 형식을 나타내는 문자열 (unknown인 경우 파일 형식을 판별할 수 없음)
|
||||
* @throws IllegalArgumentException bytes가 null이거나 너무 작은 경우
|
||||
*/
|
||||
public String detectFileType(byte[] bytes) {
|
||||
if (bytes == null || bytes.length < MIN_FILE_SIZE) {
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
try {
|
||||
// GIF 버전 확인
|
||||
if (matchSignature(bytes, FILE_SIGNATURES.get("gif_87a"))) {
|
||||
if (isValidGif(bytes)) {
|
||||
return "gif";
|
||||
}
|
||||
}
|
||||
if (matchSignature(bytes, FILE_SIGNATURES.get("gif_89a"))) {
|
||||
if (isValidGif(bytes)) {
|
||||
return "gif";
|
||||
}
|
||||
}
|
||||
|
||||
// PNG 확인
|
||||
if (matchSignature(bytes, FILE_SIGNATURES.get("png"))) {
|
||||
if (isValidPng(bytes)) {
|
||||
return "png";
|
||||
}
|
||||
}
|
||||
|
||||
// JPG 확인
|
||||
if (matchSignature(bytes, FILE_SIGNATURES.get("jpg"))) {
|
||||
if (isValidJpeg(bytes)) {
|
||||
return "jpg";
|
||||
}
|
||||
}
|
||||
|
||||
// PDF 확인
|
||||
if (matchSignature(bytes, FILE_SIGNATURES.get("pdf"))) {
|
||||
return "pdf";
|
||||
}
|
||||
|
||||
// HWPX 또는 OOXML 확인 (ZIP 기반)
|
||||
if (matchSignature(bytes, FILE_SIGNATURES.get("hwpx"))) {
|
||||
try {
|
||||
String zipType = detectZipBasedType(bytes);
|
||||
if (zipType != null) {
|
||||
return zipType;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
// HWP 또는 OLE2 확인
|
||||
if (matchSignature(bytes, FILE_SIGNATURES.get("hwp"))) {
|
||||
if (isHwpFile(bytes)) {
|
||||
return "hwp";
|
||||
}
|
||||
return detectOLE2TypeByContent(bytes);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
return "unknown";
|
||||
}
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
private static boolean isValidGif(byte[] bytes) {
|
||||
if (bytes.length < 13) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 너비와 높이 추출 (리틀 엔디안)
|
||||
int width = (bytes[7] & 0xFF) << 8 | (bytes[6] & 0xFF);
|
||||
int height = (bytes[9] & 0xFF) << 8 | (bytes[8] & 0xFF);
|
||||
|
||||
return width > 0 && height > 0;
|
||||
}
|
||||
|
||||
private static boolean isValidPng(byte[] bytes) throws IOException {
|
||||
if (bytes.length < 29) { // 8 (signature) + 4 (length) + 4 (IHDR) + 13 (IHDR data)
|
||||
return false;
|
||||
}
|
||||
|
||||
// IHDR 길이는 항상 13
|
||||
int length = ((bytes[8] & 0xFF) << 24) |
|
||||
((bytes[9] & 0xFF) << 16) |
|
||||
((bytes[10] & 0xFF) << 8) |
|
||||
(bytes[11] & 0xFF);
|
||||
if (length != 13) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// IHDR 청크 타입 확인
|
||||
String ihdrType = new String(bytes, 12, 4, StandardCharsets.US_ASCII);
|
||||
if (!ihdrType.equals("IHDR")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 너비와 높이 추출
|
||||
int width = ((bytes[16] & 0xFF) << 24) |
|
||||
((bytes[17] & 0xFF) << 16) |
|
||||
((bytes[18] & 0xFF) << 8) |
|
||||
(bytes[19] & 0xFF);
|
||||
|
||||
int height = ((bytes[20] & 0xFF) << 24) |
|
||||
((bytes[21] & 0xFF) << 16) |
|
||||
((bytes[22] & 0xFF) << 8) |
|
||||
(bytes[23] & 0xFF);
|
||||
|
||||
if (width <= 0 || height <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 비트 깊이 확인
|
||||
int bitDepth = bytes[24] & 0xFF;
|
||||
if (bitDepth != 1 && bitDepth != 2 && bitDepth != 4 &&
|
||||
bitDepth != 8 && bitDepth != 16) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 색상 타입 확인
|
||||
int colorType = bytes[25] & 0xFF;
|
||||
if (colorType != 0 && colorType != 2 && colorType != 3 &&
|
||||
colorType != 4 && colorType != 6) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 압축 방식 확인
|
||||
if (bytes[26] != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 필터 방식 확인
|
||||
if (bytes[27] != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 인터레이스 방식 확인
|
||||
return bytes[28] == 0 || bytes[28] == 1;
|
||||
}
|
||||
|
||||
private static boolean isValidJpeg(byte[] bytes) {
|
||||
if (bytes.length < 2) {
|
||||
return false;
|
||||
}
|
||||
// JPEG EOI 마커 확인 (0xFF, 0xD9)
|
||||
return (bytes[bytes.length - 2] == (byte) 0xFF &&
|
||||
bytes[bytes.length - 1] == (byte) 0xD9);
|
||||
}
|
||||
|
||||
private static boolean isHwpFile(byte[] bytes) {
|
||||
if (bytes.length < OLE_HEADER_SIZE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// OLE 헤더 건너뛰기
|
||||
int offset = OLE_HEADER_SIZE;
|
||||
int remainingLength = bytes.length - offset;
|
||||
byte[] searchBuffer = new byte[Math.min(remainingLength, BUFFER_SIZE)];
|
||||
System.arraycopy(bytes, offset, searchBuffer, 0, searchBuffer.length);
|
||||
|
||||
for (byte[] pattern : HWP_PATTERNS) {
|
||||
if (findPattern(searchBuffer, searchBuffer.length, pattern)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static String detectOLE2TypeByContent(byte[] bytes) {
|
||||
if (bytes.length < OLE_HEADER_SIZE) {
|
||||
return "ole2";
|
||||
}
|
||||
|
||||
// OLE 헤더 건너뛰기
|
||||
int offset = OLE_HEADER_SIZE;
|
||||
int remainingLength = bytes.length - offset;
|
||||
byte[] searchBuffer = new byte[Math.min(remainingLength, BUFFER_SIZE)];
|
||||
System.arraycopy(bytes, offset, searchBuffer, 0, searchBuffer.length);
|
||||
|
||||
// Word 문서 검사
|
||||
for (byte[] pattern : WORD_PATTERNS) {
|
||||
if (findPattern(searchBuffer, searchBuffer.length, pattern)) {
|
||||
return "doc";
|
||||
}
|
||||
}
|
||||
|
||||
// Excel 문서 검사
|
||||
for (byte[] pattern : EXCEL_PATTERNS) {
|
||||
if (findPattern(searchBuffer, searchBuffer.length, pattern)) {
|
||||
return "xls";
|
||||
}
|
||||
}
|
||||
|
||||
// PowerPoint 문서 검사
|
||||
for (byte[] pattern : POWERPOINT_PATTERNS) {
|
||||
if (findPattern(searchBuffer, searchBuffer.length, pattern)) {
|
||||
return "ppt";
|
||||
}
|
||||
}
|
||||
|
||||
return "ole2";
|
||||
}
|
||||
|
||||
private static String detectZipBasedType(byte[] bytes) throws IOException {
|
||||
try (ZipInputStream zis = new ZipInputStream(new java.io.ByteArrayInputStream(bytes))) {
|
||||
ZipEntry entry;
|
||||
while ((entry = zis.getNextEntry()) != null) {
|
||||
try {
|
||||
String name = entry.getName().toLowerCase();
|
||||
|
||||
// HWPX 확인
|
||||
if (name.equals("version.xml") || name.startsWith("contents/")) {
|
||||
byte[] content = new byte[SMALL_BUFFER_SIZE];
|
||||
int len = zis.read(content);
|
||||
if (len > 0) {
|
||||
String contentStr = new String(content, 0, len, StandardCharsets.UTF_8);
|
||||
if (contentStr.contains("hwpml")) {
|
||||
return "hwpx";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// OOXML 확인
|
||||
if (name.equals("[content_types].xml")) {
|
||||
byte[] content = new byte[SMALL_BUFFER_SIZE];
|
||||
int len = zis.read(content);
|
||||
String contentStr = new String(content, 0, len, StandardCharsets.UTF_8);
|
||||
|
||||
if (contentStr.contains("word/document.xml")) return "docx";
|
||||
if (contentStr.contains("xl/workbook.xml")) return "xlsx";
|
||||
if (contentStr.contains("ppt/presentation.xml")) return "pptx";
|
||||
}
|
||||
} finally {
|
||||
zis.closeEntry();
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
private static boolean matchSignature(byte[] buffer, byte[] signature) {
|
||||
if (buffer.length < signature.length) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < signature.length; i++) {
|
||||
if (buffer[i] != signature[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* KMP 알고리즘을 사용한 패턴 검색
|
||||
*/
|
||||
private static boolean findPattern(byte[] buffer, int bufferLength, byte[] pattern) {
|
||||
if (bufferLength < pattern.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int[] failureFunction = computeFailureFunction(pattern);
|
||||
int j = 0; // pattern의 인덱스
|
||||
|
||||
for (int i = 0; i < bufferLength; i++) {
|
||||
while (j > 0 && pattern[j] != buffer[i]) {
|
||||
j = failureFunction[j - 1];
|
||||
}
|
||||
if (pattern[j] == buffer[i]) {
|
||||
j++;
|
||||
}
|
||||
if (j == pattern.length) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* KMP 알고리즘의 실패 함수 계산
|
||||
*/
|
||||
private static int[] computeFailureFunction(byte[] pattern) {
|
||||
int[] failure = new int[pattern.length];
|
||||
int j = 0;
|
||||
|
||||
for (int i = 1; i < pattern.length; i++) {
|
||||
while (j > 0 && pattern[j] != pattern[i]) {
|
||||
j = failure[j - 1];
|
||||
}
|
||||
if (pattern[j] == pattern[i]) {
|
||||
j++;
|
||||
}
|
||||
failure[i] = j;
|
||||
}
|
||||
return failure;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package com.eactive.apim.portal.file.service;
|
||||
|
||||
public enum StorageType {
|
||||
DATABASE,
|
||||
FILESYSTEM
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package com.eactive.apim.portal.invitation.entity;
|
||||
|
||||
import com.eactive.apim.portal.invitation.entity.UserInvitationEnums.InvitationStatus;
|
||||
import com.eactive.eai.data.converter.LocalDateTimeToStringConverter14;
|
||||
import lombok.Data;
|
||||
import org.hibernate.annotations.Comment;
|
||||
import org.hibernate.annotations.GenericGenerator;
|
||||
|
||||
import javax.persistence.*;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
|
||||
@Entity
|
||||
@Table(name = "PTL_USER_INVITATION")
|
||||
public class UserInvitation {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(generator = "uuid")
|
||||
@GenericGenerator(name = "uuid", strategy = "uuid2")
|
||||
@Column(name = "ID", length = 36)
|
||||
private String id;
|
||||
|
||||
@Column(name = "ADMIN_ID", length = 10, nullable = false)
|
||||
@Comment("기관관리자ID")
|
||||
private String adminId;
|
||||
|
||||
@Column(name = "ORG_ID", length = 50, nullable = false)
|
||||
@Comment("기관ID")
|
||||
private String orgId;
|
||||
|
||||
@Column(name = "INVITATION_EMAIL", length = 30, nullable = false)
|
||||
@Comment("초대이메일")
|
||||
private String invitationEmail;
|
||||
|
||||
@Column(name = "INVITATION_DATE", length = 14, nullable = false)
|
||||
@Convert(converter = LocalDateTimeToStringConverter14.class)
|
||||
@Comment("초대일시")
|
||||
private LocalDateTime invitationDate;
|
||||
|
||||
@Column(name = "COMPLETE_DATE", length = 14)
|
||||
@Convert(converter = LocalDateTimeToStringConverter14.class)
|
||||
@Comment("가입완료일시")
|
||||
private LocalDateTime completeDate;
|
||||
|
||||
@Column(name = "STATUS", length = 20, nullable = false)
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Comment("상태")
|
||||
private InvitationStatus status;
|
||||
|
||||
@Column(name = "TOKEN", length = 36, nullable = false)
|
||||
private String token;
|
||||
|
||||
@Column(name = "EXPIRES_ON", length = 14, nullable = false)
|
||||
@Convert(converter = LocalDateTimeToStringConverter14.class)
|
||||
@Comment("만료일시")
|
||||
private LocalDateTime expiresOn;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.eactive.apim.portal.invitation.entity;
|
||||
|
||||
public class UserInvitationEnums {
|
||||
|
||||
public enum InvitationStatus {
|
||||
PENDING("준비"),
|
||||
COMPLETED("완료"),
|
||||
EXPIRED("만료"),
|
||||
CANCELED("취소"),
|
||||
REJECTED("반려");
|
||||
|
||||
private final String description;
|
||||
|
||||
InvitationStatus(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package com.eactive.apim.portal.invitation.event;
|
||||
|
||||
import com.eactive.apim.portal.template.service.MessageEventHandler;
|
||||
import com.eactive.apim.portal.template.service.MessageRecipient;
|
||||
import com.eactive.apim.portal.template.service.MessageSendEvent;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class UserInvitationEvent implements MessageEventHandler {
|
||||
public static final String KEY = "user_invitation";
|
||||
|
||||
@Override
|
||||
public boolean allowAdditionalRecipients() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageSendEvent createEvent(Object source, MessageRecipient recipient, Map<String, Object> params) {
|
||||
Map<String, String> requestParams = new HashMap<>();
|
||||
requestParams.put("company", params.get("company").toString());
|
||||
requestParams.put("manager", params.get("manager").toString());
|
||||
requestParams.put("url", params.get("url").toString());
|
||||
requestParams.put("userName",params.get("company").toString() + " 이용자");
|
||||
return new MessageSendEvent(source, KEY, recipient, requestParams);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getKey() {
|
||||
return KEY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayName() {
|
||||
return "이메일 사용자 초대";
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "<div>사용 가능 변수: </div><br/>"
|
||||
+ "<div> - userId 수신자 ID (이메일) </div><br/>";
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
package com.eactive.apim.portal.invitation.repository;
|
||||
|
||||
import com.eactive.apim.portal.invitation.entity.UserInvitation;
|
||||
import com.eactive.apim.portal.invitation.entity.UserInvitationEnums.InvitationStatus;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
@Repository
|
||||
public interface UserInvitationRepository extends JpaRepository<UserInvitation, String> {
|
||||
|
||||
List<UserInvitation> findByOrgIdAndStatus(String orgId, InvitationStatus status);
|
||||
|
||||
Optional<UserInvitation> findByToken(String token);
|
||||
|
||||
List<UserInvitation> findByStatus(InvitationStatus status);
|
||||
|
||||
Optional<UserInvitation> findFirstByInvitationEmailAndStatus(String email, InvitationStatus status);
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
package com.eactive.apim.portal.monitoringcode.entity;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import javax.persistence.Cacheable;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Convert;
|
||||
import javax.persistence.EmbeddedId;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import org.hibernate.annotations.Cache;
|
||||
import org.hibernate.annotations.CacheConcurrencyStrategy;
|
||||
import org.hibernate.annotations.Comment;
|
||||
import org.springframework.data.annotation.CreatedBy;
|
||||
import org.springframework.data.annotation.CreatedDate;
|
||||
import org.springframework.data.annotation.LastModifiedBy;
|
||||
import org.springframework.data.annotation.LastModifiedDate;
|
||||
|
||||
import com.eactive.eai.data.converter.LocalDateTimeToStringConverter14;
|
||||
import com.eactive.eai.data.entity.AbstractEntity;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(onlyExplicitlyIncluded = true, callSuper = true)
|
||||
|
||||
@Cacheable
|
||||
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
|
||||
@Entity
|
||||
@Table(name = "tseairm28")
|
||||
//@org.hibernate.annotations.Table(appliesTo = "tseairm28", comment = "monitoring 코드")
|
||||
public class MonitoringCode extends AbstractEntity<MonitoringCodeId> implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@EmbeddedId
|
||||
private MonitoringCodeId id;
|
||||
|
||||
@Column(nullable = false, length = 100)
|
||||
@Comment("코드명")
|
||||
private String codeName;
|
||||
|
||||
@Column(length = 50)
|
||||
@Comment("extra 1")
|
||||
private String ext1;
|
||||
|
||||
@Column(length = 50)
|
||||
@Comment("extra 2")
|
||||
private String ext2;
|
||||
|
||||
@Column(length = 50)
|
||||
@Comment("extra 3")
|
||||
private String ext3;
|
||||
|
||||
@Column(length = 50)
|
||||
@Comment("extra 4")
|
||||
private String ext4;
|
||||
|
||||
@Column(length = 50)
|
||||
@Comment("extra 5")
|
||||
private String ext5;
|
||||
|
||||
@Column(length = 50)
|
||||
@Comment("상위 코드")
|
||||
private String parentCode;
|
||||
|
||||
@Column(length = 50)
|
||||
@Comment("상위 코드 그룹")
|
||||
private String parentCodeGroup;
|
||||
|
||||
@Column(nullable = false, precision = 10)
|
||||
@Comment("정렬순서")
|
||||
private Integer seq;
|
||||
|
||||
@Column(nullable = false, length = 8)
|
||||
@Comment("생성자")
|
||||
@CreatedBy
|
||||
private String createBy;
|
||||
|
||||
@Column(nullable = false, length = 14)
|
||||
@Comment("생성 일시")
|
||||
@CreatedDate
|
||||
@Convert(converter = LocalDateTimeToStringConverter14.class)
|
||||
private LocalDateTime createOn;
|
||||
|
||||
@Column(nullable = false, length = 8)
|
||||
@Comment("수정자")
|
||||
@LastModifiedBy
|
||||
private String updateBy;
|
||||
|
||||
@Column(nullable = false, length = 14)
|
||||
@Comment("수정 일시")
|
||||
@Convert(converter = LocalDateTimeToStringConverter14.class)
|
||||
@LastModifiedDate
|
||||
private LocalDateTime updateOn;
|
||||
|
||||
@Column(nullable = false, length = 1)
|
||||
@Comment("사용여부")
|
||||
private String useYn;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "codeGroup", insertable = false, updatable = false)
|
||||
@ToString.Exclude
|
||||
private MonitoringCodeGroup monitoringCodeGroup;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package com.eactive.apim.portal.monitoringcode.entity;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
import javax.persistence.Cacheable;
|
||||
import javax.persistence.CascadeType;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.OneToMany;
|
||||
import javax.persistence.OrderBy;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import org.hibernate.annotations.Cache;
|
||||
import org.hibernate.annotations.CacheConcurrencyStrategy;
|
||||
import org.hibernate.annotations.Comment;
|
||||
|
||||
import com.eactive.eai.data.entity.AbstractEntity;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NonNull;
|
||||
import lombok.ToString;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(onlyExplicitlyIncluded = true, callSuper = true)
|
||||
|
||||
@Entity
|
||||
@Table(name = "tseairm27")
|
||||
//@org.hibernate.annotations.Table(appliesTo = "tseairm27", comment = "monitoring 코드 그룹")
|
||||
@Cacheable
|
||||
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
|
||||
public class MonitoringCodeGroup extends AbstractEntity<String> implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Id
|
||||
@Column(unique = true, nullable = false, length = 50)
|
||||
@Comment("코드 그룹")
|
||||
private String codeGroup;
|
||||
|
||||
@Column(nullable = false, length = 1000)
|
||||
@Comment("그룹 설명")
|
||||
private String description;
|
||||
|
||||
@Override
|
||||
public @NonNull String getId() {
|
||||
return codeGroup;
|
||||
}
|
||||
|
||||
@OneToMany(mappedBy = "monitoringCodeGroup", cascade = CascadeType.ALL, orphanRemoval = true)
|
||||
@ToString.Exclude
|
||||
@OrderBy("id.code ASC")
|
||||
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
|
||||
private List<MonitoringCode> monitoringCodes;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package com.eactive.apim.portal.monitoringcode.entity;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import javax.persistence.Cacheable;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Embeddable;
|
||||
|
||||
import org.hibernate.annotations.Cache;
|
||||
import org.hibernate.annotations.CacheConcurrencyStrategy;
|
||||
import org.hibernate.annotations.Comment;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
|
||||
@Embeddable
|
||||
@Cacheable
|
||||
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
|
||||
public class MonitoringCodeId implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Column(nullable = false, length = 50)
|
||||
@Comment("코드 그룹")
|
||||
private String codeGroup;
|
||||
|
||||
@Column(nullable = false, length = 50)
|
||||
@Comment("코드")
|
||||
private String code;
|
||||
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
package com.eactive.apim.portal.partnershipapplication.entity;
|
||||
|
||||
import com.eactive.apim.portal.common.entity.Auditable;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.hibernate.annotations.Comment;
|
||||
import org.hibernate.annotations.GenericGenerator;
|
||||
|
||||
import javax.persistence.*;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(onlyExplicitlyIncluded = true, callSuper = true)
|
||||
|
||||
@Entity
|
||||
@Table(name = "ptl_partnership_application")
|
||||
public class PartnershipApplication extends Auditable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Id
|
||||
@Column(name = "id", length = 38, nullable = false)
|
||||
@GenericGenerator(name = "uuid-gen", strategy = "uuid2")
|
||||
@GeneratedValue(generator = "uuid-gen", strategy = GenerationType.IDENTITY)
|
||||
private String id;
|
||||
|
||||
@Column(name = "biz_subject", length = 255, nullable = false)
|
||||
@Comment("비즈니스제목")
|
||||
private String bizSubject;
|
||||
|
||||
@Column(name = "biz_detail", columnDefinition = "TEXT", nullable = false)
|
||||
@Comment("비즈니스내용")
|
||||
private String bizDetail;
|
||||
|
||||
@Column(name = "file_id")
|
||||
@Comment("첨부파일ID")
|
||||
private String fileId;
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package com.eactive.apim.portal.portalNotice.entity;
|
||||
|
||||
import com.eactive.apim.portal.common.entity.Auditable;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.hibernate.annotations.Comment;
|
||||
import org.hibernate.annotations.GenericGenerator;
|
||||
|
||||
import javax.persistence.*;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(onlyExplicitlyIncluded = true, callSuper = true)
|
||||
|
||||
@Entity
|
||||
@Table(name = "ptl_notice")
|
||||
public class PortalNotice extends Auditable {
|
||||
|
||||
@Id
|
||||
@Column(name = "id", length = 36, nullable = false)
|
||||
@GenericGenerator(name = "uuid-gen", strategy = "uuid2")
|
||||
@GeneratedValue(generator = "uuid-gen", strategy = GenerationType.IDENTITY)
|
||||
private String id;
|
||||
|
||||
@Column(name = "notice_subject", length = 255, nullable = false)
|
||||
@Comment("공지제목")
|
||||
private String noticeSubject;
|
||||
|
||||
@Column(name = "notice_detail", columnDefinition = "TEXT", nullable = false)
|
||||
@Comment("공지내용")
|
||||
private String noticeDetail;
|
||||
|
||||
@Column(name = "file_id", length = 38)
|
||||
@Comment("파일ID")
|
||||
private String fileId;
|
||||
|
||||
|
||||
@Column(name = "read_count")
|
||||
@Comment("조회횟수")
|
||||
private Long readCount;
|
||||
|
||||
@Column(name = "use_yn", length = 1, nullable = false)
|
||||
@Comment("사용여부")
|
||||
private String useYn;
|
||||
|
||||
|
||||
@Column(name = "inquirer_name", length = 50, nullable = false)
|
||||
@Comment("작성자이름")
|
||||
private String inquirerName;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.eactive.apim.portal.portalfaq.entity;
|
||||
|
||||
public enum FaqType {
|
||||
API("API"),
|
||||
PORTAL("포탈이용"),
|
||||
PARTNERSHIP("제휴");
|
||||
|
||||
private String description;
|
||||
|
||||
FaqType(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package com.eactive.apim.portal.portalfaq.entity;
|
||||
|
||||
import com.eactive.apim.portal.common.entity.Auditable;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.hibernate.annotations.Comment;
|
||||
import org.hibernate.annotations.GenericGenerator;
|
||||
|
||||
import javax.persistence.*;
|
||||
|
||||
import static javax.persistence.EnumType.STRING;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(onlyExplicitlyIncluded = true, callSuper = true)
|
||||
|
||||
@Entity
|
||||
@Table(name = "ptl_faq")
|
||||
public class PortalFaq extends Auditable {
|
||||
|
||||
@Id
|
||||
@Column(name = "id", length = 38, nullable = false)
|
||||
@GenericGenerator(name = "uuid-gen", strategy = "uuid2")
|
||||
@GeneratedValue(generator = "uuid-gen", strategy = GenerationType.IDENTITY)
|
||||
private String id;
|
||||
|
||||
@Column(name = "faq_question", length = 255, nullable = false)
|
||||
@Comment("FAQ질문")
|
||||
private String faqQuestion;
|
||||
|
||||
@Column(name = "faq_answer", columnDefinition = "TEXT", nullable = false)
|
||||
@Comment("FAQ답변")
|
||||
private String faqAnswer;
|
||||
|
||||
|
||||
@Column(name = "use_yn", length = 1, nullable = false)
|
||||
@Comment("사용여부")
|
||||
private String useYn;
|
||||
|
||||
@Column(name = "file_id")
|
||||
@Comment("첨부파일ID")
|
||||
private String fileId;
|
||||
|
||||
@Enumerated(value = STRING)
|
||||
@Column(name = "faq_type")
|
||||
@Comment("FAQ타입")
|
||||
private FaqType faqType;
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
package com.eactive.apim.portal.portalorg.entity;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.Session;
|
||||
import org.hibernate.engine.spi.SessionImplementor;
|
||||
import org.hibernate.tuple.ValueGenerator;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class OrgCodeGenerator implements ValueGenerator<String> {
|
||||
private static final Logger log = LoggerFactory.getLogger(OrgCodeGenerator.class);
|
||||
private static final String TABLE_NAME = "PTL_ID";
|
||||
private static final String SELECT_SQL =
|
||||
"SELECT NEXT_ID FROM " + TABLE_NAME +
|
||||
" WHERE TABLE_NAME = 'PTL_ORG' FOR UPDATE";
|
||||
private static final String UPDATE_SQL =
|
||||
"UPDATE " + TABLE_NAME +
|
||||
" SET NEXT_ID = ? WHERE TABLE_NAME = 'PTL_ORG'";
|
||||
|
||||
private static final String CHECK_EXISTS_SQL =
|
||||
"SELECT COUNT(*) FROM " + TABLE_NAME +
|
||||
" WHERE TABLE_NAME = 'PTL_ORG'";
|
||||
|
||||
private static final String INSERT_SQL =
|
||||
"INSERT INTO " + TABLE_NAME +
|
||||
" (TABLE_NAME, NEXT_ID) VALUES ('PTL_ORG', ?)";
|
||||
private static final long INITIAL_VALUE = 1000000000L;
|
||||
|
||||
@Override
|
||||
public String generateValue(Session session, Object owner) {
|
||||
|
||||
if(owner instanceof PortalOrg){
|
||||
PortalOrg portalOrg= (PortalOrg) owner;
|
||||
if(portalOrg.getOrgCode() != null && !portalOrg.getOrgCode().isEmpty()){
|
||||
return portalOrg.getOrgCode();
|
||||
}
|
||||
}
|
||||
|
||||
SessionImplementor sessionImplementor = (SessionImplementor) session;
|
||||
try {
|
||||
Connection connection = sessionImplementor.getJdbcConnectionAccess()
|
||||
.obtainConnection();
|
||||
try {
|
||||
ensureTableInitialized(connection);
|
||||
return generateOrgCode(connection);
|
||||
} finally {
|
||||
sessionImplementor.getJdbcConnectionAccess()
|
||||
.releaseConnection(connection);
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
log.error("Failed to generate organization code", e);
|
||||
throw new HibernateException("Could not generate organization code", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void ensureTableInitialized(Connection connection) throws SQLException {
|
||||
// Check if entry exists
|
||||
try (PreparedStatement checkStmt = connection.prepareStatement(CHECK_EXISTS_SQL)) {
|
||||
ResultSet rs = checkStmt.executeQuery();
|
||||
if (rs.next() && rs.getLong(1) == 0) {
|
||||
// Entry doesn't exist, insert initial value
|
||||
try (PreparedStatement insertStmt = connection.prepareStatement(INSERT_SQL)) {
|
||||
insertStmt.setLong(1, INITIAL_VALUE);
|
||||
insertStmt.executeUpdate();
|
||||
log.info("Initialized PTL_ID table with initial value {} for PTL_ORG", INITIAL_VALUE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String generateOrgCode(Connection connection) throws SQLException {
|
||||
long nextVal = getAndIncrementValue(connection);
|
||||
return String.format("%010d", nextVal);
|
||||
}
|
||||
|
||||
private long getAndIncrementValue(Connection connection) throws SQLException {
|
||||
try (PreparedStatement select = connection.prepareStatement(SELECT_SQL)) {
|
||||
ResultSet rs = select.executeQuery();
|
||||
if (!rs.next()) {
|
||||
throw new HibernateException("Unable to get next organization code value");
|
||||
}
|
||||
|
||||
long nextVal = rs.getLong(1);
|
||||
validateValue(nextVal);
|
||||
|
||||
try (PreparedStatement update = connection.prepareStatement(UPDATE_SQL)) {
|
||||
update.setLong(1, nextVal + 1);
|
||||
update.executeUpdate();
|
||||
}
|
||||
|
||||
return nextVal;
|
||||
}
|
||||
}
|
||||
|
||||
private void validateValue(long value) {
|
||||
if (value > 9999999999L) {
|
||||
throw new HibernateException("Organization code sequence has reached its maximum value");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
package com.eactive.apim.portal.portalorg.entity;
|
||||
|
||||
import com.eactive.apim.portal.common.entity.Auditable;
|
||||
import com.eactive.apim.portal.portalorg.entity.PortalOrgEnums.ApprovalStatus;
|
||||
import com.eactive.apim.portal.portalorg.entity.PortalOrgEnums.OrgStatus;
|
||||
import java.io.Serializable;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.EnumType;
|
||||
import javax.persistence.Enumerated;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Table;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.hibernate.annotations.Comment;
|
||||
import org.hibernate.annotations.GenerationTime;
|
||||
import org.hibernate.annotations.GeneratorType;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(onlyExplicitlyIncluded = true, callSuper = true)
|
||||
@Entity
|
||||
@Table(name = "ptl_org")
|
||||
public class PortalOrg extends Auditable implements Serializable, com.eactive.eai.data.Data {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Id
|
||||
@Column(name = "id", length = 36, nullable = false)
|
||||
private String id;
|
||||
|
||||
@Column(name = "corp_reg_no", length = 20)
|
||||
@Comment("법인등록번호")
|
||||
private String corpRegNo;
|
||||
|
||||
@Column(name = "comp_reg_no", length = 11)
|
||||
@Comment("사업자등록번호")
|
||||
private String compRegNo;
|
||||
|
||||
@Column(name = "org_name", nullable = false, length = 50)
|
||||
@Comment("기관이름")
|
||||
private String orgName;
|
||||
|
||||
@Column(name = "org_code")
|
||||
@Comment("제휴기관코드")
|
||||
@GeneratorType(type = OrgCodeGenerator.class, when = GenerationTime.INSERT)
|
||||
private String orgCode;
|
||||
|
||||
@Column(name = "org_desc", length = 255)
|
||||
@Comment("기관설명")
|
||||
private String orgDesc;
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "org_status", length = 30)
|
||||
@Comment("기관상태")
|
||||
private OrgStatus orgStatus;
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "approval_status", length = 30)
|
||||
@Comment("승인상태")
|
||||
private ApprovalStatus approvalStatus;
|
||||
|
||||
@Column(name = "org_type", length = 10)
|
||||
@Comment("기관타입")
|
||||
private String orgType;
|
||||
|
||||
@Column(name = "ceo_name", length = 50)
|
||||
@Comment("대표자성명")
|
||||
private String ceoName;
|
||||
|
||||
@Column(name = "org_addr", length = 255)
|
||||
@Comment("기관사업장소재지")
|
||||
private String orgAddr;
|
||||
|
||||
@Column(name = "org_sectors", length = 50)
|
||||
@Comment("기관업태")
|
||||
private String orgSectors;
|
||||
|
||||
@Column(name = "org_industrytype", length = 50)
|
||||
@Comment("기관업종")
|
||||
private String orgIndustryType;
|
||||
|
||||
@Column(name = "org_phone_number", length = 20)
|
||||
@Comment("기관전화번호")
|
||||
private String orgPhoneNumber;
|
||||
|
||||
@Column(name = "ip_whitelist", length = 255)
|
||||
@Comment("사용서버IP대역")
|
||||
private String ipWhitelist;
|
||||
|
||||
@Column(name = "comp_reg_file", length = 36)
|
||||
@Comment("사업자등록증")
|
||||
private String compRegFile;
|
||||
|
||||
@Column(name = "service_name", length = 36)
|
||||
@Comment("서비스명")
|
||||
private String serviceName;
|
||||
|
||||
@Column(name = "sc_phone_number", length = 20)
|
||||
@Comment("고객센터연락처")
|
||||
private String scPhoneNumber;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user