diff --git a/src/gaea.app/src/main/kotlin/com/synebula/gaea/app/cmd/ICommandApp.kt b/src/gaea.app/src/main/kotlin/com/synebula/gaea/app/cmd/ICommandApp.kt index d888115..8e8fa79 100644 --- a/src/gaea.app/src/main/kotlin/com/synebula/gaea/app/cmd/ICommandApp.kt +++ b/src/gaea.app/src/main/kotlin/com/synebula/gaea/app/cmd/ICommandApp.kt @@ -20,6 +20,18 @@ interface ICommandApp : IApplication { var service: IService? + @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 = "没有对应服务,无法执行该操作" + } + } + } + @PostMapping fun add(@RequestBody command: TCommand): HttpMessage { return this.safeExecute("添加${this.name}数据失败 - ${if (jsonSerializer != null) jsonSerializer?.serialize(command) else ""}") { @@ -33,18 +45,6 @@ interface ICommandApp : IApplication { } } - @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 = "没有对应服务,无法执行该操作" - } - } - } - @PutMapping("/{id:.+}") fun update(@PathVariable id: TKey, @RequestBody command: TCommand): HttpMessage { return this.safeExecute("更新${this.name}失败 - ${if (jsonSerializer != null) jsonSerializer?.serialize(command) else ""}") { diff --git a/src/gaea.app/src/main/kotlin/com/synebula/gaea/app/component/EmailMessenger.kt b/src/gaea.app/src/main/kotlin/com/synebula/gaea/app/component/EmailMessenger.kt index 28a5dca..8bfbe91 100644 --- a/src/gaea.app/src/main/kotlin/com/synebula/gaea/app/component/EmailMessenger.kt +++ b/src/gaea.app/src/main/kotlin/com/synebula/gaea/app/component/EmailMessenger.kt @@ -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, files: Map): Map { + override fun sendMessage( + subject: String, + content: String, + receivers: List, + files: Map + ): Map { val result = mutableMapOf() 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 diff --git a/src/gaea.app/src/main/kotlin/com/synebula/gaea/app/component/aop/AnnotationHandler.kt b/src/gaea.app/src/main/kotlin/com/synebula/gaea/app/component/aop/AnnotationHandler.kt deleted file mode 100644 index ca75515..0000000 --- a/src/gaea.app/src/main/kotlin/com/synebula/gaea/app/component/aop/AnnotationHandler.kt +++ /dev/null @@ -1,7 +0,0 @@ -package com.synebula.gaea.app.component.aop - -import java.lang.Exception - -interface AnnotationHandler { - fun handle(clazz: Class, func: String, args: Array, exception: Exception?) -} \ No newline at end of file diff --git a/src/gaea.app/src/main/kotlin/com/synebula/gaea/app/component/aop/AppAspect.kt b/src/gaea.app/src/main/kotlin/com/synebula/gaea/app/component/aop/AppAspect.kt index 84e9901..9a89728 100644 --- a/src/gaea.app/src/main/kotlin/com/synebula/gaea/app/component/aop/AppAspect.kt +++ b/src/gaea.app/src/main/kotlin/com/synebula/gaea/app/component/aop/AppAspect.kt @@ -1,68 +1,86 @@ package com.synebula.gaea.app.component.aop import com.synebula.gaea.app.component.HttpMessage -import com.synebula.gaea.app.component.aop.annotation.SafeExec +import com.synebula.gaea.app.component.aop.annotation.ExceptionMessage +import com.synebula.gaea.app.component.aop.annotation.Handler +import com.synebula.gaea.app.component.aop.annotation.ModuleName import com.synebula.gaea.data.message.Status +import com.synebula.gaea.log.ILogger import org.aspectj.lang.JoinPoint import org.aspectj.lang.ProceedingJoinPoint import org.aspectj.lang.annotation.* import org.springframework.stereotype.Component -import org.springframework.web.context.request.RequestContextHolder -import org.springframework.web.context.request.ServletRequestAttributes -import java.util.* +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.context.ApplicationContext +import com.fasterxml.jackson.databind.ObjectMapper @Aspect @Component class AppAspect { + private val mapper = ObjectMapper() + + @Autowired + lateinit var logger: ILogger + + @Autowired + lateinit var applicationContext: ApplicationContext + @Pointcut("within(com.synebula..app.*)") fun log() { } - //后置异常通知 - @AfterThrowing("log()") - fun throws(point: JoinPoint): Any { + /** + * 后置异常通知 + */ + @AfterThrowing("log()", throwing = "ex") + fun throws(point: JoinPoint, ex: Throwable) { val clazz = point.signature.declaringType - println("${clazz.name} - ${point.signature.name}异常:${point.signature.name}") - return "error" + logger.error( + ex, + "${clazz.name}.${point.signature.name} exception:${ex.message}, args:${mapper.writeValueAsString(point.args)}" + ) } - //后置最终通知,final增强,不管是抛出异常或者正常退出都会执行 - @After("log()") - fun after(point: JoinPoint) { - println("方法最后执行.....") - } - - //环绕通知,环绕增强,相当于MethodInterceptor + /** + * 环绕通知,环绕增强,相当于MethodInterceptor + */ @Around("log()") fun around(point: ProceedingJoinPoint): Any? { val clazz = point.signature.declaringType val func = clazz.methods.find { it.name == point.signature.name }!! - val clazzAnnotations = clazz.annotations val funcAnnotations = func.annotations ?: arrayOf() - val attributes = RequestContextHolder.getRequestAttributes() as ServletRequestAttributes - val request = attributes.request - // 记录下请求内容 - println("URL : " + request.requestURL.toString()) - println("HTTP_METHOD : " + request.method) - println("IP : " + request.remoteAddr) - println("CLASS_METHOD : " + clazz.name + "." + point.signature.name) - println("ARGS : " + Arrays.toString(point.args)) + var exceptionMessage = 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 ExceptionMessage) + exceptionMessage = funcAnnotation.message + } return try { val res = point.proceed() res } catch (ex: Throwable) { - val msg = HttpMessage(Status.Success) - for (item in funcAnnotations) { - if (item.annotationClass == SafeExec::javaClass) { - } + //找到类的模块名称,否则使用类名 + var moduleName = clazz.name + val name = clazz.annotations.find { it is ModuleName } + if (name != null && name is ModuleName) { + moduleName = name.value } - println("方法${point.signature}异常: $msg - ${ex.message}") - return "error" + val message = "$moduleName - $exceptionMessage" + logger.error(ex, "$message, args: ${mapper.writeValueAsString(point.args)}") + return HttpMessage(Status.Error, message) } } } \ No newline at end of file diff --git a/src/gaea.app/src/main/kotlin/com/synebula/gaea/app/component/aop/annotation/AccessLog.kt b/src/gaea.app/src/main/kotlin/com/synebula/gaea/app/component/aop/annotation/AccessLog.kt new file mode 100644 index 0000000..28015c6 --- /dev/null +++ b/src/gaea.app/src/main/kotlin/com/synebula/gaea/app/component/aop/annotation/AccessLog.kt @@ -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 \ No newline at end of file diff --git a/src/gaea.app/src/main/kotlin/com/synebula/gaea/app/component/aop/annotation/SafeExec.kt b/src/gaea.app/src/main/kotlin/com/synebula/gaea/app/component/aop/annotation/ExceptionMessage.kt similarity index 69% rename from src/gaea.app/src/main/kotlin/com/synebula/gaea/app/component/aop/annotation/SafeExec.kt rename to src/gaea.app/src/main/kotlin/com/synebula/gaea/app/component/aop/annotation/ExceptionMessage.kt index b5b8434..89af71f 100644 --- a/src/gaea.app/src/main/kotlin/com/synebula/gaea/app/component/aop/annotation/SafeExec.kt +++ b/src/gaea.app/src/main/kotlin/com/synebula/gaea/app/component/aop/annotation/ExceptionMessage.kt @@ -3,8 +3,8 @@ package com.synebula.gaea.app.component.aop.annotation /** * 标记方法安全执行,由AOP负责try catch异常 * - * @param errorMessage 异常消息 + * @param message 异常消息 */ @Target(AnnotationTarget.FUNCTION) @Retention(AnnotationRetention.RUNTIME) -annotation class SafeExec(val errorMessage: String) +annotation class ExceptionMessage(val message: String) diff --git a/src/gaea.app/src/main/kotlin/com/synebula/gaea/app/component/aop/annotation/Handler.kt b/src/gaea.app/src/main/kotlin/com/synebula/gaea/app/component/aop/annotation/Handler.kt index 1a1d7ca..73b5b47 100644 --- a/src/gaea.app/src/main/kotlin/com/synebula/gaea/app/component/aop/annotation/Handler.kt +++ b/src/gaea.app/src/main/kotlin/com/synebula/gaea/app/component/aop/annotation/Handler.kt @@ -1,5 +1,8 @@ package com.synebula.gaea.app.component.aop.annotation +import com.synebula.gaea.app.component.aop.handler.AnnotationHandler import kotlin.reflect.KClass -annotation class Handler(val value: KClass) \ No newline at end of file +@Target(AnnotationTarget.ANNOTATION_CLASS) +@Retention(AnnotationRetention.RUNTIME) +annotation class Handler(val value: KClass) \ No newline at end of file diff --git a/src/gaea.app/src/main/kotlin/com/synebula/gaea/app/component/aop/annotation/ModuleName.kt b/src/gaea.app/src/main/kotlin/com/synebula/gaea/app/component/aop/annotation/ModuleName.kt new file mode 100644 index 0000000..3ab6e15 --- /dev/null +++ b/src/gaea.app/src/main/kotlin/com/synebula/gaea/app/component/aop/annotation/ModuleName.kt @@ -0,0 +1,8 @@ +package com.synebula.gaea.app.component.aop.annotation + +/** + * 模块的业务名称 + */ +@Target(AnnotationTarget.CLASS) +@Retention(AnnotationRetention.RUNTIME) +annotation class ModuleName(val value: String) diff --git a/src/gaea.app/src/main/kotlin/com/synebula/gaea/app/component/aop/handler/AccessLogHandler.kt b/src/gaea.app/src/main/kotlin/com/synebula/gaea/app/component/aop/handler/AccessLogHandler.kt new file mode 100644 index 0000000..1898eb3 --- /dev/null +++ b/src/gaea.app/src/main/kotlin/com/synebula/gaea/app/component/aop/handler/AccessLogHandler.kt @@ -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, func: Method, args: Array, 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 + ) + }" + ) + } +} \ No newline at end of file diff --git a/src/gaea.app/src/main/kotlin/com/synebula/gaea/app/component/aop/handler/AnnotationHandler.kt b/src/gaea.app/src/main/kotlin/com/synebula/gaea/app/component/aop/handler/AnnotationHandler.kt new file mode 100644 index 0000000..ea1c4aa --- /dev/null +++ b/src/gaea.app/src/main/kotlin/com/synebula/gaea/app/component/aop/handler/AnnotationHandler.kt @@ -0,0 +1,8 @@ +package com.synebula.gaea.app.component.aop.handler + +import java.lang.Exception +import java.lang.reflect.Method + +interface AnnotationHandler { + fun handle(clazz: Class, func: Method, args: Array, exception: Exception? = null) +} \ No newline at end of file diff --git a/src/gaea/src/main/kotlin/com/synebula/gaea/query/Params.kt b/src/gaea/src/main/kotlin/com/synebula/gaea/query/Params.kt index a4c5090..0362126 100644 --- a/src/gaea/src/main/kotlin/com/synebula/gaea/query/Params.kt +++ b/src/gaea/src/main/kotlin/com/synebula/gaea/query/Params.kt @@ -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() - private var _orders = mutableMapOf() + private var _parameters = linkedMapOf() + private var _orders = linkedMapOf() /** * 数据索引,从0开始。表示数据在总量的第几条。(index = (page - 1) * size) @@ -24,13 +24,13 @@ data class Params(var page: Int = 1, var size: Int = 10) { /** * 排序条件。 */ - var orders: Map + var orders: LinkedHashMap set(value) { - this._orders = value.toMutableMap() + this._orders = value } get() { if (this._parameters.keys.count { it.startsWith("@") } > 0) { - val params = mutableMapOf() + val params = linkedMapOf() 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 + var parameters: LinkedHashMap set(value) { - this._parameters = value.toMutableMap() + this._parameters = value } get() { if (this._parameters.keys.count { it.startsWith("@") } > 0) { - val params = mutableMapOf() + val params = linkedMapOf() this._parameters.forEach { if (it.key.startsWith("@")) { this._orders[it.key.removePrefix("@")] = Order.valueOf(it.value.toString())