Compare commits

...

10 Commits

Author SHA1 Message Date
de8fc0256f 增加query factory 2024-07-03 16:08:22 +08:00
48a24b99e1 添加文件 2024-06-29 20:18:33 +08:00
54b7cedac6 升级JDK/KOTLIN/SPRING等依赖信息 2024-06-26 17:31:44 +08:00
75743c6ef6 fix gradle config file: java & kotlin compile target jvm 2023-04-20 23:06:33 +08:00
86a4bc8b0a update package version 2023-04-18 11:22:23 +08:00
eff39eb7f8 fix spring jpa proxy bug 2023-04-18 10:50:47 +08:00
230ceea0fa update latest gradle & spring;
fix new spring package dependencies
2023-04-18 09:58:52 +08:00
b099d42883 修改jpa引用类型 2022-12-02 14:54:11 +08:00
aadf880052 调整app包结构 2022-12-01 11:17:35 +08:00
ad3cfef96f fix token 获取 bug 2022-11-23 15:41:17 +08:00
80 changed files with 1066 additions and 693 deletions

1
.gitignore vendored
View File

@@ -1,5 +1,4 @@
.* .*
gradlew*
build build
!.gitignore !.gitignore

View File

@@ -1,12 +1,17 @@
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
buildscript { buildscript {
ext { ext {
kotlin_version = '1.6.10' jvm_version = '21'
spring_version = "2.7.0" kotlin_version = '2.0.0'
spring_version = '3.3.0'
} }
repositories { repositories {
mavenLocal() 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() mavenCentral()
} }
@@ -17,19 +22,21 @@ buildscript {
subprojects { subprojects {
group 'com.synebula' group 'com.synebula'
version '1.5.0' version '1.6.0'
buildscript { buildscript {
repositories { repositories {
mavenLocal() 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() mavenCentral()
} }
} }
repositories { repositories {
mavenLocal() 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() mavenCentral()
} }
@@ -44,14 +51,18 @@ subprojects {
testApi group: 'junit', name: 'junit', version: '4.12' testApi group: 'junit', name: 'junit', version: '4.12'
} }
sourceCompatibility = 1.8 /*** 指定 Java & Kotlin 语言编译目标JVM ***/
targetCompatibility = 1.8 sourceCompatibility = "$jvm_version"
targetCompatibility = "$jvm_version"
compileKotlin { compileKotlin {
kotlinOptions.jvmTarget = "1.8" compilerOptions {
jvmTarget = JvmTarget.valueOf("JVM_$jvm_version")
}
} }
compileTestKotlin { compileTestKotlin {
kotlinOptions.jvmTarget = "1.8" compilerOptions {
jvmTarget = JvmTarget.valueOf("JVM_$jvm_version")
}
} }
publishing { publishing {

View File

@@ -1,6 +1,6 @@
#Mon May 18 17:21:26 CST 2020 #Thu Jun 06 15:31:38 CST 2024
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-all.zip
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStorePath=wrapper/dists distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

234
gradlew vendored Normal file
View 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
View 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

View File

@@ -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

View File

@@ -1,4 +1,4 @@
package com.synebula.gaea.app.component.bus package com.synebula.gaea.app.bus
import com.synebula.gaea.bus.Bus import com.synebula.gaea.bus.Bus
import org.springframework.stereotype.Component import org.springframework.stereotype.Component

View File

@@ -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.DomainSubscribe
import com.synebula.gaea.bus.IBus import com.synebula.gaea.bus.IBus

View File

@@ -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.Cache
import com.google.common.cache.CacheBuilder 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) { override fun add(key: K, value: V) {
this.guavaCache.put(key, value) this.guavaCache.put(key!!, value!!)
} }
/** /**

View File

@@ -1,6 +1,5 @@
package com.synebula.gaea.app.component.poi 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.data.date.DateTime
import com.synebula.gaea.exception.NoticeUserException import com.synebula.gaea.exception.NoticeUserException
import org.apache.poi.hpsf.Decimal import org.apache.poi.hpsf.Decimal
@@ -40,7 +39,7 @@ object Excel {
val titleFont = wb.createFont() val titleFont = wb.createFont()
titleFont.bold = true titleFont.bold = true
titleStyle.setFont(titleFont) titleStyle.setFont(titleFont)
setBorderStyle(titleStyle, BorderStyle.THIN) setBorderStyle(titleStyle)
//声明列对象 //声明列对象
// 第三步在sheet中添加表头第0行,注意老版本poi对Excel的行数列数有限制 // 第三步在sheet中添加表头第0行,注意老版本poi对Excel的行数列数有限制
@@ -62,7 +61,7 @@ object Excel {
val contentStyle = wb.createCellStyle() val contentStyle = wb.createCellStyle()
contentStyle.alignment = HorizontalAlignment.LEFT// 创建一个修改居左格式 contentStyle.alignment = HorizontalAlignment.LEFT// 创建一个修改居左格式
contentStyle.verticalAlignment = VerticalAlignment.CENTER contentStyle.verticalAlignment = VerticalAlignment.CENTER
setBorderStyle(contentStyle, BorderStyle.THIN) setBorderStyle(contentStyle)
//创建内容 //创建内容
var col = 0 var col = 0
@@ -146,6 +145,7 @@ object Excel {
this.getCellValue(cell, evaluator).toString().toIntOrNull() this.getCellValue(cell, evaluator).toString().toIntOrNull()
} }
} }
Double::class.java.name -> { Double::class.java.name -> {
if (cell.cellType == CellType.NUMERIC) { if (cell.cellType == CellType.NUMERIC) {
cell.numericCellValue cell.numericCellValue
@@ -153,6 +153,7 @@ object Excel {
this.getCellValue(cell, evaluator).toString().toDoubleOrNull() this.getCellValue(cell, evaluator).toString().toDoubleOrNull()
} }
} }
Float::class.java.name -> { Float::class.java.name -> {
if (cell.cellType == CellType.NUMERIC) { if (cell.cellType == CellType.NUMERIC) {
cell.numericCellValue.toFloat() cell.numericCellValue.toFloat()
@@ -160,6 +161,7 @@ object Excel {
this.getCellValue(cell, evaluator).toString().toFloatOrNull() this.getCellValue(cell, evaluator).toString().toFloatOrNull()
} }
} }
Decimal::class.java.name -> { Decimal::class.java.name -> {
if (cell.cellType == CellType.NUMERIC) { if (cell.cellType == CellType.NUMERIC) {
cell.numericCellValue.toBigDecimal() cell.numericCellValue.toBigDecimal()
@@ -167,6 +169,7 @@ object Excel {
this.getCellValue(cell, evaluator).toString().toBigDecimalOrNull() this.getCellValue(cell, evaluator).toString().toBigDecimalOrNull()
} }
} }
Boolean::class.java.name -> { Boolean::class.java.name -> {
if (cell.cellType == CellType.BOOLEAN) { if (cell.cellType == CellType.BOOLEAN) {
cell.booleanCellValue cell.booleanCellValue
@@ -174,11 +177,13 @@ object Excel {
this.getCellValue(cell, evaluator).toString().toBoolean() this.getCellValue(cell, evaluator).toString().toBoolean()
} }
} }
Date::class.java.name -> try { Date::class.java.name -> try {
cell.dateCellValue cell.dateCellValue
} catch (ignored: Exception) { } catch (ignored: Exception) {
DateTime(cell.stringCellValue).date DateTime(cell.stringCellValue).date
} }
else -> cell.stringCellValue else -> cell.stringCellValue
} }
rowData[columns[c].first] = value rowData[columns[c].first] = value
@@ -278,7 +283,7 @@ object Excel {
/** /**
* 设置cell style的边框 * 设置cell style的边框
*/ */
private fun setBorderStyle(style: HSSFCellStyle, borderStyle: BorderStyle) { private fun setBorderStyle(style: HSSFCellStyle, borderStyle: BorderStyle = BorderStyle.THIN) {
style.borderTop = borderStyle style.borderTop = borderStyle
style.borderRight = borderStyle style.borderRight = borderStyle
style.borderBottom = borderStyle style.borderBottom = borderStyle
@@ -297,6 +302,7 @@ object Excel {
numericCellValue numericCellValue
} }
} }
CellType.STRING -> cell.richStringCellValue.string CellType.STRING -> cell.richStringCellValue.string
CellType.BLANK -> "" CellType.BLANK -> ""
CellType.FORMULA -> evaluator.evaluate(cell).toString() CellType.FORMULA -> evaluator.evaluate(cell).toString()

View File

@@ -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()
)

View File

@@ -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.controller.cmd.ICommandApp
import com.synebula.gaea.app.query.IQueryApp import com.synebula.gaea.app.controller.query.IQueryApp
import com.synebula.gaea.data.message.HttpMessageFactory 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.ICommand
import com.synebula.gaea.domain.service.IService import com.synebula.gaea.domain.service.IService
import com.synebula.gaea.log.ILogger import com.synebula.gaea.log.ILogger
import com.synebula.gaea.query.IQuery
import org.springframework.beans.factory.annotation.Autowired import org.springframework.beans.factory.annotation.Autowired
/** /**
@@ -17,7 +17,7 @@ import org.springframework.beans.factory.annotation.Autowired
* @param query 业务查询服务 * @param query 业务查询服务
* @param logger 日志组件 * @param logger 日志组件
*/ */
open class Application<TCommand : ICommand, TView, ID>( open class DomainApplication<TCommand : ICommand, TView, ID>(
override var name: String, override var name: String,
override var service: IService<ID>, override var service: IService<ID>,
override var query: IQuery<TView, ID>, override var query: IQuery<TView, ID>,

View File

@@ -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.HttpMessage
import com.synebula.gaea.data.message.HttpMessageFactory import com.synebula.gaea.data.message.HttpMessageFactory
import com.synebula.gaea.data.message.Status 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 { try {
val authentication = SecurityContextHolder.getContext().authentication.principal.toString() val authentication = SecurityContextHolder.getContext().authentication.principal
try { try {
val gson = Gson() return authentication as UserSession
return gson.fromJson(authentication, clazz)
} catch (ex: Exception) { } catch (ex: Exception) {
logger.error(this, ex, "[$name]解析用户信息异常!用户信息:$authentication: ${ex.message}") logger.error(this, ex, "[$name]解析用户信息异常!用户信息:$authentication: ${ex.message}")
} }

View File

@@ -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.controller.cmd.ISimpleCommandApp
import com.synebula.gaea.app.query.IQueryApp import com.synebula.gaea.app.controller.query.IQueryApp
import com.synebula.gaea.data.message.HttpMessageFactory 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.model.IAggregateRoot
import com.synebula.gaea.domain.service.ISimpleService
import com.synebula.gaea.log.ILogger 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 import org.springframework.beans.factory.annotation.Autowired
/** /**
@@ -17,14 +17,13 @@ import org.springframework.beans.factory.annotation.Autowired
* @param query 业务查询服务 * @param query 业务查询服务
* @param logger 日志组件 * @param logger 日志组件
*/ */
open class SimpleApplication<TRoot : IAggregateRoot<ID>, ID>( open class RecordApplication<TRoot : IAggregateRoot<ID>, ID>(
override var name: String, override var name: String,
override var service: ISimpleService<TRoot, ID>, override var service: IService<TRoot, ID>,
override var query: IQuery<TRoot, ID>, override var query: IQuery<TRoot, ID>,
override var logger: ILogger, override var logger: ILogger,
) : ISimpleCommandApp<TRoot, ID>, IQueryApp<TRoot, ID> { ) : ISimpleCommandApp<TRoot, ID>, IQueryApp<TRoot, ID> {
@Autowired @Autowired
override lateinit var httpMessageFactory: HttpMessageFactory override lateinit var httpMessageFactory: HttpMessageFactory
} }

View File

@@ -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.data.message.HttpMessageFactory
import com.synebula.gaea.domain.service.ICommand import com.synebula.gaea.domain.service.ICommand

View File

@@ -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.HttpMessage
import com.synebula.gaea.data.message.Status import com.synebula.gaea.data.message.Status
import com.synebula.gaea.domain.service.ICommand import com.synebula.gaea.domain.service.ICommand

View File

@@ -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.HttpMessage
import com.synebula.gaea.data.message.Status import com.synebula.gaea.data.message.Status
import com.synebula.gaea.domain.model.IAggregateRoot 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 com.synebula.gaea.spring.aop.annotation.Method
import org.springframework.web.bind.annotation.* import org.springframework.web.bind.annotation.*
@@ -16,12 +16,13 @@ import org.springframework.web.bind.annotation.*
* @since 2020-05-15 * @since 2020-05-15
*/ */
interface ISimpleCommandApp<TRoot : IAggregateRoot<ID>, ID> : IApplication { interface ISimpleCommandApp<TRoot : IAggregateRoot<ID>, ID> : IApplication {
var service: ISimpleService<TRoot, ID> var service: IService<TRoot, ID>
@PostMapping @PostMapping
@Method("添加") @Method("添加")
fun add(@RequestBody entity: TRoot): HttpMessage { 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:.+}") @PutMapping("/{id:.+}")

View File

@@ -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.data.message.HttpMessageFactory
import com.synebula.gaea.domain.model.IAggregateRoot 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 com.synebula.gaea.log.ILogger
import org.springframework.beans.factory.annotation.Autowired 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>( open class SimpleCommandApp<TRoot : IAggregateRoot<ID>, ID>(
override var name: String, override var name: String,
override var service: ISimpleService<TRoot, ID>, override var service: IService<TRoot, ID>,
override var logger: ILogger, override var logger: ILogger,
) : ISimpleCommandApp<TRoot, ID> { ) : ISimpleCommandApp<TRoot, ID> {
@Autowired @Autowired

View File

@@ -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.data.message.HttpMessage
import com.synebula.gaea.query.IQuery import com.synebula.gaea.db.query.IQuery
import com.synebula.gaea.query.Params import com.synebula.gaea.db.query.Params
import com.synebula.gaea.spring.aop.annotation.Method import com.synebula.gaea.spring.aop.annotation.Method
import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable import org.springframework.web.bind.annotation.PathVariable

View File

@@ -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.data.message.HttpMessageFactory
import com.synebula.gaea.db.query.IQuery
import com.synebula.gaea.log.ILogger import com.synebula.gaea.log.ILogger
import com.synebula.gaea.query.IQuery
import org.springframework.beans.factory.annotation.Autowired import org.springframework.beans.factory.annotation.Autowired
/** /**

View File

@@ -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.JWT
import com.auth0.jwt.algorithms.Algorithm import com.auth0.jwt.algorithms.Algorithm
import com.auth0.jwt.exceptions.TokenExpiredException
import com.google.gson.Gson import com.google.gson.Gson
import com.synebula.gaea.app.struct.exception.TokenCloseExpireException
import com.synebula.gaea.log.ILogger import com.synebula.gaea.log.ILogger
import org.springframework.beans.factory.annotation.Autowired import org.springframework.beans.factory.annotation.Autowired
import org.springframework.beans.factory.annotation.Value import org.springframework.beans.factory.annotation.Value
@@ -71,6 +71,25 @@ class TokenManager {
.sign(algorithm) .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是否正确 * 校验token是否正确
* *
@@ -78,20 +97,15 @@ class TokenManager {
* @return 是否正确 * @return 是否正确
*/ */
fun <T> verify(token: String, clazz: Class<T>): T? { fun <T> verify(token: String, clazz: Class<T>): T? {
try { return try {
val now = Date()
val algorithm = Algorithm.HMAC256(secret) 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 result = JWT.require(algorithm).build().verify(token)
val json = result.getClaim(this.payload).asString() val json = result.getClaim(this.payload).asString()
return gson.fromJson(json, clazz) gson.fromJson(json, clazz)
} catch (ex: TokenExpiredException) {
null
} catch (ex: Exception) { } catch (ex: Exception) {
this.logger.debug(this, ex, "解析token出错") this.logger.error(this, ex, "解析token出错")
throw ex throw ex
} }
} }
@@ -103,19 +117,14 @@ class TokenManager {
* @return 是否正确 * @return 是否正确
*/ */
fun verify(token: String): String { fun verify(token: String): String {
try { return try {
val now = Date()
val algorithm = Algorithm.HMAC256(secret) 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 result = JWT.require(algorithm).build().verify(token)
return result.getClaim(payload).asString() result.getClaim(this.payload).asString()
} catch (ex: TokenExpiredException) {
""
} catch (ex: Exception) { } catch (ex: Exception) {
this.logger.debug(this, ex, "解析token出错") this.logger.error(this, ex, "解析token出错")
throw ex throw ex
} }
} }

View File

@@ -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.HttpMessageFactory
import com.synebula.gaea.data.message.Status 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.authentication.UsernamePasswordAuthenticationToken
import org.springframework.security.core.context.SecurityContextHolder import org.springframework.security.core.context.SecurityContextHolder
import org.springframework.web.filter.OncePerRequestFilter import org.springframework.web.filter.OncePerRequestFilter
import java.io.IOException 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") { val identity = if (this.storage == "cookie") {
request.cookies.find { it.name == tokenKey }?.value ?: "" request.cookies.find { it.name == tokenKey }?.value ?: ""
} else { } else {
request.getHeader(tokenKey) ?: "" var token = request.getHeader(tokenKey) ?: ""
if (token.isEmpty()) token = request.getParameter(tokenKey) ?: ""
token
} }
val user = this.userSessionManager.userSession(identity) val user = this.userSessionManager.userSession(identity)
if (user != null) { if (user != null) {
@@ -62,4 +64,5 @@ class WebAuthorization(
response.flushBuffer() response.flushBuffer()
} }
} }
} }

View File

@@ -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.HttpMessageFactory
import com.synebula.gaea.data.message.Status import com.synebula.gaea.data.message.Status
import org.springframework.beans.factory.annotation.Autowired import org.springframework.beans.factory.annotation.Autowired
import org.springframework.beans.factory.annotation.Value import org.springframework.beans.factory.annotation.Value
import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration 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.builders.HttpSecurity
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer
@@ -36,16 +37,18 @@ class WebSecurity {
@Bean @Bean
@Throws(Exception::class) @Throws(Exception::class)
fun filterChain(httpSecurity: HttpSecurity): SecurityFilterChain { fun filterChain(httpSecurity: HttpSecurity): SecurityFilterChain {
httpSecurity.cors().and().csrf().disable() // 跨域伪造请求限制无效 httpSecurity.cors(Customizer.withDefaults())
.csrf { it.disable() } // 跨域伪造请求限制无效
// 设置Session的创建策略为Spring Security永不创建HttpSession 不使用HttpSession来获取SecurityContext // 设置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), WebAuthorization(httpMessageFactory, userSessionManager),
UsernamePasswordAuthenticationFilter::class.java UsernamePasswordAuthenticationFilter::class.java
).exceptionHandling().authenticationEntryPoint { _, response, _ -> ).exceptionHandling {
it.authenticationEntryPoint { _, response, _ ->
response.status = Status.Success response.status = Status.Success
response.characterEncoding = "utf-8" response.characterEncoding = "utf-8"
response.contentType = "text/javascript;charset=utf-8" response.contentType = "text/javascript;charset=utf-8"
@@ -55,6 +58,7 @@ class WebSecurity {
) )
) )
} }
}
return httpSecurity.build() return httpSecurity.build()
} }
@@ -62,7 +66,7 @@ class WebSecurity {
@Bean @Bean
@Throws(Exception::class) @Throws(Exception::class)
fun ignoringCustomizer(): WebSecurityCustomizer { fun ignoringCustomizer(): WebSecurityCustomizer {
return WebSecurityCustomizer { web -> web.ignoring().antMatchers(this.signInPath) } return WebSecurityCustomizer { web -> web.ignoring().requestMatchers(this.signInPath) }
} }
/** /**

View File

@@ -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>()
}

View File

@@ -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") @Suppress("UNCHECKED_CAST")
fun <T> user(): T { fun <T : User> user(): T {
return user as T return user as T
} }
} }

View File

@@ -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 com.synebula.gaea.ext.toMd5
import org.springframework.beans.factory.annotation.Value import org.springframework.beans.factory.annotation.Value
import org.springframework.stereotype.Component import org.springframework.stereotype.Component

View File

@@ -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())

View File

@@ -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)

View File

@@ -0,0 +1 @@
com.synebula.gaea.app.autoconfig.AppAutoConfiguration

View File

@@ -5,7 +5,7 @@ ext {
dependencies { dependencies {
api project(":src:gaea") 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") implementation("org.javassist:javassist:$jassist_version")
} }

View File

@@ -1,10 +1,10 @@
package com.synebula.gaea.jpa package com.synebula.gaea.jpa
import com.synebula.gaea.query.IQuery import com.synebula.gaea.db.query.IQuery
import com.synebula.gaea.query.Page import com.synebula.gaea.db.query.Page
import com.synebula.gaea.query.Params import com.synebula.gaea.db.query.Params
import jakarta.persistence.EntityManager
import org.springframework.data.jpa.repository.support.SimpleJpaRepository 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> { class JpaQuery<TView, ID>(override var clazz: Class<TView>, entityManager: EntityManager) : IQuery<TView, ID> {
protected var repo: SimpleJpaRepository<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? { 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 return if (view.isPresent) view.get() else null
} }

View File

@@ -2,13 +2,13 @@ package com.synebula.gaea.jpa
import com.synebula.gaea.domain.model.IAggregateRoot import com.synebula.gaea.domain.model.IAggregateRoot
import com.synebula.gaea.domain.repository.IRepository import com.synebula.gaea.domain.repository.IRepository
import jakarta.persistence.EntityManager
import org.springframework.data.jpa.repository.JpaRepository import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.data.jpa.repository.support.SimpleJpaRepository import org.springframework.data.jpa.repository.support.SimpleJpaRepository
import javax.persistence.EntityManager
class JpaRepository<TAggregateRoot : IAggregateRoot<ID>, ID>( open class JpaRepository<TAggregateRoot : IAggregateRoot<ID>, ID>(
override var clazz: Class<TAggregateRoot>, final override var clazz: Class<TAggregateRoot>,
entityManager: EntityManager entityManager: EntityManager
) : IRepository<TAggregateRoot, ID> { ) : IRepository<TAggregateRoot, ID> {
protected var repo: JpaRepository<TAggregateRoot, ID>? = null protected var repo: JpaRepository<TAggregateRoot, ID>? = null

View File

@@ -1,15 +1,12 @@
package com.synebula.gaea.jpa package com.synebula.gaea.jpa
import com.synebula.gaea.data.date.DateTime import com.synebula.gaea.data.date.DateTime
import com.synebula.gaea.query.Operator import com.synebula.gaea.db.query.Operator
import com.synebula.gaea.query.Where import com.synebula.gaea.db.query.Where
import jakarta.persistence.criteria.*
import org.springframework.data.jpa.domain.Specification import org.springframework.data.jpa.domain.Specification
import java.lang.reflect.Field import java.lang.reflect.Field
import java.util.* import java.util.*
import javax.persistence.criteria.CriteriaBuilder
import javax.persistence.criteria.CriteriaQuery
import javax.persistence.criteria.Predicate
import javax.persistence.criteria.Root
/** /**
@@ -69,15 +66,16 @@ fun String.tryToDigital(field: Field): Double {
* @param clazz 类 * @param clazz 类
* @return Specification * @return Specification
*/ */
fun Map<String, String>?.toSpecification(clazz: Class<*>): Specification<*> { fun Map<String, String>.toSpecification(clazz: Class<*>): Specification<*> {
val rangeStartSuffix = "[0]" //范围查询开始后缀 val rangeStartSuffix = "[0]" //范围查询开始后缀
val rangeEndSuffix = "[1]" //范围查询结束后缀 val rangeEndSuffix = "[1]" //范围查询结束后缀
return Specification<Any?> { root: Root<Any?>, _: CriteriaQuery<*>?, criteriaBuilder: CriteriaBuilder -> return Specification<Any?> { root: Root<Any?>, _: CriteriaQuery<*>?, criteriaBuilder: CriteriaBuilder ->
val predicates: MutableList<Predicate> = ArrayList() val predicates = mutableListOf<Predicate>()
for (argumentName in this!!.keys) { for (argumentName in this.keys) {
if (this[argumentName] == null) continue try {
var fieldName = argumentName var fieldName = argumentName
var operator: Operator val fieldValue = this[argumentName]!!
var operator: Operator = Operator.Default
// 判断是否为range类型(范围内查询) // 判断是否为range类型(范围内查询)
var start = true var start = true
@@ -85,63 +83,192 @@ fun Map<String, String>?.toSpecification(clazz: Class<*>): Specification<*> {
fieldName = fieldName.substring(fieldName.length - 3) fieldName = fieldName.substring(fieldName.length - 3)
if (fieldName.endsWith(rangeEndSuffix)) start = false if (fieldName.endsWith(rangeEndSuffix)) start = false
} }
val field = clazz.getDeclaredField(fieldName) val fieldTree = fieldName.split("\\.".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
val where: Where = field.getDeclaredAnnotation(Where::class.java) //查找是否是嵌入字段, 找到最深的类型
operator = where.operator 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 (operator === Operator.Range) {
if (clazz.getDeclaredField(fieldName).type != Date::class.java) { if (field.type != Date::class.java) {
operator = if (start) Operator.Gte else Operator.Lte operator = if (start) Operator.Gte else Operator.Lte
} }
} }
var predicate: Predicate var predicate: Predicate
var digitalValue: Double var digitalValue: Double
try {
when (operator) { when (operator) {
Operator.Ne -> predicate = Operator.Ne -> predicate = criteriaBuilder.notEqual(
criteriaBuilder.notEqual(root.get<Any>(fieldName), this[fieldName]!!.toFieldType(field)) getFieldPath<Any>(root, fieldName),
typeConvert(field, fieldValue)
)
Operator.Lt -> { Operator.Lt -> try {
digitalValue = this[fieldName]!!.tryToDigital(field) digitalValue = parseDigital(field, fieldValue)
predicate = criteriaBuilder.lessThan(root.get(fieldName), digitalValue) 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 -> { Operator.Gt -> try {
digitalValue = this[fieldName]!!.tryToDigital(field) digitalValue = parseDigital(field, fieldValue)
predicate = criteriaBuilder.greaterThan(root.get(fieldName), digitalValue) 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 -> { Operator.Lte -> try {
digitalValue = this[fieldName]!!.tryToDigital(field) digitalValue = parseDigital(field, fieldValue)
predicate = criteriaBuilder.lessThanOrEqualTo(root.get(fieldName), digitalValue) 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 -> { Operator.Gte -> try {
digitalValue = this[fieldName]!!.tryToDigital(field) digitalValue = parseDigital(field, fieldValue)
predicate = criteriaBuilder.greaterThanOrEqualTo(root.get(fieldName), digitalValue) 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.Like -> predicate = criteriaBuilder.like(
Operator.Range -> { getFieldPath(root, fieldName),
predicate = if (start) { String.format("%%%s%%", fieldValue)
criteriaBuilder.greaterThanOrEqualTo(root.get(fieldName), this[argumentName]!!) )
} else {
criteriaBuilder.lessThanOrEqualTo(root.get(fieldName), this[argumentName]!!)
}
}
else -> predicate = Operator.Range -> predicate = if (start) criteriaBuilder.greaterThanOrEqualTo(
criteriaBuilder.equal(root.get<Any>(fieldName), this[fieldName]!!.toFieldType(field)) 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) predicates.add(predicate)
} catch (e: NoSuchFieldException) { } catch (e: NoSuchFieldException) {
throw Error( throw RuntimeException(e)
"class [${field.declaringClass.name}] field [${field.name}] can't annotation [@Where(${operator.declaringClass.simpleName}.${operator.name})]",
e
)
} }
} }
criteriaBuilder.and(*predicates.toTypedArray()) 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
}

View File

@@ -5,13 +5,13 @@ import org.springframework.beans.factory.FactoryBean
import org.springframework.cglib.proxy.Enhancer import org.springframework.cglib.proxy.Enhancer
import org.springframework.data.repository.Repository import org.springframework.data.repository.Repository
class JpaRepositoryFactory( class JpaDbContextFactory(
private val beanFactory: BeanFactory, private val beanFactory: BeanFactory,
private val interfaceType: Class<*>, private val interfaceType: Class<*>,
private val implBeanNames: List<String> private val implBeanNames: List<String>
) : FactoryBean<Any> { ) : FactoryBean<Any> {
override fun getObject(): Any { override fun getObject(): Any {
val handler: JpaRepositoryProxy<*, *, *> = JpaRepositoryProxy<Repository<Any, Any>, Any, Any>( val handler: JpaDbContextProxy<*, *, *> = JpaDbContextProxy<Repository<Any, Any>, Any, Any>(
beanFactory, beanFactory,
interfaceType, implBeanNames interfaceType, implBeanNames
) )

View File

@@ -1,6 +1,7 @@
package com.synebula.gaea.jpa.proxy package com.synebula.gaea.jpa.proxy
import com.synebula.gaea.jpa.proxy.method.JpaMethodProxy import com.synebula.gaea.jpa.proxy.method.JpaMethodProxy
import jakarta.persistence.EntityManager
import javassist.* import javassist.*
import javassist.bytecode.AnnotationsAttribute import javassist.bytecode.AnnotationsAttribute
import javassist.bytecode.MethodInfo import javassist.bytecode.MethodInfo
@@ -24,9 +25,8 @@ import org.springframework.data.repository.Repository
import java.lang.reflect.Method import java.lang.reflect.Method
import java.lang.reflect.ParameterizedType import java.lang.reflect.ParameterizedType
import java.lang.reflect.Type 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, beanFactory: BeanFactory,
interfaceType: Class<*>, interfaceType: Class<*>,
implementBeanNames: List<String>? implementBeanNames: List<String>?

View File

@@ -8,5 +8,5 @@ import kotlin.reflect.KClass
@Retention(AnnotationRetention.RUNTIME) @Retention(AnnotationRetention.RUNTIME)
@MustBeDocumented @MustBeDocumented
@Inherited @Inherited
@Import(JpaRepositoryRegister::class) @Import(JpaDbContextRegister::class)
annotation class JpaRepositoryProxyScan(val basePackages: Array<String> = [], val scanInterfaces: Array<KClass<*>> = []) annotation class JpaDbContextProxyScan(val basePackages: Array<String> = [], val scanInterfaces: Array<KClass<*>> = [])

View File

@@ -24,17 +24,20 @@ import org.springframework.util.ClassUtils
import java.util.* import java.util.*
import java.util.stream.Collectors import java.util.stream.Collectors
class JpaRepositoryRegister : ImportBeanDefinitionRegistrar, ResourceLoaderAware, BeanClassLoaderAware, class JpaDbContextRegister : ImportBeanDefinitionRegistrar, ResourceLoaderAware, BeanClassLoaderAware,
EnvironmentAware, EnvironmentAware,
BeanFactoryAware { BeanFactoryAware {
private lateinit var environment: Environment private lateinit var environment: Environment
private lateinit var resourceLoader: ResourceLoader private lateinit var resourceLoader: ResourceLoader
private var classLoader: ClassLoader? = null private var classLoader: ClassLoader? = null
private var beanFactory: BeanFactory? = null private var beanFactory: BeanFactory? = null
override fun registerBeanDefinitions(metadata: AnnotationMetadata, registry: BeanDefinitionRegistry) { override fun registerBeanDefinitions(metadata: AnnotationMetadata, registry: BeanDefinitionRegistry) {
val attributes = AnnotationAttributes( val attributes = AnnotationAttributes(
metadata.getAnnotationAttributes( metadata.getAnnotationAttributes(
JpaRepositoryProxyScan::class.java.name JpaDbContextProxyScan::class.java.name
) ?: mapOf() ) ?: mapOf()
) )
val basePackages = attributes.getStringArray("basePackages") val basePackages = attributes.getStringArray("basePackages")
@@ -55,7 +58,7 @@ class JpaRepositoryRegister : ImportBeanDefinitionRegistrar, ResourceLoaderAware
val implClazzDefinitions = scan(basePackages, arrayOf(beanClazzTypeFilter)) val implClazzDefinitions = scan(basePackages, arrayOf(beanClazzTypeFilter))
for (definition in implClazzDefinitions) { for (definition in implClazzDefinitions) {
definition.isAutowireCandidate = false definition.isAutowireCandidate = false
registry.registerBeanDefinition(Objects.requireNonNull(definition.beanClassName), definition) registry.registerBeanDefinition(Objects.requireNonNull(definition.beanClassName!!), definition)
} }
// 构建bean定义 // 构建bean定义
// 1 bean参数 // 1 bean参数
@@ -66,7 +69,7 @@ class JpaRepositoryRegister : ImportBeanDefinitionRegistrar, ResourceLoaderAware
builder.addConstructorArgValue(beanClazz) builder.addConstructorArgValue(beanClazz)
builder.addConstructorArgValue(implBeanNames) builder.addConstructorArgValue(implBeanNames)
val definition = builder.rawBeanDefinition as GenericBeanDefinition val definition = builder.rawBeanDefinition as GenericBeanDefinition
definition.beanClass = JpaRepositoryFactory::class.java definition.beanClass = JpaDbContextFactory::class.java
definition.autowireMode = GenericBeanDefinition.AUTOWIRE_BY_TYPE definition.autowireMode = GenericBeanDefinition.AUTOWIRE_BY_TYPE
registry.registerBeanDefinition(beanClazz.name, definition) registry.registerBeanDefinition(beanClazz.name, definition)
} }

View File

@@ -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.DefaultMethodResolver
import com.synebula.gaea.jpa.proxy.method.resolver.FindMethodResolver import com.synebula.gaea.jpa.proxy.method.resolver.FindMethodResolver
import com.synebula.gaea.jpa.proxy.method.resolver.PageMethodResolver 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.domain.Pageable
import org.springframework.data.jpa.domain.Specification import org.springframework.data.jpa.domain.Specification
import java.lang.reflect.InvocationTargetException import java.lang.reflect.InvocationTargetException

View File

@@ -10,8 +10,8 @@ class FindMethodResolver(targetMethodName: String, clazz: Class<*>) : AbstractMe
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
override fun mappingArguments(args: Array<Any>): Array<Any> { override fun mappingArguments(args: Array<Any>): Array<Any> {
val params = args[0] as Map<String, String>? val params = args[0] as Map<String, String>?
val specification = params.toSpecification(entityClazz) val specification = params?.toSpecification(entityClazz)
return arrayOf(specification) return if (specification != null) arrayOf(specification) else arrayOf()
} }
override fun mappingResult(result: Any): Any { override fun mappingResult(result: Any): Any {

View File

@@ -1,15 +1,16 @@
package com.synebula.gaea.jpa.proxy.method.resolver package com.synebula.gaea.jpa.proxy.method.resolver
import com.synebula.gaea.jpa.toSpecification import com.synebula.gaea.jpa.toSpecification
import com.synebula.gaea.query.Order import com.synebula.gaea.db.query.Order
import com.synebula.gaea.query.Params 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.Page
import org.springframework.data.domain.PageRequest import org.springframework.data.domain.PageRequest
import org.springframework.data.domain.Pageable import org.springframework.data.domain.Pageable
import org.springframework.data.domain.Sort import org.springframework.data.domain.Sort
import java.util.* import java.util.*
import javax.persistence.EmbeddedId import com.synebula.gaea.db.query.Page as QueryPage
import javax.persistence.Id
/** /**
* 分页方法参数映射 * 分页方法参数映射
@@ -29,8 +30,7 @@ class PageMethodResolver(targetMethodName: String, clazz: Class<*>) : AbstractMe
val fields = entityClazz.declaredFields val fields = entityClazz.declaredFields
for (field in fields) { for (field in fields) {
val isId = Arrays.stream(field.declaredAnnotations).anyMatch { annotation: Annotation -> val isId = Arrays.stream(field.declaredAnnotations).anyMatch { annotation: Annotation ->
(annotation.annotationClass.java == Id::class.java (annotation.annotationClass.java == Id::class.java || annotation.annotationClass.java == EmbeddedId::class.java)
|| annotation.annotationClass.java == EmbeddedId::class.java)
} }
if (isId) { if (isId) {
sort = Sort.by(Sort.Direction.ASC, field.name) 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 { override fun mappingResult(result: Any): Any {
val page = result as Page<*> val page = result as Page<*>
// Page 页面从0开始 // Page 页面从0开始 [com.synebula.gaea.query.Page as QueryPage]
return com.synebula.gaea.query.Page(page.number + 1, page.size, page.totalElements.toInt(), page.content) return QueryPage(page.number + 1, page.size, page.totalElements.toInt(), page.content)
} }
} }

View File

@@ -1,9 +1,9 @@
package com.synebula.gaea.mongodb package com.synebula.gaea.mongodb
import com.synebula.gaea.data.date.DateTime import com.synebula.gaea.data.date.DateTime
import com.synebula.gaea.query.Operator import com.synebula.gaea.db.query.Operator
import com.synebula.gaea.query.Order import com.synebula.gaea.db.query.Order
import com.synebula.gaea.query.Where import com.synebula.gaea.db.query.Where
import org.springframework.data.domain.Sort import org.springframework.data.domain.Sort
import org.springframework.data.mongodb.core.query.Criteria import org.springframework.data.mongodb.core.query.Criteria
import org.springframework.data.mongodb.core.query.Query import org.springframework.data.mongodb.core.query.Query

View File

@@ -4,11 +4,11 @@ import com.synebula.gaea.spring.autoconfig.Factory
import com.synebula.gaea.spring.autoconfig.Proxy import com.synebula.gaea.spring.autoconfig.Proxy
import org.springframework.beans.factory.BeanFactory import org.springframework.beans.factory.BeanFactory
class MongodbRepositoryFactory( class MongoDbContextFactory(
supertype: Class<*>, supertype: Class<*>,
var beanFactory: BeanFactory, var beanFactory: BeanFactory,
) : Factory(supertype) { ) : Factory(supertype) {
override fun createProxy(): Proxy { override fun createProxy(): Proxy {
return MongodbRepositoryProxy(supertype, this.beanFactory) return MongoDbContextProxy(supertype, this.beanFactory)
} }
} }

View File

@@ -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}] ")
}
}
}

View File

@@ -1,7 +1,8 @@
package com.synebula.gaea.mongodb.autoconfig 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.domain.repository.IRepository
import com.synebula.gaea.query.IQuery
import com.synebula.gaea.spring.autoconfig.Register import com.synebula.gaea.spring.autoconfig.Register
import org.springframework.beans.factory.config.BeanDefinition import org.springframework.beans.factory.config.BeanDefinition
import org.springframework.beans.factory.support.BeanDefinitionBuilder 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.annotation.AnnotationAttributes
import org.springframework.core.type.AnnotationMetadata import org.springframework.core.type.AnnotationMetadata
class MongodbRepositoryRegister : Register() { class MongoDbContextRegister : Register() {
override fun scan(metadata: AnnotationMetadata): Map<String, BeanDefinition> { override fun scan(metadata: AnnotationMetadata): Map<String, BeanDefinition> {
val result = mutableMapOf<String, BeanDefinition>() val result = mutableMapOf<String, BeanDefinition>()
// 获取注解参数信息:basePackages // 获取注解参数信息:basePackages
val attributes = AnnotationAttributes( val attributes = AnnotationAttributes(
metadata.getAnnotationAttributes( metadata.getAnnotationAttributes(
MongodbRepositoryScan::class.java.name MongoDbRepositoryScan::class.java.name
) ?: mapOf() ) ?: mapOf()
) )
val basePackages = attributes.getStringArray("basePackages") val basePackages = attributes.getStringArray("basePackages")
val beanDefinitions = this.doScan( val beanDefinitions = this.doScan(
basePackages, 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 -> beanDefinitions.forEach { beanDefinition ->
// 获取实际的bean类型 // 获取实际的bean类型
@@ -44,7 +45,7 @@ class MongodbRepositoryRegister : Register() {
builder.addConstructorArgValue(beanClazz) builder.addConstructorArgValue(beanClazz)
builder.addConstructorArgValue(this._beanFactory) builder.addConstructorArgValue(this._beanFactory)
val definition = builder.rawBeanDefinition as GenericBeanDefinition val definition = builder.rawBeanDefinition as GenericBeanDefinition
definition.beanClass = MongodbRepositoryFactory::class.java definition.beanClass = MongoDbContextFactory::class.java
definition.autowireMode = GenericBeanDefinition.AUTOWIRE_BY_TYPE definition.autowireMode = GenericBeanDefinition.AUTOWIRE_BY_TYPE
result[beanClazz.name] = definition result[beanClazz.name] = definition
} }
@@ -59,7 +60,7 @@ class MongodbRepositoryRegister : Register() {
builder.addConstructorArgValue(this._beanFactory) builder.addConstructorArgValue(this._beanFactory)
builder.addConstructorArgValue(emptyArray<String>()) builder.addConstructorArgValue(emptyArray<String>())
val definition = builder.rawBeanDefinition as GenericBeanDefinition val definition = builder.rawBeanDefinition as GenericBeanDefinition
definition.beanClass = MongodbRepositoryFactory::class.java definition.beanClass = MongoDbContextFactory::class.java
definition.autowireMode = GenericBeanDefinition.AUTOWIRE_BY_TYPE definition.autowireMode = GenericBeanDefinition.AUTOWIRE_BY_TYPE
result[IRepository::class.java.name] = definition result[IRepository::class.java.name] = definition
} }

View File

@@ -8,5 +8,5 @@ import java.lang.annotation.Inherited
@Retention(AnnotationRetention.RUNTIME) @Retention(AnnotationRetention.RUNTIME)
@MustBeDocumented @MustBeDocumented
@Inherited @Inherited
@Import(MongodbRepositoryRegister::class) @Import(MongoDbContextRegister::class)
annotation class MongodbRepositoryScan(val basePackages: Array<String> = []) annotation class MongoDbRepositoryScan(val basePackages: Array<String> = [])

View File

@@ -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}] ")
}
}
}

View File

@@ -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() {
}
}

View File

@@ -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.ext.firstCharLowerCase
import com.synebula.gaea.mongodb.order import com.synebula.gaea.mongodb.order
import com.synebula.gaea.mongodb.select import com.synebula.gaea.mongodb.select
import com.synebula.gaea.mongodb.where import com.synebula.gaea.mongodb.where
import com.synebula.gaea.mongodb.whereId import com.synebula.gaea.mongodb.whereId
import com.synebula.gaea.query.IQuery import com.synebula.gaea.db.query.IQuery
import com.synebula.gaea.query.Page import com.synebula.gaea.db.query.Page
import com.synebula.gaea.query.Params import com.synebula.gaea.db.query.Params
import com.synebula.gaea.query.Table import com.synebula.gaea.db.query.Table
import com.synebula.gaea.reflect.fieldNames import com.synebula.gaea.reflect.fieldNames
import org.springframework.data.mongodb.core.MongoTemplate import org.springframework.data.mongodb.core.MongoTemplate
import org.springframework.data.mongodb.core.query.Criteria import org.springframework.data.mongodb.core.query.Criteria
import org.springframework.data.mongodb.core.query.Query 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) : open class MongodbQuery<TView, ID>(override var clazz: Class<TView>, var template: MongoTemplate) :
IQuery<TView, ID> { IQuery<TView, ID> {

View File

@@ -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.db.query.IQuery
import com.synebula.gaea.query.IQueryFactory import com.synebula.gaea.db.query.IQueryFactory
import org.springframework.data.mongodb.core.MongoTemplate import org.springframework.data.mongodb.core.MongoTemplate
class MongodbQueryFactory(var template: MongoTemplate) : IQueryFactory { class MongodbQueryFactory(var template: MongoTemplate) : IQueryFactory {

View File

@@ -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}]的集合")
}
}
}

View File

@@ -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()
}
}

View File

@@ -5,7 +5,6 @@ import com.synebula.gaea.data.serialization.json.IJsonSerializer
class HttpMessage(private var serializer: IJsonSerializer) : DataMessage<Any>() { class HttpMessage(private var serializer: IJsonSerializer) : DataMessage<Any>() {
constructor(data: Any, serializer: IJsonSerializer) : this(serializer) { constructor(data: Any, serializer: IJsonSerializer) : this(serializer) {
this.data = data this.data = data
} }

View File

@@ -0,0 +1,10 @@
package com.synebula.gaea.data.permission
/**
* 元素授权类型
*/
enum class AuthorityType {
Default,
Deny,
Allow
}

View File

@@ -0,0 +1,28 @@
package com.synebula.gaea.data.permission
/**
* 角色权限类型
*/
enum class PermissionType {
/**
* 拥有所有权限
*/
All,
/**
* 拥有最大权限
* 不配置无权则有权限
*/
Maximum,
/**
* 最小权限
* 不配置有权则无权限
*/
Minimum,
/**
* 没有任何权限
*/
None
}

View File

@@ -1,4 +1,4 @@
package com.synebula.gaea.domain.model package com.synebula.gaea.db
/** /**
* 继承本接口说明对象为实体类型 * 继承本接口说明对象为实体类型

View File

@@ -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
}

View File

@@ -1,4 +1,4 @@
package com.synebula.gaea.domain.repository.context package com.synebula.gaea.db.context
/** /**
* 表示所有继承于该接口的类型都是Unit Of Work的一种实现 * 表示所有继承于该接口的类型都是Unit Of Work的一种实现

View File

@@ -1,4 +1,4 @@
package com.synebula.gaea.query package com.synebula.gaea.db.query
/** /**
* 查询基接口, 其中方法都指定了查询的视图类型 * 查询基接口, 其中方法都指定了查询的视图类型

View File

@@ -1,5 +1,4 @@
package com.synebula.gaea.query package com.synebula.gaea.db.query
/** /**
* Query 工厂接口 定义了Query的创建方法 * Query 工厂接口 定义了Query的创建方法

View File

@@ -1,4 +1,4 @@
package com.synebula.gaea.query package com.synebula.gaea.db.query
enum class Operator { enum class Operator {
/** /**

View File

@@ -1,4 +1,4 @@
package com.synebula.gaea.query package com.synebula.gaea.db.query
/** /**
* class OrderType * class OrderType

View File

@@ -1,4 +1,4 @@
package com.synebula.gaea.query package com.synebula.gaea.db.query
/** /**
* 分页数据 * 分页数据

View File

@@ -1,4 +1,4 @@
package com.synebula.gaea.query package com.synebula.gaea.db.query
/** /**
* class 分页参数信息 * class 分页参数信息

View File

@@ -1,3 +1,3 @@
package com.synebula.gaea.query package com.synebula.gaea.db.query
annotation class Table(val name: String = "") annotation class Table(val name: String = "")

View File

@@ -1,4 +1,4 @@
package com.synebula.gaea.query package com.synebula.gaea.db.query
/** /**
* 字段注解规定字段的查询方式 * 字段注解规定字段的查询方式

View File

@@ -1,6 +1,6 @@
package com.synebula.gaea.domain.event package com.synebula.gaea.domain.event
import com.synebula.gaea.data.message.IEvent 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

View File

@@ -1,6 +1,7 @@
package com.synebula.gaea.domain.event package com.synebula.gaea.domain.event
import com.synebula.gaea.data.message.IEvent import com.synebula.gaea.data.message.IEvent
import com.synebula.gaea.db.IEntity
import com.synebula.gaea.domain.model.IAggregateRoot 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

View File

@@ -1,5 +1,5 @@
package com.synebula.gaea.domain.model package com.synebula.gaea.domain.model
abstract class AggregateRoot<ID> : Entity<ID>(), IAggregateRoot<ID> { abstract class AggregateRoot<ID> : Entity<ID>(), IAggregateRoot<ID> {
override var alive: Boolean = true override var avalible: Boolean = true
} }

View File

@@ -1,3 +1,5 @@
package com.synebula.gaea.domain.model package com.synebula.gaea.domain.model
import com.synebula.gaea.db.IEntity
abstract class Entity<ID> : IEntity<ID> abstract class Entity<ID> : IEntity<ID>

View File

@@ -1,5 +1,7 @@
package com.synebula.gaea.domain.model 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
} }

View File

@@ -1,7 +0,0 @@
package com.synebula.gaea.domain.model
/**
* 继承本接口,说明对象为值类型。
* @author alex
*/
interface IValue

View File

@@ -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
}

View File

@@ -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)
}

View File

@@ -7,7 +7,6 @@ import com.synebula.gaea.domain.event.AfterRemoveEvent
import com.synebula.gaea.domain.event.BeforeRemoveEvent import com.synebula.gaea.domain.event.BeforeRemoveEvent
import com.synebula.gaea.domain.model.IAggregateRoot import com.synebula.gaea.domain.model.IAggregateRoot
import com.synebula.gaea.domain.repository.IRepository 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 clazz: Class<TRoot>,
protected open var repository: IRepository<TRoot, ID>, protected open var repository: IRepository<TRoot, ID>,
protected open var mapper: IObjectMapper, protected open var mapper: IObjectMapper,
) : IService<ID> {
@Resource
protected open var bus: IBus<Any>? = null protected open var bus: IBus<Any>? = null
) : IService<ID> {
/** /**
* 增加对象 * 增加对象

View File

@@ -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)
}
}

View File

@@ -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>
}

View File

@@ -0,0 +1,5 @@
package com.synebula.gaea.record.model
import com.synebula.gaea.db.IEntity
interface IRecord<ID> : IEntity<ID>

View File

@@ -1,4 +1,4 @@
package com.synebula.gaea.domain.record package com.synebula.gaea.record.model
import java.util.* import java.util.*

View File

@@ -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.data.message.DataMessage
import com.synebula.gaea.domain.model.IAggregateRoot import com.synebula.gaea.db.IEntity
import com.synebula.gaea.log.ILogger import com.synebula.gaea.log.ILogger
@@ -11,7 +11,7 @@ import com.synebula.gaea.log.ILogger
* @version 0.0.1 * @version 0.0.1
* @since 2016年9月18日 下午2:23:15 * @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 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>)
/** /**
* 增加对象 * 增加对象

View File

@@ -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)
}
}