0.2.1 增加gaea.app gaea.mongo模块功能
This commit is contained in:
@@ -2,9 +2,9 @@ publishing {
|
||||
publications {
|
||||
mavenJava(MavenPublication) {
|
||||
group 'com.synebula'
|
||||
artifactId 'gaea.data'
|
||||
version project.version
|
||||
artifactId 'gaea'
|
||||
version "$version"
|
||||
from components.java
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,47 @@
|
||||
package com.synebula.gaea.data.message
|
||||
|
||||
import java.util.Date
|
||||
import java.util.*
|
||||
|
||||
open class Message<T> {
|
||||
/**
|
||||
*
|
||||
* 用来统一Http返回消息类型,通常使用json格式传递
|
||||
*
|
||||
* @param status http编码。200成功,400错误,500异常
|
||||
* @tparam T 消息数据类型
|
||||
*/
|
||||
open class Message<T>(var status: Int = Status.Success) {
|
||||
/**
|
||||
* 传递的业务数据
|
||||
*/
|
||||
var data: T? = null
|
||||
|
||||
/**
|
||||
* 附带提示消息
|
||||
*/
|
||||
var message = ""
|
||||
|
||||
/**
|
||||
* 消息时间戳
|
||||
*/
|
||||
val timestamp: Long = Date().time
|
||||
|
||||
constructor(data: T) : this(Status.Success) {
|
||||
this.data = data
|
||||
}
|
||||
|
||||
constructor(status: Int, message: String) : this(status) {
|
||||
this.message = message
|
||||
}
|
||||
|
||||
constructor(status: Int, data: T, message: String) : this(status) {
|
||||
this.data = data
|
||||
this.message = message
|
||||
}
|
||||
|
||||
open fun from(other: Message<T>) {
|
||||
this.status = other.status
|
||||
this.data = other.data
|
||||
this.message = other.message
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.synebula.gaea.data.message
|
||||
|
||||
object Status {
|
||||
/**
|
||||
* 成功
|
||||
*/
|
||||
val Success = 200
|
||||
|
||||
/**
|
||||
* 失败
|
||||
*/
|
||||
val Failure = 400
|
||||
|
||||
/**
|
||||
* 错误
|
||||
*/
|
||||
val Error = 500
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
package com.synebula.gaea.data.message.http
|
||||
|
||||
import com.synebula.gaea.data.message.Message
|
||||
|
||||
/**
|
||||
*
|
||||
* 用来统一Http返回消息类型,通常使用json格式传递
|
||||
*
|
||||
* @param status http编码。200成功,400错误,500异常
|
||||
* @tparam T 消息数据类型
|
||||
*/
|
||||
class HttpMessage<T>(var status: Int = HttpStatus.Success.code) : Message<T>() {
|
||||
/**
|
||||
* 附带提示消息
|
||||
*/
|
||||
var message = ""
|
||||
|
||||
constructor(data: T) : this(HttpStatus.Success.code) {
|
||||
this.data = data
|
||||
}
|
||||
|
||||
constructor(status: Int, message: String) : this(status) {
|
||||
this.message = message
|
||||
}
|
||||
|
||||
constructor(status: Int, data: T, message: String) : this(status) {
|
||||
this.data = data
|
||||
this.message = message
|
||||
}
|
||||
|
||||
fun from(other: HttpMessage<T>) {
|
||||
this.status = other.status
|
||||
this.data = other.data
|
||||
this.message = message
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
package com.synebula.gaea.data.message.http
|
||||
|
||||
/**
|
||||
* 状态类型。
|
||||
*
|
||||
* @author alex
|
||||
* @version 0.0.1
|
||||
* @since 2016年9月6日 下午3:27:55
|
||||
*/
|
||||
enum class HttpStatus(val code: Int) {
|
||||
/**
|
||||
* 成功
|
||||
*/
|
||||
Success(200),
|
||||
|
||||
/**
|
||||
* 失败
|
||||
*/
|
||||
Failure(400),
|
||||
|
||||
/**
|
||||
* 错误
|
||||
*/
|
||||
Error(500);
|
||||
|
||||
companion object {
|
||||
fun valueOf(code: Int): HttpStatus {
|
||||
return when (code) {
|
||||
200 -> HttpStatus.Success
|
||||
400 -> HttpStatus.Failure
|
||||
else -> HttpStatus.Error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
package com.synebula.gaea.domain.model
|
||||
|
||||
abstract class AggregateRoot<TKey> : Entity<TKey>(), IAggregateRoot<TKey>
|
||||
@@ -0,0 +1,3 @@
|
||||
package com.synebula.gaea.domain.model
|
||||
|
||||
abstract class Entity<TKey> : IEntity<TKey>
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.synebula.gaea.domain.model
|
||||
|
||||
/**
|
||||
* 继承本接口,说明对象为聚合根。
|
||||
*
|
||||
* @author alex
|
||||
**/
|
||||
interface IAggregateRoot<TKey> : IEntity<TKey>
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.synebula.gaea.domain.model
|
||||
|
||||
/**
|
||||
* 继承本接口,说明对象为实体类型。
|
||||
*
|
||||
* @author alex
|
||||
*
|
||||
* @param <TKey> 主键的类型。
|
||||
**/
|
||||
interface IEntity<TKey> {
|
||||
|
||||
/**
|
||||
* 实体ID
|
||||
*/
|
||||
var id: TKey
|
||||
|
||||
/**
|
||||
* 实体对象是否有效。
|
||||
*/
|
||||
var alive: Boolean
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.synebula.gaea.domain.model
|
||||
|
||||
/**
|
||||
* 继承本接口,说明对象为值类型。
|
||||
* @author alex
|
||||
*/
|
||||
interface IValue
|
||||
@@ -0,0 +1,3 @@
|
||||
package com.synebula.gaea.domain.model.complex
|
||||
|
||||
abstract class ComplexAggregateRoot<TKey, TSecond> : ComplexEntity<TKey, TSecond>(), IComplexAggregateRoot<TKey, TSecond>
|
||||
@@ -0,0 +1,5 @@
|
||||
package com.synebula.gaea.domain.model.complex
|
||||
|
||||
|
||||
abstract class ComplexEntity<TKey, TSecond> : IComplexEntity<TKey, TSecond> {
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.synebula.gaea.domain.model.complex
|
||||
|
||||
/**
|
||||
* 继承本接口,说明对象为聚合根。
|
||||
*
|
||||
* @param <TKey> 主键的类型。
|
||||
* @author alex
|
||||
*/
|
||||
interface IComplexAggregateRoot<TKey, TSecond> : IComplexEntity<TKey, TSecond>
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.synebula.gaea.domain.model.complex
|
||||
|
||||
import com.synebula.gaea.domain.model.IEntity
|
||||
|
||||
interface IComplexEntity<TKey, TSecond> : IEntity<TKey> {
|
||||
var secondary: TSecond
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.synebula.gaea.domain.repository
|
||||
|
||||
import com.synebula.gaea.domain.model.complex.IComplexAggregateRoot
|
||||
|
||||
/**
|
||||
* 继承本接口表示对象为仓储类。
|
||||
*
|
||||
* @param <TAggregateRoot> this T is the parameter
|
||||
* @author alex
|
||||
*/
|
||||
interface IComplexRepository<TAggregateRoot : IComplexAggregateRoot<TKey, TSecond>, TKey, TSecond> {
|
||||
|
||||
/**
|
||||
* 插入单个对象。
|
||||
*
|
||||
* @param obj 需要插入的对象。
|
||||
* @return 返回原对象,如果对象ID为自增,则补充自增ID。
|
||||
*/
|
||||
fun add(obj: TAggregateRoot)
|
||||
|
||||
/**
|
||||
* 更新对象。
|
||||
*
|
||||
* @param obj 需要更新的对象。
|
||||
* @return
|
||||
*/
|
||||
fun update(obj: TAggregateRoot)
|
||||
|
||||
/**
|
||||
* 通过id删除该条数据
|
||||
*
|
||||
* @param key 对象ID。
|
||||
* @param secondary 对象副主键。
|
||||
* @return
|
||||
*/
|
||||
fun remove(key: TKey, secondary: TSecond)
|
||||
|
||||
/**
|
||||
* 根据ID获取对象。
|
||||
*
|
||||
* @param key 对象ID。
|
||||
* @return
|
||||
*/
|
||||
operator fun get(key: TKey, secondary: TSecond): TAggregateRoot
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.synebula.gaea.domain.repository
|
||||
|
||||
import com.synebula.gaea.domain.model.IAggregateRoot
|
||||
|
||||
/**
|
||||
* 继承本接口表示对象为仓储类。
|
||||
*
|
||||
* @param <TAggregateRoot> this T is the parameter
|
||||
* @author alex
|
||||
*/
|
||||
interface IRepository<TAggregateRoot : IAggregateRoot<TKey>, TKey> {
|
||||
|
||||
/**
|
||||
* 插入单个对象。
|
||||
*
|
||||
* @param obj 需要插入的对象。
|
||||
* @return 返回原对象,如果对象ID为自增,则补充自增ID。
|
||||
*/
|
||||
fun add(obj: TAggregateRoot)
|
||||
|
||||
/**
|
||||
* 更新对象。
|
||||
*
|
||||
* @param obj 需要更新的对象。
|
||||
* @return
|
||||
*/
|
||||
fun update(obj: TAggregateRoot)
|
||||
|
||||
/**
|
||||
* 通过id删除该条数据
|
||||
*
|
||||
* @param id
|
||||
* @return
|
||||
*/
|
||||
fun remove(id: TKey)
|
||||
|
||||
/**
|
||||
* 根据ID获取对象。
|
||||
*
|
||||
* @param id 对象ID。
|
||||
* @return
|
||||
*/
|
||||
operator fun get(id: TKey): TAggregateRoot
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.synebula.gaea.domain.repository
|
||||
|
||||
import com.synebula.gaea.domain.model.IAggregateRoot
|
||||
|
||||
interface ITypedRepository<TAggregateRoot : IAggregateRoot<TKey>, TKey> {
|
||||
/**
|
||||
* 插入单个对象。
|
||||
*
|
||||
* @param obj 需要插入的对象。
|
||||
* @return 返回原对象,如果对象ID为自增,则补充自增ID。
|
||||
*/
|
||||
fun add(obj: TAggregateRoot)
|
||||
|
||||
/**
|
||||
* 更新对象。
|
||||
*
|
||||
* @param obj 需要更新的对象。
|
||||
* @return
|
||||
*/
|
||||
fun update(obj: TAggregateRoot)
|
||||
|
||||
/**
|
||||
* 通过id删除该条数据
|
||||
*
|
||||
* @param id id
|
||||
* @param clazz 操作数据的类型
|
||||
*/
|
||||
fun remove(id: TKey, clazz: Class<TAggregateRoot>)
|
||||
|
||||
/**
|
||||
* 根据ID获取对象。
|
||||
*
|
||||
* @param id id
|
||||
* @param clazz 操作数据的类型
|
||||
* @return 聚合根
|
||||
*/
|
||||
fun get(id: TKey, clazz: Class<TAggregateRoot>): TAggregateRoot
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.synebula.gaea.domain.repository.context
|
||||
|
||||
import com.synebula.gaea.domain.model.IAggregateRoot
|
||||
|
||||
/**
|
||||
* 继承自IUnitOfWork,表示实现了工作单元模式的上下文接口。
|
||||
*
|
||||
* @author alex
|
||||
*/
|
||||
interface IContext : IUnitOfWork {
|
||||
/**
|
||||
* 将指定的聚合根标注为“新建”状态。
|
||||
* @param obj
|
||||
*/
|
||||
fun <TType : IAggregateRoot<TKey>, TKey> add(obj: TType)
|
||||
|
||||
/**
|
||||
* 将指定的聚合根标注为“更改”状态。
|
||||
*
|
||||
* @param obj
|
||||
*/
|
||||
fun <TType : IAggregateRoot<TKey>, TKey> update(obj: TType)
|
||||
|
||||
/**
|
||||
* 将指定的聚合根标注为“删除”状态。
|
||||
*
|
||||
* @param obj
|
||||
*/
|
||||
fun <TType : IAggregateRoot<TKey>, TKey> remove(obj: TType)
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.synebula.gaea.domain.repository.context
|
||||
|
||||
/**
|
||||
* 表示所有继承于该接口的类型都是Unit Of Work的一种实现。
|
||||
*
|
||||
* @author alex
|
||||
*/
|
||||
interface IUnitOfWork {
|
||||
|
||||
/**
|
||||
* 获得一个boolean值,该值表述了当前的Unit Of Work事务是否已被提交。
|
||||
*
|
||||
* @return 返回Unit Of Work事务是否已被提交。
|
||||
*/
|
||||
val isCommitted: Boolean
|
||||
|
||||
/**
|
||||
* 提交当前的Unit Of Work事务。
|
||||
*/
|
||||
fun commit()
|
||||
|
||||
/**
|
||||
* 回滚当前的Unit Of Work事务。
|
||||
*/
|
||||
fun rollback()
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.synebula.gaea.domain.repository.specifications
|
||||
|
||||
/**
|
||||
* 表示第一个规约对象和第二个规约对象反转的结果取与。
|
||||
*
|
||||
* @author alex
|
||||
*
|
||||
* @param <T>
|
||||
* 规约对象的类型。
|
||||
*/
|
||||
class AndNotSpecification<T>
|
||||
/**
|
||||
* 构造一个新的混合规约对象。
|
||||
*
|
||||
* @param left
|
||||
* 表达式左侧规约对象。
|
||||
* @param right
|
||||
* 表达式右侧规约对象。
|
||||
*/
|
||||
(left: ISpecification<T>, right: ISpecification<T>) : CompositeSpecification<T>(left, right) {
|
||||
|
||||
override fun isSatisfiedBy(obj: T): Boolean {
|
||||
return left.isSatisfiedBy(obj) && NotSpecification(right).isSatisfiedBy(obj)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.synebula.gaea.domain.repository.specifications
|
||||
|
||||
/**
|
||||
* 表示第一个规约对象和第二个规约对象取与。
|
||||
*
|
||||
* @author alex
|
||||
*
|
||||
* @param <T>
|
||||
* 规约对象的类型。
|
||||
*/
|
||||
class AndSpecification<T>
|
||||
/**
|
||||
* 构造一个新的混合规约对象。
|
||||
*
|
||||
* @param left
|
||||
* 表达式左侧规约对象。
|
||||
* @param right
|
||||
* 表达式右侧规约对象。
|
||||
*/
|
||||
(left: ISpecification<T>, right: ISpecification<T>) : CompositeSpecification<T>(left, right) {
|
||||
|
||||
override fun isSatisfiedBy(obj: T): Boolean {
|
||||
return left.isSatisfiedBy(obj) && right.isSatisfiedBy(obj)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.synebula.gaea.domain.repository.specifications
|
||||
|
||||
/**
|
||||
* 任何对象都返回真。
|
||||
*
|
||||
* @author alex
|
||||
*
|
||||
* @param <T>
|
||||
* 规约对象的类型。
|
||||
*/
|
||||
class AnySpecification<T> : Specification<T>() {
|
||||
|
||||
override fun isSatisfiedBy(obj: T): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.synebula.gaea.domain.repository.specifications
|
||||
|
||||
/**
|
||||
* 实现了接口ICompositeSpecification<T>,混合规约的基类。
|
||||
* @author alex
|
||||
*
|
||||
* @param <T>
|
||||
</T> */
|
||||
abstract class CompositeSpecification<T>
|
||||
/**
|
||||
* 构造一个新的混合规约对象。
|
||||
*
|
||||
* @param left
|
||||
* 表达式左侧规约对象。
|
||||
* @param right
|
||||
* 表达式右侧规约对象。
|
||||
*/
|
||||
(
|
||||
/**
|
||||
* 表达式左侧规约对象。
|
||||
*/
|
||||
override val left: ISpecification<T>,
|
||||
/**
|
||||
* 表达式右侧规约对象。
|
||||
*/
|
||||
override val right: ISpecification<T>) : Specification<T>(), ICompositeSpecification<T>
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.synebula.gaea.domain.repository.specifications
|
||||
|
||||
/**
|
||||
* 定义一个有两个规约对象混合规约接口。
|
||||
*
|
||||
* @author alex
|
||||
*
|
||||
* @param <T>
|
||||
* 规约对象的类型。
|
||||
*/
|
||||
interface ICompositeSpecification<T> : ISpecification<T> {
|
||||
|
||||
/**
|
||||
* 获取左侧规约对象。
|
||||
*
|
||||
* @return 返回左侧规约对象。
|
||||
*/
|
||||
val left: ISpecification<T>
|
||||
|
||||
/**
|
||||
* 获取右侧规约对象。
|
||||
*
|
||||
* @return 返回右侧规约对象。
|
||||
*/
|
||||
val right: ISpecification<T>
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package com.synebula.gaea.domain.repository.specifications
|
||||
|
||||
/**
|
||||
* 声明规约对象的公共接口。
|
||||
*
|
||||
* @author alex
|
||||
*
|
||||
* @param <T>
|
||||
*/
|
||||
interface ISpecification<T> {
|
||||
/**
|
||||
* 判断是否符合条件。
|
||||
*
|
||||
* @param obj
|
||||
* 规约类型的对象。
|
||||
* @return 符合条件的返回True。
|
||||
*/
|
||||
fun isSatisfiedBy(obj: T): Boolean
|
||||
|
||||
/**
|
||||
* 结合当前规约对象和另一规约对象进行与判断。
|
||||
*
|
||||
* @param other
|
||||
* 需要进行与结合的另一个规约对象。
|
||||
* @return 结合后的规约对象。
|
||||
*/
|
||||
fun and(other: ISpecification<T>): ISpecification<T>
|
||||
|
||||
/**
|
||||
* 结合当前规约对象和另一规约对象进行或判断。
|
||||
*
|
||||
* @param other
|
||||
* 需要进行或结合的另一个规约对象。
|
||||
* @return 结合后的规约对象。
|
||||
*/
|
||||
fun or(other: ISpecification<T>): ISpecification<T>
|
||||
|
||||
/**
|
||||
* 结合当前规约对象和另一规约对象进行与且非判断
|
||||
*
|
||||
* @param other
|
||||
* 需要进行非结合的另一个规约对象。
|
||||
* @return 结合后的规约对象。
|
||||
*/
|
||||
fun andNot(other: ISpecification<T>): ISpecification<T>
|
||||
|
||||
/**
|
||||
* 结合当前规约对象和另一规约对象进行或非判断。
|
||||
*
|
||||
* @param other
|
||||
* 需要进行或非结合的另一个规约对象。
|
||||
* @return 结合后的规约对象。
|
||||
*/
|
||||
fun orNot(other: ISpecification<T>): ISpecification<T>
|
||||
|
||||
/**
|
||||
* 反转当前规约的判断结果。
|
||||
*
|
||||
* @return 反转后的规约对象。
|
||||
*/
|
||||
operator fun not(): ISpecification<T>
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.synebula.gaea.domain.repository.specifications
|
||||
|
||||
/**
|
||||
* 任何对象都返回假。
|
||||
*
|
||||
* @author alex
|
||||
*
|
||||
* @param <T>
|
||||
* 规约对象的类型。
|
||||
*/
|
||||
class NoneSpecification<T> : Specification<T>() {
|
||||
|
||||
override fun isSatisfiedBy(obj: T): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.synebula.gaea.domain.repository.specifications
|
||||
|
||||
/**
|
||||
* 逆反规约。
|
||||
*
|
||||
* @author alex
|
||||
*
|
||||
* @param <T> 规约对象的类型。
|
||||
*
|
||||
* @param spec 需要逆反的规约对象。
|
||||
*
|
||||
*/
|
||||
class NotSpecification<T>(private val spec: ISpecification<T>) : Specification<T>() {
|
||||
|
||||
override fun isSatisfiedBy(obj: T): Boolean {
|
||||
return !spec.isSatisfiedBy(obj)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.synebula.gaea.domain.repository.specifications
|
||||
|
||||
/**
|
||||
* 表示第一个规约对象和第二个规约对象反转的结果取或。
|
||||
*
|
||||
* @author alex
|
||||
*
|
||||
* @param <T>
|
||||
* 规约对象的类型。
|
||||
*/
|
||||
class OrNotSpecification<T>
|
||||
/**
|
||||
* 构造一个新的混合规约对象。
|
||||
*
|
||||
* @param left
|
||||
* 表达式左侧规约对象。
|
||||
* @param right
|
||||
* 表达式右侧规约对象。
|
||||
*/
|
||||
(left: ISpecification<T>, right: ISpecification<T>) : CompositeSpecification<T>(left, right) {
|
||||
|
||||
override fun isSatisfiedBy(obj: T): Boolean {
|
||||
return left.isSatisfiedBy(obj) || NotSpecification(right).isSatisfiedBy(obj)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.synebula.gaea.domain.repository.specifications
|
||||
|
||||
class OrSpecification<T>
|
||||
/**
|
||||
* 构造一个新的混合规约对象。
|
||||
*
|
||||
* @param left
|
||||
* 表达式左侧规约对象。
|
||||
* @param right
|
||||
* 表达式右侧规约对象。
|
||||
*/
|
||||
(left: ISpecification<T>, right: ISpecification<T>) : CompositeSpecification<T>(left, right) {
|
||||
|
||||
override fun isSatisfiedBy(obj: T): Boolean {
|
||||
return left.isSatisfiedBy(obj) || right.isSatisfiedBy(obj)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.synebula.gaea.domain.repository.specifications
|
||||
|
||||
abstract class Specification<T> : ISpecification<T> {
|
||||
|
||||
abstract override fun isSatisfiedBy(obj: T): Boolean
|
||||
|
||||
override fun and(other: ISpecification<T>): ISpecification<T> {
|
||||
return AndSpecification(this, other)
|
||||
}
|
||||
|
||||
override fun or(other: ISpecification<T>): ISpecification<T> {
|
||||
return OrSpecification(this, other)
|
||||
}
|
||||
|
||||
override fun andNot(other: ISpecification<T>): ISpecification<T> {
|
||||
return AndNotSpecification(this, other)
|
||||
}
|
||||
|
||||
override fun orNot(other: ISpecification<T>): ISpecification<T> {
|
||||
return OrNotSpecification(this, other)
|
||||
}
|
||||
|
||||
override fun not(): ISpecification<T> {
|
||||
return NotSpecification(this)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.synebula.gaea.domain.service
|
||||
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* 命令基础实现类,发给service的命令。
|
||||
*
|
||||
* @author alex
|
||||
* @version 0.1
|
||||
* @since 2018 18-2-8
|
||||
*/
|
||||
class Command : ICommand {
|
||||
override var timestamp = 0L
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package com.synebula.gaea.domain.service
|
||||
|
||||
import com.synebula.gaea.data.IObjectConverter
|
||||
import com.synebula.gaea.data.message.Message
|
||||
import com.synebula.gaea.domain.model.complex.IComplexAggregateRoot
|
||||
import com.synebula.gaea.domain.repository.IComplexRepository
|
||||
import com.synebula.gaea.log.ILogger
|
||||
|
||||
/**
|
||||
* 复合主键的服务基类
|
||||
*
|
||||
* @author alex
|
||||
* @version 0.1
|
||||
* @since 2018 18-2-8
|
||||
*/
|
||||
open class ComplexService<TAggregateRoot : IComplexAggregateRoot<TKey, TSecond>, TKey, TSecond>
|
||||
(var logger: ILogger, protected var repository: IComplexRepository<TAggregateRoot, TKey, TSecond>,
|
||||
protected var converter: IObjectConverter, protected var aggregateRootClass: Class<TAggregateRoot>)
|
||||
: IComplexService<TKey, TSecond> {
|
||||
|
||||
override fun add(command: ICommand): Message<Pair<TKey, TSecond>> {
|
||||
val msg = Message<Pair<TKey, TSecond>>()
|
||||
val root = this.convert(command)
|
||||
repository.add(root)
|
||||
msg.data = Pair<TKey, TSecond>(root.id, root.secondary)
|
||||
return msg
|
||||
}
|
||||
|
||||
override fun update(key: TKey, secondary: TSecond, command: ICommand) {
|
||||
val root = this.convert(command)
|
||||
root.id = key
|
||||
root.secondary = secondary
|
||||
repository.update(root)
|
||||
}
|
||||
|
||||
override fun remove(key: TKey, secondary: TSecond) {
|
||||
repository.remove(key, secondary)
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换ICommand类型到聚合根类型,默认实现,根据需要进行覆写。
|
||||
*
|
||||
* @param command
|
||||
* @return
|
||||
*/
|
||||
protected fun convert(command: ICommand): TAggregateRoot {
|
||||
try {
|
||||
return converter.convert(command, aggregateRootClass)
|
||||
} catch (ex: Exception) {
|
||||
throw RuntimeException("command not match aggregate root", ex)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.synebula.gaea.domain.service
|
||||
|
||||
/**
|
||||
* 命令接口,发给service的命令。
|
||||
*
|
||||
* @author alex
|
||||
* @version 0.1
|
||||
* @since 2018 18-2-8
|
||||
*/
|
||||
interface ICommand {
|
||||
/**
|
||||
* 时间戳。
|
||||
*/
|
||||
var timestamp: Long
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.synebula.gaea.domain.service
|
||||
|
||||
import com.synebula.gaea.data.message.Message
|
||||
|
||||
/**
|
||||
* class IFlatService
|
||||
*
|
||||
* @author alex
|
||||
* @version 0.1
|
||||
* @since 2018 18-2-8
|
||||
*/
|
||||
interface IComplexService<TKey, TSecond> {
|
||||
|
||||
fun add(command: ICommand): Message<Pair<TKey, TSecond>>
|
||||
|
||||
fun update(key: TKey, secondary: TSecond, command: ICommand)
|
||||
|
||||
fun remove(key: TKey, secondary: TSecond)
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.synebula.gaea.domain.service
|
||||
|
||||
import com.synebula.gaea.data.message.Message
|
||||
import com.synebula.gaea.log.ILogger
|
||||
|
||||
|
||||
/**
|
||||
* 继承该接口,表明对象为领域服务。
|
||||
* @author alex
|
||||
* @version 0.0.1
|
||||
* @since 2016年9月18日 下午2:23:15
|
||||
*/
|
||||
interface IService<TKey> {
|
||||
/**
|
||||
* 日志组件。若无日志组件则默认为NullLogger对象,不会为null。
|
||||
*/
|
||||
var logger: ILogger
|
||||
|
||||
fun add(command: ICommand): Message<TKey>
|
||||
|
||||
fun update(key: TKey, command: ICommand)
|
||||
|
||||
fun remove(key: TKey)
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package com.synebula.gaea.domain.service
|
||||
|
||||
import com.synebula.gaea.data.IObjectConverter
|
||||
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
|
||||
|
||||
|
||||
/**
|
||||
* class FlatService
|
||||
*
|
||||
* @author alex
|
||||
* @version 0.1
|
||||
* @since 2018 18-2-8
|
||||
*/
|
||||
open class Service<TAggregateRoot : IAggregateRoot<TKey>, TKey>
|
||||
(override var logger: ILogger,
|
||||
protected var repository: IRepository<TAggregateRoot, TKey>,
|
||||
protected var converter: IObjectConverter,
|
||||
protected var aggregateRootClass: Class<TAggregateRoot>) : IService<TKey> {
|
||||
|
||||
override fun add(command: ICommand): Message<TKey> {
|
||||
val msg = Message<TKey>()
|
||||
val root = this.convert(command)
|
||||
repository.add(root)
|
||||
msg.data = root.id
|
||||
return msg
|
||||
}
|
||||
|
||||
override fun update(key: TKey, command: ICommand) {
|
||||
val root = this.convert(command)
|
||||
root.id = key
|
||||
repository.update(root)
|
||||
}
|
||||
|
||||
override fun remove(key: TKey) {
|
||||
repository.remove(key)
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换ICommand类型到聚合根类型,默认实现,根据需要进行覆写。
|
||||
*
|
||||
* @param command
|
||||
* @return
|
||||
*/
|
||||
protected fun convert(command: ICommand): TAggregateRoot {
|
||||
try {
|
||||
return converter.convert(command, aggregateRootClass)
|
||||
} catch (ex: Exception) {
|
||||
throw RuntimeException("command not match aggregate root", ex)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.synebula.gaea.io.scan
|
||||
|
||||
/**
|
||||
*
|
||||
* @author alex
|
||||
* @version 0.0.1
|
||||
* @since 2016年9月20日 上午10:50:06
|
||||
*/
|
||||
object ClassLoaderContext {
|
||||
/**
|
||||
* 获得class loader<br></br>
|
||||
* 若当前线程class loader不存在,取当前类的class loader
|
||||
*
|
||||
* @return 类加载器
|
||||
*/
|
||||
fun get(): ClassLoader {
|
||||
var classLoader: ClassLoader? = Thread.currentThread().contextClassLoader
|
||||
if (classLoader == null) {
|
||||
classLoader = ClassLoaderContext::class.java.classLoader
|
||||
}
|
||||
return classLoader!!
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package com.synebula.gaea.io.scan
|
||||
|
||||
import java.io.IOException
|
||||
import java.net.URL
|
||||
import java.util.Enumeration
|
||||
import java.util.HashSet
|
||||
|
||||
/**
|
||||
*
|
||||
* @author alex
|
||||
* @version 0.0.1
|
||||
* @since 2016年9月20日 下午2:59:47
|
||||
*/
|
||||
object ClassPath {
|
||||
|
||||
/**
|
||||
* 获得Java ClassPath路径
|
||||
* @return 获得Java ClassPath路径,不包括 jre
|
||||
*/
|
||||
fun get(): Array<String> {
|
||||
val paths = System.getProperty("java.class.path")
|
||||
val separator = System.getProperty("path.separator")
|
||||
return paths.split(separator.toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得ClassPath
|
||||
*
|
||||
* @param packageName 包名称
|
||||
* @return ClassPath路径字符串集合
|
||||
*/
|
||||
fun get(packageName: String): Array<String> {
|
||||
val paths = HashSet<String>()
|
||||
var path = packageName.replace(".", "/")
|
||||
// 判断路径最后一个字符是不是"/",如果不是加上
|
||||
path = if (path.lastIndexOf("/") != path.length - 1) "$path/" else path
|
||||
|
||||
var resources: Enumeration<URL>? = null
|
||||
try {
|
||||
resources = ClassLoaderContext.get().getResources(path)
|
||||
} catch (e: IOException) {
|
||||
}
|
||||
|
||||
while (resources!!.hasMoreElements()) {
|
||||
paths.add(resources.nextElement().path)
|
||||
}
|
||||
return paths.toTypedArray()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,202 @@
|
||||
package com.synebula.gaea.io.scan
|
||||
|
||||
import java.io.File
|
||||
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.jar.JarFile
|
||||
|
||||
/**
|
||||
* 包扫描组件
|
||||
* @author alex
|
||||
* @version 0.0.1
|
||||
* @since 2016年9月20日 下午2:56:50
|
||||
*
|
||||
* @param packageName 扫描的包名
|
||||
*/
|
||||
class ClassScanner(private var packageName: String) {
|
||||
/**
|
||||
* 类过滤器列表
|
||||
*/
|
||||
private var classFilters: Array<out IClassFilter>? = null
|
||||
|
||||
/**
|
||||
* 扫描的类
|
||||
*/
|
||||
private val classes: MutableSet<Class<*>> = HashSet()
|
||||
|
||||
/**
|
||||
* 文件过滤器
|
||||
*/
|
||||
private val fileFilter = FileFilter { file -> file.isDirectory || file.name.endsWith(".class") || file.name.endsWith(".jar") }
|
||||
|
||||
|
||||
/**
|
||||
* 构造方法。
|
||||
*
|
||||
* @param packageName 需要扫描的包名。
|
||||
* @param classFilter
|
||||
*/
|
||||
constructor(packageName: String, vararg classFilter: IClassFilter) : this(packageName) {
|
||||
this.classFilters = classFilter
|
||||
}
|
||||
|
||||
/**
|
||||
* 开始扫描。
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
fun scan(): Collection<Class<*>> {
|
||||
// 如果类集合不为空,先清空。
|
||||
if (classes.isNotEmpty())
|
||||
classes.clear()
|
||||
|
||||
var paths = ClassPath.get(packageName)// 先扫描当前类路径下文件
|
||||
this.scanPath(paths)
|
||||
if (classes.isEmpty()) {// 若当前类路径下文件没有,扫描java路径
|
||||
paths = ClassPath.get()
|
||||
this.scanPath(paths)
|
||||
}
|
||||
return classes
|
||||
}
|
||||
|
||||
/*
|
||||
* Private
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* 填充满足条件的class 填充到 classes<br></br>
|
||||
* 同时会判断给定的路径是否为Jar包内的路径,如果是,则扫描此Jar包
|
||||
*
|
||||
* @param paths Class文件路径或者所在目录Jar包路径
|
||||
*/
|
||||
private fun scanPath(paths: Array<String>) {
|
||||
for (path in paths) {
|
||||
try {
|
||||
// 路径编码,防止由于路径中空格和中文导致的Jar找不到
|
||||
val realPath = URLDecoder.decode(path, Charset.defaultCharset().name())
|
||||
val files = scanDirectory(realPath)
|
||||
for (file in files) {
|
||||
val fileName = file.toString()
|
||||
if (fileName.contains(".jar") && !fileName.endsWith(".class"))
|
||||
scanJar(file)
|
||||
else if (fileName.endsWith(".class"))
|
||||
scanClass(realPath, file)
|
||||
}
|
||||
} catch (e: UnsupportedEncodingException) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 扫描文件,过滤添加类文件。
|
||||
*
|
||||
* @param path 扫描的磁盘路径
|
||||
*/
|
||||
private fun scanDirectory(path: String): Collection<File> {
|
||||
var realPath = path
|
||||
val files = LinkedList<File>()
|
||||
val index = realPath.lastIndexOf(".jar")
|
||||
if (index != -1) {// 首先判path是不是jar包下,如果是说明当前path是文件
|
||||
// jar文件
|
||||
realPath = realPath.substring(0, index + 4)// 截取jar路径 [xxx/xxx.jar]
|
||||
if (realPath.startsWith("file:"))
|
||||
realPath = realPath.substring(5) // 去掉文件前缀[file:]
|
||||
files.add(File(realPath))
|
||||
} else {// 否则扫描文件夹下文件
|
||||
val directory = LinkedList<File>()
|
||||
directory.add(File(realPath))
|
||||
var list: Array<File>?
|
||||
var file: File
|
||||
while (directory.size != 0) {
|
||||
file = directory.poll()
|
||||
list = file.listFiles(fileFilter)
|
||||
if (list!!.isNotEmpty()) {
|
||||
for (item in list) {
|
||||
if (item.isDirectory)
|
||||
directory.add(item)
|
||||
else
|
||||
files.add(item)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return files
|
||||
}
|
||||
|
||||
/**
|
||||
* 扫描jar文件,过滤添加类文件。
|
||||
*
|
||||
* @param file 扫描的jar文件
|
||||
*/
|
||||
private fun scanJar(file: File) {
|
||||
try {
|
||||
val jar = JarFile(file)
|
||||
val entries = jar.entries()
|
||||
val list = Collections.list(entries)
|
||||
for (entry in list) {
|
||||
if (entry.name.endsWith(".class")) {
|
||||
val className = entry.name.replace("/", ".").replace(".class", "")
|
||||
filterClass(className)
|
||||
}
|
||||
}
|
||||
jar.close()
|
||||
} catch (ex: Throwable) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 扫描.class类文件,过滤添加类文件。
|
||||
*
|
||||
* @param path 路径
|
||||
* @param file 文件
|
||||
*/
|
||||
private fun scanClass(path: String, file: File) {
|
||||
var realPath = path
|
||||
if (!realPath.endsWith(File.separator)) {
|
||||
realPath += File.separator
|
||||
}
|
||||
var absolutePath = file.absolutePath
|
||||
if (packageName.isEmpty())
|
||||
absolutePath = absolutePath.substring(realPath.length)
|
||||
|
||||
val filePathWithDot = absolutePath.replace(File.separator, ".")
|
||||
val subIndex = filePathWithDot.indexOf(packageName)
|
||||
if (subIndex != -1) {
|
||||
val endIndex = filePathWithDot.lastIndexOf(".class")
|
||||
val className = filePathWithDot.substring(subIndex, endIndex)
|
||||
filterClass(className)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 过滤符合要求的类,添加到结果列表中。
|
||||
*
|
||||
* @param className 类名
|
||||
*/
|
||||
private fun filterClass(className: String) {
|
||||
if (className.startsWith(packageName)) {
|
||||
try {
|
||||
val clazz = Class.forName(className, false, ClassLoaderContext.get())
|
||||
if (classFilters == null)
|
||||
classes.add(clazz)
|
||||
else {
|
||||
for (iClassFilter in classFilters!!) {
|
||||
if (iClassFilter.accept(clazz))
|
||||
classes.add(clazz)
|
||||
}
|
||||
}
|
||||
} catch (ex: Throwable) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.synebula.gaea.io.scan
|
||||
|
||||
/**
|
||||
* 类过滤器,用于过滤不需要加载的类<br></br>
|
||||
* @author alex
|
||||
* @version 0.0.1
|
||||
* @since 2016年9月18日 下午4:41:29
|
||||
*/
|
||||
interface IClassFilter {
|
||||
fun accept(clazz: Class<*>): Boolean
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.synebula.gaea.query
|
||||
|
||||
/**
|
||||
* 查询基接口。
|
||||
*
|
||||
* @author wxf
|
||||
*/
|
||||
interface IComplexQuery<TView, TKey, TSecond> {
|
||||
|
||||
|
||||
/**
|
||||
* 根据Key获取对象。
|
||||
*
|
||||
* @param key 对象Key。
|
||||
* @return
|
||||
*/
|
||||
fun get(key: TKey, secondary: TSecond): TView
|
||||
|
||||
/**
|
||||
* 根据实体类条件查询所有符合条件记录
|
||||
*
|
||||
* @param parameters 查询条件。
|
||||
* @return list
|
||||
*/
|
||||
fun list(parameters: Map<String, Any>): List<TView>
|
||||
|
||||
/**
|
||||
* 根据条件查询符合条件记录的数量
|
||||
*
|
||||
* @param parameters 查询条件。
|
||||
* @return int
|
||||
*/
|
||||
fun count(parameters: Map<String, Any>): Int
|
||||
|
||||
/**
|
||||
* 根据实体类条件查询所有符合条件记录(分页查询)
|
||||
*
|
||||
* @param param 分页条件
|
||||
* @return
|
||||
*/
|
||||
fun paging(param: PagingParam): PagingData<TView>
|
||||
}
|
||||
42
src/gaea/src/main/kotlin/com/synebula/gaea/query/IQuery.kt
Normal file
42
src/gaea/src/main/kotlin/com/synebula/gaea/query/IQuery.kt
Normal file
@@ -0,0 +1,42 @@
|
||||
package com.synebula.gaea.query
|
||||
|
||||
/**
|
||||
* 查询基接口。
|
||||
*
|
||||
* @author alex
|
||||
*/
|
||||
interface IQuery<TView, TKey> {
|
||||
|
||||
|
||||
/**
|
||||
* 根据Key获取对象。
|
||||
*
|
||||
* @param key 对象Key。
|
||||
* @return
|
||||
*/
|
||||
fun get(key: TKey): TView?
|
||||
|
||||
/**
|
||||
* 根据实体类条件查询所有符合条件记录
|
||||
*
|
||||
* @param params 查询条件。
|
||||
* @return list
|
||||
*/
|
||||
fun list(params: Map<String, Any>?): List<TView>
|
||||
|
||||
/**
|
||||
* 根据条件查询符合条件记录的数量
|
||||
*
|
||||
* @param params 查询条件。
|
||||
* @return int
|
||||
*/
|
||||
fun count(params: Map<String, Any>?): Int
|
||||
|
||||
/**
|
||||
* 根据实体类条件查询所有符合条件记录(分页查询)
|
||||
*
|
||||
* @param params 分页条件
|
||||
* @return 分页数据
|
||||
*/
|
||||
fun paging(params: PagingParam): PagingData<TView>
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.synebula.gaea.query
|
||||
|
||||
/**
|
||||
* class OrderType
|
||||
*
|
||||
* @author alex
|
||||
* @version 0.1
|
||||
* @since 2018 18-2-6
|
||||
*/
|
||||
enum class OrderType {
|
||||
/**
|
||||
* 升序排列
|
||||
*/
|
||||
ASC,
|
||||
|
||||
/**
|
||||
* 降序排序
|
||||
*/
|
||||
DESC
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package com.synebula.gaea.query
|
||||
|
||||
/**
|
||||
* 分页数据。
|
||||
* 真实数据行无后台遍历需求,直接使用object类型表示即可.
|
||||
*
|
||||
* @author alex
|
||||
*/
|
||||
class PagingData<T> {
|
||||
/**
|
||||
* 页码,从1开始。
|
||||
*/
|
||||
var page: Int = 0
|
||||
|
||||
/**
|
||||
* 每页数据量。
|
||||
*/
|
||||
|
||||
var size: Int = 0
|
||||
|
||||
/**
|
||||
* 总数据量。
|
||||
*/
|
||||
|
||||
var total: Int = 0
|
||||
|
||||
/**
|
||||
* 数据索引,从0开始。表示数据在总量的第几条。(index = (page - 1) * size )
|
||||
*/
|
||||
var index: Int
|
||||
get() = (page - 1) * size
|
||||
|
||||
/**
|
||||
* 结果数据。
|
||||
*/
|
||||
var data = listOf<T>()
|
||||
|
||||
|
||||
/**
|
||||
* 数据构造。
|
||||
*
|
||||
* @param page 页码,从1开始。
|
||||
* @param size 每页数据量。
|
||||
*/
|
||||
constructor(page: Int, size: Int) : super() {
|
||||
this.page = page
|
||||
this.size = size
|
||||
this.index = (page - 1) * size + 1
|
||||
}
|
||||
|
||||
/**
|
||||
* 数据构造。
|
||||
*
|
||||
* @param page 页码,从1开始。
|
||||
* @param size 每页数据量。
|
||||
* @param total 总数据量。
|
||||
* @param data 结果数据。
|
||||
*/
|
||||
|
||||
constructor(page: Int, size: Int, total: Int, data: List<T>) : super() {
|
||||
this.page = page
|
||||
this.size = size
|
||||
this.total = total
|
||||
this.index = (page - 1) * size + 1
|
||||
this.data = data
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.synebula.gaea.query
|
||||
|
||||
/**
|
||||
* class PagingParam
|
||||
*
|
||||
* @author alex
|
||||
* @version 0.1
|
||||
* @since 2018 18-2-5
|
||||
*/
|
||||
data class PagingParam(var page: Int = 1, var size: Int = 10) {
|
||||
|
||||
/**
|
||||
* 数据索引,从0开始。表示数据在总量的第几条。(index = (page - 1) * size)
|
||||
*/
|
||||
var index: Long = 0
|
||||
get() = (page.toLong() - 1) * size.toLong()
|
||||
private set
|
||||
|
||||
/**
|
||||
* 排序条件。
|
||||
*/
|
||||
var orderBy: MutableMap<String, OrderType> = hashMapOf()
|
||||
|
||||
/**
|
||||
* 查询条件。
|
||||
*/
|
||||
var parameters: MutableMap<String, Any> = hashMapOf()
|
||||
|
||||
/**
|
||||
* 添加查询条件
|
||||
*/
|
||||
fun addParameter(field: String, value: Any): PagingParam {
|
||||
parameters[field] = value
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加排序条件
|
||||
*/
|
||||
fun addOrderBy(field: String, type: OrderType = OrderType.ASC): PagingParam {
|
||||
orderBy[field] = type
|
||||
return this
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user