合并dev
This commit is contained in:
@@ -21,14 +21,14 @@ allprojects {
|
||||
|
||||
subprojects {
|
||||
ext {
|
||||
version '0.6.1'
|
||||
version '0.13.1'
|
||||
spring_version = "2.3.0.RELEASE"
|
||||
}
|
||||
|
||||
buildscript {
|
||||
repositories {
|
||||
mavenLocal()
|
||||
maven { url 'http://maven.aliyuMongoRepositoryn.com/nexus/content/groups/public/' }
|
||||
maven { url 'http://maven.aliyun.com/nexus/content/groups/public/' }
|
||||
mavenCentral()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,22 @@
|
||||
buildscript {
|
||||
dependencies {
|
||||
classpath("org.jetbrains.kotlin:kotlin-allopen:$kotlin_version")
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: 'kotlin-spring'
|
||||
|
||||
dependencies {
|
||||
compile project(":src:gaea")
|
||||
compile("org.springframework.boot:spring-boot-starter-web:$spring_version")
|
||||
compile("org.springframework.boot:spring-boot-starter-aop:$spring_version")
|
||||
compile("org.springframework.boot:spring-boot-starter-mail:$spring_version")
|
||||
compile("org.springframework.boot:spring-boot-starter-security:$spring_version")
|
||||
compile group: 'net.sf.dozer', name: 'dozer', version: '5.5.1'
|
||||
compile group: 'org.apache.poi', name: 'poi', version: '4.1.2'
|
||||
compile group: 'org.apache.poi', name: 'poi-ooxml', version: '5.0.0'
|
||||
compile group: 'com.google.code.gson', name: 'gson', version: '2.8.6'
|
||||
compile group: 'com.google.guava', name: 'guava', version: '30.1.1-jre'
|
||||
compile group: 'com.auth0', name: 'java-jwt', version: '3.14.0'
|
||||
}
|
||||
|
||||
publishing {
|
||||
|
||||
@@ -20,8 +20,8 @@ import javax.annotation.Resource
|
||||
open class Application<TCommand : ICommand, TView, TKey>(
|
||||
override var name: String,
|
||||
override var clazz: Class<TView>,
|
||||
override var service: IService<TKey>?,
|
||||
override var query: IQuery?,
|
||||
override var service: IService<TKey>,
|
||||
override var query: IQuery,
|
||||
override var logger: ILogger?
|
||||
) : ICommandApp<TCommand, TKey>, IQueryApp<TView, TKey> {
|
||||
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
package com.synebula.gaea.app
|
||||
|
||||
import com.synebula.gaea.app.component.HttpMessage
|
||||
import com.google.gson.Gson
|
||||
import com.synebula.gaea.app.struct.HttpMessage
|
||||
import com.synebula.gaea.data.message.Status
|
||||
import com.synebula.gaea.log.ILogger
|
||||
import org.springframework.security.core.context.SecurityContextHolder
|
||||
|
||||
interface IApplication {
|
||||
|
||||
@@ -21,7 +23,7 @@ interface IApplication {
|
||||
* 安全执行
|
||||
*/
|
||||
fun safeExecute(error: String, process: ((msg: HttpMessage) -> Unit)): HttpMessage {
|
||||
val msg = HttpMessage(Status.Success)
|
||||
val msg = HttpMessage()
|
||||
try {
|
||||
process(msg)
|
||||
logger?.debug(this, "$name business execute success")
|
||||
@@ -37,7 +39,7 @@ interface IApplication {
|
||||
* 可抛出自定义异常信息的安全controller实现了异常捕获和消息组成。
|
||||
*/
|
||||
fun throwExecute(error: String, process: ((msg: HttpMessage) -> Unit)): HttpMessage {
|
||||
val msg = HttpMessage(Status.Success)
|
||||
val msg = HttpMessage()
|
||||
try {
|
||||
process(msg)
|
||||
logger?.debug(this, "$name business execute success")
|
||||
@@ -47,4 +49,23 @@ interface IApplication {
|
||||
}
|
||||
return msg
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户信息
|
||||
* @param clazz 用户信息结构类
|
||||
*/
|
||||
fun <T> sessionUser(clazz: Class<T>): T? {
|
||||
try {
|
||||
val authentication = SecurityContextHolder.getContext().authentication.principal.toString()
|
||||
try {
|
||||
val gson = Gson()
|
||||
return gson.fromJson<T>(authentication, clazz)
|
||||
} catch (ex: Exception) {
|
||||
logger?.error(this, ex, "[$name]解析用户信息异常!用户信息:$authentication: ${ex.message}")
|
||||
}
|
||||
} catch (ex: Exception) {
|
||||
logger?.error(this, ex, "[$name]获取用户信息异常!${ex.message}")
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.synebula.gaea.app
|
||||
|
||||
import com.synebula.gaea.app.cmd.ILazyCommandApp
|
||||
import com.synebula.gaea.app.query.IQueryApp
|
||||
import com.synebula.gaea.data.serialization.json.IJsonSerializer
|
||||
import com.synebula.gaea.domain.model.IAggregateRoot
|
||||
import com.synebula.gaea.domain.service.ILazyService
|
||||
import com.synebula.gaea.log.ILogger
|
||||
import com.synebula.gaea.query.IQuery
|
||||
import javax.annotation.Resource
|
||||
|
||||
/**
|
||||
* 联合服务,同时实现了ILazyCommandApp和IQueryApp接口
|
||||
*
|
||||
* @param name 业务名称
|
||||
* @param service 业务domain服务
|
||||
* @param query 业务查询服务
|
||||
* @param logger 日志组件
|
||||
*/
|
||||
open class LazyApplication<TRoot : IAggregateRoot<TKey>, TKey>(
|
||||
override var name: String,
|
||||
override var clazz: Class<TRoot>, //view class type
|
||||
override var service: ILazyService<TRoot, TKey>,
|
||||
override var query: IQuery,
|
||||
override var logger: ILogger?
|
||||
) : ILazyCommandApp<TRoot, TKey>, IQueryApp<TRoot, TKey> {
|
||||
|
||||
@Resource
|
||||
override var jsonSerializer: IJsonSerializer? = null
|
||||
}
|
||||
@@ -14,9 +14,10 @@ import javax.annotation.Resource
|
||||
* @param logger 日志组件
|
||||
*/
|
||||
open class CommandApp<TCommand : ICommand, TKey>(
|
||||
override var name: String,
|
||||
override var service: IService<TKey>?,
|
||||
override var logger: ILogger?) : ICommandApp<TCommand, TKey> {
|
||||
override var name: String,
|
||||
override var service: IService<TKey>,
|
||||
override var logger: ILogger?
|
||||
) : ICommandApp<TCommand, TKey> {
|
||||
@Resource
|
||||
override var jsonSerializer: IJsonSerializer? = null
|
||||
}
|
||||
@@ -1,7 +1,8 @@
|
||||
package com.synebula.gaea.app.cmd
|
||||
|
||||
import com.synebula.gaea.app.IApplication
|
||||
import com.synebula.gaea.app.component.HttpMessage
|
||||
import com.synebula.gaea.app.component.aop.annotation.MethodName
|
||||
import com.synebula.gaea.app.struct.HttpMessage
|
||||
import com.synebula.gaea.data.message.Status
|
||||
import com.synebula.gaea.data.serialization.json.IJsonSerializer
|
||||
import com.synebula.gaea.domain.service.ICommand
|
||||
@@ -18,42 +19,32 @@ import org.springframework.web.bind.annotation.*
|
||||
interface ICommandApp<TCommand : ICommand, TKey> : IApplication {
|
||||
var jsonSerializer: IJsonSerializer?
|
||||
|
||||
var service: IService<TKey>?
|
||||
var service: IService<TKey>
|
||||
|
||||
@PostMapping
|
||||
@MethodName("添加")
|
||||
fun add(@RequestBody command: TCommand): HttpMessage {
|
||||
return this.safeExecute("添加${this.name}数据失败 - ${if (jsonSerializer != null) jsonSerializer?.serialize(command) else ""}") {
|
||||
if (this.service != null) {
|
||||
val msg = this.service!!.add(command)
|
||||
it.load(msg)
|
||||
} else {
|
||||
it.status = Status.Error
|
||||
it.message = "没有对应服务,无法执行该操作"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id:.+}")
|
||||
fun remove(@PathVariable id: TKey): HttpMessage {
|
||||
return this.safeExecute("删除${this.name}[id: $id]失败") {
|
||||
if (this.service != null)
|
||||
it.data = this.service!!.remove(id)
|
||||
else {
|
||||
it.status = Status.Error
|
||||
it.message = "没有对应服务,无法执行该操作"
|
||||
}
|
||||
}
|
||||
return HttpMessage(service.add(command))
|
||||
}
|
||||
|
||||
@PutMapping("/{id:.+}")
|
||||
@MethodName("更新")
|
||||
fun update(@PathVariable id: TKey, @RequestBody command: TCommand): HttpMessage {
|
||||
return this.safeExecute("更新${this.name}失败 - ${if (jsonSerializer != null) jsonSerializer?.serialize(command) else ""}") {
|
||||
if (this.service != null)
|
||||
this.service!!.update(id, command)
|
||||
else {
|
||||
it.status = Status.Error
|
||||
it.message = "没有对应服务,无法执行该操作"
|
||||
}
|
||||
this.service.update(id, command)
|
||||
return HttpMessage()
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id:.+}")
|
||||
@MethodName("删除")
|
||||
fun remove(@PathVariable id: TKey): HttpMessage {
|
||||
val msg = HttpMessage()
|
||||
try {
|
||||
msg.data = this.service.remove(id)
|
||||
} catch (ex: IllegalStateException) {
|
||||
msg.status = Status.Error
|
||||
msg.message = ex.message ?: ""
|
||||
}
|
||||
|
||||
return msg
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
package com.synebula.gaea.app.cmd
|
||||
|
||||
import com.synebula.gaea.app.IApplication
|
||||
import com.synebula.gaea.app.component.aop.annotation.MethodName
|
||||
import com.synebula.gaea.app.struct.HttpMessage
|
||||
import com.synebula.gaea.data.message.Status
|
||||
import com.synebula.gaea.data.serialization.json.IJsonSerializer
|
||||
import com.synebula.gaea.domain.model.IAggregateRoot
|
||||
import com.synebula.gaea.domain.service.ILazyService
|
||||
import org.springframework.web.bind.annotation.*
|
||||
|
||||
/**
|
||||
* 直接使用实体对象提供服务的api
|
||||
*
|
||||
* @author alex
|
||||
* @version 0.1
|
||||
* @since 2020-05-15
|
||||
*/
|
||||
interface ILazyCommandApp<TRoot : IAggregateRoot<TKey>, TKey> : IApplication {
|
||||
var jsonSerializer: IJsonSerializer?
|
||||
|
||||
var service: ILazyService<TRoot, TKey>
|
||||
|
||||
@PostMapping
|
||||
@MethodName("添加")
|
||||
fun add(@RequestBody entity: TRoot): HttpMessage {
|
||||
return HttpMessage(service.add(entity))
|
||||
}
|
||||
|
||||
@PutMapping("/{id:.+}")
|
||||
@MethodName("更新")
|
||||
fun update(@PathVariable id: TKey, @RequestBody entity: TRoot): HttpMessage {
|
||||
this.service.update(id, entity)
|
||||
return HttpMessage()
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id:.+}")
|
||||
@MethodName("删除")
|
||||
fun remove(@PathVariable id: TKey): HttpMessage {
|
||||
val msg = HttpMessage()
|
||||
try {
|
||||
msg.data = this.service.remove(id)
|
||||
} catch (ex: IllegalStateException) {
|
||||
msg.status = Status.Error
|
||||
msg.message = ex.message ?: ""
|
||||
}
|
||||
|
||||
return msg
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.synebula.gaea.app.cmd
|
||||
|
||||
import com.synebula.gaea.data.serialization.json.IJsonSerializer
|
||||
import com.synebula.gaea.domain.model.IAggregateRoot
|
||||
import com.synebula.gaea.domain.service.ICommand
|
||||
import com.synebula.gaea.domain.service.ILazyService
|
||||
import com.synebula.gaea.domain.service.IService
|
||||
import com.synebula.gaea.log.ILogger
|
||||
import javax.annotation.Resource
|
||||
|
||||
/**
|
||||
* 指令服务,同时实现ICommandApp
|
||||
*
|
||||
* @param name 业务名称
|
||||
* @param service 业务domain服务
|
||||
* @param logger 日志组件
|
||||
*/
|
||||
open class LazyCommandApp<TRoot : IAggregateRoot<TKey>, TKey>(
|
||||
override var name: String,
|
||||
override var service: ILazyService<TRoot, TKey>,
|
||||
override var logger: ILogger?
|
||||
) : ILazyCommandApp<TRoot, TKey> {
|
||||
@Resource
|
||||
override var jsonSerializer: IJsonSerializer? = null
|
||||
}
|
||||
@@ -11,24 +11,21 @@ import org.springframework.stereotype.Component
|
||||
import java.io.File
|
||||
|
||||
@Component
|
||||
class EmailMessenger : IEmailMessenger {
|
||||
class EmailMessenger(var mailSender: JavaMailSender?) : IEmailMessenger {
|
||||
|
||||
@Autowired
|
||||
private lateinit var mailSender: JavaMailSender
|
||||
|
||||
@Value("\${spring.mail.host}")
|
||||
@Value("\${spring.mail.host:}")
|
||||
var host = ""
|
||||
|
||||
@Value("\${spring.mail.port}")
|
||||
@Value("\${spring.mail.port:}")
|
||||
var port = ""
|
||||
|
||||
@Value("\${spring.mail.sender}")
|
||||
@Value("\${spring.mail.sender:}")
|
||||
var sender = ""
|
||||
|
||||
@Value("\${spring.mail.username}")
|
||||
@Value("\${spring.mail.username:}")
|
||||
var username = ""
|
||||
|
||||
@Value("\${spring.mail.password}")
|
||||
@Value("\${spring.mail.password:}")
|
||||
var password = ""
|
||||
|
||||
@Autowired
|
||||
@@ -42,13 +39,20 @@ class EmailMessenger : IEmailMessenger {
|
||||
* @param receivers 邮件接受者
|
||||
* @param files 附件
|
||||
*/
|
||||
override fun sendMessage(subject: String, content: String, receivers: List<String>, files: Map<String, String>): Map<String, Boolean> {
|
||||
override fun sendMessage(
|
||||
subject: String,
|
||||
content: String,
|
||||
receivers: List<String>,
|
||||
files: Map<String, String>
|
||||
): Map<String, Boolean> {
|
||||
val result = mutableMapOf<String, Boolean>()
|
||||
this.check()
|
||||
receivers.forEach { receiver ->
|
||||
result[receiver] = true
|
||||
if (this.mailSender == null)
|
||||
throw Exception("没有配置JavaMailSender的实现实例,请重新配置")
|
||||
try {
|
||||
val mail = mailSender.createMimeMessage()
|
||||
val mail = this.mailSender!!.createMimeMessage()
|
||||
val mimeMessageHelper = MimeMessageHelper(mail, true, "utf-8")
|
||||
mimeMessageHelper.setFrom(sender)
|
||||
mimeMessageHelper.setTo(receiver)
|
||||
@@ -59,7 +63,7 @@ class EmailMessenger : IEmailMessenger {
|
||||
val file = FileSystemResource(File(path))
|
||||
mimeMessageHelper.addAttachment(name, file)
|
||||
}
|
||||
mailSender.send(mail) //发送
|
||||
this.mailSender!!.send(mail) //发送
|
||||
} catch (e: Exception) {
|
||||
logger.error(e, "发送邮件[$subject]至地址[$receiver]失败")
|
||||
result[receiver] = false
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.synebula.gaea.app.component
|
||||
|
||||
import com.google.common.eventbus.AsyncEventBus
|
||||
import com.google.common.eventbus.EventBus
|
||||
import com.synebula.gaea.event.IEvent
|
||||
import com.synebula.gaea.event.IEventBus
|
||||
import org.springframework.stereotype.Component
|
||||
import java.util.concurrent.Executors
|
||||
|
||||
@Component
|
||||
class EventBus : IEventBus {
|
||||
|
||||
/**
|
||||
* 同步事件总线
|
||||
*/
|
||||
var eventBus = EventBus()
|
||||
|
||||
/**
|
||||
* 异步事件总线
|
||||
*/
|
||||
var asyncEventBus = AsyncEventBus(Executors.newFixedThreadPool(2))
|
||||
|
||||
override fun register(obj: Any) {
|
||||
eventBus.register(obj)
|
||||
asyncEventBus.register(obj)
|
||||
}
|
||||
|
||||
override fun unregister(obj: Any) {
|
||||
eventBus.unregister(obj)
|
||||
asyncEventBus.unregister(obj)
|
||||
}
|
||||
|
||||
override fun publish(event: IEvent) {
|
||||
eventBus.post(event)
|
||||
}
|
||||
|
||||
override fun publishAsync(event: IEvent) {
|
||||
asyncEventBus.post(event)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.synebula.gaea.app.component
|
||||
|
||||
import com.google.common.eventbus.Subscribe
|
||||
import com.synebula.gaea.event.IEventBus
|
||||
import org.springframework.beans.BeansException
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor
|
||||
import org.springframework.stereotype.Component
|
||||
import java.lang.reflect.Method
|
||||
|
||||
|
||||
@Component
|
||||
class EventBusSubscriberProcessor : BeanPostProcessor {
|
||||
|
||||
// 事件总线bean由Spring IoC容器负责创建,这里只需要通过@Autowired注解注入该bean即可使用事件总线
|
||||
@Autowired
|
||||
var eventBus: IEventBus? = null
|
||||
|
||||
@Throws(BeansException::class)
|
||||
override fun postProcessBeforeInitialization(bean: Any, beanName: String): Any {
|
||||
return bean
|
||||
}
|
||||
|
||||
//对于每个容器执行了初始化的 bean,如果这个 bean 的某个方法注解了@Subscribe,则将该 bean 注册到事件总线
|
||||
@Throws(BeansException::class)
|
||||
override fun postProcessAfterInitialization(bean: Any, beanName: String): Any {
|
||||
// for each method in the bean
|
||||
val methods: Array<Method> = bean.javaClass.methods
|
||||
for (method in methods) {
|
||||
// check the annotations on that method
|
||||
val annotations: Array<Annotation> = method.getAnnotations()
|
||||
for (annotation in annotations) {
|
||||
// if it contains the Subscribe annotation
|
||||
if (annotation.annotationClass == Subscribe::class) {
|
||||
// 如果这是一个Guava @Subscribe注解的事件监听器方法,说明所在bean实例
|
||||
// 对应一个Guava事件监听器类,将该bean实例注册到Guava事件总线
|
||||
eventBus?.register(bean)
|
||||
return bean
|
||||
}
|
||||
}
|
||||
}
|
||||
return bean
|
||||
}
|
||||
|
||||
}
|
||||
@@ -48,7 +48,7 @@ class Logger : ILogger {
|
||||
|
||||
override fun trace(format: String, vararg args: Any) {
|
||||
if (this.logger.isTraceEnabled) {
|
||||
val message = String.format(format, *args)
|
||||
val message = if (args.isEmpty()) format else String.format(format, *args)
|
||||
this.logger.trace(message)
|
||||
}
|
||||
}
|
||||
@@ -56,7 +56,7 @@ class Logger : ILogger {
|
||||
|
||||
override fun trace(t: Throwable, format: String, vararg args: Any) {
|
||||
if (this.logger.isTraceEnabled) {
|
||||
val message = String.format(format, *args)
|
||||
val message = if (args.isEmpty()) format else String.format(format, *args)
|
||||
this.logger.trace(message, t)
|
||||
}
|
||||
}
|
||||
@@ -64,7 +64,7 @@ class Logger : ILogger {
|
||||
override fun trace(obj: Any, format: String, vararg args: Any) {
|
||||
if (this.logger.isTraceEnabled) {
|
||||
val real = this.getLogger(obj)
|
||||
val message = String.format(format, *args)
|
||||
val message = if (args.isEmpty()) format else String.format(format, *args)
|
||||
real.trace(message)
|
||||
}
|
||||
}
|
||||
@@ -72,7 +72,7 @@ class Logger : ILogger {
|
||||
override fun trace(obj: Any, t: Throwable?, format: String, vararg args: Any) {
|
||||
if (this.logger.isTraceEnabled) {
|
||||
val real = this.getLogger(obj)
|
||||
val message = String.format(format, *args)
|
||||
val message = if (args.isEmpty()) format else String.format(format, *args)
|
||||
real.trace(message, t)
|
||||
}
|
||||
}
|
||||
@@ -95,7 +95,7 @@ class Logger : ILogger {
|
||||
|
||||
override fun debug(format: String, vararg args: Any) {
|
||||
if (this.logger.isDebugEnabled) {
|
||||
val message = String.format(format, *args)
|
||||
val message = if (args.isEmpty()) format else String.format(format, *args)
|
||||
this.logger.debug(message)
|
||||
}
|
||||
}
|
||||
@@ -103,7 +103,7 @@ class Logger : ILogger {
|
||||
|
||||
override fun debug(t: Throwable, format: String, vararg args: Any) {
|
||||
if (this.logger.isDebugEnabled) {
|
||||
val message = String.format(format, *args)
|
||||
val message = if (args.isEmpty()) format else String.format(format, *args)
|
||||
this.logger.debug(message, t)
|
||||
}
|
||||
}
|
||||
@@ -111,7 +111,7 @@ class Logger : ILogger {
|
||||
override fun debug(obj: Any, format: String, vararg args: Any) {
|
||||
if (this.logger.isDebugEnabled) {
|
||||
val real = this.getLogger(obj)
|
||||
val message = String.format(format, *args)
|
||||
val message = if (args.isEmpty()) format else String.format(format, *args)
|
||||
real.debug(message)
|
||||
}
|
||||
}
|
||||
@@ -119,7 +119,7 @@ class Logger : ILogger {
|
||||
override fun debug(obj: Any, t: Throwable?, format: String, vararg args: Any) {
|
||||
if (this.logger.isDebugEnabled) {
|
||||
val real = this.getLogger(obj)
|
||||
val message = String.format(format, *args)
|
||||
val message = if (args.isEmpty()) format else String.format(format, *args)
|
||||
real.debug(message, t)
|
||||
}
|
||||
}
|
||||
@@ -140,7 +140,7 @@ class Logger : ILogger {
|
||||
|
||||
override fun info(format: String, vararg args: Any) {
|
||||
if (this.logger.isInfoEnabled) {
|
||||
val message = String.format(format, *args)
|
||||
val message = if (args.isEmpty()) format else String.format(format, *args)
|
||||
this.logger.info(message)
|
||||
}
|
||||
}
|
||||
@@ -148,7 +148,7 @@ class Logger : ILogger {
|
||||
|
||||
override fun info(t: Throwable, format: String, vararg args: Any) {
|
||||
if (this.logger.isInfoEnabled) {
|
||||
val message = String.format(format, *args)
|
||||
val message = if (args.isEmpty()) format else String.format(format, *args)
|
||||
this.logger.info(message, t)
|
||||
}
|
||||
}
|
||||
@@ -156,7 +156,7 @@ class Logger : ILogger {
|
||||
override fun info(obj: Any, format: String, vararg args: Any) {
|
||||
if (this.logger.isInfoEnabled) {
|
||||
val real = this.getLogger(obj)
|
||||
val message = String.format(format, *args)
|
||||
val message = if (args.isEmpty()) format else String.format(format, *args)
|
||||
real.info(message)
|
||||
}
|
||||
}
|
||||
@@ -164,7 +164,7 @@ class Logger : ILogger {
|
||||
override fun info(obj: Any, t: Throwable?, format: String, vararg args: Any) {
|
||||
if (this.logger.isInfoEnabled) {
|
||||
val real = this.getLogger(obj)
|
||||
val message = String.format(format, *args)
|
||||
val message = if (args.isEmpty()) format else String.format(format, *args)
|
||||
real.info(message, t)
|
||||
}
|
||||
}
|
||||
@@ -185,7 +185,7 @@ class Logger : ILogger {
|
||||
|
||||
override fun warn(format: String, vararg args: Any) {
|
||||
if (this.logger.isWarnEnabled) {
|
||||
val message = String.format(format, *args)
|
||||
val message = if (args.isEmpty()) format else String.format(format, *args)
|
||||
this.logger.warn(message)
|
||||
}
|
||||
}
|
||||
@@ -193,7 +193,7 @@ class Logger : ILogger {
|
||||
|
||||
override fun warn(t: Throwable, format: String, vararg args: Any) {
|
||||
if (this.logger.isWarnEnabled) {
|
||||
val message = String.format(format, *args)
|
||||
val message = if (args.isEmpty()) format else String.format(format, *args)
|
||||
this.logger.warn(message, t)
|
||||
}
|
||||
}
|
||||
@@ -201,7 +201,7 @@ class Logger : ILogger {
|
||||
override fun warn(obj: Any, format: String, vararg args: Any) {
|
||||
if (this.logger.isWarnEnabled) {
|
||||
val real = this.getLogger(obj)
|
||||
val message = String.format(format, *args)
|
||||
val message = if (args.isEmpty()) format else String.format(format, *args)
|
||||
real.warn(message)
|
||||
}
|
||||
}
|
||||
@@ -209,7 +209,7 @@ class Logger : ILogger {
|
||||
override fun warn(obj: Any, t: Throwable?, format: String, vararg args: Any) {
|
||||
if (this.logger.isWarnEnabled) {
|
||||
val real = this.getLogger(obj)
|
||||
val message = String.format(format, *args)
|
||||
val message = if (args.isEmpty()) format else String.format(format, *args)
|
||||
real.warn(message, t)
|
||||
}
|
||||
}
|
||||
@@ -232,7 +232,7 @@ class Logger : ILogger {
|
||||
|
||||
override fun error(format: String, vararg args: Any) {
|
||||
if (this.logger.isErrorEnabled) {
|
||||
val message = String.format(format, *args)
|
||||
val message = if (args.isEmpty()) format else String.format(format, *args)
|
||||
this.logger.error(message)
|
||||
}
|
||||
}
|
||||
@@ -240,7 +240,7 @@ class Logger : ILogger {
|
||||
|
||||
override fun error(t: Throwable, format: String, vararg args: Any) {
|
||||
if (this.logger.isErrorEnabled) {
|
||||
val message = String.format(format, *args)
|
||||
val message = if (args.isEmpty()) format else String.format(format, *args)
|
||||
this.logger.error(message, t)
|
||||
}
|
||||
}
|
||||
@@ -248,7 +248,7 @@ class Logger : ILogger {
|
||||
override fun error(obj: Any, format: String, vararg args: Any) {
|
||||
if (this.logger.isErrorEnabled) {
|
||||
val real = this.getLogger(obj)
|
||||
val message = String.format(format, *args)
|
||||
val message = if (args.isEmpty()) format else String.format(format, *args)
|
||||
real.error(message)
|
||||
}
|
||||
}
|
||||
@@ -256,7 +256,7 @@ class Logger : ILogger {
|
||||
override fun error(obj: Any, t: Throwable?, format: String, vararg args: Any) {
|
||||
if (this.logger.isErrorEnabled) {
|
||||
val real = this.getLogger(obj)
|
||||
val message = String.format(format, *args)
|
||||
val message = if (args.isEmpty()) format else String.format(format, *args)
|
||||
real.error(message, t)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
package com.synebula.gaea.app.component.aop
|
||||
|
||||
import com.google.gson.Gson
|
||||
import com.synebula.gaea.app.IApplication
|
||||
import com.synebula.gaea.app.component.aop.annotation.Handler
|
||||
import com.synebula.gaea.app.component.aop.annotation.MethodName
|
||||
import com.synebula.gaea.app.component.aop.annotation.ModuleName
|
||||
import com.synebula.gaea.app.struct.HttpMessage
|
||||
import com.synebula.gaea.data.message.Status
|
||||
import com.synebula.gaea.exception.NoticeUserException
|
||||
import com.synebula.gaea.log.ILogger
|
||||
import org.aspectj.lang.ProceedingJoinPoint
|
||||
import org.aspectj.lang.annotation.Around
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.context.ApplicationContext
|
||||
import org.springframework.core.DefaultParameterNameDiscoverer
|
||||
|
||||
abstract class AppAspect {
|
||||
private var paramDiscover = DefaultParameterNameDiscoverer()
|
||||
|
||||
private val gson = Gson()
|
||||
|
||||
@Autowired
|
||||
lateinit var logger: ILogger
|
||||
|
||||
@Autowired
|
||||
lateinit var applicationContext: ApplicationContext
|
||||
|
||||
/**
|
||||
* 定义切面的方法
|
||||
*/
|
||||
abstract fun func()
|
||||
|
||||
|
||||
/**
|
||||
* 环绕通知,环绕增强,相当于MethodInterceptor
|
||||
*/
|
||||
@Around("func()")
|
||||
fun around(point: ProceedingJoinPoint): Any? {
|
||||
val clazz = point.`this`.javaClass //获取实际对象的类型避免获取到父类
|
||||
val func = point.signature.declaringType.methods.find {
|
||||
it.name == point.signature.name
|
||||
}!!//获取声明类型中的方法信息
|
||||
val funcAnnotations = func.annotations ?: arrayOf()
|
||||
|
||||
var funcName = func.name
|
||||
//遍历方法注解
|
||||
for (funcAnnotation in funcAnnotations) {
|
||||
val annotations = funcAnnotation.annotationClass.annotations
|
||||
|
||||
//尝试寻找方法注解的处理类
|
||||
val handler = annotations.find { it is Handler }
|
||||
if (handler != null && handler is Handler) {
|
||||
val handleClazz = applicationContext.getBean(handler.value.java)
|
||||
handleClazz.handle(clazz, func, point.args)
|
||||
}
|
||||
if (funcAnnotation is MethodName)
|
||||
funcName = funcAnnotation.name
|
||||
}
|
||||
|
||||
return try {
|
||||
point.proceed()
|
||||
} catch (ex: Throwable) {
|
||||
//找到类的模块名称,否则使用类名
|
||||
var moduleName = clazz.name
|
||||
if (IApplication::class.java.isAssignableFrom(clazz)) {
|
||||
moduleName = (point.`this` as IApplication).name
|
||||
} else {
|
||||
val name = clazz.annotations.find { it is ModuleName }
|
||||
if (name != null && name is ModuleName) {
|
||||
moduleName = name.value
|
||||
}
|
||||
}
|
||||
var message = "$moduleName - $funcName 异常"
|
||||
if (ex is NoticeUserException) {
|
||||
message = "$message: ${ex.message}"
|
||||
} else {
|
||||
message = "$message。"
|
||||
|
||||
}
|
||||
logger.error(
|
||||
ex,
|
||||
"$message。Method args ${
|
||||
paramDiscover.getParameterNames(func)?.contentToString()
|
||||
} values is ${
|
||||
gson.toJson(point.args)
|
||||
}"
|
||||
)
|
||||
return HttpMessage(Status.Error, message)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.synebula.gaea.app.component.aop.annotation
|
||||
|
||||
import com.synebula.gaea.app.component.aop.handler.AccessLogHandler
|
||||
|
||||
@Target(AnnotationTarget.FUNCTION)
|
||||
@Handler(AccessLogHandler::class)
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
annotation class AccessLog
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.synebula.gaea.app.component.aop.annotation
|
||||
|
||||
import com.synebula.gaea.app.component.aop.handler.AnnotationHandler
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
@Target(AnnotationTarget.ANNOTATION_CLASS)
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
annotation class Handler(val value: KClass<out AnnotationHandler>)
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.synebula.gaea.app.component.aop.annotation
|
||||
|
||||
import java.lang.annotation.Inherited
|
||||
|
||||
/**
|
||||
* 标记方法名称,由AOP负责记录异常时使用该名称
|
||||
*
|
||||
* @param name 异常消息
|
||||
*/
|
||||
@Inherited
|
||||
@Target(AnnotationTarget.FUNCTION)
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
annotation class MethodName(val name: String)
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.synebula.gaea.app.component.aop.annotation
|
||||
|
||||
/**
|
||||
* 模块的业务名称
|
||||
*/
|
||||
@Target(AnnotationTarget.CLASS)
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
annotation class ModuleName(val value: String)
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.synebula.gaea.app.component.aop.handler
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper
|
||||
import com.synebula.gaea.log.ILogger
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.stereotype.Component
|
||||
import org.springframework.web.context.request.RequestContextHolder
|
||||
import org.springframework.web.context.request.ServletRequestAttributes
|
||||
import java.lang.reflect.Method
|
||||
|
||||
@Component
|
||||
class AccessLogHandler : AnnotationHandler {
|
||||
private val mapper = ObjectMapper()
|
||||
|
||||
@Autowired
|
||||
lateinit var logger: ILogger
|
||||
|
||||
override fun handle(clazz: Class<Any>, func: Method, args: Array<Any>, exception: Exception?) {
|
||||
val attributes = RequestContextHolder.getRequestAttributes() as ServletRequestAttributes
|
||||
val request = attributes.request
|
||||
logger.info(
|
||||
"${request.method} ${request.requestURL} from ${request.remoteAddr}, call function ${clazz.name}.${func.name}, args: ${
|
||||
mapper.writeValueAsString(
|
||||
args
|
||||
)
|
||||
}"
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.synebula.gaea.app.component.aop.handler
|
||||
|
||||
import java.lang.reflect.Method
|
||||
|
||||
interface AnnotationHandler {
|
||||
fun handle(clazz: Class<Any>, func: Method, args: Array<Any>, exception: Exception? = null)
|
||||
}
|
||||
@@ -0,0 +1,306 @@
|
||||
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
|
||||
import org.apache.poi.hssf.usermodel.HSSFCell
|
||||
import org.apache.poi.hssf.usermodel.HSSFCellStyle
|
||||
import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator
|
||||
import org.apache.poi.hssf.usermodel.HSSFWorkbook
|
||||
import org.apache.poi.ss.formula.BaseFormulaEvaluator
|
||||
import org.apache.poi.ss.usermodel.*
|
||||
import org.apache.poi.xssf.usermodel.XSSFFormulaEvaluator
|
||||
import org.apache.poi.xssf.usermodel.XSSFWorkbook
|
||||
import org.springframework.web.multipart.MultipartFile
|
||||
import java.util.*
|
||||
|
||||
|
||||
/**
|
||||
* Excel操作对象
|
||||
*/
|
||||
object Excel {
|
||||
|
||||
/**
|
||||
* 导出excel
|
||||
*
|
||||
* @param data 需要导出的数据
|
||||
* @return excel表格数据
|
||||
*/
|
||||
fun export(data: ExcelData, writeTo: (wb: HSSFWorkbook) -> Unit) {
|
||||
// 第一步,创建一个HSSFWorkbook,对应一个Excel文件
|
||||
val wb = HSSFWorkbook()
|
||||
// 第二步,在workbook中添加一个sheet,对应Excel文件中的sheet,并设置默认样式
|
||||
val sheet = wb.createSheet(data.title)
|
||||
|
||||
// 第四步,创建单元格,并设置值表头 设置表头居中
|
||||
val titleStyle = wb.createCellStyle()
|
||||
titleStyle.alignment = HorizontalAlignment.CENTER// 创建一个居中格式
|
||||
titleStyle.verticalAlignment = VerticalAlignment.CENTER
|
||||
val titleFont = wb.createFont()
|
||||
titleFont.bold = true
|
||||
titleStyle.setFont(titleFont)
|
||||
setBorderStyle(titleStyle, BorderStyle.THIN)
|
||||
|
||||
//声明列对象
|
||||
// 第三步,在sheet中添加表头第0行,注意老版本poi对Excel的行数列数有限制
|
||||
var row = sheet.createRow(0)
|
||||
row.height = 25 * 20
|
||||
var cell: HSSFCell
|
||||
//创建标题
|
||||
for (col in data.columnNames.indices) {
|
||||
try {
|
||||
cell = row.createCell(col)
|
||||
cell.setCellStyle(titleStyle)
|
||||
cell.setCellValue(data.columnNames[col])
|
||||
setColumnWidth(data.columnNames[col], col, sheet)
|
||||
} catch (ex: RuntimeException) {
|
||||
throw Exception("创建索引${col}列[${data.columnNames[col]}]时出现异常", ex)
|
||||
}
|
||||
}
|
||||
|
||||
val contentStyle = wb.createCellStyle()
|
||||
contentStyle.alignment = HorizontalAlignment.LEFT// 创建一个修改居左格式
|
||||
contentStyle.verticalAlignment = VerticalAlignment.CENTER
|
||||
setBorderStyle(contentStyle, BorderStyle.THIN)
|
||||
|
||||
//创建内容
|
||||
var col = 0
|
||||
for (i in data.data.indices) {
|
||||
try {
|
||||
row = sheet.createRow(i + 1)
|
||||
row.height = 20 * 20
|
||||
col = 0
|
||||
while (col < data.data[i].size) {
|
||||
cell = row.createCell(col)
|
||||
cell.setCellStyle(contentStyle)
|
||||
cell.setCellValue(data.data[i][col])
|
||||
setColumnWidth(data.data[i][col], col, sheet)
|
||||
|
||||
col++
|
||||
}
|
||||
} catch (ex: RuntimeException) {
|
||||
throw Exception("创建索引${i}行[${data.columnNames[col]}]时出现异常", ex)
|
||||
}
|
||||
}
|
||||
writeTo(wb)
|
||||
}
|
||||
|
||||
/**
|
||||
* 导入文件
|
||||
*
|
||||
* @param file 上传文件流
|
||||
* @param columns 文件列名称、类型定义
|
||||
* @param rowStart 数据起始行,默认0
|
||||
* @param columnStart 数据起始列,默认0
|
||||
*
|
||||
* @return ExcelData
|
||||
*/
|
||||
fun import(
|
||||
file: MultipartFile,
|
||||
columns: List<Pair<String, String>>,
|
||||
rowStart: Int = 0,
|
||||
columnStart: Int = 0,
|
||||
emptyRowFilter: (row: Row) -> Boolean =
|
||||
{ row ->
|
||||
row.getCell(
|
||||
0,
|
||||
Row.MissingCellPolicy.RETURN_NULL_AND_BLANK
|
||||
) == null
|
||||
}
|
||||
): List<Map<String, Any?>> {
|
||||
if (file.originalFilename?.endsWith(".xls") != true && file.originalFilename?.endsWith(".xlsx") != true)
|
||||
throw RuntimeException("无法识别的文件格式[${file.originalFilename}]")
|
||||
|
||||
val evaluator: BaseFormulaEvaluator
|
||||
val workbook = if (file.originalFilename?.endsWith(".xls") == true) {
|
||||
val wb = HSSFWorkbook(file.inputStream)
|
||||
evaluator = HSSFFormulaEvaluator(wb)
|
||||
wb
|
||||
} else {
|
||||
val wb = XSSFWorkbook(file.inputStream)
|
||||
evaluator = XSSFFormulaEvaluator(wb)
|
||||
wb
|
||||
}
|
||||
|
||||
val sheet = workbook.getSheetAt(0)
|
||||
|
||||
val data = mutableListOf<Map<String, Any?>>()
|
||||
for (r in rowStart..sheet.lastRowNum) {
|
||||
val row = sheet.getRow(r) ?: continue
|
||||
if (emptyRowFilter(row)) continue //空行不处理
|
||||
val rowData = mutableMapOf<String, Any?>()
|
||||
for (c in columnStart until columns.size + columnStart) {
|
||||
try {
|
||||
val column = columns[c]
|
||||
val cell = row.getCell(c, Row.MissingCellPolicy.RETURN_BLANK_AS_NULL)
|
||||
if (cell == null) {
|
||||
rowData[columns[c].first] = ""
|
||||
continue
|
||||
}
|
||||
val value: Any? = when (column.second) {
|
||||
Int::class.java.name -> {
|
||||
if (cell.cellType == CellType.NUMERIC) {
|
||||
cell.numericCellValue.toInt()
|
||||
} else {
|
||||
this.getCellValue(cell, evaluator).toString().toIntOrNull()
|
||||
}
|
||||
}
|
||||
Double::class.java.name -> {
|
||||
if (cell.cellType == CellType.NUMERIC) {
|
||||
cell.numericCellValue
|
||||
} else {
|
||||
this.getCellValue(cell, evaluator).toString().toDoubleOrNull()
|
||||
}
|
||||
}
|
||||
Float::class.java.name -> {
|
||||
if (cell.cellType == CellType.NUMERIC) {
|
||||
cell.numericCellValue.toFloat()
|
||||
} else {
|
||||
this.getCellValue(cell, evaluator).toString().toFloatOrNull()
|
||||
}
|
||||
}
|
||||
Decimal::class.java.name -> {
|
||||
if (cell.cellType == CellType.NUMERIC) {
|
||||
cell.numericCellValue.toBigDecimal()
|
||||
} else {
|
||||
this.getCellValue(cell, evaluator).toString().toBigDecimalOrNull()
|
||||
}
|
||||
}
|
||||
Boolean::class.java.name -> {
|
||||
if (cell.cellType == CellType.BOOLEAN) {
|
||||
cell.booleanCellValue
|
||||
} else {
|
||||
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
|
||||
} catch (ex: Exception) {
|
||||
throw RuntimeException("解析EXCEL文件${file.originalFilename}第${c + 1}行第${c + 1}列出错", ex)
|
||||
}
|
||||
}
|
||||
data.add(rowData)
|
||||
}
|
||||
workbook.close()
|
||||
file.inputStream.close()
|
||||
return data
|
||||
}
|
||||
|
||||
/**
|
||||
* 导入文件
|
||||
*
|
||||
* @param file 上传文件流
|
||||
* @param rowStartIndex 数据起始行,默认0
|
||||
* @param columnStartIndex 数据起始列,默认0
|
||||
*
|
||||
* @return ExcelData
|
||||
*/
|
||||
fun import(
|
||||
file: MultipartFile,
|
||||
columnSize: Int = 0,
|
||||
rowStartIndex: Int = 0,
|
||||
columnStartIndex: Int = 0,
|
||||
emptyRowFilter: (row: Row) -> Boolean = { row ->
|
||||
row.getCell(
|
||||
0,
|
||||
Row.MissingCellPolicy.RETURN_NULL_AND_BLANK
|
||||
) == null
|
||||
}
|
||||
): List<Map<String, String>> {
|
||||
if (file.originalFilename?.endsWith(".xls") != true && file.originalFilename?.endsWith(".xlsx") != true)
|
||||
throw NoticeUserException("无法识别的文件格式[${file.originalFilename}]")
|
||||
val evaluator: BaseFormulaEvaluator
|
||||
val workbook = if (file.originalFilename?.endsWith(".xls") == true) {
|
||||
val wb = HSSFWorkbook(file.inputStream)
|
||||
evaluator = HSSFFormulaEvaluator(wb)
|
||||
wb
|
||||
} else {
|
||||
val wb = XSSFWorkbook(file.inputStream)
|
||||
evaluator = XSSFFormulaEvaluator(wb)
|
||||
wb
|
||||
}
|
||||
val sheet = workbook.getSheetAt(0)
|
||||
|
||||
val titles = mutableListOf<String>()
|
||||
val titleRow = sheet.getRow(rowStartIndex)
|
||||
val size = if (columnSize != 0) columnSize else titleRow.physicalNumberOfCells //列数
|
||||
for (i in columnStartIndex until size) {
|
||||
titles.add(titleRow.getCell(i).stringCellValue)
|
||||
}
|
||||
|
||||
val data = mutableListOf<Map<String, String>>()
|
||||
for (r in (rowStartIndex + 1)..sheet.lastRowNum) {
|
||||
val row = sheet.getRow(r) ?: continue
|
||||
if (emptyRowFilter(row)) continue //空行不处理
|
||||
val rowData = mutableMapOf<String, String>()
|
||||
for (c in columnStartIndex until size + columnStartIndex) {
|
||||
try {
|
||||
val title = titles[c]
|
||||
val cell = row.getCell(c, Row.MissingCellPolicy.RETURN_BLANK_AS_NULL)
|
||||
if (cell == null) {
|
||||
rowData[title] = ""
|
||||
continue
|
||||
}
|
||||
val value = getCellValue(cell, evaluator)
|
||||
rowData[title] = value.toString()
|
||||
} catch (ex: Exception) {
|
||||
throw NoticeUserException("解析EXCEL文件${file.originalFilename}第${r + 1}行第${c + 1}列出错", ex)
|
||||
}
|
||||
}
|
||||
data.add(rowData)
|
||||
}
|
||||
workbook.close()
|
||||
file.inputStream.close()
|
||||
return data
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置列宽
|
||||
*
|
||||
* @param content 列内容
|
||||
* @param col 列索引
|
||||
* @param sheet 需要设置的表格
|
||||
*/
|
||||
private fun setColumnWidth(content: String, col: Int, sheet: Sheet) {
|
||||
//设置列宽
|
||||
val width = (content.length * 1.5 * 256).toInt()
|
||||
if (width > sheet.getColumnWidth(col))
|
||||
sheet.setColumnWidth(col, width)
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置cell style的边框
|
||||
*/
|
||||
private fun setBorderStyle(style: HSSFCellStyle, borderStyle: BorderStyle) {
|
||||
style.borderTop = borderStyle
|
||||
style.borderRight = borderStyle
|
||||
style.borderBottom = borderStyle
|
||||
style.borderLeft = borderStyle
|
||||
}
|
||||
|
||||
private fun getCellValue(cell: Cell, evaluator: BaseFormulaEvaluator): Any {
|
||||
return when (cell.cellType) {
|
||||
CellType.BOOLEAN -> cell.booleanCellValue
|
||||
CellType.ERROR -> cell.errorCellValue
|
||||
CellType.NUMERIC -> {
|
||||
val numericCellValue: Double = cell.numericCellValue
|
||||
if (DateUtil.isCellDateFormatted(cell)) {
|
||||
DateUtil.getLocalDateTime(numericCellValue)
|
||||
} else {
|
||||
numericCellValue
|
||||
}
|
||||
}
|
||||
CellType.STRING -> cell.richStringCellValue.string
|
||||
CellType.BLANK -> ""
|
||||
CellType.FORMULA -> evaluator.evaluate(cell).toString()
|
||||
else -> throw Exception("匹配类型错误")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,107 +0,0 @@
|
||||
package com.synebula.gaea.app.component.poi.excel
|
||||
|
||||
import org.apache.poi.hssf.usermodel.HSSFCell
|
||||
import org.apache.poi.hssf.usermodel.HSSFCellStyle
|
||||
import org.apache.poi.hssf.usermodel.HSSFWorkbook
|
||||
import org.apache.poi.ss.usermodel.BorderStyle
|
||||
import org.apache.poi.ss.usermodel.HorizontalAlignment
|
||||
import org.apache.poi.ss.usermodel.Sheet
|
||||
import org.apache.poi.ss.usermodel.VerticalAlignment
|
||||
import java.lang.Exception
|
||||
import java.lang.RuntimeException
|
||||
import kotlin.math.ceil
|
||||
|
||||
/**
|
||||
* Excel操作对象
|
||||
*/
|
||||
object Excel {
|
||||
|
||||
/**
|
||||
* 导出excel
|
||||
*
|
||||
* @param data 需要导出的数据
|
||||
* @return excel表格数据
|
||||
*/
|
||||
fun export(data: ExcelData, writeTo: (wb: HSSFWorkbook) -> Unit) {
|
||||
// 第一步,创建一个HSSFWorkbook,对应一个Excel文件
|
||||
val wb = HSSFWorkbook()
|
||||
// 第二步,在workbook中添加一个sheet,对应Excel文件中的sheet,并设置默认样式
|
||||
val sheet = wb.createSheet(data.title)
|
||||
|
||||
// 第四步,创建单元格,并设置值表头 设置表头居中
|
||||
val titleStyle = wb.createCellStyle()
|
||||
titleStyle.alignment = HorizontalAlignment.CENTER// 创建一个居中格式
|
||||
titleStyle.verticalAlignment = VerticalAlignment.CENTER
|
||||
val titleFont = wb.createFont()
|
||||
titleFont.bold = true
|
||||
titleStyle.setFont(titleFont)
|
||||
this.setBorderStyle(titleStyle, BorderStyle.THIN)
|
||||
|
||||
//声明列对象
|
||||
// 第三步,在sheet中添加表头第0行,注意老版本poi对Excel的行数列数有限制
|
||||
var row = sheet.createRow(0)
|
||||
row.height = 25 * 20
|
||||
var cell: HSSFCell
|
||||
//创建标题
|
||||
for (col in data.columnNames.indices) {
|
||||
try {
|
||||
cell = row.createCell(col)
|
||||
cell.setCellStyle(titleStyle)
|
||||
cell.setCellValue(data.columnNames[col])
|
||||
this.setColumnWidth(data.columnNames[col], col, sheet)
|
||||
} catch (ex: RuntimeException) {
|
||||
throw Exception("创建索引${col}列[${data.columnNames[col]}]时出现异常", ex)
|
||||
}
|
||||
}
|
||||
|
||||
val contentStyle = wb.createCellStyle()
|
||||
contentStyle.alignment = HorizontalAlignment.LEFT// 创建一个修改居左格式
|
||||
contentStyle.verticalAlignment = VerticalAlignment.CENTER
|
||||
this.setBorderStyle(contentStyle, BorderStyle.THIN)
|
||||
|
||||
//创建内容
|
||||
var col = 0
|
||||
for (i in data.data.indices) {
|
||||
try {
|
||||
row = sheet.createRow(i + 1)
|
||||
row.height = 20 * 20
|
||||
col = 0
|
||||
while (col < data.data[i].size) {
|
||||
cell = row.createCell(col)
|
||||
cell.setCellStyle(contentStyle)
|
||||
cell.setCellValue(data.data[i][col])
|
||||
this.setColumnWidth(data.data[i][col], col, sheet)
|
||||
|
||||
col++
|
||||
}
|
||||
} catch (ex: RuntimeException) {
|
||||
throw Exception("创建索引${i}行[${data.columnNames[col]}]时出现异常", ex)
|
||||
}
|
||||
}
|
||||
writeTo(wb)
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置列宽
|
||||
*
|
||||
* @param content 列内容
|
||||
* @param col 列索引
|
||||
* @param sheet 需要设置的表格
|
||||
*/
|
||||
private fun setColumnWidth(content: String, col: Int, sheet: Sheet) {
|
||||
//设置列宽
|
||||
val width = (content.length * 1.5 * 256).toInt()
|
||||
if (width > sheet.getColumnWidth(col))
|
||||
sheet.setColumnWidth(col, width)
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置cell style的边框
|
||||
*/
|
||||
private fun setBorderStyle(style: HSSFCellStyle, borderStyle: BorderStyle) {
|
||||
style.borderTop = borderStyle
|
||||
style.borderRight = borderStyle
|
||||
style.borderBottom = borderStyle
|
||||
style.borderLeft = borderStyle
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
package com.synebula.gaea.app.component.security
|
||||
|
||||
import com.auth0.jwt.JWT
|
||||
import com.auth0.jwt.algorithms.Algorithm
|
||||
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
|
||||
import org.springframework.stereotype.Component
|
||||
import java.util.*
|
||||
|
||||
@Component
|
||||
class TokenManager {
|
||||
@Autowired
|
||||
private lateinit var logger: ILogger
|
||||
|
||||
@Autowired
|
||||
private lateinit var gson: Gson
|
||||
|
||||
@Value("\${jwt.secret:}")
|
||||
val secret: String = ""
|
||||
|
||||
/**
|
||||
* 短期有效期, 默认一天。单位分钟
|
||||
*/
|
||||
@Value("\${jwt.expire.normal:${24 * 60}}")
|
||||
private val normalExpire = ""
|
||||
|
||||
/**
|
||||
* 长期有效期, 默认一年。单位分钟
|
||||
*/
|
||||
@Value("\${jwt.expire.remember:${365 * 24 * 60}}")
|
||||
private val rememberExpire = ""
|
||||
|
||||
/**
|
||||
* 放到header的名称
|
||||
*/
|
||||
val header = "token"
|
||||
|
||||
/**
|
||||
* 业务载荷名称
|
||||
*/
|
||||
val payload = "user"
|
||||
|
||||
/**
|
||||
* 生成签名,5min后过期
|
||||
*
|
||||
* @param user 用户
|
||||
* @return 加密的token
|
||||
*/
|
||||
fun sign(user: Any, remember: Boolean = false): String {
|
||||
return this.sign(gson.toJson(user), remember)
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成签名,5min后过期
|
||||
*
|
||||
* @param userJson 用户 Json
|
||||
* @return 加密的token
|
||||
*/
|
||||
fun sign(userJson: String, remember: Boolean = false): String {
|
||||
val milli = this.expireMilliseconds(if (remember) rememberExpire.toLong() else normalExpire.toLong())
|
||||
val date = Date(System.currentTimeMillis() + milli)
|
||||
val algorithm: Algorithm = Algorithm.HMAC256(secret)
|
||||
// 附带username信息
|
||||
return JWT.create()
|
||||
.withClaim(this.payload, userJson)
|
||||
.withIssuedAt(Date())
|
||||
.withExpiresAt(date)
|
||||
.sign(algorithm)
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验token是否正确
|
||||
*
|
||||
* @param token 密钥
|
||||
* @return 是否正确
|
||||
*/
|
||||
fun <T> verify(token: String, clazz: Class<T>): T? {
|
||||
try {
|
||||
val now = Date()
|
||||
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)
|
||||
} catch (ex: Exception) {
|
||||
this.logger.debug(this, ex, "解析token出错")
|
||||
throw ex
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验token是否正确
|
||||
*
|
||||
* @param token 密钥
|
||||
* @return 是否正确
|
||||
*/
|
||||
fun verify(token: String): String {
|
||||
try {
|
||||
val now = Date()
|
||||
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()
|
||||
} catch (ex: Exception) {
|
||||
this.logger.debug(this, ex, "解析token出错")
|
||||
throw ex
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取超时毫秒
|
||||
* @param minutes 分钟数
|
||||
*/
|
||||
private fun expireMilliseconds(minutes: Long): Long {
|
||||
return minutes * 60 * 1000
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.synebula.gaea.app.component.security
|
||||
|
||||
import com.synebula.gaea.app.struct.HttpMessage
|
||||
import com.synebula.gaea.app.struct.exception.TokenCloseExpireException
|
||||
import com.synebula.gaea.data.message.Status
|
||||
import org.springframework.security.authentication.AuthenticationManager
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
|
||||
import org.springframework.security.core.context.SecurityContextHolder
|
||||
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter
|
||||
import java.io.IOException
|
||||
import javax.servlet.FilterChain
|
||||
import javax.servlet.ServletException
|
||||
import javax.servlet.http.HttpServletRequest
|
||||
import javax.servlet.http.HttpServletResponse
|
||||
|
||||
|
||||
/**
|
||||
* 登录成功后 走此类进行鉴权操作
|
||||
*/
|
||||
class WebAuthorization(authenticationManager: AuthenticationManager, var tokenManager: TokenManager) :
|
||||
BasicAuthenticationFilter(authenticationManager) {
|
||||
|
||||
/**
|
||||
* 在过滤之前和之后执行的事件
|
||||
*/
|
||||
@Throws(IOException::class, ServletException::class)
|
||||
override fun doFilterInternal(request: HttpServletRequest, response: HttpServletResponse, chain: FilterChain) {
|
||||
val token = request.getHeader(tokenManager.header)
|
||||
try {
|
||||
val user = tokenManager.verify(token)
|
||||
val authentication =
|
||||
UsernamePasswordAuthenticationToken(user, null, null)
|
||||
SecurityContextHolder.getContext().authentication = authentication
|
||||
super.doFilterInternal(request, response, chain)
|
||||
} catch (ex: TokenCloseExpireException) {
|
||||
response.status = Status.Success
|
||||
response.characterEncoding = "utf-8"
|
||||
response.contentType = "text/javascript;charset=utf-8"
|
||||
response.writer.print(HttpMessage(Status.Reauthorize, tokenManager.sign(ex.payload), "重新下发认证消息"))
|
||||
response.flushBuffer()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package com.synebula.gaea.app.component.security
|
||||
|
||||
import com.synebula.gaea.app.struct.HttpMessage
|
||||
import com.synebula.gaea.data.message.Status
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity
|
||||
import org.springframework.security.config.annotation.web.builders.WebSecurity
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter
|
||||
import org.springframework.security.config.http.SessionCreationPolicy
|
||||
import org.springframework.stereotype.Component
|
||||
import org.springframework.web.cors.CorsConfiguration
|
||||
import org.springframework.web.cors.CorsConfigurationSource
|
||||
import org.springframework.web.cors.UrlBasedCorsConfigurationSource
|
||||
|
||||
|
||||
@Component
|
||||
@EnableWebSecurity
|
||||
class WebSecurity : WebSecurityConfigurerAdapter() {
|
||||
@Autowired
|
||||
lateinit var tokenManager: TokenManager
|
||||
|
||||
/**
|
||||
* 安全配置
|
||||
*/
|
||||
@Throws(Exception::class)
|
||||
override fun configure(http: HttpSecurity) {
|
||||
// 跨域共享
|
||||
http.cors()
|
||||
.and().csrf().disable() // 跨域伪造请求限制无效
|
||||
.authorizeRequests()
|
||||
.anyRequest().authenticated()// 资源任何人都可访问
|
||||
.and()
|
||||
.addFilter(WebAuthorization(authenticationManager(), tokenManager))// 添加JWT鉴权拦截器
|
||||
.sessionManagement()
|
||||
.sessionCreationPolicy(SessionCreationPolicy.STATELESS) // 设置Session的创建策略为:Spring Security永不创建HttpSession 不使用HttpSession来获取SecurityContext
|
||||
.and()
|
||||
.exceptionHandling()
|
||||
.authenticationEntryPoint { _, response, _ ->
|
||||
response.status = Status.Success
|
||||
response.characterEncoding = "utf-8"
|
||||
response.contentType = "text/javascript;charset=utf-8"
|
||||
response.writer.print(HttpMessage(Status.Unauthorized, "用户未登录,请重新登录后尝试!"))
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(Exception::class)
|
||||
override fun configure(web: WebSecurity) {
|
||||
web.ignoring().antMatchers("/sign/**")
|
||||
}
|
||||
|
||||
/**
|
||||
* 跨域配置
|
||||
* @return 基于URL的跨域配置信息
|
||||
*/
|
||||
@Bean
|
||||
fun corsConfigurationSource(): CorsConfigurationSource {
|
||||
val configuration = CorsConfiguration()
|
||||
configuration.allowedOrigins = listOf("*")
|
||||
configuration.allowedMethods = listOf("*")
|
||||
configuration.allowedHeaders = listOf("*")
|
||||
// 如果所有的属性不全部配置,一定要执行该方法
|
||||
configuration.applyPermitDefaultValues()
|
||||
val source = UrlBasedCorsConfigurationSource()
|
||||
// 注册跨域配置
|
||||
source.registerCorsConfiguration("/**", configuration)
|
||||
return source
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
package com.synebula.gaea.app.query
|
||||
|
||||
import com.synebula.gaea.app.IApplication
|
||||
import com.synebula.gaea.app.component.HttpMessage
|
||||
import com.synebula.gaea.data.message.Status
|
||||
import com.synebula.gaea.app.component.aop.annotation.MethodName
|
||||
import com.synebula.gaea.app.struct.HttpMessage
|
||||
import com.synebula.gaea.query.IQuery
|
||||
import com.synebula.gaea.query.Params
|
||||
import org.springframework.web.bind.annotation.GetMapping
|
||||
@@ -13,54 +13,38 @@ interface IQueryApp<TView, TKey> : IApplication {
|
||||
/**
|
||||
* 查询服务
|
||||
*/
|
||||
var query: IQuery?
|
||||
var query: IQuery
|
||||
|
||||
/**
|
||||
* 查询的View类型
|
||||
*/
|
||||
var clazz: Class<TView>
|
||||
|
||||
@MethodName("获取数据")
|
||||
@GetMapping("/{id:.+}")
|
||||
fun get(@PathVariable id: TKey): HttpMessage {
|
||||
return this.doQuery("获取${this.name}数据失败") {
|
||||
this.query!!.get(id, clazz)
|
||||
}
|
||||
val data = this.query.get(id, clazz)
|
||||
val msg = HttpMessage()
|
||||
msg.data = data
|
||||
return msg
|
||||
}
|
||||
|
||||
@MethodName("获取列表数据")
|
||||
@GetMapping
|
||||
fun list(@RequestParam params: LinkedHashMap<String, Any>): HttpMessage {
|
||||
return this.doQuery("获取${this.name}列表数据失败") {
|
||||
this.query!!.list(params, clazz)
|
||||
}
|
||||
val data = this.query.list(params, clazz)
|
||||
return HttpMessage(data)
|
||||
}
|
||||
|
||||
@MethodName("获取分页数据")
|
||||
@GetMapping("/segments/{size}/pages/{page}")
|
||||
fun paging(
|
||||
@PathVariable size: Int,
|
||||
@PathVariable page: Int,
|
||||
@RequestParam parameters: LinkedHashMap<String, Any>
|
||||
): HttpMessage {
|
||||
return this.doQuery("获取${this.name}分页数据[条数:$size,页码:$page]失败") {
|
||||
val data = Params(page, size, parameters)
|
||||
this.query!!.paging(data, clazz)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 抽取查询业务判断功能
|
||||
*
|
||||
* @param error 错误消息
|
||||
* @param biz 业务执行逻辑
|
||||
*/
|
||||
fun doQuery(error: String, biz: (() -> Any?)): HttpMessage {
|
||||
return this.safeExecute(error) {
|
||||
if (this.query != null) {
|
||||
it.data = biz()
|
||||
} else {
|
||||
it.status = Status.Error
|
||||
it.message = "没有对应服务,无法执行该操作"
|
||||
}
|
||||
}
|
||||
val params = Params(page, size, parameters)
|
||||
val data = this.query.paging(params, clazz)
|
||||
return HttpMessage(data)
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,6 @@ import com.synebula.gaea.query.IQuery
|
||||
open class QueryApp<TView, TKey>(
|
||||
override var name: String,
|
||||
override var clazz: Class<TView>,
|
||||
override var query: IQuery?,
|
||||
override var query: IQuery,
|
||||
override var logger: ILogger?
|
||||
) : IQueryApp<TView, TKey>
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.synebula.gaea.app.component.poi.excel
|
||||
package com.synebula.gaea.app.struct
|
||||
|
||||
class ExcelData(var title: String = "",
|
||||
var columnNames: List<String> = listOf(),
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.synebula.gaea.app.component
|
||||
package com.synebula.gaea.app.struct
|
||||
|
||||
import com.google.gson.Gson
|
||||
import com.synebula.gaea.data.message.DataMessage
|
||||
|
||||
class HttpMessage() : DataMessage<Any>() {
|
||||
@@ -22,4 +23,8 @@ class HttpMessage() : DataMessage<Any>() {
|
||||
this.message = msg.message
|
||||
this.data = msg.data
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return Gson().toJson(this)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package com.synebula.gaea.app.struct.exception
|
||||
|
||||
import com.auth0.jwt.exceptions.TokenExpiredException
|
||||
|
||||
class TokenCloseExpireException(msg: String, var payload: String) : TokenExpiredException(msg)
|
||||
@@ -42,8 +42,12 @@ fun Query.where(
|
||||
|
||||
//日期类型特殊处理为String类型
|
||||
val fieldType = onFieldType(key)
|
||||
if (fieldType != null && value.javaClass != fieldType && fieldType == Date::class.java) {
|
||||
value = DateTime(value.toString(), "yyyy-MM-dd HH:mm:ss").date
|
||||
if (fieldType != null && value.javaClass != fieldType) {
|
||||
when (fieldType) {
|
||||
Date::class.java -> value = DateTime(value.toString(), "yyyy-MM-ddTHH:mm:ss").date
|
||||
Int::class.java -> value = value.toString().toInt()
|
||||
Integer::class.java -> value = value.toString().toInt()
|
||||
}
|
||||
}
|
||||
|
||||
val where = onWhere(key)
|
||||
|
||||
@@ -20,8 +20,8 @@ open class MongoRepository(private var repo: MongoTemplate) : IRepository {
|
||||
override fun <TAggregateRoot : IAggregateRoot<TKey>, TKey> get(
|
||||
id: TKey,
|
||||
clazz: Class<TAggregateRoot>
|
||||
): TAggregateRoot {
|
||||
return this.repo.findOne(whereId(id), clazz) as TAggregateRoot
|
||||
): TAggregateRoot? {
|
||||
return this.repo.findOne(whereId(id), clazz)
|
||||
}
|
||||
|
||||
override fun <TAggregateRoot : IAggregateRoot<TKey>, TKey> update(
|
||||
@@ -35,6 +35,9 @@ open class MongoRepository(private var repo: MongoTemplate) : IRepository {
|
||||
this.repo.save(obj)
|
||||
}
|
||||
|
||||
override fun <TAggregateRoot : IAggregateRoot<TKey>, TKey> add(obj: List<TAggregateRoot>, clazz: Class<TAggregateRoot>) {
|
||||
this.repo.insert(obj, clazz)
|
||||
}
|
||||
|
||||
override fun <TAggregateRoot> count(params: Map<String, Any>?, clazz: Class<TAggregateRoot>): Int {
|
||||
val query = Query()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package com.synebula.gaea.data.cache
|
||||
|
||||
import java.util.Date
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
*
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package com.synebula.gaea.data.code
|
||||
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Date
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* @author alex
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package com.synebula.gaea.data.code
|
||||
|
||||
import java.util.Random
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* 固定长度随机编号生成。
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package com.synebula.gaea.data.code
|
||||
|
||||
import java.util.UUID
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* 全球唯一编号生成。
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package com.synebula.gaea.data.date
|
||||
|
||||
import java.util.*
|
||||
import kotlin.math.ceil
|
||||
import kotlin.math.floor
|
||||
|
||||
/**
|
||||
* 校准时间。
|
||||
@@ -61,9 +63,9 @@ class AlignTime {
|
||||
*/
|
||||
fun ceilingTime(lastTime: DateTime, intervalSeconds: Int): DateTime {
|
||||
val span = lastTime - this.baseTime
|
||||
val count = Math.ceil(span.totalSeconds / intervalSeconds).toInt()
|
||||
val count = ceil(span.totalSeconds / intervalSeconds).toInt()
|
||||
val newTime = DateTime(this.baseTime.date)
|
||||
newTime.addSeconds(count * intervalSeconds * 1L)
|
||||
newTime.addSecond(count * intervalSeconds)
|
||||
return newTime
|
||||
}
|
||||
|
||||
@@ -97,9 +99,9 @@ class AlignTime {
|
||||
*/
|
||||
fun floorTime(lastTime: DateTime, intervalSeconds: Int): DateTime {
|
||||
val span = lastTime - this.baseTime
|
||||
val count = Math.floor(span.totalSeconds / intervalSeconds).toInt()
|
||||
val count = floor(span.totalSeconds / intervalSeconds).toInt()
|
||||
val newTime = DateTime(this.baseTime.date)
|
||||
newTime.addSeconds(count * intervalSeconds * 1L)
|
||||
newTime.addSecond(count * intervalSeconds)
|
||||
return newTime
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@ import java.util.*
|
||||
/**
|
||||
* 时间格式,方便用于获取Date格式的多种形式
|
||||
*/
|
||||
class DateTime : Comparable<DateTime> {
|
||||
class DateTime() : Comparable<DateTime> {
|
||||
|
||||
// 内部存储日历格式方便操作
|
||||
/**
|
||||
@@ -25,7 +25,15 @@ class DateTime : Comparable<DateTime> {
|
||||
/**
|
||||
* 列出时间的级别数组
|
||||
*/
|
||||
private val calendarLevel = intArrayOf(Calendar.MILLISECOND, Calendar.SECOND, Calendar.MINUTE, Calendar.HOUR_OF_DAY, Calendar.DAY_OF_MONTH, Calendar.MONTH, Calendar.YEAR)
|
||||
private val calendarLevel = intArrayOf(
|
||||
Calendar.MILLISECOND,
|
||||
Calendar.SECOND,
|
||||
Calendar.MINUTE,
|
||||
Calendar.HOUR_OF_DAY,
|
||||
Calendar.DAY_OF_MONTH,
|
||||
Calendar.MONTH,
|
||||
Calendar.YEAR
|
||||
)
|
||||
|
||||
val date: Date
|
||||
get() = calendar.time
|
||||
@@ -43,60 +51,169 @@ class DateTime : Comparable<DateTime> {
|
||||
return instance.time
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前时间年
|
||||
*/
|
||||
var year: Int
|
||||
get() = calendar.get(Calendar.YEAR)
|
||||
set(value) = calendar.set(Calendar.YEAR, value)
|
||||
|
||||
/**
|
||||
* 当前时间月
|
||||
*/
|
||||
var month: Int
|
||||
get() = calendar.get(Calendar.MONTH)
|
||||
set(value) = calendar.set(Calendar.MONTH, value)
|
||||
|
||||
/**
|
||||
* 当前时间天
|
||||
*/
|
||||
var day: Int
|
||||
get() = calendar.get(Calendar.DAY_OF_MONTH)
|
||||
set(value) = calendar.set(Calendar.DAY_OF_MONTH, value)
|
||||
|
||||
/**
|
||||
* 获取时间
|
||||
*/
|
||||
val time: Time
|
||||
get() = Time(calendar.time)
|
||||
|
||||
val firstDay: DateTime
|
||||
/**
|
||||
* 获取当月天数
|
||||
*/
|
||||
val days: Int
|
||||
get() {
|
||||
return this.calendar.getActualMaximum(Calendar.DAY_OF_MONTH)
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前月第一天
|
||||
*/
|
||||
val firstDayOfMonth: DateTime
|
||||
get() {
|
||||
val instance = calendar.clone() as Calendar
|
||||
instance.set(Calendar.DAY_OF_MONTH, 1)
|
||||
return DateTime(instance)
|
||||
}
|
||||
|
||||
val lastDay: DateTime
|
||||
/**
|
||||
* 当前月最后一天
|
||||
*/
|
||||
val lastDayOfMonth: DateTime
|
||||
get() {
|
||||
val instance = calendar.clone() as Calendar
|
||||
instance.set(Calendar.DAY_OF_MONTH, instance.getActualMaximum(Calendar.DAY_OF_MONTH))
|
||||
return DateTime(instance)
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前周第一天
|
||||
*/
|
||||
val firstDayOfWeek: DateTime
|
||||
get() {
|
||||
val instance = calendar.clone() as Calendar
|
||||
instance.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY)
|
||||
instance.set(Calendar.HOUR_OF_DAY, 0)
|
||||
instance.set(Calendar.MINUTE, 0)
|
||||
instance.set(Calendar.SECOND, 0)
|
||||
instance.set(Calendar.MILLISECOND, 0)
|
||||
return DateTime(instance)
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前周最后一天
|
||||
*/
|
||||
val lastDayOfWeek: DateTime
|
||||
get() {
|
||||
val instance = this.firstDayOfWeek.calendar.clone() as Calendar
|
||||
instance.add(Calendar.DAY_OF_WEEK, 6)//当前周第一天加6天
|
||||
instance.set(Calendar.HOUR_OF_DAY, 23)
|
||||
instance.set(Calendar.MINUTE, 59)
|
||||
instance.set(Calendar.SECOND, 59)
|
||||
instance.set(Calendar.MILLISECOND, 0)
|
||||
return DateTime(instance)
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前天最早时间
|
||||
*/
|
||||
val earliestTime: DateTime
|
||||
get() {
|
||||
val instance = this.calendar.clone() as Calendar
|
||||
instance.set(Calendar.HOUR_OF_DAY, 0)
|
||||
instance.set(Calendar.MINUTE, 0)
|
||||
instance.set(Calendar.SECOND, 0)
|
||||
instance.set(Calendar.MILLISECOND, 0)
|
||||
return DateTime(instance)
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前天最晚时间
|
||||
*/
|
||||
val latestTime: DateTime
|
||||
get() {
|
||||
val instance = this.calendar.clone() as Calendar
|
||||
instance.set(Calendar.HOUR_OF_DAY, 23)
|
||||
instance.set(Calendar.MINUTE, 59)
|
||||
instance.set(Calendar.SECOND, 59)
|
||||
instance.set(Calendar.MILLISECOND, 0)
|
||||
return DateTime(instance)
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前时间最上一月
|
||||
*/
|
||||
val previousMonth: DateTime
|
||||
get() {
|
||||
return this.addMonth(-1)
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前时间下一月
|
||||
*/
|
||||
val nextMonth: DateTime
|
||||
get() {
|
||||
return this.addMonth(1)
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前时间最上一天
|
||||
*/
|
||||
val previousDay: DateTime
|
||||
get() {
|
||||
return this.addDay(-1)
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前时间下一天
|
||||
*/
|
||||
val nextDay: DateTime
|
||||
get() {
|
||||
return this.addDay(1)
|
||||
}
|
||||
|
||||
/**
|
||||
* 从Date格式转化
|
||||
*/
|
||||
constructor(date: Date) {
|
||||
constructor(date: Date) : this() {
|
||||
this.calendar.time = date
|
||||
this.calendar.set(Calendar.MILLISECOND, 0)
|
||||
}
|
||||
|
||||
/**
|
||||
* 从Calendar格式转化
|
||||
*/
|
||||
constructor(calendar: Calendar) {
|
||||
constructor(calendar: Calendar) : this() {
|
||||
this.calendar = calendar
|
||||
this.calendar.set(Calendar.MILLISECOND, 0)
|
||||
}
|
||||
|
||||
/**
|
||||
* 从Date格式转化
|
||||
*/
|
||||
constructor(date: String, format: String) {
|
||||
constructor(date: String, format: String = "yyyy-MM-dd HH:mm:ss") : this() {
|
||||
val formatter = SimpleDateFormat(format)
|
||||
try {
|
||||
val value = formatter.parse(date)
|
||||
this.calendar.time = value
|
||||
this.calendar.set(Calendar.MILLISECOND, 0)
|
||||
} catch (e: ParseException) {
|
||||
throw RuntimeException("date string can't format to date", e)
|
||||
}
|
||||
@@ -105,9 +222,8 @@ class DateTime : Comparable<DateTime> {
|
||||
/**
|
||||
* 从Date格式转化。需要注意的是月0代表是一月,以此类推。
|
||||
*/
|
||||
constructor(year: Int, month: Int, day: Int = 0, hour: Int = 0, minute: Int = 0, second: Int = 0) {
|
||||
constructor(year: Int, month: Int, day: Int = 0, hour: Int = 0, minute: Int = 0, second: Int = 0) : this() {
|
||||
this.calendar.set(year, month, day, hour, minute, second)
|
||||
this.calendar.set(Calendar.MILLISECOND, 0)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -156,10 +272,11 @@ class DateTime : Comparable<DateTime> {
|
||||
* 比较当前时间是否在目标时间范围内。
|
||||
* @param start 目标开始时间。
|
||||
* @param end 目标结束时间。
|
||||
* @return 是否。
|
||||
* @return true or false
|
||||
*/
|
||||
fun between(start: DateTime, end: DateTime): Boolean {
|
||||
return this in start..end
|
||||
fun isBetween(start: DateTime, end: DateTime): Boolean {
|
||||
//return this in start..end
|
||||
return start.dateNoTime.compareTo(this.dateNoTime) * this.dateNoTime.compareTo(end.dateNoTime) >= 0
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -167,27 +284,234 @@ class DateTime : Comparable<DateTime> {
|
||||
* @param start 目标开始时间。
|
||||
* @param end 目标结束时间。
|
||||
* @param level 比较时间的最小级别。
|
||||
* @return 是否。
|
||||
* @return true or false
|
||||
*/
|
||||
fun between(start: DateTime, end: DateTime, level: TimeUnit): Boolean {
|
||||
fun isBetween(start: DateTime, end: DateTime, level: TimeUnit): Boolean {
|
||||
return this.compareTo(start, level) >= 0 && this.compareTo(end, level) <= 0
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 增加秒
|
||||
* 判断当前时间是否在某时间后
|
||||
*
|
||||
* @param other 另一时间
|
||||
* @return true or false
|
||||
*/
|
||||
fun addSeconds(seconds: Long) {
|
||||
if (seconds <= Int.MAX_VALUE)
|
||||
this.calendar.add(Calendar.SECOND, seconds.toInt())
|
||||
else {
|
||||
val span = TimeSpan(seconds * 1000)
|
||||
this.calendar.add(Calendar.DAY_OF_MONTH, span.day)
|
||||
this.calendar.add(Calendar.HOUR_OF_DAY, span.hour)
|
||||
this.calendar.add(Calendar.MINUTE, span.minute)
|
||||
this.calendar.add(Calendar.SECOND, span.second)
|
||||
}
|
||||
fun isAfter(other: DateTime): Boolean {
|
||||
return this.date.after(other.date)
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断当前时间是否在某时间前
|
||||
*
|
||||
* @param other 另一时间
|
||||
* @return true or false
|
||||
*/
|
||||
fun isBefore(other: DateTime): Boolean {
|
||||
return this.date.before(other.date)
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断当前时间是否同一天
|
||||
*
|
||||
* @param other 另一时间
|
||||
* @return true or false
|
||||
*/
|
||||
fun isSameDay(other: DateTime): Boolean {
|
||||
return this.calendar.get(Calendar.ERA) == other.calendar.get(Calendar.ERA)
|
||||
&& this.calendar.get(Calendar.YEAR) == other.calendar.get(Calendar.YEAR)
|
||||
&& this.calendar.get(Calendar.DAY_OF_YEAR) == other.calendar.get(Calendar.DAY_OF_YEAR)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 加减年。
|
||||
*
|
||||
* @param year 月份。可以为负数,即为减。
|
||||
* @return this
|
||||
*/
|
||||
fun addYear(year: Int): DateTime {
|
||||
this.calendar.add(Calendar.YEAR, year)
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* 加减月份。
|
||||
*
|
||||
* @param month 月份。可以为负数,即为减。
|
||||
* @return this
|
||||
*/
|
||||
fun addMonth(month: Int): DateTime {
|
||||
this.calendar.add(Calendar.MONTH, month)
|
||||
return this
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 加减日期。
|
||||
*
|
||||
* @param day 天数。可以为负数,即为减。
|
||||
* @return this
|
||||
*/
|
||||
fun addDay(day: Int): DateTime {
|
||||
this.calendar.add(Calendar.DAY_OF_MONTH, day)
|
||||
return this
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 加减小时。
|
||||
*
|
||||
* @param hour 小时。可以为负数,即为减。
|
||||
* @return this
|
||||
*/
|
||||
fun addHour(hour: Int): DateTime {
|
||||
this.calendar.add(Calendar.HOUR_OF_DAY, hour)
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* 加减分钟。
|
||||
*
|
||||
* @param minute 分钟。可以为负数,即为减。
|
||||
* @return this
|
||||
*/
|
||||
fun addMinute(minute: Int): DateTime {
|
||||
this.calendar.add(Calendar.MINUTE, minute)
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* 加减秒
|
||||
* @param second 秒数。可以为负数,即为减。
|
||||
* @return this
|
||||
*/
|
||||
fun addSecond(second: Int): DateTime {
|
||||
this.calendar.add(Calendar.SECOND, second)
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* 加减毫秒
|
||||
* @param millisecond 毫秒数。可以为负数,即为减。
|
||||
* @return this
|
||||
*/
|
||||
fun addMillisecond(millisecond: Int): DateTime {
|
||||
this.calendar.add(Calendar.MILLISECOND, millisecond)
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置年。
|
||||
*
|
||||
* @param year 月份。
|
||||
* @return this
|
||||
*/
|
||||
fun setYear(year: Int): DateTime {
|
||||
this.calendar.set(Calendar.YEAR, year)
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置月份。
|
||||
*
|
||||
* @param month 月份。
|
||||
* @return this
|
||||
*/
|
||||
fun setMonth(month: Int): DateTime {
|
||||
this.calendar.set(Calendar.MONTH, month)
|
||||
return this
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 设置日期。
|
||||
*
|
||||
* @param day 天数。
|
||||
* @return this
|
||||
*/
|
||||
fun setDay(day: Int): DateTime {
|
||||
this.calendar.set(Calendar.DAY_OF_MONTH, day)
|
||||
return this
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 设置小时。
|
||||
*
|
||||
* @param hour 小时。
|
||||
* @return this
|
||||
*/
|
||||
fun setHour(hour: Int): DateTime {
|
||||
this.calendar.set(Calendar.HOUR_OF_DAY, hour)
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置分钟。
|
||||
*
|
||||
* @param minute 分钟。
|
||||
* @return this
|
||||
*/
|
||||
fun setMinute(minute: Int): DateTime {
|
||||
this.calendar.set(Calendar.MINUTE, minute)
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置秒
|
||||
* @param second 总秒数。
|
||||
* @return this
|
||||
*/
|
||||
fun setSecond(second: Int): DateTime {
|
||||
this.calendar.set(Calendar.SECOND, second)
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置毫秒。
|
||||
* @param millisecond 毫秒数。
|
||||
* @return this
|
||||
*/
|
||||
fun setMillisecond(millisecond: Int): DateTime {
|
||||
this.calendar.set(Calendar.MILLISECOND, millisecond)
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前天是否工作日
|
||||
*/
|
||||
fun isWorkday(): Boolean {
|
||||
return !this.isWeekend()
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前天是否周末
|
||||
*/
|
||||
fun isWeekend(): Boolean {
|
||||
val dayOfWeek = this.calendar.get(Calendar.DAY_OF_WEEK)
|
||||
return (dayOfWeek == Calendar.SATURDAY || dayOfWeek == Calendar.SUNDAY)
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空毫秒,避免误差
|
||||
*/
|
||||
fun clearMillisecond() {
|
||||
this.calendar.set(Calendar.MILLISECOND, 0)
|
||||
}
|
||||
|
||||
/**
|
||||
* 克隆当前对象
|
||||
*
|
||||
* @return 返回新的DateTime对象
|
||||
*/
|
||||
fun clone(): DateTime {
|
||||
return DateTime(this.calendar.clone() as Calendar)
|
||||
}
|
||||
|
||||
/**
|
||||
* 时间相见
|
||||
*/
|
||||
operator fun minus(other: DateTime): TimeSpan {
|
||||
return TimeSpan(this.milliseconds - other.milliseconds)
|
||||
}
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
package com.synebula.gaea.data.date
|
||||
|
||||
import java.math.BigDecimal
|
||||
import java.text.ParseException
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Calendar
|
||||
import java.util.Date
|
||||
import java.util.*
|
||||
|
||||
|
||||
/**
|
||||
|
||||
@@ -6,9 +6,6 @@
|
||||
*/
|
||||
package com.synebula.gaea.data.date
|
||||
|
||||
import java.math.BigDecimal
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
*
|
||||
* @author whj
|
||||
|
||||
@@ -9,9 +9,7 @@ open class Message {
|
||||
/**
|
||||
* 获取状态是否成功
|
||||
*/
|
||||
val success: Boolean
|
||||
get() = this.status == Status.Success
|
||||
|
||||
fun success(): Boolean = this.status == Status.Success
|
||||
|
||||
/**
|
||||
* 附带提示消息
|
||||
|
||||
@@ -6,11 +6,21 @@ object Status {
|
||||
*/
|
||||
const val Success = 200
|
||||
|
||||
/**
|
||||
* 重新授权
|
||||
*/
|
||||
const val Reauthorize = 205
|
||||
|
||||
/**
|
||||
* 失败
|
||||
*/
|
||||
const val Failure = 400
|
||||
|
||||
/**
|
||||
* 未授权
|
||||
*/
|
||||
const val Unauthorized = 401
|
||||
|
||||
/**
|
||||
* 错误
|
||||
*/
|
||||
|
||||
@@ -15,6 +15,14 @@ interface IRepository {
|
||||
*/
|
||||
fun <TAggregateRoot : IAggregateRoot<TKey>, TKey> add(obj: TAggregateRoot, clazz: Class<TAggregateRoot>)
|
||||
|
||||
/**
|
||||
* 插入多个个对象。
|
||||
*
|
||||
* @param obj 需要插入的对象。
|
||||
* @return 返回原对象,如果对象ID为自增,则补充自增ID。
|
||||
*/
|
||||
fun <TAggregateRoot : IAggregateRoot<TKey>, TKey> add(obj: List<TAggregateRoot>, clazz: Class<TAggregateRoot>)
|
||||
|
||||
/**
|
||||
* 更新对象。
|
||||
*
|
||||
@@ -38,7 +46,7 @@ interface IRepository {
|
||||
* @param clazz 操作数据的类型
|
||||
* @return 聚合根
|
||||
*/
|
||||
fun <TAggregateRoot : IAggregateRoot<TKey>, TKey> get(id: TKey, clazz: Class<TAggregateRoot>): TAggregateRoot
|
||||
fun <TAggregateRoot : IAggregateRoot<TKey>, TKey> get(id: TKey, clazz: Class<TAggregateRoot>): TAggregateRoot?
|
||||
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
package com.synebula.gaea.domain.service
|
||||
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* 命令基础实现类,发给service的命令。
|
||||
*
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.synebula.gaea.domain.service
|
||||
|
||||
import com.synebula.gaea.data.message.DataMessage
|
||||
import com.synebula.gaea.data.message.Message
|
||||
import com.synebula.gaea.domain.model.IAggregateRoot
|
||||
import com.synebula.gaea.log.ILogger
|
||||
|
||||
|
||||
/**
|
||||
* 继承该接口,表明对象为领域服务。
|
||||
* @author alex
|
||||
* @version 0.0.1
|
||||
* @since 2016年9月18日 下午2:23:15
|
||||
*/
|
||||
interface ILazyService<TAggregateRoot : IAggregateRoot<TKey>, TKey> {
|
||||
/**
|
||||
* 日志组件。
|
||||
*/
|
||||
var logger: ILogger
|
||||
|
||||
fun add(root: TAggregateRoot): DataMessage<TKey>
|
||||
|
||||
fun update(id: TKey, root: TAggregateRoot)
|
||||
|
||||
fun remove(id: TKey)
|
||||
|
||||
/**
|
||||
* 添加一个删除对象前执行监听器。
|
||||
* @param key 监听器标志。
|
||||
* @param func 监听方法。
|
||||
*/
|
||||
fun addBeforeRemoveListener(key: String, func: (id: TKey) -> Message)
|
||||
|
||||
/**
|
||||
* 移除一个删除对象前执行监听器。
|
||||
* @param key 监听器标志。
|
||||
*/
|
||||
fun removeBeforeRemoveListener(key: String)
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package com.synebula.gaea.domain.service
|
||||
|
||||
import com.synebula.gaea.data.IObjectConverter
|
||||
import com.synebula.gaea.data.message.DataMessage
|
||||
import com.synebula.gaea.data.message.Message
|
||||
import com.synebula.gaea.domain.model.IAggregateRoot
|
||||
import com.synebula.gaea.domain.repository.IRepository
|
||||
import com.synebula.gaea.log.ILogger
|
||||
|
||||
|
||||
/**
|
||||
* 依赖了IRepository仓储借口的服务实现类 Service
|
||||
* 该类依赖仓储接口 @see IRepository, 需要显式提供聚合根的class对象
|
||||
*
|
||||
* @param repository 仓储对象
|
||||
* @param clazz 聚合根类对象
|
||||
* @param logger 日志组件
|
||||
* @author alex
|
||||
* @version 0.1
|
||||
* @since 2020-05-17
|
||||
*/
|
||||
open class LazyService<TAggregateRoot : IAggregateRoot<TKey>, TKey>(
|
||||
protected open var clazz: Class<TAggregateRoot>,
|
||||
protected open var repository: IRepository,
|
||||
override var logger: ILogger
|
||||
) : ILazyService<TAggregateRoot, TKey> {
|
||||
|
||||
/**
|
||||
* 删除对象前执行监听器。
|
||||
*/
|
||||
protected val beforeRemoveListeners = mutableMapOf<String, (id: TKey) -> Message>()
|
||||
|
||||
/**
|
||||
* 添加一个删除对象前执行监听器。
|
||||
* @param key 监听器标志。
|
||||
* @param func 监听方法。
|
||||
*/
|
||||
override fun addBeforeRemoveListener(key: String, func: (id: TKey) -> Message) {
|
||||
this.beforeRemoveListeners[key] = func
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除一个删除对象前执行监听器。
|
||||
* @param key 监听器标志。
|
||||
*/
|
||||
override fun removeBeforeRemoveListener(key: String) {
|
||||
this.beforeRemoveListeners.remove(key)
|
||||
}
|
||||
|
||||
override fun add(root: TAggregateRoot): DataMessage<TKey> {
|
||||
val msg = DataMessage<TKey>()
|
||||
this.repository.add(root, this.clazz)
|
||||
msg.data = root.id
|
||||
return msg
|
||||
}
|
||||
|
||||
override fun update(id: TKey, root: TAggregateRoot) {
|
||||
root.id = id
|
||||
this.repository.update(root, this.clazz)
|
||||
}
|
||||
|
||||
override fun remove(id: TKey) {
|
||||
val functions = this.beforeRemoveListeners.values
|
||||
var msg: Message
|
||||
for (func in functions) {
|
||||
msg = func(id)
|
||||
if (!msg.success()) {
|
||||
throw IllegalStateException(msg.message)
|
||||
}
|
||||
}
|
||||
this.repository.remove(id, this.clazz)
|
||||
}
|
||||
}
|
||||
@@ -68,8 +68,8 @@ open class Service<TAggregateRoot : IAggregateRoot<TKey>, TKey>(
|
||||
var msg: Message
|
||||
for (func in functions) {
|
||||
msg = func(id)
|
||||
if (!msg.success) {
|
||||
throw java.lang.RuntimeException(msg.message)
|
||||
if (!msg.success()) {
|
||||
throw IllegalStateException(msg.message)
|
||||
}
|
||||
}
|
||||
this.repository.remove(id, this.clazz)
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
package com.synebula.gaea.event
|
||||
|
||||
interface IEvent {
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.synebula.gaea.event
|
||||
|
||||
interface IEventBus {
|
||||
|
||||
/**
|
||||
* 注册事件Listener
|
||||
* @param obj Listener所在类
|
||||
*/
|
||||
fun register(obj: Any)
|
||||
|
||||
/**
|
||||
* 取消注册事件Listener
|
||||
* @param obj Listener所在类
|
||||
*/
|
||||
fun unregister(obj: Any)
|
||||
|
||||
/**
|
||||
* 同步发布事件
|
||||
* @param event 事件
|
||||
*/
|
||||
fun publish(event: IEvent)
|
||||
|
||||
/**
|
||||
* 异步发布事件
|
||||
* @param event 事件
|
||||
*/
|
||||
fun publishAsync(event: IEvent)
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package com.synebula.gaea.exception
|
||||
|
||||
/**
|
||||
* 需要通知给用户的异常
|
||||
*/
|
||||
class NoticeUserException(message: String, cause: Exception? = null) : Exception(message, cause)
|
||||
@@ -2,8 +2,7 @@ package com.synebula.gaea.io.scan
|
||||
|
||||
import java.io.IOException
|
||||
import java.net.URL
|
||||
import java.util.Enumeration
|
||||
import java.util.HashSet
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
*
|
||||
|
||||
@@ -5,9 +5,7 @@ import java.io.FileFilter
|
||||
import java.io.UnsupportedEncodingException
|
||||
import java.net.URLDecoder
|
||||
import java.nio.charset.Charset
|
||||
import java.util.Collections
|
||||
import java.util.HashSet
|
||||
import java.util.LinkedList
|
||||
import java.util.*
|
||||
import java.util.jar.JarFile
|
||||
|
||||
/**
|
||||
|
||||
@@ -11,8 +11,8 @@ import com.synebula.gaea.query.type.Order
|
||||
*/
|
||||
data class Params(var page: Int = 1, var size: Int = 10) {
|
||||
|
||||
private var _parameters = mutableMapOf<String, Any>()
|
||||
private var _orders = mutableMapOf<String, Order>()
|
||||
private var _parameters = linkedMapOf<String, Any>()
|
||||
private var _orders = linkedMapOf<String, Order>()
|
||||
|
||||
/**
|
||||
* 数据索引,从0开始。表示数据在总量的第几条。(index = (page - 1) * size)
|
||||
@@ -24,13 +24,13 @@ data class Params(var page: Int = 1, var size: Int = 10) {
|
||||
/**
|
||||
* 排序条件。
|
||||
*/
|
||||
var orders: Map<String, Order>
|
||||
var orders: LinkedHashMap<String, Order>
|
||||
set(value) {
|
||||
this._orders = value.toMutableMap()
|
||||
this._orders = value
|
||||
}
|
||||
get() {
|
||||
if (this._parameters.keys.count { it.startsWith("@") } > 0) {
|
||||
val params = mutableMapOf<String, Any>()
|
||||
val params = linkedMapOf<String, Any>()
|
||||
this._parameters.forEach {
|
||||
if (it.key.startsWith("@")) {
|
||||
this._orders[it.key.removePrefix("@")] = Order.valueOf(it.value.toString())
|
||||
@@ -45,13 +45,13 @@ data class Params(var page: Int = 1, var size: Int = 10) {
|
||||
/**
|
||||
* 查询条件。
|
||||
*/
|
||||
var parameters: Map<String, Any>
|
||||
var parameters: LinkedHashMap<String, Any>
|
||||
set(value) {
|
||||
this._parameters = value.toMutableMap()
|
||||
this._parameters = value
|
||||
}
|
||||
get() {
|
||||
if (this._parameters.keys.count { it.startsWith("@") } > 0) {
|
||||
val params = mutableMapOf<String, Any>()
|
||||
val params = linkedMapOf<String, Any>()
|
||||
this._parameters.forEach {
|
||||
if (it.key.startsWith("@")) {
|
||||
this._orders[it.key.removePrefix("@")] = Order.valueOf(it.value.toString())
|
||||
|
||||
Reference in New Issue
Block a user