Compare commits
10 Commits
19c3640f4f
...
de8fc0256f
| Author | SHA1 | Date | |
|---|---|---|---|
| de8fc0256f | |||
| 48a24b99e1 | |||
| 54b7cedac6 | |||
| 75743c6ef6 | |||
| 86a4bc8b0a | |||
| eff39eb7f8 | |||
| 230ceea0fa | |||
| b099d42883 | |||
| aadf880052 | |||
| ad3cfef96f |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,5 +1,4 @@
|
||||
.*
|
||||
gradlew*
|
||||
build
|
||||
|
||||
!.gitignore
|
||||
33
build.gradle
33
build.gradle
@@ -1,12 +1,17 @@
|
||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||
|
||||
buildscript {
|
||||
ext {
|
||||
kotlin_version = '1.6.10'
|
||||
spring_version = "2.7.0"
|
||||
jvm_version = '21'
|
||||
kotlin_version = '2.0.0'
|
||||
spring_version = '3.3.0'
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenLocal()
|
||||
maven { url 'https://maven.aliyun.com/nexus/content/groups/public/' }
|
||||
maven { url 'https://maven.aliyun.com/repository/gradle-plugin' }
|
||||
maven { url 'https://maven.aliyun.com/repository/central' }
|
||||
maven { url 'https://maven.aliyun.com/repository/public' }
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
@@ -17,19 +22,21 @@ buildscript {
|
||||
|
||||
subprojects {
|
||||
group 'com.synebula'
|
||||
version '1.5.0'
|
||||
version '1.6.0'
|
||||
|
||||
buildscript {
|
||||
repositories {
|
||||
mavenLocal()
|
||||
maven { url 'https://maven.aliyun.com/nexus/content/groups/public/' }
|
||||
maven { url 'https://maven.aliyun.com/repository/central' }
|
||||
maven { url 'https://maven.aliyun.com/repository/public' }
|
||||
mavenCentral()
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenLocal()
|
||||
maven { url 'https://maven.aliyun.com/nexus/content/groups/public/' }
|
||||
maven { url 'https://maven.aliyun.com/repository/central' }
|
||||
maven { url 'https://maven.aliyun.com/repository/public' }
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
@@ -44,14 +51,18 @@ subprojects {
|
||||
testApi group: 'junit', name: 'junit', version: '4.12'
|
||||
}
|
||||
|
||||
sourceCompatibility = 1.8
|
||||
targetCompatibility = 1.8
|
||||
|
||||
/*** 指定 Java & Kotlin 语言编译目标JVM ***/
|
||||
sourceCompatibility = "$jvm_version"
|
||||
targetCompatibility = "$jvm_version"
|
||||
compileKotlin {
|
||||
kotlinOptions.jvmTarget = "1.8"
|
||||
compilerOptions {
|
||||
jvmTarget = JvmTarget.valueOf("JVM_$jvm_version")
|
||||
}
|
||||
}
|
||||
compileTestKotlin {
|
||||
kotlinOptions.jvmTarget = "1.8"
|
||||
compilerOptions {
|
||||
jvmTarget = JvmTarget.valueOf("JVM_$jvm_version")
|
||||
}
|
||||
}
|
||||
|
||||
publishing {
|
||||
|
||||
6
gradle/wrapper/gradle-wrapper.properties
vendored
6
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,6 +1,6 @@
|
||||
#Mon May 18 17:21:26 CST 2020
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-all.zip
|
||||
#Thu Jun 06 15:31:38 CST 2024
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
||||
234
gradlew
vendored
Normal file
234
gradlew
vendored
Normal file
@@ -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" "$@"
|
||||
90
gradlew.bat
vendored
Normal file
90
gradlew.bat
vendored
Normal file
@@ -0,0 +1,90 @@
|
||||
@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,8 @@
|
||||
package com.synebula.gaea.app.autoconfig
|
||||
|
||||
import org.springframework.context.annotation.ComponentScan
|
||||
import org.springframework.context.annotation.Configuration
|
||||
|
||||
@Configuration
|
||||
@ComponentScan(basePackages = ["com.synebula.gaea.app.bus", "com.synebula.gaea.app.component", "com.synebula.gaea.app.security"])
|
||||
class AppAutoConfiguration
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.synebula.gaea.app.component.bus
|
||||
package com.synebula.gaea.app.bus
|
||||
|
||||
import com.synebula.gaea.bus.Bus
|
||||
import org.springframework.stereotype.Component
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.synebula.gaea.app.component.bus
|
||||
package com.synebula.gaea.app.bus
|
||||
|
||||
import com.synebula.gaea.bus.DomainSubscribe
|
||||
import com.synebula.gaea.bus.IBus
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.synebula.gaea.app.component.cache
|
||||
package com.synebula.gaea.app.cache
|
||||
|
||||
import com.google.common.cache.Cache
|
||||
import com.google.common.cache.CacheBuilder
|
||||
@@ -24,7 +24,7 @@ open class Cache<K, V>(expire: Int) : ICache<K, V> {
|
||||
}
|
||||
|
||||
override fun add(key: K, value: V) {
|
||||
this.guavaCache.put(key, value)
|
||||
this.guavaCache.put(key!!, value!!)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1,6 +1,5 @@
|
||||
package com.synebula.gaea.app.component.poi
|
||||
|
||||
import com.synebula.gaea.app.struct.ExcelData
|
||||
import com.synebula.gaea.data.date.DateTime
|
||||
import com.synebula.gaea.exception.NoticeUserException
|
||||
import org.apache.poi.hpsf.Decimal
|
||||
@@ -40,7 +39,7 @@ object Excel {
|
||||
val titleFont = wb.createFont()
|
||||
titleFont.bold = true
|
||||
titleStyle.setFont(titleFont)
|
||||
setBorderStyle(titleStyle, BorderStyle.THIN)
|
||||
setBorderStyle(titleStyle)
|
||||
|
||||
//声明列对象
|
||||
// 第三步,在sheet中添加表头第0行,注意老版本poi对Excel的行数列数有限制
|
||||
@@ -62,7 +61,7 @@ object Excel {
|
||||
val contentStyle = wb.createCellStyle()
|
||||
contentStyle.alignment = HorizontalAlignment.LEFT// 创建一个修改居左格式
|
||||
contentStyle.verticalAlignment = VerticalAlignment.CENTER
|
||||
setBorderStyle(contentStyle, BorderStyle.THIN)
|
||||
setBorderStyle(contentStyle)
|
||||
|
||||
//创建内容
|
||||
var col = 0
|
||||
@@ -146,6 +145,7 @@ object Excel {
|
||||
this.getCellValue(cell, evaluator).toString().toIntOrNull()
|
||||
}
|
||||
}
|
||||
|
||||
Double::class.java.name -> {
|
||||
if (cell.cellType == CellType.NUMERIC) {
|
||||
cell.numericCellValue
|
||||
@@ -153,6 +153,7 @@ object Excel {
|
||||
this.getCellValue(cell, evaluator).toString().toDoubleOrNull()
|
||||
}
|
||||
}
|
||||
|
||||
Float::class.java.name -> {
|
||||
if (cell.cellType == CellType.NUMERIC) {
|
||||
cell.numericCellValue.toFloat()
|
||||
@@ -160,6 +161,7 @@ object Excel {
|
||||
this.getCellValue(cell, evaluator).toString().toFloatOrNull()
|
||||
}
|
||||
}
|
||||
|
||||
Decimal::class.java.name -> {
|
||||
if (cell.cellType == CellType.NUMERIC) {
|
||||
cell.numericCellValue.toBigDecimal()
|
||||
@@ -167,6 +169,7 @@ object Excel {
|
||||
this.getCellValue(cell, evaluator).toString().toBigDecimalOrNull()
|
||||
}
|
||||
}
|
||||
|
||||
Boolean::class.java.name -> {
|
||||
if (cell.cellType == CellType.BOOLEAN) {
|
||||
cell.booleanCellValue
|
||||
@@ -174,11 +177,13 @@ object Excel {
|
||||
this.getCellValue(cell, evaluator).toString().toBoolean()
|
||||
}
|
||||
}
|
||||
|
||||
Date::class.java.name -> try {
|
||||
cell.dateCellValue
|
||||
} catch (ignored: Exception) {
|
||||
DateTime(cell.stringCellValue).date
|
||||
}
|
||||
|
||||
else -> cell.stringCellValue
|
||||
}
|
||||
rowData[columns[c].first] = value
|
||||
@@ -278,7 +283,7 @@ object Excel {
|
||||
/**
|
||||
* 设置cell style的边框
|
||||
*/
|
||||
private fun setBorderStyle(style: HSSFCellStyle, borderStyle: BorderStyle) {
|
||||
private fun setBorderStyle(style: HSSFCellStyle, borderStyle: BorderStyle = BorderStyle.THIN) {
|
||||
style.borderTop = borderStyle
|
||||
style.borderRight = borderStyle
|
||||
style.borderBottom = borderStyle
|
||||
@@ -297,6 +302,7 @@ object Excel {
|
||||
numericCellValue
|
||||
}
|
||||
}
|
||||
|
||||
CellType.STRING -> cell.richStringCellValue.string
|
||||
CellType.BLANK -> ""
|
||||
CellType.FORMULA -> evaluator.evaluate(cell).toString()
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.synebula.gaea.app.component.poi
|
||||
|
||||
class ExcelData(
|
||||
var title: String = "",
|
||||
var columnNames: List<String> = listOf(),
|
||||
var data: List<List<String>> = listOf()
|
||||
)
|
||||
@@ -1,12 +1,12 @@
|
||||
package com.synebula.gaea.app
|
||||
package com.synebula.gaea.app.controller
|
||||
|
||||
import com.synebula.gaea.app.cmd.ICommandApp
|
||||
import com.synebula.gaea.app.query.IQueryApp
|
||||
import com.synebula.gaea.app.controller.cmd.ICommandApp
|
||||
import com.synebula.gaea.app.controller.query.IQueryApp
|
||||
import com.synebula.gaea.data.message.HttpMessageFactory
|
||||
import com.synebula.gaea.db.query.IQuery
|
||||
import com.synebula.gaea.domain.service.ICommand
|
||||
import com.synebula.gaea.domain.service.IService
|
||||
import com.synebula.gaea.log.ILogger
|
||||
import com.synebula.gaea.query.IQuery
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
|
||||
/**
|
||||
@@ -17,7 +17,7 @@ import org.springframework.beans.factory.annotation.Autowired
|
||||
* @param query 业务查询服务
|
||||
* @param logger 日志组件
|
||||
*/
|
||||
open class Application<TCommand : ICommand, TView, ID>(
|
||||
open class DomainApplication<TCommand : ICommand, TView, ID>(
|
||||
override var name: String,
|
||||
override var service: IService<ID>,
|
||||
override var query: IQuery<TView, ID>,
|
||||
@@ -1,6 +1,6 @@
|
||||
package com.synebula.gaea.app
|
||||
package com.synebula.gaea.app.controller
|
||||
|
||||
import com.google.gson.Gson
|
||||
import com.synebula.gaea.app.security.session.UserSession
|
||||
import com.synebula.gaea.data.message.HttpMessage
|
||||
import com.synebula.gaea.data.message.HttpMessageFactory
|
||||
import com.synebula.gaea.data.message.Status
|
||||
@@ -58,14 +58,12 @@ interface IApplication {
|
||||
|
||||
/**
|
||||
* 获取用户信息
|
||||
* @param clazz 用户信息结构类
|
||||
*/
|
||||
fun <T> userSession(clazz: Class<T>): T? {
|
||||
fun userSession(): UserSession? {
|
||||
try {
|
||||
val authentication = SecurityContextHolder.getContext().authentication.principal.toString()
|
||||
val authentication = SecurityContextHolder.getContext().authentication.principal
|
||||
try {
|
||||
val gson = Gson()
|
||||
return gson.fromJson(authentication, clazz)
|
||||
return authentication as UserSession
|
||||
} catch (ex: Exception) {
|
||||
logger.error(this, ex, "[$name]解析用户信息异常!用户信息:$authentication: ${ex.message}")
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
package com.synebula.gaea.app
|
||||
package com.synebula.gaea.app.controller
|
||||
|
||||
import com.synebula.gaea.app.cmd.ISimpleCommandApp
|
||||
import com.synebula.gaea.app.query.IQueryApp
|
||||
import com.synebula.gaea.app.controller.cmd.ISimpleCommandApp
|
||||
import com.synebula.gaea.app.controller.query.IQueryApp
|
||||
import com.synebula.gaea.data.message.HttpMessageFactory
|
||||
import com.synebula.gaea.db.query.IQuery
|
||||
import com.synebula.gaea.domain.model.IAggregateRoot
|
||||
import com.synebula.gaea.domain.service.ISimpleService
|
||||
import com.synebula.gaea.log.ILogger
|
||||
import com.synebula.gaea.query.IQuery
|
||||
import com.synebula.gaea.record.service.IService
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
|
||||
/**
|
||||
@@ -17,14 +17,13 @@ import org.springframework.beans.factory.annotation.Autowired
|
||||
* @param query 业务查询服务
|
||||
* @param logger 日志组件
|
||||
*/
|
||||
open class SimpleApplication<TRoot : IAggregateRoot<ID>, ID>(
|
||||
open class RecordApplication<TRoot : IAggregateRoot<ID>, ID>(
|
||||
override var name: String,
|
||||
override var service: ISimpleService<TRoot, ID>,
|
||||
override var service: IService<TRoot, ID>,
|
||||
override var query: IQuery<TRoot, ID>,
|
||||
override var logger: ILogger,
|
||||
) : ISimpleCommandApp<TRoot, ID>, IQueryApp<TRoot, ID> {
|
||||
|
||||
|
||||
@Autowired
|
||||
override lateinit var httpMessageFactory: HttpMessageFactory
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.synebula.gaea.app.cmd
|
||||
package com.synebula.gaea.app.controller.cmd
|
||||
|
||||
import com.synebula.gaea.data.message.HttpMessageFactory
|
||||
import com.synebula.gaea.domain.service.ICommand
|
||||
@@ -1,6 +1,6 @@
|
||||
package com.synebula.gaea.app.cmd
|
||||
package com.synebula.gaea.app.controller.cmd
|
||||
|
||||
import com.synebula.gaea.app.IApplication
|
||||
import com.synebula.gaea.app.controller.IApplication
|
||||
import com.synebula.gaea.data.message.HttpMessage
|
||||
import com.synebula.gaea.data.message.Status
|
||||
import com.synebula.gaea.domain.service.ICommand
|
||||
@@ -1,10 +1,10 @@
|
||||
package com.synebula.gaea.app.cmd
|
||||
package com.synebula.gaea.app.controller.cmd
|
||||
|
||||
import com.synebula.gaea.app.IApplication
|
||||
import com.synebula.gaea.app.controller.IApplication
|
||||
import com.synebula.gaea.data.message.HttpMessage
|
||||
import com.synebula.gaea.data.message.Status
|
||||
import com.synebula.gaea.domain.model.IAggregateRoot
|
||||
import com.synebula.gaea.domain.service.ISimpleService
|
||||
import com.synebula.gaea.record.service.IService
|
||||
import com.synebula.gaea.spring.aop.annotation.Method
|
||||
import org.springframework.web.bind.annotation.*
|
||||
|
||||
@@ -16,12 +16,13 @@ import org.springframework.web.bind.annotation.*
|
||||
* @since 2020-05-15
|
||||
*/
|
||||
interface ISimpleCommandApp<TRoot : IAggregateRoot<ID>, ID> : IApplication {
|
||||
var service: ISimpleService<TRoot, ID>
|
||||
var service: IService<TRoot, ID>
|
||||
|
||||
@PostMapping
|
||||
@Method("添加")
|
||||
fun add(@RequestBody entity: TRoot): HttpMessage {
|
||||
return this.httpMessageFactory.create(service.add(entity))
|
||||
val id = service.add(entity)
|
||||
return this.httpMessageFactory.create(id!!)
|
||||
}
|
||||
|
||||
@PutMapping("/{id:.+}")
|
||||
@@ -1,8 +1,8 @@
|
||||
package com.synebula.gaea.app.cmd
|
||||
package com.synebula.gaea.app.controller.cmd
|
||||
|
||||
import com.synebula.gaea.data.message.HttpMessageFactory
|
||||
import com.synebula.gaea.domain.model.IAggregateRoot
|
||||
import com.synebula.gaea.domain.service.ISimpleService
|
||||
import com.synebula.gaea.record.service.IService
|
||||
import com.synebula.gaea.log.ILogger
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
|
||||
@@ -15,7 +15,7 @@ import org.springframework.beans.factory.annotation.Autowired
|
||||
*/
|
||||
open class SimpleCommandApp<TRoot : IAggregateRoot<ID>, ID>(
|
||||
override var name: String,
|
||||
override var service: ISimpleService<TRoot, ID>,
|
||||
override var service: IService<TRoot, ID>,
|
||||
override var logger: ILogger,
|
||||
) : ISimpleCommandApp<TRoot, ID> {
|
||||
@Autowired
|
||||
@@ -1,9 +1,9 @@
|
||||
package com.synebula.gaea.app.query
|
||||
package com.synebula.gaea.app.controller.query
|
||||
|
||||
import com.synebula.gaea.app.IApplication
|
||||
import com.synebula.gaea.app.controller.IApplication
|
||||
import com.synebula.gaea.data.message.HttpMessage
|
||||
import com.synebula.gaea.query.IQuery
|
||||
import com.synebula.gaea.query.Params
|
||||
import com.synebula.gaea.db.query.IQuery
|
||||
import com.synebula.gaea.db.query.Params
|
||||
import com.synebula.gaea.spring.aop.annotation.Method
|
||||
import org.springframework.web.bind.annotation.GetMapping
|
||||
import org.springframework.web.bind.annotation.PathVariable
|
||||
@@ -1,8 +1,8 @@
|
||||
package com.synebula.gaea.app.query
|
||||
package com.synebula.gaea.app.controller.query
|
||||
|
||||
import com.synebula.gaea.data.message.HttpMessageFactory
|
||||
import com.synebula.gaea.db.query.IQuery
|
||||
import com.synebula.gaea.log.ILogger
|
||||
import com.synebula.gaea.query.IQuery
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
|
||||
/**
|
||||
@@ -1,9 +1,9 @@
|
||||
package com.synebula.gaea.app.component.security
|
||||
package com.synebula.gaea.app.security
|
||||
|
||||
import com.auth0.jwt.JWT
|
||||
import com.auth0.jwt.algorithms.Algorithm
|
||||
import com.auth0.jwt.exceptions.TokenExpiredException
|
||||
import com.google.gson.Gson
|
||||
import com.synebula.gaea.app.struct.exception.TokenCloseExpireException
|
||||
import com.synebula.gaea.log.ILogger
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.beans.factory.annotation.Value
|
||||
@@ -71,6 +71,25 @@ class TokenManager {
|
||||
.sign(algorithm)
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验token是否正确
|
||||
*
|
||||
* @param token 密钥
|
||||
* @param func 自定义验证方法
|
||||
*/
|
||||
fun verifyTime(token: String, func: (total: Long, remain: Long) -> Unit) {
|
||||
try {
|
||||
val now = Date()
|
||||
val jwt = JWT.decode(token)
|
||||
val total = jwt.expiresAt.time - jwt.issuedAt.time //总时间
|
||||
val remain = jwt.expiresAt.time - now.time //剩余的时间
|
||||
func(total, remain)
|
||||
} catch (ex: Exception) {
|
||||
this.logger.error(this, ex, "解析token出错")
|
||||
throw ex
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验token是否正确
|
||||
*
|
||||
@@ -78,21 +97,16 @@ class TokenManager {
|
||||
* @return 是否正确
|
||||
*/
|
||||
fun <T> verify(token: String, clazz: Class<T>): T? {
|
||||
try {
|
||||
val now = Date()
|
||||
return try {
|
||||
val algorithm = Algorithm.HMAC256(secret)
|
||||
val jwt = JWT.decode(token)
|
||||
val remain = jwt.expiresAt.time - now.time //剩余的时间
|
||||
val total = jwt.expiresAt.time - jwt.issuedAt.time //总时间
|
||||
if (remain > 0 && 1.0 * remain / total <= 0.3) //存活时间少于总时间的1/3重新下发
|
||||
throw TokenCloseExpireException("", JWT.decode(token).getClaim("user").asString())
|
||||
|
||||
val result = JWT.require(algorithm).build().verify(token)
|
||||
val json = result.getClaim(this.payload).asString()
|
||||
return gson.fromJson(json, clazz)
|
||||
gson.fromJson(json, clazz)
|
||||
} catch (ex: TokenExpiredException) {
|
||||
null
|
||||
} catch (ex: Exception) {
|
||||
this.logger.debug(this, ex, "解析token出错")
|
||||
throw ex
|
||||
this.logger.error(this, ex, "解析token出错")
|
||||
throw ex
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,20 +117,15 @@ class TokenManager {
|
||||
* @return 是否正确
|
||||
*/
|
||||
fun verify(token: String): String {
|
||||
try {
|
||||
val now = Date()
|
||||
return try {
|
||||
val algorithm = Algorithm.HMAC256(secret)
|
||||
val jwt = JWT.decode(token)
|
||||
val remain = jwt.expiresAt.time - now.time //剩余的时间
|
||||
val total = jwt.expiresAt.time - jwt.issuedAt.time //总时间
|
||||
if (remain > 0 && 1.0 * remain / total <= 0.3) //存活时间少于总时间的1/3重新下发
|
||||
throw TokenCloseExpireException("", JWT.decode(token).getClaim("user").asString())
|
||||
|
||||
val result = JWT.require(algorithm).build().verify(token)
|
||||
return result.getClaim(payload).asString()
|
||||
result.getClaim(this.payload).asString()
|
||||
} catch (ex: TokenExpiredException) {
|
||||
""
|
||||
} catch (ex: Exception) {
|
||||
this.logger.debug(this, ex, "解析token出错")
|
||||
throw ex
|
||||
this.logger.error(this, ex, "解析token出错")
|
||||
throw ex
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
package com.synebula.gaea.app.component.security
|
||||
package com.synebula.gaea.app.security
|
||||
|
||||
import com.synebula.gaea.app.component.security.session.UserSessionManager
|
||||
import com.synebula.gaea.app.security.session.UserSessionManager
|
||||
import com.synebula.gaea.data.message.HttpMessageFactory
|
||||
import com.synebula.gaea.data.message.Status
|
||||
import jakarta.servlet.FilterChain
|
||||
import jakarta.servlet.ServletException
|
||||
import jakarta.servlet.http.HttpServletRequest
|
||||
import jakarta.servlet.http.HttpServletResponse
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
|
||||
import org.springframework.security.core.context.SecurityContextHolder
|
||||
import org.springframework.web.filter.OncePerRequestFilter
|
||||
import java.io.IOException
|
||||
import javax.servlet.FilterChain
|
||||
import javax.servlet.ServletException
|
||||
import javax.servlet.http.HttpServletRequest
|
||||
import javax.servlet.http.HttpServletResponse
|
||||
|
||||
|
||||
/**
|
||||
@@ -40,7 +40,9 @@ class WebAuthorization(
|
||||
val identity = if (this.storage == "cookie") {
|
||||
request.cookies.find { it.name == tokenKey }?.value ?: ""
|
||||
} else {
|
||||
request.getHeader(tokenKey) ?: ""
|
||||
var token = request.getHeader(tokenKey) ?: ""
|
||||
if (token.isEmpty()) token = request.getParameter(tokenKey) ?: ""
|
||||
token
|
||||
}
|
||||
val user = this.userSessionManager.userSession(identity)
|
||||
if (user != null) {
|
||||
@@ -62,4 +64,5 @@ class WebAuthorization(
|
||||
response.flushBuffer()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,12 +1,13 @@
|
||||
package com.synebula.gaea.app.component.security
|
||||
package com.synebula.gaea.app.security
|
||||
|
||||
import com.synebula.gaea.app.component.security.session.UserSessionManager
|
||||
import com.synebula.gaea.app.security.session.UserSessionManager
|
||||
import com.synebula.gaea.data.message.HttpMessageFactory
|
||||
import com.synebula.gaea.data.message.Status
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.beans.factory.annotation.Value
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.security.config.Customizer
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer
|
||||
@@ -36,24 +37,27 @@ class WebSecurity {
|
||||
@Bean
|
||||
@Throws(Exception::class)
|
||||
fun filterChain(httpSecurity: HttpSecurity): SecurityFilterChain {
|
||||
httpSecurity.cors().and().csrf().disable() // 跨域伪造请求限制无效
|
||||
httpSecurity.cors(Customizer.withDefaults())
|
||||
.csrf { it.disable() } // 跨域伪造请求限制无效
|
||||
// 设置Session的创建策略为:Spring Security永不创建HttpSession 不使用HttpSession来获取SecurityContext
|
||||
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
|
||||
.sessionManagement { it.sessionCreationPolicy(SessionCreationPolicy.STATELESS) }
|
||||
// 除了登录接口其他资源都必须登录访问
|
||||
.and().authorizeRequests().antMatchers(this.signInPath).permitAll().anyRequest().authenticated()
|
||||
.authorizeHttpRequests { it.requestMatchers(this.signInPath).permitAll().anyRequest().authenticated() }
|
||||
// 添加鉴权拦截器
|
||||
.and().addFilterBefore(
|
||||
.addFilterBefore(
|
||||
WebAuthorization(httpMessageFactory, userSessionManager),
|
||||
UsernamePasswordAuthenticationFilter::class.java
|
||||
).exceptionHandling().authenticationEntryPoint { _, response, _ ->
|
||||
response.status = Status.Success
|
||||
response.characterEncoding = "utf-8"
|
||||
response.contentType = "text/javascript;charset=utf-8"
|
||||
response.writer.print(
|
||||
this.httpMessageFactory.create(
|
||||
Status.Unauthorized, "用户未登录,请重新登录后尝试!"
|
||||
).exceptionHandling {
|
||||
it.authenticationEntryPoint { _, response, _ ->
|
||||
response.status = Status.Success
|
||||
response.characterEncoding = "utf-8"
|
||||
response.contentType = "text/javascript;charset=utf-8"
|
||||
response.writer.print(
|
||||
this.httpMessageFactory.create(
|
||||
Status.Unauthorized, "用户未登录,请重新登录后尝试!"
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return httpSecurity.build()
|
||||
@@ -62,7 +66,7 @@ class WebSecurity {
|
||||
@Bean
|
||||
@Throws(Exception::class)
|
||||
fun ignoringCustomizer(): WebSecurityCustomizer {
|
||||
return WebSecurityCustomizer { web -> web.ignoring().antMatchers(this.signInPath) }
|
||||
return WebSecurityCustomizer { web -> web.ignoring().requestMatchers(this.signInPath) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.synebula.gaea.app.security.session
|
||||
|
||||
import com.synebula.gaea.data.permission.AuthorityType
|
||||
import com.synebula.gaea.data.permission.PermissionType
|
||||
|
||||
open class User {
|
||||
/**
|
||||
* 权限类型, 根据类型来动态应用[permissions]中的权限信息. 通常配置在角色中
|
||||
*/
|
||||
var permissionType = PermissionType.Minimum
|
||||
|
||||
/**
|
||||
* 用户的权限信息. 通常通过角色配置
|
||||
*/
|
||||
var permissions = mapOf<String, AuthorityType>()
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.synebula.gaea.app.component.security.session
|
||||
package com.synebula.gaea.app.security.session
|
||||
|
||||
/**
|
||||
* 登陆用户会话信息
|
||||
@@ -27,7 +27,7 @@ class UserSession(var uid: String, var user: Any) {
|
||||
* 获取指定类型的用户信息
|
||||
*/
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
fun <T> user(): T {
|
||||
fun <T : User> user(): T {
|
||||
return user as T
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
package com.synebula.gaea.app.component.security.session
|
||||
package com.synebula.gaea.app.security.session
|
||||
|
||||
import com.synebula.gaea.app.component.cache.Cache
|
||||
import com.synebula.gaea.app.cache.Cache
|
||||
import com.synebula.gaea.ext.toMd5
|
||||
import org.springframework.beans.factory.annotation.Value
|
||||
import org.springframework.stereotype.Component
|
||||
@@ -1,5 +0,0 @@
|
||||
package com.synebula.gaea.app.struct
|
||||
|
||||
class ExcelData(var title: String = "",
|
||||
var columnNames: List<String> = listOf(),
|
||||
var data: List<List<String>> = listOf())
|
||||
@@ -1,5 +0,0 @@
|
||||
package com.synebula.gaea.app.struct.exception
|
||||
|
||||
import com.auth0.jwt.exceptions.TokenExpiredException
|
||||
|
||||
class TokenCloseExpireException(msg: String, var payload: String) : TokenExpiredException(msg)
|
||||
@@ -0,0 +1 @@
|
||||
com.synebula.gaea.app.autoconfig.AppAutoConfiguration
|
||||
@@ -5,7 +5,7 @@ ext {
|
||||
dependencies {
|
||||
api project(":src:gaea")
|
||||
|
||||
implementation("org.springframework.boot:spring-boot-starter-data-jpa:$spring_version")
|
||||
api("org.springframework.boot:spring-boot-starter-data-jpa:$spring_version")
|
||||
implementation("org.javassist:javassist:$jassist_version")
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
package com.synebula.gaea.jpa
|
||||
|
||||
import com.synebula.gaea.query.IQuery
|
||||
import com.synebula.gaea.query.Page
|
||||
import com.synebula.gaea.query.Params
|
||||
import com.synebula.gaea.db.query.IQuery
|
||||
import com.synebula.gaea.db.query.Page
|
||||
import com.synebula.gaea.db.query.Params
|
||||
import jakarta.persistence.EntityManager
|
||||
import org.springframework.data.jpa.repository.support.SimpleJpaRepository
|
||||
import javax.persistence.EntityManager
|
||||
|
||||
class JpaQuery<TView, ID>(override var clazz: Class<TView>, entityManager: EntityManager) : IQuery<TView, ID> {
|
||||
protected var repo: SimpleJpaRepository<TView, ID>
|
||||
@@ -14,7 +14,7 @@ class JpaQuery<TView, ID>(override var clazz: Class<TView>, entityManager: Entit
|
||||
}
|
||||
|
||||
override operator fun get(id: ID): TView? {
|
||||
val view = this.repo.findById(id)
|
||||
val view = this.repo.findById(id!!)
|
||||
return if (view.isPresent) view.get() else null
|
||||
}
|
||||
|
||||
|
||||
@@ -2,13 +2,13 @@ package com.synebula.gaea.jpa
|
||||
|
||||
import com.synebula.gaea.domain.model.IAggregateRoot
|
||||
import com.synebula.gaea.domain.repository.IRepository
|
||||
import jakarta.persistence.EntityManager
|
||||
import org.springframework.data.jpa.repository.JpaRepository
|
||||
import org.springframework.data.jpa.repository.support.SimpleJpaRepository
|
||||
import javax.persistence.EntityManager
|
||||
|
||||
|
||||
class JpaRepository<TAggregateRoot : IAggregateRoot<ID>, ID>(
|
||||
override var clazz: Class<TAggregateRoot>,
|
||||
open class JpaRepository<TAggregateRoot : IAggregateRoot<ID>, ID>(
|
||||
final override var clazz: Class<TAggregateRoot>,
|
||||
entityManager: EntityManager
|
||||
) : IRepository<TAggregateRoot, ID> {
|
||||
protected var repo: JpaRepository<TAggregateRoot, ID>? = null
|
||||
|
||||
@@ -1,15 +1,12 @@
|
||||
package com.synebula.gaea.jpa
|
||||
|
||||
import com.synebula.gaea.data.date.DateTime
|
||||
import com.synebula.gaea.query.Operator
|
||||
import com.synebula.gaea.query.Where
|
||||
import com.synebula.gaea.db.query.Operator
|
||||
import com.synebula.gaea.db.query.Where
|
||||
import jakarta.persistence.criteria.*
|
||||
import org.springframework.data.jpa.domain.Specification
|
||||
import java.lang.reflect.Field
|
||||
import java.util.*
|
||||
import javax.persistence.criteria.CriteriaBuilder
|
||||
import javax.persistence.criteria.CriteriaQuery
|
||||
import javax.persistence.criteria.Predicate
|
||||
import javax.persistence.criteria.Root
|
||||
|
||||
|
||||
/**
|
||||
@@ -69,79 +66,209 @@ fun String.tryToDigital(field: Field): Double {
|
||||
* @param clazz 类
|
||||
* @return Specification
|
||||
*/
|
||||
fun Map<String, String>?.toSpecification(clazz: Class<*>): Specification<*> {
|
||||
fun Map<String, String>.toSpecification(clazz: Class<*>): Specification<*> {
|
||||
val rangeStartSuffix = "[0]" //范围查询开始后缀
|
||||
val rangeEndSuffix = "[1]" //范围查询结束后缀
|
||||
return Specification<Any?> { root: Root<Any?>, _: CriteriaQuery<*>?, criteriaBuilder: CriteriaBuilder ->
|
||||
val predicates: MutableList<Predicate> = ArrayList()
|
||||
for (argumentName in this!!.keys) {
|
||||
if (this[argumentName] == null) continue
|
||||
var fieldName = argumentName
|
||||
var operator: Operator
|
||||
|
||||
// 判断是否为range类型(范围内查询)
|
||||
var start = true
|
||||
if (fieldName.endsWith(rangeStartSuffix) || fieldName.endsWith(rangeEndSuffix)) {
|
||||
fieldName = fieldName.substring(fieldName.length - 3)
|
||||
if (fieldName.endsWith(rangeEndSuffix)) start = false
|
||||
}
|
||||
val field = clazz.getDeclaredField(fieldName)
|
||||
val where: Where = field.getDeclaredAnnotation(Where::class.java)
|
||||
operator = where.operator
|
||||
|
||||
// 如果是范围内容, 判断是数值类型还是时间类型
|
||||
if (operator === Operator.Range) {
|
||||
if (clazz.getDeclaredField(fieldName).type != Date::class.java) {
|
||||
operator = if (start) Operator.Gte else Operator.Lte
|
||||
}
|
||||
}
|
||||
var predicate: Predicate
|
||||
var digitalValue: Double
|
||||
val predicates = mutableListOf<Predicate>()
|
||||
for (argumentName in this.keys) {
|
||||
try {
|
||||
var fieldName = argumentName
|
||||
val fieldValue = this[argumentName]!!
|
||||
var operator: Operator = Operator.Default
|
||||
|
||||
// 判断是否为range类型(范围内查询)
|
||||
var start = true
|
||||
if (fieldName.endsWith(rangeStartSuffix) || fieldName.endsWith(rangeEndSuffix)) {
|
||||
fieldName = fieldName.substring(fieldName.length - 3)
|
||||
if (fieldName.endsWith(rangeEndSuffix)) start = false
|
||||
}
|
||||
val fieldTree = fieldName.split("\\.".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
|
||||
//查找是否是嵌入字段, 找到最深的类型
|
||||
var field: Field
|
||||
if (fieldTree.isNotEmpty()) {
|
||||
var hostClass = clazz //需要查找字段所在的class
|
||||
var i = 0
|
||||
do {
|
||||
field = hostClass.getDeclaredField(fieldTree[i])
|
||||
hostClass = field.type
|
||||
i++
|
||||
} while (i < fieldTree.size)
|
||||
} else {
|
||||
field = clazz.getDeclaredField(fieldName)
|
||||
}
|
||||
val where = field.getDeclaredAnnotation(Where::class.java)
|
||||
if (where != null) operator = where.operator
|
||||
|
||||
// 如果是范围内容, 判断是数值类型还是时间类型
|
||||
if (operator === Operator.Range) {
|
||||
if (field.type != Date::class.java) {
|
||||
operator = if (start) Operator.Gte else Operator.Lte
|
||||
}
|
||||
}
|
||||
var predicate: Predicate
|
||||
var digitalValue: Double
|
||||
when (operator) {
|
||||
Operator.Ne -> predicate =
|
||||
criteriaBuilder.notEqual(root.get<Any>(fieldName), this[fieldName]!!.toFieldType(field))
|
||||
Operator.Ne -> predicate = criteriaBuilder.notEqual(
|
||||
getFieldPath<Any>(root, fieldName),
|
||||
typeConvert(field, fieldValue)
|
||||
)
|
||||
|
||||
Operator.Lt -> {
|
||||
digitalValue = this[fieldName]!!.tryToDigital(field)
|
||||
predicate = criteriaBuilder.lessThan(root.get(fieldName), digitalValue)
|
||||
Operator.Lt -> try {
|
||||
digitalValue = parseDigital(field, fieldValue)
|
||||
predicate = criteriaBuilder.lessThan(getFieldPath(root, fieldName), digitalValue)
|
||||
} catch (e: Exception) {
|
||||
throw RuntimeException(
|
||||
String.format(
|
||||
"class [%s] field [%s] can not use annotation Where(Operator.lt)",
|
||||
field.declaringClass.name,
|
||||
field.name
|
||||
), e
|
||||
)
|
||||
}
|
||||
|
||||
Operator.Gt -> {
|
||||
digitalValue = this[fieldName]!!.tryToDigital(field)
|
||||
predicate = criteriaBuilder.greaterThan(root.get(fieldName), digitalValue)
|
||||
Operator.Gt -> try {
|
||||
digitalValue = parseDigital(field, fieldValue)
|
||||
predicate = criteriaBuilder.greaterThan(
|
||||
getFieldPath(
|
||||
root,
|
||||
fieldName
|
||||
), digitalValue
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
throw RuntimeException(
|
||||
String.format(
|
||||
"class [%s] field [%s] can not use annotation Where(Operator.gt)",
|
||||
field.declaringClass.name,
|
||||
field.name
|
||||
), e
|
||||
)
|
||||
}
|
||||
|
||||
Operator.Lte -> {
|
||||
digitalValue = this[fieldName]!!.tryToDigital(field)
|
||||
predicate = criteriaBuilder.lessThanOrEqualTo(root.get(fieldName), digitalValue)
|
||||
Operator.Lte -> try {
|
||||
digitalValue = parseDigital(field, fieldValue)
|
||||
predicate = criteriaBuilder.lessThanOrEqualTo(
|
||||
getFieldPath(
|
||||
root,
|
||||
fieldName
|
||||
), digitalValue
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
throw RuntimeException(
|
||||
String.format(
|
||||
"class [%s] field [%s] can not use annotation Where(Operator.lte)",
|
||||
field.declaringClass.name,
|
||||
field.name
|
||||
), e
|
||||
)
|
||||
}
|
||||
|
||||
Operator.Gte -> {
|
||||
digitalValue = this[fieldName]!!.tryToDigital(field)
|
||||
predicate = criteriaBuilder.greaterThanOrEqualTo(root.get(fieldName), digitalValue)
|
||||
Operator.Gte -> try {
|
||||
digitalValue = parseDigital(field, fieldValue)
|
||||
predicate = criteriaBuilder.greaterThanOrEqualTo(
|
||||
getFieldPath(
|
||||
root,
|
||||
fieldName
|
||||
), digitalValue
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
throw RuntimeException(
|
||||
String.format(
|
||||
"class [%s] field [%s] can not use annotation Where(Operator.gte)",
|
||||
field.declaringClass.name,
|
||||
field.name
|
||||
), e
|
||||
)
|
||||
}
|
||||
|
||||
Operator.Like -> predicate = criteriaBuilder.like(root.get(fieldName), "%${this[fieldName]}%")
|
||||
Operator.Range -> {
|
||||
predicate = if (start) {
|
||||
criteriaBuilder.greaterThanOrEqualTo(root.get(fieldName), this[argumentName]!!)
|
||||
} else {
|
||||
criteriaBuilder.lessThanOrEqualTo(root.get(fieldName), this[argumentName]!!)
|
||||
}
|
||||
}
|
||||
Operator.Like -> predicate = criteriaBuilder.like(
|
||||
getFieldPath(root, fieldName),
|
||||
String.format("%%%s%%", fieldValue)
|
||||
)
|
||||
|
||||
else -> predicate =
|
||||
criteriaBuilder.equal(root.get<Any>(fieldName), this[fieldName]!!.toFieldType(field))
|
||||
Operator.Range -> predicate = if (start) criteriaBuilder.greaterThanOrEqualTo(
|
||||
getFieldPath(root, fieldName), this[argumentName]!!
|
||||
) else criteriaBuilder.lessThanOrEqualTo(
|
||||
getFieldPath(root, fieldName), this[argumentName]!!
|
||||
)
|
||||
|
||||
else -> predicate = criteriaBuilder.equal(
|
||||
getFieldPath<Any>(root, fieldName),
|
||||
typeConvert(field, fieldValue)
|
||||
)
|
||||
}
|
||||
predicates.add(predicate)
|
||||
} catch (e: NoSuchFieldException) {
|
||||
throw Error(
|
||||
"class [${field.declaringClass.name}] field [${field.name}] can't annotation [@Where(${operator.declaringClass.simpleName}.${operator.name})]",
|
||||
e
|
||||
)
|
||||
throw RuntimeException(e)
|
||||
}
|
||||
}
|
||||
criteriaBuilder.and(*predicates.toTypedArray())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取字段在
|
||||
*/
|
||||
fun <Y> getFieldPath(root: Root<Any?>, field: String): Path<Y>? {
|
||||
val fieldTree = field.split("\\.".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
|
||||
var path: Path<Y>
|
||||
if (fieldTree.isNotEmpty()) {
|
||||
path = root.get(fieldTree[0])
|
||||
for (i in 1 until fieldTree.size) {
|
||||
path = path.get(fieldTree[i])
|
||||
}
|
||||
return path
|
||||
}
|
||||
return root.get(field)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 类型转换
|
||||
*
|
||||
* @param field 字段对象
|
||||
* @param value 值
|
||||
* @return object
|
||||
*/
|
||||
fun typeConvert(field: Field, value: String): Any {
|
||||
var result: Any = value
|
||||
val fieldType = field.type
|
||||
if (fieldType != value.javaClass) {
|
||||
if (Int::class.java == fieldType || Int::class.javaPrimitiveType == fieldType) {
|
||||
result = value.toInt()
|
||||
}
|
||||
if (Double::class.java == fieldType || Double::class.javaPrimitiveType == fieldType) {
|
||||
result = value.toDouble()
|
||||
}
|
||||
if (Float::class.java == fieldType || Float::class.javaPrimitiveType == fieldType) {
|
||||
result = value.toFloat()
|
||||
}
|
||||
if (Date::class.java == fieldType) {
|
||||
result = DateTime(value, "yyyy-MM-dd HH:mm:ss").date
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化数值类型
|
||||
*
|
||||
* @param field 字段对象
|
||||
* @param value 值
|
||||
* @return double
|
||||
*/
|
||||
fun parseDigital(field: Field, value: String): Double {
|
||||
val result: Double
|
||||
val fieldType = field.type
|
||||
result =
|
||||
if (Int::class.java == fieldType || Int::class.javaPrimitiveType == fieldType || Double::class.java == fieldType || Double::class.javaPrimitiveType == fieldType || Float::class.java == fieldType || Float::class.javaPrimitiveType == fieldType) {
|
||||
value.toDouble()
|
||||
} else throw java.lang.RuntimeException(
|
||||
String.format(
|
||||
"class [%s] field [%s] is not digital",
|
||||
field.declaringClass.name,
|
||||
field.name
|
||||
)
|
||||
)
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -5,13 +5,13 @@ import org.springframework.beans.factory.FactoryBean
|
||||
import org.springframework.cglib.proxy.Enhancer
|
||||
import org.springframework.data.repository.Repository
|
||||
|
||||
class JpaRepositoryFactory(
|
||||
class JpaDbContextFactory(
|
||||
private val beanFactory: BeanFactory,
|
||||
private val interfaceType: Class<*>,
|
||||
private val implBeanNames: List<String>
|
||||
) : FactoryBean<Any> {
|
||||
override fun getObject(): Any {
|
||||
val handler: JpaRepositoryProxy<*, *, *> = JpaRepositoryProxy<Repository<Any, Any>, Any, Any>(
|
||||
val handler: JpaDbContextProxy<*, *, *> = JpaDbContextProxy<Repository<Any, Any>, Any, Any>(
|
||||
beanFactory,
|
||||
interfaceType, implBeanNames
|
||||
)
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.synebula.gaea.jpa.proxy
|
||||
|
||||
import com.synebula.gaea.jpa.proxy.method.JpaMethodProxy
|
||||
import jakarta.persistence.EntityManager
|
||||
import javassist.*
|
||||
import javassist.bytecode.AnnotationsAttribute
|
||||
import javassist.bytecode.MethodInfo
|
||||
@@ -24,9 +25,8 @@ import org.springframework.data.repository.Repository
|
||||
import java.lang.reflect.Method
|
||||
import java.lang.reflect.ParameterizedType
|
||||
import java.lang.reflect.Type
|
||||
import javax.persistence.EntityManager
|
||||
|
||||
class JpaRepositoryProxy<T : Repository<S, ID>?, S, ID>(
|
||||
class JpaDbContextProxy<T : Repository<S, ID>?, S, ID>(
|
||||
beanFactory: BeanFactory,
|
||||
interfaceType: Class<*>,
|
||||
implementBeanNames: List<String>?
|
||||
@@ -8,5 +8,5 @@ import kotlin.reflect.KClass
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
@MustBeDocumented
|
||||
@Inherited
|
||||
@Import(JpaRepositoryRegister::class)
|
||||
annotation class JpaRepositoryProxyScan(val basePackages: Array<String> = [], val scanInterfaces: Array<KClass<*>> = [])
|
||||
@Import(JpaDbContextRegister::class)
|
||||
annotation class JpaDbContextProxyScan(val basePackages: Array<String> = [], val scanInterfaces: Array<KClass<*>> = [])
|
||||
@@ -24,17 +24,20 @@ import org.springframework.util.ClassUtils
|
||||
import java.util.*
|
||||
import java.util.stream.Collectors
|
||||
|
||||
class JpaRepositoryRegister : ImportBeanDefinitionRegistrar, ResourceLoaderAware, BeanClassLoaderAware,
|
||||
class JpaDbContextRegister : ImportBeanDefinitionRegistrar, ResourceLoaderAware, BeanClassLoaderAware,
|
||||
EnvironmentAware,
|
||||
BeanFactoryAware {
|
||||
|
||||
private lateinit var environment: Environment
|
||||
private lateinit var resourceLoader: ResourceLoader
|
||||
private var classLoader: ClassLoader? = null
|
||||
private var beanFactory: BeanFactory? = null
|
||||
|
||||
|
||||
override fun registerBeanDefinitions(metadata: AnnotationMetadata, registry: BeanDefinitionRegistry) {
|
||||
val attributes = AnnotationAttributes(
|
||||
metadata.getAnnotationAttributes(
|
||||
JpaRepositoryProxyScan::class.java.name
|
||||
JpaDbContextProxyScan::class.java.name
|
||||
) ?: mapOf()
|
||||
)
|
||||
val basePackages = attributes.getStringArray("basePackages")
|
||||
@@ -55,7 +58,7 @@ class JpaRepositoryRegister : ImportBeanDefinitionRegistrar, ResourceLoaderAware
|
||||
val implClazzDefinitions = scan(basePackages, arrayOf(beanClazzTypeFilter))
|
||||
for (definition in implClazzDefinitions) {
|
||||
definition.isAutowireCandidate = false
|
||||
registry.registerBeanDefinition(Objects.requireNonNull(definition.beanClassName), definition)
|
||||
registry.registerBeanDefinition(Objects.requireNonNull(definition.beanClassName!!), definition)
|
||||
}
|
||||
// 构建bean定义
|
||||
// 1 bean参数
|
||||
@@ -66,7 +69,7 @@ class JpaRepositoryRegister : ImportBeanDefinitionRegistrar, ResourceLoaderAware
|
||||
builder.addConstructorArgValue(beanClazz)
|
||||
builder.addConstructorArgValue(implBeanNames)
|
||||
val definition = builder.rawBeanDefinition as GenericBeanDefinition
|
||||
definition.beanClass = JpaRepositoryFactory::class.java
|
||||
definition.beanClass = JpaDbContextFactory::class.java
|
||||
definition.autowireMode = GenericBeanDefinition.AUTOWIRE_BY_TYPE
|
||||
registry.registerBeanDefinition(beanClazz.name, definition)
|
||||
}
|
||||
@@ -5,7 +5,7 @@ import com.synebula.gaea.jpa.proxy.method.resolver.AbstractMethodResolver
|
||||
import com.synebula.gaea.jpa.proxy.method.resolver.DefaultMethodResolver
|
||||
import com.synebula.gaea.jpa.proxy.method.resolver.FindMethodResolver
|
||||
import com.synebula.gaea.jpa.proxy.method.resolver.PageMethodResolver
|
||||
import com.synebula.gaea.query.Params
|
||||
import com.synebula.gaea.db.query.Params
|
||||
import org.springframework.data.domain.Pageable
|
||||
import org.springframework.data.jpa.domain.Specification
|
||||
import java.lang.reflect.InvocationTargetException
|
||||
|
||||
@@ -10,8 +10,8 @@ class FindMethodResolver(targetMethodName: String, clazz: Class<*>) : AbstractMe
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun mappingArguments(args: Array<Any>): Array<Any> {
|
||||
val params = args[0] as Map<String, String>?
|
||||
val specification = params.toSpecification(entityClazz)
|
||||
return arrayOf(specification)
|
||||
val specification = params?.toSpecification(entityClazz)
|
||||
return if (specification != null) arrayOf(specification) else arrayOf()
|
||||
}
|
||||
|
||||
override fun mappingResult(result: Any): Any {
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
package com.synebula.gaea.jpa.proxy.method.resolver
|
||||
|
||||
import com.synebula.gaea.jpa.toSpecification
|
||||
import com.synebula.gaea.query.Order
|
||||
import com.synebula.gaea.query.Params
|
||||
import com.synebula.gaea.db.query.Order
|
||||
import com.synebula.gaea.db.query.Params
|
||||
import jakarta.persistence.EmbeddedId
|
||||
import jakarta.persistence.Id
|
||||
import org.springframework.data.domain.Page
|
||||
import org.springframework.data.domain.PageRequest
|
||||
import org.springframework.data.domain.Pageable
|
||||
import org.springframework.data.domain.Sort
|
||||
import java.util.*
|
||||
import javax.persistence.EmbeddedId
|
||||
import javax.persistence.Id
|
||||
import com.synebula.gaea.db.query.Page as QueryPage
|
||||
|
||||
/**
|
||||
* 分页方法参数映射
|
||||
@@ -29,8 +30,7 @@ class PageMethodResolver(targetMethodName: String, clazz: Class<*>) : AbstractMe
|
||||
val fields = entityClazz.declaredFields
|
||||
for (field in fields) {
|
||||
val isId = Arrays.stream(field.declaredAnnotations).anyMatch { annotation: Annotation ->
|
||||
(annotation.annotationClass.java == Id::class.java
|
||||
|| annotation.annotationClass.java == EmbeddedId::class.java)
|
||||
(annotation.annotationClass.java == Id::class.java || annotation.annotationClass.java == EmbeddedId::class.java)
|
||||
}
|
||||
if (isId) {
|
||||
sort = Sort.by(Sort.Direction.ASC, field.name)
|
||||
@@ -50,7 +50,7 @@ class PageMethodResolver(targetMethodName: String, clazz: Class<*>) : AbstractMe
|
||||
override fun mappingResult(result: Any): Any {
|
||||
val page = result as Page<*>
|
||||
|
||||
// Page 页面从0开始
|
||||
return com.synebula.gaea.query.Page(page.number + 1, page.size, page.totalElements.toInt(), page.content)
|
||||
// Page 页面从0开始 [com.synebula.gaea.query.Page as QueryPage]
|
||||
return QueryPage(page.number + 1, page.size, page.totalElements.toInt(), page.content)
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
package com.synebula.gaea.mongodb
|
||||
|
||||
import com.synebula.gaea.data.date.DateTime
|
||||
import com.synebula.gaea.query.Operator
|
||||
import com.synebula.gaea.query.Order
|
||||
import com.synebula.gaea.query.Where
|
||||
import com.synebula.gaea.db.query.Operator
|
||||
import com.synebula.gaea.db.query.Order
|
||||
import com.synebula.gaea.db.query.Where
|
||||
import org.springframework.data.domain.Sort
|
||||
import org.springframework.data.mongodb.core.query.Criteria
|
||||
import org.springframework.data.mongodb.core.query.Query
|
||||
|
||||
@@ -4,11 +4,11 @@ import com.synebula.gaea.spring.autoconfig.Factory
|
||||
import com.synebula.gaea.spring.autoconfig.Proxy
|
||||
import org.springframework.beans.factory.BeanFactory
|
||||
|
||||
class MongodbRepositoryFactory(
|
||||
class MongoDbContextFactory(
|
||||
supertype: Class<*>,
|
||||
var beanFactory: BeanFactory,
|
||||
) : Factory(supertype) {
|
||||
override fun createProxy(): Proxy {
|
||||
return MongodbRepositoryProxy(supertype, this.beanFactory)
|
||||
return MongoDbContextProxy(supertype, this.beanFactory)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package com.synebula.gaea.mongodb.autoconfig
|
||||
|
||||
import com.synebula.gaea.db.context.IDbContext
|
||||
import com.synebula.gaea.domain.repository.IRepository
|
||||
import com.synebula.gaea.mongodb.db.query.MongodbQuery
|
||||
import com.synebula.gaea.mongodb.repository.MongodbRepository
|
||||
import com.synebula.gaea.db.query.IQuery
|
||||
import com.synebula.gaea.reflect.getGenericInterface
|
||||
import com.synebula.gaea.spring.autoconfig.Proxy
|
||||
import org.springframework.beans.factory.BeanFactory
|
||||
import org.springframework.data.mongodb.core.MongoTemplate
|
||||
import java.lang.reflect.Method
|
||||
|
||||
class MongoDbContextProxy(
|
||||
private var supertype: Class<*>, private var beanFactory: BeanFactory
|
||||
) : Proxy() {
|
||||
|
||||
/**
|
||||
* 实际的执行类
|
||||
*/
|
||||
private var context: Any?
|
||||
|
||||
init {
|
||||
if (this.supertype.interfaces.any { it == IDbContext::class.java }) {
|
||||
this.context = beanFactory.getBean(IDbContext::class.java)
|
||||
if (context == null) {
|
||||
val constructor = IDbContext::class.java.getConstructor(Class::class.java, MongoTemplate::class.java)
|
||||
this.context = constructor.newInstance(this.beanFactory.getBean(MongoTemplate::class.java))
|
||||
|
||||
}
|
||||
} else {
|
||||
// 判断接口类型
|
||||
val clazz: Class<*> // 代理服务类型
|
||||
val interfaceClazz: Class<*> // 代理服务接口
|
||||
|
||||
if (this.supertype.interfaces.any { it == IQuery::class.java }) {
|
||||
clazz = MongodbQuery::class.java
|
||||
interfaceClazz = IQuery::class.java
|
||||
} else {
|
||||
clazz = MongodbRepository::class.java
|
||||
interfaceClazz = IRepository::class.java
|
||||
}
|
||||
val constructor = clazz.getConstructor(Class::class.java, MongoTemplate::class.java)
|
||||
this.context = constructor.newInstance(
|
||||
this.supertype.getGenericInterface(interfaceClazz)!!.actualTypeArguments[0],
|
||||
this.beanFactory.getBean(MongoTemplate::class.java)
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行代理方法
|
||||
*
|
||||
* @param proxy 代理对象
|
||||
* @param method 需要执行的方法
|
||||
* @param args 参数列表
|
||||
* @return 方法执行结果
|
||||
*/
|
||||
override fun exec(proxy: Any, method: Method, args: Array<Any>): Any? {
|
||||
try {
|
||||
val proxyMethod: Method = this.context!!.javaClass.getMethod(method.name, *method.parameterTypes)
|
||||
return proxyMethod.invoke(this.context, *args)
|
||||
} catch (ex: NoSuchMethodException) {
|
||||
throw NoSuchMethodException("method [${method.toGenericString()}] not implements in class [${this.context!!.javaClass}], you must implements interface [${this.supertype.name}] ")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,7 +1,8 @@
|
||||
package com.synebula.gaea.mongodb.autoconfig
|
||||
|
||||
import com.synebula.gaea.db.context.IDbContext
|
||||
import com.synebula.gaea.db.query.IQuery
|
||||
import com.synebula.gaea.domain.repository.IRepository
|
||||
import com.synebula.gaea.query.IQuery
|
||||
import com.synebula.gaea.spring.autoconfig.Register
|
||||
import org.springframework.beans.factory.config.BeanDefinition
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder
|
||||
@@ -9,20 +10,20 @@ import org.springframework.beans.factory.support.GenericBeanDefinition
|
||||
import org.springframework.core.annotation.AnnotationAttributes
|
||||
import org.springframework.core.type.AnnotationMetadata
|
||||
|
||||
class MongodbRepositoryRegister : Register() {
|
||||
class MongoDbContextRegister : Register() {
|
||||
override fun scan(metadata: AnnotationMetadata): Map<String, BeanDefinition> {
|
||||
val result = mutableMapOf<String, BeanDefinition>()
|
||||
|
||||
// 获取注解参数信息:basePackages
|
||||
val attributes = AnnotationAttributes(
|
||||
metadata.getAnnotationAttributes(
|
||||
MongodbRepositoryScan::class.java.name
|
||||
MongoDbRepositoryScan::class.java.name
|
||||
) ?: mapOf()
|
||||
)
|
||||
val basePackages = attributes.getStringArray("basePackages")
|
||||
val beanDefinitions = this.doScan(
|
||||
basePackages,
|
||||
arrayOf(this.interfaceFilter(arrayOf(IRepository::class.java, IQuery::class.java)))
|
||||
arrayOf(this.interfaceFilter(arrayOf(IDbContext::class.java, IQuery::class.java, IRepository::class.java)))
|
||||
)
|
||||
beanDefinitions.forEach { beanDefinition ->
|
||||
// 获取实际的bean类型
|
||||
@@ -44,7 +45,7 @@ class MongodbRepositoryRegister : Register() {
|
||||
builder.addConstructorArgValue(beanClazz)
|
||||
builder.addConstructorArgValue(this._beanFactory)
|
||||
val definition = builder.rawBeanDefinition as GenericBeanDefinition
|
||||
definition.beanClass = MongodbRepositoryFactory::class.java
|
||||
definition.beanClass = MongoDbContextFactory::class.java
|
||||
definition.autowireMode = GenericBeanDefinition.AUTOWIRE_BY_TYPE
|
||||
result[beanClazz.name] = definition
|
||||
}
|
||||
@@ -59,7 +60,7 @@ class MongodbRepositoryRegister : Register() {
|
||||
builder.addConstructorArgValue(this._beanFactory)
|
||||
builder.addConstructorArgValue(emptyArray<String>())
|
||||
val definition = builder.rawBeanDefinition as GenericBeanDefinition
|
||||
definition.beanClass = MongodbRepositoryFactory::class.java
|
||||
definition.beanClass = MongoDbContextFactory::class.java
|
||||
definition.autowireMode = GenericBeanDefinition.AUTOWIRE_BY_TYPE
|
||||
result[IRepository::class.java.name] = definition
|
||||
}
|
||||
@@ -8,5 +8,5 @@ import java.lang.annotation.Inherited
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
@MustBeDocumented
|
||||
@Inherited
|
||||
@Import(MongodbRepositoryRegister::class)
|
||||
annotation class MongodbRepositoryScan(val basePackages: Array<String> = [])
|
||||
@Import(MongoDbContextRegister::class)
|
||||
annotation class MongoDbRepositoryScan(val basePackages: Array<String> = [])
|
||||
@@ -1,55 +0,0 @@
|
||||
package com.synebula.gaea.mongodb.autoconfig
|
||||
|
||||
import com.synebula.gaea.domain.repository.IRepository
|
||||
import com.synebula.gaea.mongodb.query.MongodbQuery
|
||||
import com.synebula.gaea.mongodb.repository.MongodbRepository
|
||||
import com.synebula.gaea.query.IQuery
|
||||
import com.synebula.gaea.reflect.getGenericInterface
|
||||
import com.synebula.gaea.spring.autoconfig.Proxy
|
||||
import org.springframework.beans.factory.BeanFactory
|
||||
import org.springframework.data.mongodb.core.MongoTemplate
|
||||
import java.lang.reflect.Method
|
||||
|
||||
class MongodbRepositoryProxy(
|
||||
private var supertype: Class<*>, private var beanFactory: BeanFactory
|
||||
) : Proxy() {
|
||||
|
||||
private var mongodbRepo: Any
|
||||
|
||||
init {
|
||||
// 判断接口类型
|
||||
val clazz: Class<*> // 代理服务类型
|
||||
val interfaceClazz: Class<*> // 代理服务接口
|
||||
if (this.supertype.interfaces.any { it == IRepository::class.java }) {
|
||||
clazz = MongodbRepository::class.java
|
||||
interfaceClazz = IRepository::class.java
|
||||
} else {
|
||||
clazz = MongodbQuery::class.java
|
||||
interfaceClazz = IQuery::class.java
|
||||
}
|
||||
|
||||
val constructor = clazz.getConstructor(Class::class.java, MongoTemplate::class.java)
|
||||
this.mongodbRepo = constructor.newInstance(
|
||||
this.supertype.getGenericInterface(interfaceClazz)!!.actualTypeArguments[0],
|
||||
this.beanFactory.getBean(MongoTemplate::class.java)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行代理方法
|
||||
*
|
||||
* @param proxy 代理对象
|
||||
* @param method 需要执行的方法
|
||||
* @param args 参数列表
|
||||
* @return 方法执行结果
|
||||
*/
|
||||
override fun exec(proxy: Any, method: Method, args: Array<Any>): Any? {
|
||||
try {
|
||||
val proxyMethod: Method = this.mongodbRepo.javaClass.getMethod(method.name, *method.parameterTypes)
|
||||
return proxyMethod.invoke(this.mongodbRepo, *args)
|
||||
} catch (ex: NoSuchMethodException) {
|
||||
throw NoSuchMethodException("method [${method.toGenericString()}] not implements in class [${this.mongodbRepo.javaClass}], you must implements interface [${this.supertype.name}] ")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package com.synebula.gaea.mongodb.db.context
|
||||
|
||||
import com.synebula.gaea.db.IEntity
|
||||
import com.synebula.gaea.db.context.IDbContext
|
||||
import com.synebula.gaea.mongodb.where
|
||||
import com.synebula.gaea.mongodb.whereId
|
||||
import org.springframework.data.mongodb.core.MongoTemplate
|
||||
import org.springframework.data.mongodb.core.query.Query
|
||||
|
||||
class MongodbContext(
|
||||
protected var template: MongoTemplate
|
||||
) : IDbContext {
|
||||
|
||||
override fun <TEntity : IEntity<ID>, ID> add(entity: TEntity, clazz: Class<TEntity>) {
|
||||
this.template.save(entity)
|
||||
}
|
||||
|
||||
override fun <TEntity : IEntity<ID>, ID> add(entities: List<TEntity>, clazz: Class<TEntity>) {
|
||||
this.template.insert(entities, clazz)
|
||||
}
|
||||
|
||||
override fun <TEntity : IEntity<ID>, ID> remove(id: ID, clazz: Class<TEntity>) {
|
||||
this.template.remove(whereId(id), clazz)
|
||||
}
|
||||
|
||||
override fun <TEntity : IEntity<ID>, ID> get(id: ID, clazz: Class<TEntity>): TEntity? {
|
||||
return this.template.findOne(whereId(id), clazz)
|
||||
}
|
||||
|
||||
override fun <TEntity : IEntity<ID>, ID> update(entity: TEntity, clazz: Class<TEntity>) {
|
||||
this.template.save(entity)
|
||||
}
|
||||
|
||||
override fun <TEntity : IEntity<ID>, ID> update(entities: List<TEntity>, clazz: Class<TEntity>) {
|
||||
this.template.save(entities)
|
||||
}
|
||||
|
||||
override fun <TEntity : IEntity<ID>, ID> count(params: Map<String, String>?, clazz: Class<TEntity>): Int {
|
||||
val query = Query()
|
||||
return this.template.count(query.where(params, clazz), clazz).toInt()
|
||||
}
|
||||
|
||||
override val isCommitted=true
|
||||
|
||||
override fun commit() {
|
||||
}
|
||||
|
||||
override fun rollback() {
|
||||
}
|
||||
}
|
||||
@@ -1,24 +1,20 @@
|
||||
package com.synebula.gaea.mongodb.query
|
||||
package com.synebula.gaea.mongodb.db.query
|
||||
|
||||
|
||||
import com.synebula.gaea.ext.firstCharLowerCase
|
||||
import com.synebula.gaea.mongodb.order
|
||||
import com.synebula.gaea.mongodb.select
|
||||
import com.synebula.gaea.mongodb.where
|
||||
import com.synebula.gaea.mongodb.whereId
|
||||
import com.synebula.gaea.query.IQuery
|
||||
import com.synebula.gaea.query.Page
|
||||
import com.synebula.gaea.query.Params
|
||||
import com.synebula.gaea.query.Table
|
||||
import com.synebula.gaea.db.query.IQuery
|
||||
import com.synebula.gaea.db.query.Page
|
||||
import com.synebula.gaea.db.query.Params
|
||||
import com.synebula.gaea.db.query.Table
|
||||
import com.synebula.gaea.reflect.fieldNames
|
||||
import org.springframework.data.mongodb.core.MongoTemplate
|
||||
import org.springframework.data.mongodb.core.query.Criteria
|
||||
import org.springframework.data.mongodb.core.query.Query
|
||||
|
||||
/**
|
||||
* 实现IQuery的Mongodb查询类
|
||||
* @param template MongodbRepo对象
|
||||
*/
|
||||
|
||||
open class MongodbQuery<TView, ID>(override var clazz: Class<TView>, var template: MongoTemplate) :
|
||||
IQuery<TView, ID> {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package com.synebula.gaea.mongodb.query
|
||||
package com.synebula.gaea.mongodb.db.query
|
||||
|
||||
import com.synebula.gaea.query.IQuery
|
||||
import com.synebula.gaea.query.IQueryFactory
|
||||
import com.synebula.gaea.db.query.IQuery
|
||||
import com.synebula.gaea.db.query.IQueryFactory
|
||||
import org.springframework.data.mongodb.core.MongoTemplate
|
||||
|
||||
class MongodbQueryFactory(var template: MongoTemplate) : IQueryFactory {
|
||||
@@ -1,88 +0,0 @@
|
||||
package com.synebula.gaea.mongodb.query
|
||||
|
||||
|
||||
import com.synebula.gaea.ext.firstCharLowerCase
|
||||
import com.synebula.gaea.mongodb.order
|
||||
import com.synebula.gaea.mongodb.select
|
||||
import com.synebula.gaea.mongodb.where
|
||||
import com.synebula.gaea.mongodb.whereId
|
||||
import com.synebula.gaea.query.IUniversalQuery
|
||||
import com.synebula.gaea.query.Page
|
||||
import com.synebula.gaea.query.Params
|
||||
import com.synebula.gaea.query.Table
|
||||
import com.synebula.gaea.reflect.fieldNames
|
||||
import org.springframework.data.mongodb.core.MongoTemplate
|
||||
import org.springframework.data.mongodb.core.query.Criteria
|
||||
import org.springframework.data.mongodb.core.query.Query
|
||||
|
||||
/**
|
||||
* 实现IQuery的Mongodb查询类
|
||||
* @param template MongodbRepo对象
|
||||
*/
|
||||
open class MongodbUniversalQuery(var template: MongoTemplate) : IUniversalQuery {
|
||||
|
||||
/**
|
||||
* 使用View解析是collection时是否校验存在,默认不校验
|
||||
*/
|
||||
var validViewCollection = false
|
||||
|
||||
override fun <TView, ID> get(id: ID, clazz: Class<TView>): TView? {
|
||||
return this.template.findOne(whereId(id), clazz, this.collection(clazz))
|
||||
}
|
||||
|
||||
override fun <TView> list(params: Map<String, String>?, clazz: Class<TView>): List<TView> {
|
||||
val fields = this.fields(clazz)
|
||||
val query = Query()
|
||||
query.where(params, clazz)
|
||||
query.select(fields)
|
||||
return this.find(query, clazz)
|
||||
}
|
||||
|
||||
override fun <TView> count(params: Map<String, String>?, clazz: Class<TView>): Int {
|
||||
val query = Query()
|
||||
return this.template.count(query.where(params, clazz), this.collection(clazz)).toInt()
|
||||
}
|
||||
|
||||
override fun <TView> paging(params: Params, clazz: Class<TView>): Page<TView> {
|
||||
val query = Query()
|
||||
val fields = this.fields(clazz)
|
||||
val result = Page<TView>(params.page, params.size)
|
||||
result.total = this.count(params.parameters, clazz)
|
||||
//如果总数和索引相同,说明该页没有数据,直接跳到上一页
|
||||
if (result.total == result.index) {
|
||||
params.page -= 1
|
||||
result.page -= 1
|
||||
}
|
||||
query.select(fields)
|
||||
query.where(params.parameters, clazz)
|
||||
query.with(order(params.orders))
|
||||
query.skip(params.index).limit(params.size)
|
||||
result.data = this.find(query, clazz)
|
||||
return result
|
||||
}
|
||||
|
||||
override fun <TView> range(field: String, params: List<Any>, clazz: Class<TView>): List<TView> {
|
||||
return this.find(Query.query(Criteria.where(field).`in`(params)), clazz)
|
||||
}
|
||||
|
||||
protected fun <TView> find(query: Query, clazz: Class<TView>): List<TView> {
|
||||
return this.template.find(query, clazz, this.collection(clazz))
|
||||
}
|
||||
|
||||
fun <TView> fields(clazz: Class<TView>): Array<String> {
|
||||
return clazz.fieldNames().toTypedArray()
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取collection
|
||||
*/
|
||||
fun <TView> collection(clazz: Class<TView>): String {
|
||||
val table = clazz.getDeclaredAnnotation(Table::class.java)
|
||||
return if (table != null) table.name
|
||||
else {
|
||||
val name = clazz.simpleName.removeSuffix("View").firstCharLowerCase()
|
||||
if (!validViewCollection || this.template.collectionExists(name)) name
|
||||
else throw RuntimeException("找不到名为[${clazz.name}]的集合")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
package com.synebula.gaea.mongodb.repository
|
||||
|
||||
import com.synebula.gaea.domain.model.IAggregateRoot
|
||||
import com.synebula.gaea.domain.repository.IUniversalRepository
|
||||
import com.synebula.gaea.mongodb.where
|
||||
import com.synebula.gaea.mongodb.whereId
|
||||
import org.springframework.data.mongodb.core.MongoTemplate
|
||||
import org.springframework.data.mongodb.core.query.Query
|
||||
|
||||
/**
|
||||
* 实现ITypedRepository的Mongodb仓储类
|
||||
* @param repo MongodbRepo对象
|
||||
*/
|
||||
open class MongodbUniversalRepository(private var repo: MongoTemplate) : IUniversalRepository {
|
||||
|
||||
override fun <TAggregateRoot : IAggregateRoot<ID>, ID> remove(id: ID, clazz: Class<TAggregateRoot>) {
|
||||
this.repo.remove(whereId(id), clazz)
|
||||
}
|
||||
|
||||
override fun <TAggregateRoot : IAggregateRoot<ID>, ID> get(
|
||||
id: ID,
|
||||
clazz: Class<TAggregateRoot>,
|
||||
): TAggregateRoot? {
|
||||
return this.repo.findOne(whereId(id), clazz)
|
||||
}
|
||||
|
||||
override fun <TAggregateRoot : IAggregateRoot<ID>, ID> update(
|
||||
root: TAggregateRoot,
|
||||
clazz: Class<TAggregateRoot>,
|
||||
) {
|
||||
this.repo.save(root)
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新多个个对象。
|
||||
*
|
||||
* @param roots 需要更新的对象。
|
||||
*/
|
||||
override fun <TAggregateRoot : IAggregateRoot<ID>, ID> update(
|
||||
roots: List<TAggregateRoot>,
|
||||
clazz: Class<TAggregateRoot>
|
||||
) {
|
||||
this.repo.save(roots)
|
||||
}
|
||||
|
||||
override fun <TAggregateRoot : IAggregateRoot<ID>, ID> add(root: TAggregateRoot, clazz: Class<TAggregateRoot>) {
|
||||
this.repo.save(root)
|
||||
}
|
||||
|
||||
override fun <TAggregateRoot : IAggregateRoot<ID>, ID> add(
|
||||
roots: List<TAggregateRoot>,
|
||||
clazz: Class<TAggregateRoot>,
|
||||
) {
|
||||
this.repo.insert(roots, clazz)
|
||||
}
|
||||
|
||||
override fun <TAggregateRoot> count(params: Map<String, String>?, clazz: Class<TAggregateRoot>): Int {
|
||||
val query = Query()
|
||||
return this.repo.count(query.where(params, clazz), clazz).toInt()
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,6 @@ import com.synebula.gaea.data.serialization.json.IJsonSerializer
|
||||
|
||||
class HttpMessage(private var serializer: IJsonSerializer) : DataMessage<Any>() {
|
||||
|
||||
|
||||
constructor(data: Any, serializer: IJsonSerializer) : this(serializer) {
|
||||
this.data = data
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.synebula.gaea.data.permission
|
||||
|
||||
/**
|
||||
* 元素授权类型
|
||||
*/
|
||||
enum class AuthorityType {
|
||||
Default,
|
||||
Deny,
|
||||
Allow
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.synebula.gaea.data.permission
|
||||
|
||||
/**
|
||||
* 角色权限类型
|
||||
*/
|
||||
enum class PermissionType {
|
||||
/**
|
||||
* 拥有所有权限
|
||||
*/
|
||||
All,
|
||||
|
||||
/**
|
||||
* 拥有最大权限
|
||||
* 不配置无权则有权限
|
||||
*/
|
||||
Maximum,
|
||||
|
||||
/**
|
||||
* 最小权限
|
||||
* 不配置有权则无权限
|
||||
*/
|
||||
Minimum,
|
||||
|
||||
/**
|
||||
* 没有任何权限
|
||||
*/
|
||||
None
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.synebula.gaea.domain.model
|
||||
package com.synebula.gaea.db
|
||||
|
||||
/**
|
||||
* 继承本接口,说明对象为实体类型。
|
||||
@@ -0,0 +1,66 @@
|
||||
package com.synebula.gaea.db.context
|
||||
|
||||
import com.synebula.gaea.db.IEntity
|
||||
|
||||
/**
|
||||
* 继承自IUnitOfWork,表示实现了工作单元模式的上下文接口。
|
||||
*
|
||||
* @author alex
|
||||
*/
|
||||
interface IDbContext : IUnitOfWork {
|
||||
/**
|
||||
* 插入单个对象。
|
||||
*
|
||||
* @param entity 需要插入的对象。
|
||||
* @return 返回原对象,如果对象ID为自增,则补充自增ID。
|
||||
*/
|
||||
fun <TEntity : IEntity<ID>, ID> add(entity: TEntity, clazz: Class<TEntity>)
|
||||
|
||||
/**
|
||||
* 插入多个个对象。
|
||||
*
|
||||
* @param entities 需要插入的对象。
|
||||
*/
|
||||
fun <TEntity : IEntity<ID>, ID> add(entities: List<TEntity>, clazz: Class<TEntity>)
|
||||
|
||||
/**
|
||||
* 更新对象。
|
||||
*
|
||||
* @param entity 需要更新的对象。
|
||||
* @return
|
||||
*/
|
||||
fun <TEntity : IEntity<ID>, ID> update(entity: TEntity, clazz: Class<TEntity>)
|
||||
|
||||
/**
|
||||
* 更新多个个对象。
|
||||
*
|
||||
* @param entities 需要更新的对象。
|
||||
*/
|
||||
fun <TEntity : IEntity<ID>, ID> update(entities: List<TEntity>, clazz: Class<TEntity>)
|
||||
|
||||
/**
|
||||
* 通过id删除该条数据
|
||||
*
|
||||
* @param id id
|
||||
* @param clazz 操作数据的类型
|
||||
*/
|
||||
fun <TEntity : IEntity<ID>, ID> remove(id: ID, clazz: Class<TEntity>)
|
||||
|
||||
/**
|
||||
* 根据ID获取对象。
|
||||
*
|
||||
* @param id id
|
||||
* @param clazz 操作数据的类型
|
||||
* @return 聚合根
|
||||
*/
|
||||
fun <TEntity : IEntity<ID>, ID> get(id: ID, clazz: Class<TEntity>): TEntity?
|
||||
|
||||
|
||||
/**
|
||||
* 根据条件查询符合条件记录的数量
|
||||
*
|
||||
* @param params 查询条件。
|
||||
* @return int
|
||||
*/
|
||||
fun <TEntity : IEntity<ID>, ID> count(params: Map<String, String>?, clazz: Class<TEntity>): Int
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.synebula.gaea.domain.repository.context
|
||||
package com.synebula.gaea.db.context
|
||||
|
||||
/**
|
||||
* 表示所有继承于该接口的类型都是Unit Of Work的一种实现。
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.synebula.gaea.query
|
||||
package com.synebula.gaea.db.query
|
||||
|
||||
/**
|
||||
* 查询基接口, 其中方法都指定了查询的视图类型。
|
||||
@@ -1,5 +1,4 @@
|
||||
package com.synebula.gaea.query
|
||||
|
||||
package com.synebula.gaea.db.query
|
||||
|
||||
/**
|
||||
* Query 工厂接口。 定义了Query的创建方法。
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.synebula.gaea.query
|
||||
package com.synebula.gaea.db.query
|
||||
|
||||
enum class Operator {
|
||||
/**
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.synebula.gaea.query
|
||||
package com.synebula.gaea.db.query
|
||||
|
||||
/**
|
||||
* class OrderType
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.synebula.gaea.query
|
||||
package com.synebula.gaea.db.query
|
||||
|
||||
/**
|
||||
* 分页数据。
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.synebula.gaea.query
|
||||
package com.synebula.gaea.db.query
|
||||
|
||||
/**
|
||||
* class 分页参数信息
|
||||
@@ -1,3 +1,3 @@
|
||||
package com.synebula.gaea.query
|
||||
package com.synebula.gaea.db.query
|
||||
|
||||
annotation class Table(val name: String = "")
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.synebula.gaea.query
|
||||
package com.synebula.gaea.db.query
|
||||
|
||||
/**
|
||||
* 字段注解,规定字段的查询方式
|
||||
@@ -1,6 +1,6 @@
|
||||
package com.synebula.gaea.domain.event
|
||||
|
||||
import com.synebula.gaea.data.message.IEvent
|
||||
import com.synebula.gaea.domain.model.IAggregateRoot
|
||||
import com.synebula.gaea.db.IEntity
|
||||
|
||||
class AfterRemoveEvent<T : IAggregateRoot<I>, I>(var id: I? = null) : IEvent
|
||||
class AfterRemoveEvent<T : IEntity<I>, I>(var id: I? = null) : IEvent
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.synebula.gaea.domain.event
|
||||
|
||||
import com.synebula.gaea.data.message.IEvent
|
||||
import com.synebula.gaea.db.IEntity
|
||||
import com.synebula.gaea.domain.model.IAggregateRoot
|
||||
|
||||
class BeforeRemoveEvent<T : IAggregateRoot<I>, I>(var id: I? = null) : IEvent
|
||||
class BeforeRemoveEvent<T : IEntity<I>, I>(var id: I? = null) : IEvent
|
||||
@@ -1,5 +1,5 @@
|
||||
package com.synebula.gaea.domain.model
|
||||
|
||||
abstract class AggregateRoot<ID> : Entity<ID>(), IAggregateRoot<ID> {
|
||||
override var alive: Boolean = true
|
||||
override var avalible: Boolean = true
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
package com.synebula.gaea.domain.model
|
||||
|
||||
import com.synebula.gaea.db.IEntity
|
||||
|
||||
abstract class Entity<ID> : IEntity<ID>
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.synebula.gaea.domain.model
|
||||
|
||||
import com.synebula.gaea.db.IEntity
|
||||
|
||||
/**
|
||||
* 继承本接口,说明对象为聚合根。
|
||||
*
|
||||
@@ -10,5 +12,5 @@ interface IAggregateRoot<ID> : IEntity<ID> {
|
||||
/**
|
||||
* 实体对象是否有效。
|
||||
*/
|
||||
var alive: Boolean
|
||||
var avalible: Boolean
|
||||
}
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
package com.synebula.gaea.domain.model
|
||||
|
||||
/**
|
||||
* 继承本接口,说明对象为值类型。
|
||||
* @author alex
|
||||
*/
|
||||
interface IValue
|
||||
@@ -1,65 +0,0 @@
|
||||
package com.synebula.gaea.domain.repository
|
||||
|
||||
import com.synebula.gaea.domain.model.IAggregateRoot
|
||||
|
||||
/**
|
||||
* 定义了提供增删改的仓储接口。
|
||||
* 本接口泛型放置到方法上,并需要显式提供聚合根的class对象
|
||||
*/
|
||||
interface IUniversalRepository {
|
||||
/**
|
||||
* 插入单个对象。
|
||||
*
|
||||
* @param root 需要插入的对象。
|
||||
* @return 返回原对象,如果对象ID为自增,则补充自增ID。
|
||||
*/
|
||||
fun <TAggregateRoot : IAggregateRoot<ID>, ID> add(root: TAggregateRoot, clazz: Class<TAggregateRoot>)
|
||||
|
||||
/**
|
||||
* 插入多个个对象。
|
||||
*
|
||||
* @param roots 需要插入的对象。
|
||||
*/
|
||||
fun <TAggregateRoot : IAggregateRoot<ID>, ID> add(roots: List<TAggregateRoot>, clazz: Class<TAggregateRoot>)
|
||||
|
||||
/**
|
||||
* 更新对象。
|
||||
*
|
||||
* @param root 需要更新的对象。
|
||||
* @return
|
||||
*/
|
||||
fun <TAggregateRoot : IAggregateRoot<ID>, ID> update(root: TAggregateRoot, clazz: Class<TAggregateRoot>)
|
||||
|
||||
/**
|
||||
* 更新多个个对象。
|
||||
*
|
||||
* @param roots 需要更新的对象。
|
||||
*/
|
||||
fun <TAggregateRoot : IAggregateRoot<ID>, ID> update(roots: List<TAggregateRoot>, clazz: Class<TAggregateRoot>)
|
||||
|
||||
/**
|
||||
* 通过id删除该条数据
|
||||
*
|
||||
* @param id id
|
||||
* @param clazz 操作数据的类型
|
||||
*/
|
||||
fun <TAggregateRoot : IAggregateRoot<ID>, ID> remove(id: ID, clazz: Class<TAggregateRoot>)
|
||||
|
||||
/**
|
||||
* 根据ID获取对象。
|
||||
*
|
||||
* @param id id
|
||||
* @param clazz 操作数据的类型
|
||||
* @return 聚合根
|
||||
*/
|
||||
fun <TAggregateRoot : IAggregateRoot<ID>, ID> get(id: ID, clazz: Class<TAggregateRoot>): TAggregateRoot?
|
||||
|
||||
|
||||
/**
|
||||
* 根据条件查询符合条件记录的数量
|
||||
*
|
||||
* @param params 查询条件。
|
||||
* @return int
|
||||
*/
|
||||
fun <TAggregateRoot> count(params: Map<String, String>?, clazz: Class<TAggregateRoot>): Int
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
package com.synebula.gaea.domain.repository.context
|
||||
|
||||
import com.synebula.gaea.domain.model.IAggregateRoot
|
||||
|
||||
/**
|
||||
* 继承自IUnitOfWork,表示实现了工作单元模式的上下文接口。
|
||||
*
|
||||
* @author alex
|
||||
*/
|
||||
interface IContext : IUnitOfWork {
|
||||
/**
|
||||
* 将指定的聚合根标注为“新建”状态。
|
||||
* @param obj 聚合根
|
||||
*/
|
||||
fun <T : IAggregateRoot<ID>, ID> add(obj: T)
|
||||
|
||||
/**
|
||||
* 将指定的聚合根标注为“更改”状态。
|
||||
*
|
||||
* @param obj 聚合根
|
||||
*/
|
||||
fun <T : IAggregateRoot<ID>, ID> update(obj: T)
|
||||
|
||||
/**
|
||||
* 将指定的聚合根标注为“删除”状态。
|
||||
*
|
||||
* @param obj 聚合根
|
||||
*/
|
||||
fun <T : IAggregateRoot<ID>, ID> remove(obj: T)
|
||||
}
|
||||
@@ -7,7 +7,6 @@ import com.synebula.gaea.domain.event.AfterRemoveEvent
|
||||
import com.synebula.gaea.domain.event.BeforeRemoveEvent
|
||||
import com.synebula.gaea.domain.model.IAggregateRoot
|
||||
import com.synebula.gaea.domain.repository.IRepository
|
||||
import javax.annotation.Resource
|
||||
|
||||
|
||||
/**
|
||||
@@ -25,9 +24,8 @@ open class Service<TRoot : IAggregateRoot<ID>, ID>(
|
||||
protected open var clazz: Class<TRoot>,
|
||||
protected open var repository: IRepository<TRoot, ID>,
|
||||
protected open var mapper: IObjectMapper,
|
||||
) : IService<ID> {
|
||||
@Resource
|
||||
protected open var bus: IBus<Any>? = null
|
||||
) : IService<ID> {
|
||||
|
||||
/**
|
||||
* 增加对象
|
||||
|
||||
@@ -1,69 +0,0 @@
|
||||
package com.synebula.gaea.domain.service
|
||||
|
||||
import com.synebula.gaea.bus.IBus
|
||||
import com.synebula.gaea.data.message.DataMessage
|
||||
import com.synebula.gaea.domain.event.AfterRemoveEvent
|
||||
import com.synebula.gaea.domain.event.BeforeRemoveEvent
|
||||
import com.synebula.gaea.domain.model.IAggregateRoot
|
||||
import com.synebula.gaea.domain.repository.IRepository
|
||||
import com.synebula.gaea.log.ILogger
|
||||
import javax.annotation.Resource
|
||||
|
||||
|
||||
/**
|
||||
* 依赖了IRepository仓储借口的服务实现类 GenericsService
|
||||
* 该类依赖仓储接口 @see IGenericsRepository, 需要显式提供聚合根的class对象
|
||||
*
|
||||
* @param repository 仓储对象
|
||||
* @param clazz 聚合根类对象
|
||||
* @param logger 日志组件
|
||||
* @author alex
|
||||
* @version 0.1
|
||||
* @since 2020-05-17
|
||||
*/
|
||||
open class SimpleService<TRoot : IAggregateRoot<ID>, ID>(
|
||||
protected open var clazz: Class<TRoot>,
|
||||
protected open var repository: IRepository<TRoot, ID>,
|
||||
override var logger: ILogger,
|
||||
) : ISimpleService<TRoot, ID> {
|
||||
@Resource
|
||||
protected open var bus: IBus<Any>? = null
|
||||
|
||||
override fun add(root: TRoot): DataMessage<ID> {
|
||||
val msg = DataMessage<ID>()
|
||||
this.repository.add(root)
|
||||
msg.data = root.id
|
||||
return msg
|
||||
}
|
||||
|
||||
override fun update(id: ID, root: TRoot) {
|
||||
root.id = id
|
||||
this.repository.update(root)
|
||||
}
|
||||
|
||||
override fun remove(id: ID) {
|
||||
val beforeRemoveEvent = BeforeRemoveEvent<TRoot, ID>(id)
|
||||
this.bus?.publish(beforeRemoveEvent.topic(this.clazz), beforeRemoveEvent)
|
||||
this.repository.remove(id)
|
||||
val afterRemoveEvent = AfterRemoveEvent<TRoot, ID>(id)
|
||||
this.bus?.publish(afterRemoveEvent.topic(this.clazz), afterRemoveEvent)
|
||||
}
|
||||
|
||||
/**
|
||||
* 增加对象
|
||||
*
|
||||
* @param roots 增加对象命令列表
|
||||
*/
|
||||
override fun add(roots: List<TRoot>) {
|
||||
this.repository.add(roots)
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量更新对象
|
||||
*
|
||||
* @param roots 更新对象命令列表
|
||||
*/
|
||||
override fun update(roots: List<TRoot>) {
|
||||
this.repository.update(roots)
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
package com.synebula.gaea.query
|
||||
|
||||
/**
|
||||
* 查询基接口, 其中方法都指定了查询的视图类型。
|
||||
*
|
||||
* @author alex
|
||||
*/
|
||||
interface IUniversalQuery {
|
||||
/**
|
||||
* 根据Key获取对象。
|
||||
*
|
||||
* @param id 对象Key。
|
||||
* @return 视图结果
|
||||
*/
|
||||
fun <TView, ID> get(id: ID, clazz: Class<TView>): TView?
|
||||
|
||||
/**
|
||||
* 根据实体类条件查询所有符合条件记录
|
||||
*
|
||||
* @param params 查询条件。
|
||||
* @return 视图列表
|
||||
*/
|
||||
fun <TView> list(params: Map<String, String>?, clazz: Class<TView>): List<TView>
|
||||
|
||||
/**
|
||||
* 根据条件查询符合条件记录的数量
|
||||
*
|
||||
* @param params 查询条件。
|
||||
* @return 数量
|
||||
*/
|
||||
fun <TView> count(params: Map<String, String>?, clazz: Class<TView>): Int
|
||||
|
||||
/**
|
||||
* 根据实体类条件查询所有符合条件记录(分页查询)
|
||||
*
|
||||
* @param params 分页条件
|
||||
* @return 分页数据
|
||||
*/
|
||||
fun <TView> paging(params: Params, clazz: Class<TView>): Page<TView>
|
||||
|
||||
/**
|
||||
* 查询条件范围内数据。
|
||||
* @param field 查询字段
|
||||
* @param params 查询条件
|
||||
*
|
||||
* @return 视图列表
|
||||
*/
|
||||
fun <TView> range(field: String, params: List<Any>, clazz: Class<TView>): List<TView>
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package com.synebula.gaea.record.model
|
||||
|
||||
import com.synebula.gaea.db.IEntity
|
||||
|
||||
interface IRecord<ID> : IEntity<ID>
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.synebula.gaea.domain.record
|
||||
package com.synebula.gaea.record.model
|
||||
|
||||
import java.util.*
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package com.synebula.gaea.domain.service
|
||||
package com.synebula.gaea.record.service
|
||||
|
||||
import com.synebula.gaea.data.message.DataMessage
|
||||
import com.synebula.gaea.domain.model.IAggregateRoot
|
||||
import com.synebula.gaea.db.IEntity
|
||||
import com.synebula.gaea.log.ILogger
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ import com.synebula.gaea.log.ILogger
|
||||
* @version 0.0.1
|
||||
* @since 2016年9月18日 下午2:23:15
|
||||
*/
|
||||
interface ISimpleService<TAggregateRoot : IAggregateRoot<ID>, ID> {
|
||||
interface IService<Entity : IEntity<ID>, ID> {
|
||||
/**
|
||||
* 日志组件。
|
||||
*/
|
||||
@@ -20,31 +20,31 @@ interface ISimpleService<TAggregateRoot : IAggregateRoot<ID>, ID> {
|
||||
/**
|
||||
* 增加对象
|
||||
*
|
||||
* @param root 增加对象命令
|
||||
* @param entity 增加对象命令
|
||||
*/
|
||||
fun add(root: TAggregateRoot): DataMessage<ID>
|
||||
fun add(entity: Entity): ID?
|
||||
|
||||
/**
|
||||
* 增加对象
|
||||
*
|
||||
* @param roots 增加对象命令列表
|
||||
* @param entities 增加对象命令列表
|
||||
*/
|
||||
fun add(roots: List<TAggregateRoot>)
|
||||
fun add(entities: List<Entity>)
|
||||
|
||||
/**
|
||||
* 更新对象
|
||||
*
|
||||
* @param id 对象ID
|
||||
* @param root 更新对象命令
|
||||
* @param entity 更新对象命令
|
||||
*/
|
||||
fun update(id: ID, root: TAggregateRoot)
|
||||
fun update(id: ID, entity: Entity)
|
||||
|
||||
/**
|
||||
* 批量更新对象
|
||||
*
|
||||
* @param roots 更新对象命令列表
|
||||
* @param entities 更新对象命令列表
|
||||
*/
|
||||
fun update(roots: List<TAggregateRoot>)
|
||||
fun update(entities: List<Entity>)
|
||||
|
||||
/**
|
||||
* 增加对象
|
||||
@@ -0,0 +1,65 @@
|
||||
package com.synebula.gaea.record.service
|
||||
|
||||
import com.synebula.gaea.bus.IBus
|
||||
import com.synebula.gaea.data.message.DataMessage
|
||||
import com.synebula.gaea.db.context.IDbContext
|
||||
import com.synebula.gaea.domain.event.AfterRemoveEvent
|
||||
import com.synebula.gaea.domain.event.BeforeRemoveEvent
|
||||
import com.synebula.gaea.log.ILogger
|
||||
import com.synebula.gaea.record.model.IRecord
|
||||
|
||||
|
||||
/**
|
||||
* 依赖了IRepository仓储借口的服务实现类 GenericsService
|
||||
* 该类依赖仓储接口 @see IGenericsRepository, 需要显式提供聚合根的class对象
|
||||
*
|
||||
* @param context 仓储对象
|
||||
* @param clazz 聚合根类对象
|
||||
* @param logger 日志组件
|
||||
* @author alex
|
||||
* @version 0.1
|
||||
* @since 2020-05-17
|
||||
*/
|
||||
open class Service<TEntity : IRecord<ID>, ID>(
|
||||
protected open var clazz: Class<TEntity>,
|
||||
protected open var context: IDbContext,
|
||||
protected open var bus: IBus<Any>? = null,
|
||||
override var logger: ILogger
|
||||
) : IService<TEntity, ID> {
|
||||
|
||||
override fun add(entity: TEntity): ID? {
|
||||
this.context.add(entity, clazz)
|
||||
return entity.id
|
||||
}
|
||||
|
||||
override fun update(id: ID, entity: TEntity) {
|
||||
entity.id = id
|
||||
this.context.update(entity, clazz)
|
||||
}
|
||||
|
||||
override fun remove(id: ID) {
|
||||
val beforeRemoveEvent = BeforeRemoveEvent<TEntity, ID>(id)
|
||||
this.bus?.publish(beforeRemoveEvent.topic(this.clazz), beforeRemoveEvent)
|
||||
this.context.remove(id, clazz)
|
||||
val afterRemoveEvent = AfterRemoveEvent<TEntity, ID>(id)
|
||||
this.bus?.publish(afterRemoveEvent.topic(this.clazz), afterRemoveEvent)
|
||||
}
|
||||
|
||||
/**
|
||||
* 增加对象
|
||||
*
|
||||
* @param entitys 增加对象命令列表
|
||||
*/
|
||||
override fun add(entitys: List<TEntity>) {
|
||||
this.context.add(entitys, clazz)
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量更新对象
|
||||
*
|
||||
* @param entitys 更新对象命令列表
|
||||
*/
|
||||
override fun update(entitys: List<TEntity>) {
|
||||
this.context.update(entitys, clazz)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user