From fa18c92a02e826c483b11e2a8ed48c1fcf16b2de Mon Sep 17 00:00:00 2001 From: AiKrai Date: Wed, 12 Mar 2025 17:56:45 +0800 Subject: [PATCH] =?UTF-8?q?refactor(domain):=20=E5=8F=82=E6=95=B0=E8=8E=B7?= =?UTF-8?q?=E5=8F=96=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- vertx-demo/src/main/kotlin/app/Application.kt | 1 + .../kotlin/app/config/auth/TokenService.kt | 2 +- .../kotlin/app/controller/AuthController.kt | 2 +- .../kotlin/app/controller/Demo1Controller.kt | 12 +- .../app/{ => data}/domain/account/Account.kt | 10 +- .../domain/account/AccountRepository.kt | 6 +- .../domain/account/AccountRepositoryImpl.kt | 6 +- .../app/{ => data}/domain/account/LoginDTO.kt | 2 +- .../account/modle/AccountRoleAccessDTO.kt | 8 +- .../domain/account/modle/AccountRoleDTO.kt | 4 +- .../domain/account/modle/LoginUser.kt | 0 .../kotlin/app/{ => data}/domain/menu/Menu.kt | 2 +- .../app/{ => data}/domain/menu/MenuManager.kt | 5 +- .../{ => data}/domain/menu/MenuRepository.kt | 4 +- .../domain/menu/MenuRepositoryImpl.kt | 3 +- .../app/{ => data}/domain/menu/MenuType.kt | 0 .../{ => data}/domain/menu/modle/RouterVo.kt | 0 .../domain/menu/modle/TreeSelect.kt | 4 +- .../kotlin/app/{ => data}/domain/role/Role.kt | 2 +- .../{ => data}/domain/role/RoleRepository.kt | 2 +- .../domain/role/RoleRepositoryImpl.kt | 2 +- .../main/kotlin/app/data/emun/DeleteEnum.kt | 27 +++ .../src/main/kotlin/app/data/emun/Status.kt | 27 +++ .../src/main/kotlin/app/domain/CargoType.kt | 18 -- .../app/service/account/AccountService.kt | 6 +- .../main/kotlin/app/verticle/WebVerticle.kt | 2 +- .../org/aikrai/vertx/context/RouterBuilder.kt | 110 +++++++----- .../org/aikrai/vertx/db/QueryWrapperImpl.kt | 3 +- .../org/aikrai/vertx/db/RepositoryImpl.kt | 39 ++++- .../aikrai/vertx/db/annotation/Annotation.kt | 9 + .../jackson/ColumnAnnotationIntrospector.kt | 7 +- .../vertx/openapi/OpenApiSpecGenerator.kt | 161 +++++++++++++----- 32 files changed, 343 insertions(+), 143 deletions(-) rename vertx-demo/src/main/kotlin/app/{ => data}/domain/account/Account.kt (72%) rename vertx-demo/src/main/kotlin/app/{ => data}/domain/account/AccountRepository.kt (78%) rename vertx-demo/src/main/kotlin/app/{ => data}/domain/account/AccountRepositoryImpl.kt (96%) rename vertx-demo/src/main/kotlin/app/{ => data}/domain/account/LoginDTO.kt (68%) rename vertx-demo/src/main/kotlin/app/{ => data}/domain/account/modle/AccountRoleAccessDTO.kt (75%) rename vertx-demo/src/main/kotlin/app/{ => data}/domain/account/modle/AccountRoleDTO.kt (76%) rename vertx-demo/src/main/kotlin/app/{ => data}/domain/account/modle/LoginUser.kt (100%) rename vertx-demo/src/main/kotlin/app/{ => data}/domain/menu/Menu.kt (95%) rename vertx-demo/src/main/kotlin/app/{ => data}/domain/menu/MenuManager.kt (97%) rename vertx-demo/src/main/kotlin/app/{ => data}/domain/menu/MenuRepository.kt (72%) rename vertx-demo/src/main/kotlin/app/{ => data}/domain/menu/MenuRepositoryImpl.kt (83%) rename vertx-demo/src/main/kotlin/app/{ => data}/domain/menu/MenuType.kt (100%) rename vertx-demo/src/main/kotlin/app/{ => data}/domain/menu/modle/RouterVo.kt (100%) rename vertx-demo/src/main/kotlin/app/{ => data}/domain/menu/modle/TreeSelect.kt (91%) rename vertx-demo/src/main/kotlin/app/{ => data}/domain/role/Role.kt (92%) rename vertx-demo/src/main/kotlin/app/{ => data}/domain/role/RoleRepository.kt (85%) rename vertx-demo/src/main/kotlin/app/{ => data}/domain/role/RoleRepositoryImpl.kt (89%) create mode 100644 vertx-demo/src/main/kotlin/app/data/emun/DeleteEnum.kt create mode 100644 vertx-demo/src/main/kotlin/app/data/emun/Status.kt delete mode 100644 vertx-demo/src/main/kotlin/app/domain/CargoType.kt diff --git a/vertx-demo/src/main/kotlin/app/Application.kt b/vertx-demo/src/main/kotlin/app/Application.kt index 7110ec3..88f83e9 100644 --- a/vertx-demo/src/main/kotlin/app/Application.kt +++ b/vertx-demo/src/main/kotlin/app/Application.kt @@ -17,6 +17,7 @@ object Application { Config.init(vertx) val getIt = InjectConfig.configure(vertx) val mainVerticle = getIt.getInstance(MainVerticle::class.java) + vertx.deployVerticle(mainVerticle).onComplete { if (it.failed()) { logger.error { "MainVerticle startup failed: ${it.cause()?.stackTraceToString()}" } diff --git a/vertx-demo/src/main/kotlin/app/config/auth/TokenService.kt b/vertx-demo/src/main/kotlin/app/config/auth/TokenService.kt index c39bcfd..44f4c36 100644 --- a/vertx-demo/src/main/kotlin/app/config/auth/TokenService.kt +++ b/vertx-demo/src/main/kotlin/app/config/auth/TokenService.kt @@ -1,6 +1,6 @@ package app.config.auth -import app.domain.account.AccountRepository +import app.data.domain.account.AccountRepository import app.port.reids.RedisClient import cn.hutool.core.lang.Snowflake import cn.hutool.core.util.IdUtil diff --git a/vertx-demo/src/main/kotlin/app/controller/AuthController.kt b/vertx-demo/src/main/kotlin/app/controller/AuthController.kt index 0f251a9..c69e23f 100644 --- a/vertx-demo/src/main/kotlin/app/controller/AuthController.kt +++ b/vertx-demo/src/main/kotlin/app/controller/AuthController.kt @@ -1,6 +1,6 @@ package app.controller -import app.domain.account.LoginDTO +import app.data.domain.account.LoginDTO import app.service.account.AccountService import com.google.inject.Inject import io.vertx.ext.web.RoutingContext diff --git a/vertx-demo/src/main/kotlin/app/controller/Demo1Controller.kt b/vertx-demo/src/main/kotlin/app/controller/Demo1Controller.kt index f98f5d1..a4f7046 100644 --- a/vertx-demo/src/main/kotlin/app/controller/Demo1Controller.kt +++ b/vertx-demo/src/main/kotlin/app/controller/Demo1Controller.kt @@ -1,8 +1,8 @@ package app.controller -import app.domain.CargoType -import app.domain.account.Account -import app.domain.account.AccountRepository +import app.data.domain.account.Account +import app.data.domain.account.AccountRepository +import app.data.emun.Status import app.service.account.AccountService import com.google.inject.Inject import mu.KotlinLogging @@ -28,13 +28,15 @@ class Demo1Controller @Inject constructor( @D("name", "姓名") name: String?, @D("age", "年龄") age: Int?, @D("list", "列表") list: List?, - @D("cargoType", "货物类型") cargoType: CargoType? + @D("status", "状态-0正常,1禁用,2删除") status: Status?, + @D("account", "账号") account: Account? ) { logger.info { "你好" } println(age) println(list) println("test-$name") - println(cargoType) + println(status) + println(account) } @D("事务测试") diff --git a/vertx-demo/src/main/kotlin/app/domain/account/Account.kt b/vertx-demo/src/main/kotlin/app/data/domain/account/Account.kt similarity index 72% rename from vertx-demo/src/main/kotlin/app/domain/account/Account.kt rename to vertx-demo/src/main/kotlin/app/data/domain/account/Account.kt index b5a26e1..a23cd6f 100644 --- a/vertx-demo/src/main/kotlin/app/domain/account/Account.kt +++ b/vertx-demo/src/main/kotlin/app/data/domain/account/Account.kt @@ -1,6 +1,8 @@ -package app.domain.account +package app.data.domain.account +import app.data.emun.Status import org.aikrai.vertx.db.annotation.* +import org.aikrai.vertx.jackson.JsonUtil import org.aikrai.vertx.utlis.BaseEntity import java.sql.Timestamp @@ -23,7 +25,7 @@ class Account : BaseEntity() { var password: String? = null - var status: Char? = null + var status: Status? = Status.ACTIVE var delFlag: Char? = null @@ -31,4 +33,8 @@ class Account : BaseEntity() { @TableField(fill = FieldFill.UPDATE) var loginDate: Timestamp? = null + + override fun toString(): String { + return JsonUtil.toJsonStr(this) + } } diff --git a/vertx-demo/src/main/kotlin/app/domain/account/AccountRepository.kt b/vertx-demo/src/main/kotlin/app/data/domain/account/AccountRepository.kt similarity index 78% rename from vertx-demo/src/main/kotlin/app/domain/account/AccountRepository.kt rename to vertx-demo/src/main/kotlin/app/data/domain/account/AccountRepository.kt index 2e2e2d6..555e0ed 100644 --- a/vertx-demo/src/main/kotlin/app/domain/account/AccountRepository.kt +++ b/vertx-demo/src/main/kotlin/app/data/domain/account/AccountRepository.kt @@ -1,7 +1,7 @@ -package app.domain.account +package app.data.domain.account -import app.base.domain.auth.modle.AccountRoleDTO -import app.domain.account.modle.AccountRoleAccessDTO +import app.data.domain.account.modle.AccountRoleAccessDTO +import app.data.domain.account.modle.AccountRoleDTO import com.google.inject.ImplementedBy import org.aikrai.vertx.db.Repository diff --git a/vertx-demo/src/main/kotlin/app/domain/account/AccountRepositoryImpl.kt b/vertx-demo/src/main/kotlin/app/data/domain/account/AccountRepositoryImpl.kt similarity index 96% rename from vertx-demo/src/main/kotlin/app/domain/account/AccountRepositoryImpl.kt rename to vertx-demo/src/main/kotlin/app/data/domain/account/AccountRepositoryImpl.kt index 7bb7c61..59a7a00 100644 --- a/vertx-demo/src/main/kotlin/app/domain/account/AccountRepositoryImpl.kt +++ b/vertx-demo/src/main/kotlin/app/data/domain/account/AccountRepositoryImpl.kt @@ -1,7 +1,7 @@ -package app.domain.account +package app.data.domain.account -import app.base.domain.auth.modle.AccountRoleDTO -import app.domain.account.modle.AccountRoleAccessDTO +import app.data.domain.account.modle.AccountRoleAccessDTO +import app.data.domain.account.modle.AccountRoleDTO import com.google.inject.Inject import io.vertx.sqlclient.SqlClient import org.aikrai.vertx.db.RepositoryImpl diff --git a/vertx-demo/src/main/kotlin/app/domain/account/LoginDTO.kt b/vertx-demo/src/main/kotlin/app/data/domain/account/LoginDTO.kt similarity index 68% rename from vertx-demo/src/main/kotlin/app/domain/account/LoginDTO.kt rename to vertx-demo/src/main/kotlin/app/data/domain/account/LoginDTO.kt index 3132eee..58433bf 100644 --- a/vertx-demo/src/main/kotlin/app/domain/account/LoginDTO.kt +++ b/vertx-demo/src/main/kotlin/app/data/domain/account/LoginDTO.kt @@ -1,4 +1,4 @@ -package app.domain.account +package app.data.domain.account data class LoginDTO( var username: String, diff --git a/vertx-demo/src/main/kotlin/app/domain/account/modle/AccountRoleAccessDTO.kt b/vertx-demo/src/main/kotlin/app/data/domain/account/modle/AccountRoleAccessDTO.kt similarity index 75% rename from vertx-demo/src/main/kotlin/app/domain/account/modle/AccountRoleAccessDTO.kt rename to vertx-demo/src/main/kotlin/app/data/domain/account/modle/AccountRoleAccessDTO.kt index b989f54..905ef00 100644 --- a/vertx-demo/src/main/kotlin/app/domain/account/modle/AccountRoleAccessDTO.kt +++ b/vertx-demo/src/main/kotlin/app/data/domain/account/modle/AccountRoleAccessDTO.kt @@ -1,8 +1,8 @@ -package app.domain.account.modle +package app.data.domain.account.modle -import app.domain.account.Account -import app.domain.menu.Menu -import app.domain.role.Role +import app.data.domain.account.Account +import app.data.domain.menu.Menu +import app.data.domain.role.Role data class AccountRoleAccessDTO( val account: Account, diff --git a/vertx-demo/src/main/kotlin/app/domain/account/modle/AccountRoleDTO.kt b/vertx-demo/src/main/kotlin/app/data/domain/account/modle/AccountRoleDTO.kt similarity index 76% rename from vertx-demo/src/main/kotlin/app/domain/account/modle/AccountRoleDTO.kt rename to vertx-demo/src/main/kotlin/app/data/domain/account/modle/AccountRoleDTO.kt index 734fc3e..aa9a36c 100644 --- a/vertx-demo/src/main/kotlin/app/domain/account/modle/AccountRoleDTO.kt +++ b/vertx-demo/src/main/kotlin/app/data/domain/account/modle/AccountRoleDTO.kt @@ -1,6 +1,6 @@ -package app.base.domain.auth.modle +package app.data.domain.account.modle -import app.domain.role.Role +import app.data.domain.role.Role data class AccountRoleDTO( val id: Long, diff --git a/vertx-demo/src/main/kotlin/app/domain/account/modle/LoginUser.kt b/vertx-demo/src/main/kotlin/app/data/domain/account/modle/LoginUser.kt similarity index 100% rename from vertx-demo/src/main/kotlin/app/domain/account/modle/LoginUser.kt rename to vertx-demo/src/main/kotlin/app/data/domain/account/modle/LoginUser.kt diff --git a/vertx-demo/src/main/kotlin/app/domain/menu/Menu.kt b/vertx-demo/src/main/kotlin/app/data/domain/menu/Menu.kt similarity index 95% rename from vertx-demo/src/main/kotlin/app/domain/menu/Menu.kt rename to vertx-demo/src/main/kotlin/app/data/domain/menu/Menu.kt index cd33f4b..6c20871 100644 --- a/vertx-demo/src/main/kotlin/app/domain/menu/Menu.kt +++ b/vertx-demo/src/main/kotlin/app/data/domain/menu/Menu.kt @@ -1,4 +1,4 @@ -package app.domain.menu +package app.data.domain.menu import org.aikrai.vertx.db.annotation.IdType import org.aikrai.vertx.db.annotation.TableId diff --git a/vertx-demo/src/main/kotlin/app/domain/menu/MenuManager.kt b/vertx-demo/src/main/kotlin/app/data/domain/menu/MenuManager.kt similarity index 97% rename from vertx-demo/src/main/kotlin/app/domain/menu/MenuManager.kt rename to vertx-demo/src/main/kotlin/app/data/domain/menu/MenuManager.kt index 90e7c99..6b6a12a 100644 --- a/vertx-demo/src/main/kotlin/app/domain/menu/MenuManager.kt +++ b/vertx-demo/src/main/kotlin/app/data/domain/menu/MenuManager.kt @@ -1,7 +1,6 @@ -package app.domain.menu +package app.data.domain.menu -import app.base.domain.auth.menu.MenuRepository -import app.domain.account.Account +import app.data.domain.account.Account import com.google.inject.Inject import com.google.inject.Singleton import io.vertx.ext.auth.User diff --git a/vertx-demo/src/main/kotlin/app/domain/menu/MenuRepository.kt b/vertx-demo/src/main/kotlin/app/data/domain/menu/MenuRepository.kt similarity index 72% rename from vertx-demo/src/main/kotlin/app/domain/menu/MenuRepository.kt rename to vertx-demo/src/main/kotlin/app/data/domain/menu/MenuRepository.kt index 660e6bf..1bcc314 100644 --- a/vertx-demo/src/main/kotlin/app/domain/menu/MenuRepository.kt +++ b/vertx-demo/src/main/kotlin/app/data/domain/menu/MenuRepository.kt @@ -1,7 +1,5 @@ -package app.base.domain.auth.menu +package app.data.domain.menu -import app.domain.menu.Menu -import app.domain.menu.MenuRepositoryImpl import com.google.inject.ImplementedBy import org.aikrai.vertx.db.Repository diff --git a/vertx-demo/src/main/kotlin/app/domain/menu/MenuRepositoryImpl.kt b/vertx-demo/src/main/kotlin/app/data/domain/menu/MenuRepositoryImpl.kt similarity index 83% rename from vertx-demo/src/main/kotlin/app/domain/menu/MenuRepositoryImpl.kt rename to vertx-demo/src/main/kotlin/app/data/domain/menu/MenuRepositoryImpl.kt index 308ca64..27668eb 100644 --- a/vertx-demo/src/main/kotlin/app/domain/menu/MenuRepositoryImpl.kt +++ b/vertx-demo/src/main/kotlin/app/data/domain/menu/MenuRepositoryImpl.kt @@ -1,6 +1,5 @@ -package app.domain.menu +package app.data.domain.menu -import app.base.domain.auth.menu.MenuRepository import com.google.inject.Inject import io.vertx.sqlclient.SqlClient import org.aikrai.vertx.db.RepositoryImpl diff --git a/vertx-demo/src/main/kotlin/app/domain/menu/MenuType.kt b/vertx-demo/src/main/kotlin/app/data/domain/menu/MenuType.kt similarity index 100% rename from vertx-demo/src/main/kotlin/app/domain/menu/MenuType.kt rename to vertx-demo/src/main/kotlin/app/data/domain/menu/MenuType.kt diff --git a/vertx-demo/src/main/kotlin/app/domain/menu/modle/RouterVo.kt b/vertx-demo/src/main/kotlin/app/data/domain/menu/modle/RouterVo.kt similarity index 100% rename from vertx-demo/src/main/kotlin/app/domain/menu/modle/RouterVo.kt rename to vertx-demo/src/main/kotlin/app/data/domain/menu/modle/RouterVo.kt diff --git a/vertx-demo/src/main/kotlin/app/domain/menu/modle/TreeSelect.kt b/vertx-demo/src/main/kotlin/app/data/domain/menu/modle/TreeSelect.kt similarity index 91% rename from vertx-demo/src/main/kotlin/app/domain/menu/modle/TreeSelect.kt rename to vertx-demo/src/main/kotlin/app/data/domain/menu/modle/TreeSelect.kt index 3c35fed..5a6daff 100644 --- a/vertx-demo/src/main/kotlin/app/domain/menu/modle/TreeSelect.kt +++ b/vertx-demo/src/main/kotlin/app/data/domain/menu/modle/TreeSelect.kt @@ -1,6 +1,6 @@ -package app.base.domain.auth.menu.modle +package app.data.domain.menu.modle -import app.domain.menu.Menu +import app.data.domain.menu.Menu class TreeSelect { diff --git a/vertx-demo/src/main/kotlin/app/domain/role/Role.kt b/vertx-demo/src/main/kotlin/app/data/domain/role/Role.kt similarity index 92% rename from vertx-demo/src/main/kotlin/app/domain/role/Role.kt rename to vertx-demo/src/main/kotlin/app/data/domain/role/Role.kt index d8cd6f0..5cd1aa7 100644 --- a/vertx-demo/src/main/kotlin/app/domain/role/Role.kt +++ b/vertx-demo/src/main/kotlin/app/data/domain/role/Role.kt @@ -1,4 +1,4 @@ -package app.domain.role +package app.data.domain.role import org.aikrai.vertx.db.annotation.TableName import org.aikrai.vertx.utlis.BaseEntity diff --git a/vertx-demo/src/main/kotlin/app/domain/role/RoleRepository.kt b/vertx-demo/src/main/kotlin/app/data/domain/role/RoleRepository.kt similarity index 85% rename from vertx-demo/src/main/kotlin/app/domain/role/RoleRepository.kt rename to vertx-demo/src/main/kotlin/app/data/domain/role/RoleRepository.kt index 32df038..e0a8148 100644 --- a/vertx-demo/src/main/kotlin/app/domain/role/RoleRepository.kt +++ b/vertx-demo/src/main/kotlin/app/data/domain/role/RoleRepository.kt @@ -1,4 +1,4 @@ -package app.domain.role +package app.data.domain.role import com.google.inject.ImplementedBy import org.aikrai.vertx.db.Repository diff --git a/vertx-demo/src/main/kotlin/app/domain/role/RoleRepositoryImpl.kt b/vertx-demo/src/main/kotlin/app/data/domain/role/RoleRepositoryImpl.kt similarity index 89% rename from vertx-demo/src/main/kotlin/app/domain/role/RoleRepositoryImpl.kt rename to vertx-demo/src/main/kotlin/app/data/domain/role/RoleRepositoryImpl.kt index 8b30a45..d8ba1d2 100644 --- a/vertx-demo/src/main/kotlin/app/domain/role/RoleRepositoryImpl.kt +++ b/vertx-demo/src/main/kotlin/app/data/domain/role/RoleRepositoryImpl.kt @@ -1,4 +1,4 @@ -package app.domain.role +package app.data.domain.role import com.google.inject.Inject import io.vertx.sqlclient.SqlClient diff --git a/vertx-demo/src/main/kotlin/app/data/emun/DeleteEnum.kt b/vertx-demo/src/main/kotlin/app/data/emun/DeleteEnum.kt new file mode 100644 index 0000000..b5df594 --- /dev/null +++ b/vertx-demo/src/main/kotlin/app/data/emun/DeleteEnum.kt @@ -0,0 +1,27 @@ +package app.data.emun + +import com.fasterxml.jackson.annotation.JsonCreator +import com.fasterxml.jackson.annotation.JsonValue +import org.aikrai.vertx.db.annotation.EnumValue + +enum class DeleteEnum(private val code: Int, private val description: String) { + UNDELETED(0, "未删除"), + DELETED(2, "已删除"); + + @JsonValue + @EnumValue + fun getCode(): Int { + return this.code + } + + override fun toString(): String { + return this.description + } + + companion object { + @JsonCreator + fun parse(code: Int): DeleteEnum? { + return entries.find { it.code == code } + } + } +} diff --git a/vertx-demo/src/main/kotlin/app/data/emun/Status.kt b/vertx-demo/src/main/kotlin/app/data/emun/Status.kt new file mode 100644 index 0000000..c2440a0 --- /dev/null +++ b/vertx-demo/src/main/kotlin/app/data/emun/Status.kt @@ -0,0 +1,27 @@ +package app.data.emun + +import com.fasterxml.jackson.annotation.JsonCreator +import com.fasterxml.jackson.annotation.JsonValue +import org.aikrai.vertx.db.annotation.EnumValue + +enum class Status(private val code: Int, private val description: String) { + ACTIVE(0, "正常"), + INACTIVE(1, "禁用"), + DELETED(2, "删除"); + + @JsonValue + @EnumValue + public fun getCode(): Int { + return code + } + override fun toString(): String { + return description + } + + companion object { + @JsonCreator + fun parse(code: Int): Status? { + return entries.find { it.code == code } + } + } +} diff --git a/vertx-demo/src/main/kotlin/app/domain/CargoType.kt b/vertx-demo/src/main/kotlin/app/domain/CargoType.kt deleted file mode 100644 index 1cd3053..0000000 --- a/vertx-demo/src/main/kotlin/app/domain/CargoType.kt +++ /dev/null @@ -1,18 +0,0 @@ -package app.domain - -enum class CargoType(val message: String) { - - RECEIVING("收货"), - - SHIPPING("发货"), - - INTERNAL_TRANSFER("内部调拨") - ; - - companion object { - fun parse(value: String?): CargoType? { - if (value.isNullOrBlank()) return null - return CargoType.values().find { it.name == value || it.message == value } - } - } -} diff --git a/vertx-demo/src/main/kotlin/app/service/account/AccountService.kt b/vertx-demo/src/main/kotlin/app/service/account/AccountService.kt index ee5ca20..4950878 100644 --- a/vertx-demo/src/main/kotlin/app/service/account/AccountService.kt +++ b/vertx-demo/src/main/kotlin/app/service/account/AccountService.kt @@ -1,9 +1,9 @@ package app.service.account import app.config.auth.TokenService -import app.domain.account.Account -import app.domain.account.AccountRepository -import app.domain.account.LoginDTO +import app.data.domain.account.Account +import app.data.domain.account.AccountRepository +import app.data.domain.account.LoginDTO import cn.hutool.core.lang.Snowflake import cn.hutool.crypto.SecureUtil import com.google.inject.Inject diff --git a/vertx-demo/src/main/kotlin/app/verticle/WebVerticle.kt b/vertx-demo/src/main/kotlin/app/verticle/WebVerticle.kt index 3619d9b..7035a06 100644 --- a/vertx-demo/src/main/kotlin/app/verticle/WebVerticle.kt +++ b/vertx-demo/src/main/kotlin/app/verticle/WebVerticle.kt @@ -4,7 +4,7 @@ import app.config.RespBean import app.config.auth.JwtAuthenticationHandler import app.config.auth.ResponseHandler import app.config.auth.TokenService -import app.domain.account.Account +import app.data.domain.account.Account import app.port.aipfox.ApifoxClient import cn.hutool.core.lang.Snowflake import com.google.inject.Inject diff --git a/vertx-fw/src/main/kotlin/org/aikrai/vertx/context/RouterBuilder.kt b/vertx-fw/src/main/kotlin/org/aikrai/vertx/context/RouterBuilder.kt index 91d332e..0fb96b8 100644 --- a/vertx-fw/src/main/kotlin/org/aikrai/vertx/context/RouterBuilder.kt +++ b/vertx-fw/src/main/kotlin/org/aikrai/vertx/context/RouterBuilder.kt @@ -3,6 +3,7 @@ package org.aikrai.vertx.context import cn.hutool.core.util.StrUtil import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper import io.vertx.core.http.HttpMethod +import io.vertx.core.json.JsonObject import io.vertx.ext.auth.User import io.vertx.ext.web.Router import io.vertx.ext.web.RoutingContext @@ -11,6 +12,8 @@ import kotlinx.coroutines.launch import org.aikrai.vertx.auth.* import org.aikrai.vertx.config.resp.DefaultResponseHandler import org.aikrai.vertx.config.resp.ResponseHandlerInterface +import org.aikrai.vertx.db.annotation.EnumValue +import org.aikrai.vertx.jackson.JsonUtil import org.aikrai.vertx.utlis.ClassUtil import org.aikrai.vertx.utlis.Meta import org.reflections.Reflections @@ -73,8 +76,10 @@ class RouterBuilder( isList = parameter.type.classifier == List::class, isComplex = !parameter.type.classifier.toString().startsWith("class kotlin.") && !parameter.type.classifier.toString().startsWith("class io.vertx") && + !(parameter.type.javaType as Class<*>).isEnum && !parameter.type.javaType.javaClass.isEnum && - parameter.type.javaType is Class<*> + parameter.type.javaType is Class<*>, + isEnum = typeClass?.isEnum ?: (parameter.type.javaType as Class<*>).isEnum ) } routeInfoCache[reqPath to httpMethod] = @@ -163,15 +168,6 @@ class RouterBuilder( } } - private fun getReqPath(prefix: String, clazz: Class<*>): String { - val basePath = if (prefix.isNotBlank()) { - StrUtil.toCamelCase(StrUtil.toUnderlineCase(prefix)) - } else { - StrUtil.toCamelCase(StrUtil.toUnderlineCase(clazz.simpleName.removeSuffix("Controller"))) - } - return "/$basePath".replace("//", "/") - } - private fun getReqPath(prefix: String, clazz: Class<*>, method: Method): String { var classPath = if (prefix.isNotBlank()) { StrUtil.toCamelCase(StrUtil.toUnderlineCase(prefix)) @@ -189,31 +185,57 @@ class RouterBuilder( val queryParams = ctx.queryParams().entries().associate { it.key to it.value } val combinedParams = formAttributes + queryParams // 解析Body - val bodyStr = if (!ctx.body().isEmpty) ctx.body().asString() else "" - val bodyAsMap = if (bodyStr.isNotBlank()) { - try { - objectMapper.readValue(bodyStr, Map::class.java) as Map - } catch (e: Exception) { - emptyMap() - } - } else { - emptyMap() - } + val bodyObj = if (!ctx.body().isEmpty) ctx.body().asJsonObject() else null + val bodyMap = bodyObj?.map ?: emptyMap() paramsInfo.forEach { param -> if (param.isList) { - val listParamValue = ctx.queryParams().getAll(param.name) - if (listParamValue.isEmpty() && !param.isNullable) throw IllegalArgumentException("Missing required parameter: ${param.name}") - params.add(listParamValue) + var value = ctx.queryParams().getAll(param.name) + if (value.isEmpty() && bodyMap[param.name] != null) { + value = (bodyMap[param.name] as Collection<*>).map { it.toString() }.toMutableList() + } + if (value.isEmpty() && !param.isNullable) { + throw IllegalArgumentException("Missing required parameter: ${param.name}") + } + params.add(value.ifEmpty { null }) return@forEach } + + if (param.isEnum) { + val value = sequenceOf( + combinedParams[param.name], + bodyMap[param.name] + ).filterNotNull().map { it.toString() }.firstOrNull() + + val enumValueMethod = param.type.methods.find { method -> + method.isAnnotationPresent(EnumValue::class.java) + } + val enumValue = param.type.enumConstants.firstOrNull { enumConstant -> + if (enumValueMethod != null) { + enumValueMethod.invoke(enumConstant).toString() == value + } else { + (enumConstant as Enum<*>).name == value + } + } + if (enumValue != null) params.add(enumValue) + return@forEach + } + if (param.isComplex) { try { - val value = objectMapper.readValue(bodyStr, param.type) - params.add(value) + val value = sequenceOf( + if (paramsInfo.size == 1) bodyObj else null, + bodyMap[param.name]?.let { JsonUtil.toJsonObject(it) }, + combinedParams[param.name]?.let { JsonObject(it) }, + bodyObj + ).filterNotNull().firstOrNull { !it.isEmpty } + if (value?.isEmpty == true && !param.isNullable) { + throw IllegalArgumentException("Missing required parameter: ${param.name}") + } + params.add(if (value == null || value.isEmpty) null else JsonUtil.parseObject(value, param.type)) return@forEach } catch (e: Exception) { - if (!param.isNullable) throw IllegalArgumentException("Failed to parse request body for parameter: ${param.name}") + throw IllegalArgumentException(e.message, e) } } @@ -222,7 +244,7 @@ class RouterBuilder( RoutingContext::class.java -> ctx User::class.java -> ctx.user() else -> { - val bodyValue = bodyAsMap[param.name] + val bodyValue = bodyMap[param.name] val paramValue = bodyValue?.toString() ?: combinedParams[param.name] when { paramValue == null -> { @@ -254,16 +276,12 @@ class RouterBuilder( * @return 转换为目标类型的参数值,如果转换失败则返回 `null`。 */ private fun getParamValue(paramValue: String, type: Class<*>): Any? { - return when { - type.isEnum -> { - type.enumConstants.firstOrNull { (it as Enum<*>).name.equals(paramValue, ignoreCase = true) } - } - - type == String::class.java -> paramValue - type == Int::class.java || type == Integer::class.java -> paramValue.toIntOrNull() - type == Long::class.java || type == Long::class.java -> paramValue.toLongOrNull() - type == Double::class.java || type == Double::class.java -> paramValue.toDoubleOrNull() - type == Boolean::class.java || type == Boolean::class.java -> paramValue.toBoolean() + return when (type) { + String::class.java -> paramValue + Int::class.java, Integer::class.java -> paramValue.toIntOrNull() + Long::class.java, Long::class.java -> paramValue.toLongOrNull() + Double::class.java, Double::class.java -> paramValue.toDoubleOrNull() + Boolean::class.java, Boolean::class.java -> paramValue.toBoolean() else -> paramValue } } @@ -298,6 +316,21 @@ class RouterBuilder( private fun serializeToJson(obj: Any?): String { return objectMapper.writeValueAsString(obj) } + + private fun getEnumValue(enumValue: Any?): Any? { + if (enumValue == null || !enumValue::class.java.isEnum) { + return null // 不是枚举或为空,直接返回 null + } + val enumClass = enumValue::class.java + val methods = enumClass.declaredMethods + for (method in methods) { + if (method.isAnnotationPresent(EnumValue::class.java)) { + method.isAccessible = true // 如果方法是私有的,设置为可访问 + return method.invoke(enumValue) // 调用带有 @EnumValue 注解的方法 + } + } + return null // 没有找到带有 @EnumValue 注解的方法 + } } private data class RouteInfo( @@ -316,6 +349,7 @@ class RouterBuilder( val type: Class<*>, val isNullable: Boolean, val isList: Boolean, - val isComplex: Boolean + val isComplex: Boolean, + val isEnum: Boolean ) } diff --git a/vertx-fw/src/main/kotlin/org/aikrai/vertx/db/QueryWrapperImpl.kt b/vertx-fw/src/main/kotlin/org/aikrai/vertx/db/QueryWrapperImpl.kt index 2d50acb..4f784fc 100644 --- a/vertx-fw/src/main/kotlin/org/aikrai/vertx/db/QueryWrapperImpl.kt +++ b/vertx-fw/src/main/kotlin/org/aikrai/vertx/db/QueryWrapperImpl.kt @@ -263,6 +263,7 @@ class QueryWrapperImpl( "IN", "NOT IN" -> { params[it.column] = "(${(it.value as Collection<*>).joinToString(",")})" } + else -> { params[it.column] = it.value.toString() } @@ -292,7 +293,7 @@ class QueryWrapperImpl( .execute(params) .coAwait() .toList() - return objs.map { JsonUtil.parseObject(it.encode(), clazz) }.also { conditions.clear() } + return objs.map { JsonUtil.parseObject(it, clazz, true) }.also { conditions.clear() } } catch (e: Exception) { conditions.clear() throw Meta.repository(e.javaClass.simpleName, e.message) diff --git a/vertx-fw/src/main/kotlin/org/aikrai/vertx/db/RepositoryImpl.kt b/vertx-fw/src/main/kotlin/org/aikrai/vertx/db/RepositoryImpl.kt index 9e1587e..9766579 100644 --- a/vertx-fw/src/main/kotlin/org/aikrai/vertx/db/RepositoryImpl.kt +++ b/vertx-fw/src/main/kotlin/org/aikrai/vertx/db/RepositoryImpl.kt @@ -35,6 +35,7 @@ open class RepositoryImpl( private val idFieldCache = ConcurrentHashMap() private val idFieldNameCache = ConcurrentHashMap() private val tableNameCache = ConcurrentHashMap() + // sqlCache private val baseSqlCache = ConcurrentHashMap>() private val queryClientCache = ConcurrentHashMap() @@ -93,7 +94,7 @@ open class RepositoryImpl( val idAnnotation = idField.getAnnotation(TableId::class.java) val idValue = idField.get(t) val excludeId = idAnnotation != null && (idValue == null || idValue == 0L || idValue == -1L) && - (idAnnotation.type == IdType.AUTO) + (idAnnotation.type == IdType.AUTO) val sqlKey = if (excludeId) "createExcludeId" else "createIncludeId" val sqlTemplate = getOrCreateSql(tableName, sqlKey) { @@ -120,6 +121,7 @@ open class RepositoryImpl( IdType.INPUT -> { if (idValue == 0L || idValue == -1L) throw Meta.repository("CreateError", "must provide ID value") } + IdType.ASSIGN_ID -> params[idField.name] = IdUtil.getSnowflakeNextId() IdType.ASSIGN_UUID -> params[idField.name] = IdUtil.simpleUUID() else -> {} @@ -137,6 +139,7 @@ open class RepositoryImpl( else -> null } } + else -> null } if (value != null) params[field.name] = value @@ -331,11 +334,36 @@ open class RepositoryImpl( // 获取非空字段及其值 private fun getNonNullFields(t: TEntity): Map { - return fields.filter { !it.isAnnotationPresent(Transient::class.java) && it.get(t) != null } - .associate { it.name to it.get(t) } + return fields + .filter { + !it.isAnnotationPresent(Transient::class.java) && it.get(t) != null + } + .associate { + val value = it.get(t) + if (it.type.isEnum) { + it.name to (getEnumValue(value) ?: (value as Enum<*>).name) + } else { + it.name to value + } + } } - /** + private fun getEnumValue(enumValue: Any?): Any? { + if (enumValue == null || !enumValue::class.java.isEnum) { + return null // 不是枚举或为空,直接返回 null + } + val enumClass = enumValue::class.java + val methods = enumClass.declaredMethods + for (method in methods) { + if (method.isAnnotationPresent(EnumValue::class.java)) { + method.isAccessible = true // 如果方法是私有的,设置为可访问 + return method.invoke(enumValue) // 调用带有 @EnumValue 注解的方法 + } + } + return null // 没有找到带有 @EnumValue 注解的方法 + } + + /** * 生成批量 INSERT SQL 语句的函数 * @param objects 要插入的对象列表 * @return 生成的 SQL 语句字符串 @@ -366,10 +394,13 @@ open class RepositoryImpl( is Number, is Boolean -> value.toString() // 数字和布尔类型,直接转换为字符串 is Timestamp -> // 时间戳类型 "'${OffsetDateTime.ofInstant(value.toInstant(), ZoneId.systemDefault())}'" + is Array<*> -> // 数组类型处理 if (value.isEmpty()) "'{}'" else "'{${value.joinToString(",") { escapeSql(it?.toString() ?: "NULL") }}}'" + is Collection<*> -> // 集合类型处理 if (value.isEmpty()) "'{}'" else "'{${value.joinToString(",") { escapeSql(it?.toString() ?: "NULL") }}}'" + else -> "'${escapeSql(value.toString())}'" // 其他类型,调用 toString() 后转义并加单引号 } // 构建 VALUES 部分,每个对象对应一组值 diff --git a/vertx-fw/src/main/kotlin/org/aikrai/vertx/db/annotation/Annotation.kt b/vertx-fw/src/main/kotlin/org/aikrai/vertx/db/annotation/Annotation.kt index 62e8acd..297ced8 100644 --- a/vertx-fw/src/main/kotlin/org/aikrai/vertx/db/annotation/Annotation.kt +++ b/vertx-fw/src/main/kotlin/org/aikrai/vertx/db/annotation/Annotation.kt @@ -1,5 +1,9 @@ package org.aikrai.vertx.db.annotation +import java.lang.annotation.Documented +import java.lang.annotation.ElementType +import java.lang.annotation.RetentionPolicy + @MustBeDocumented @Retention(AnnotationRetention.RUNTIME) @Target(AnnotationTarget.CLASS, AnnotationTarget.ANNOTATION_CLASS) @@ -32,6 +36,11 @@ annotation class TableField( // val numericScale: String = "" ) +@MustBeDocumented +@Retention(AnnotationRetention.RUNTIME) +@Target(AnnotationTarget.FIELD, AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.FUNCTION) +annotation class EnumValue + /** * IdType * @property key Int diff --git a/vertx-fw/src/main/kotlin/org/aikrai/vertx/jackson/ColumnAnnotationIntrospector.kt b/vertx-fw/src/main/kotlin/org/aikrai/vertx/jackson/ColumnAnnotationIntrospector.kt index 97c4a45..9b120c6 100644 --- a/vertx-fw/src/main/kotlin/org/aikrai/vertx/jackson/ColumnAnnotationIntrospector.kt +++ b/vertx-fw/src/main/kotlin/org/aikrai/vertx/jackson/ColumnAnnotationIntrospector.kt @@ -15,8 +15,9 @@ class ColumnAnnotationIntrospector : JacksonAnnotationIntrospector() { } private fun getColumnName(annotated: Annotated?): PropertyName? { - if (annotated == null) return null - val column = annotated.getAnnotation(TableField::class.java) - return column?.let { PropertyName(it.value) } + return null +// if (annotated == null) return null +// val column = annotated.getAnnotation(TableField::class.java) +// return column?.let { PropertyName(it.value) } } } diff --git a/vertx-fw/src/main/kotlin/org/aikrai/vertx/openapi/OpenApiSpecGenerator.kt b/vertx-fw/src/main/kotlin/org/aikrai/vertx/openapi/OpenApiSpecGenerator.kt index f919344..c681380 100644 --- a/vertx-fw/src/main/kotlin/org/aikrai/vertx/openapi/OpenApiSpecGenerator.kt +++ b/vertx-fw/src/main/kotlin/org/aikrai/vertx/openapi/OpenApiSpecGenerator.kt @@ -14,20 +14,27 @@ import io.swagger.v3.oas.models.parameters.Parameter import io.swagger.v3.oas.models.parameters.RequestBody import io.swagger.v3.oas.models.responses.ApiResponse import io.swagger.v3.oas.models.responses.ApiResponses +import io.swagger.v3.oas.models.security.SecurityScheme import io.swagger.v3.oas.models.servers.Server import mu.KotlinLogging import org.aikrai.vertx.context.Controller import org.aikrai.vertx.context.CustomizeRequest import org.aikrai.vertx.context.D +import org.aikrai.vertx.db.annotation.EnumValue import org.aikrai.vertx.utlis.ClassUtil import org.reflections.Reflections import java.lang.reflect.Method import java.lang.reflect.ParameterizedType +import java.lang.reflect.Type import java.sql.Date import java.sql.Time import java.sql.Timestamp import java.time.* +import kotlin.reflect.KClass import kotlin.reflect.KParameter +import kotlin.reflect.KType +import kotlin.reflect.full.declaredMemberProperties +import kotlin.reflect.jvm.javaField import kotlin.reflect.jvm.javaType import kotlin.reflect.jvm.kotlinFunction @@ -47,6 +54,8 @@ class OpenApiSpecGenerator { private val PRIMITIVE_TYPE_MAPPING = mapOf( // java.lang classes String::class.java to "string", + Char::class.java to "string", + java.lang.Character::class.java to "string", Int::class.java to "integer", Integer::class.java to "integer", Long::class.java to "number", @@ -222,35 +231,17 @@ class OpenApiSpecGenerator { * @return ApiResponses 对象 */ private fun generateResponsesFromReturnType(method: Method): ApiResponses { - val returnType = method.kotlinFunction?.returnType?.javaType - val schema = when (returnType) { - // 处理泛型返回类型 - is ParameterizedType -> { - val rawType = returnType.rawType as Class<*> - val typeArguments = returnType.actualTypeArguments - when { - // 处理集合类型 - Collection::class.java.isAssignableFrom(rawType) -> { - Schema().apply { - type = "array" - items = generateSchema( - typeArguments[0].let { - when (it) { - is Class<*> -> it - is ParameterizedType -> it.rawType as Class<*> - else -> Any::class.java - } - } - ) - } - } - // 可以添加其他泛型类型的处理 - else -> generateSchema(rawType) - } - } - // 处理普通类型 - is Class<*> -> generateSchema(returnType) - else -> Schema().type("object") + val returnType = method.kotlinFunction?.returnType + // 创建 RespBean 的架构 + val respBeanSchema = Schema().apply { + type = "object" + properties = mapOf( + "code" to Schema().type("integer").example(200), + "message" to Schema().type("string").example("Success"), + "data" to generateDataSchema(returnType), + "requestId" to Schema().type("integer").format("int64").example(1899712678486753280) + ) + required = listOf("code", "message", "data") } return ApiResponses().addApiResponse( @@ -259,13 +250,69 @@ class OpenApiSpecGenerator { description = "OK" content = Content().addMediaType( "application/json", - MediaType().schema(schema) + MediaType().schema(respBeanSchema) ) headers = mapOf() } ) } + // 新增辅助方法,用于生成 data 字段的 Schema + private fun generateDataSchema(returnType: KType?): Schema { + if (returnType == null) { + return Schema().type("null") + } + + return when (val classifier = returnType.classifier) { + is KClass<*> -> { + when { + // 处理集合类型 + Collection::class.java.isAssignableFrom(classifier.java) -> { + Schema().apply { + type = "array" + items = generateSchema( + (returnType.arguments.firstOrNull()?.type?.classifier as? KClass<*> ?: Any::class).java, + false + ) + } + } + else -> generateSchema(classifier.java) + } + } + else -> Schema().type("object") + } + +// return when (returnType) { +// // 处理泛型返回类型 +// is ParameterizedType -> { +// val rawType = returnType.rawType as Class<*> +// val typeArguments = returnType.actualTypeArguments +// when { +// // 处理集合类型 +// Collection::class.java.isAssignableFrom(rawType) -> { +// Schema().apply { +// type = "array" +// items = generateSchema( +// typeArguments[0].let { +// when (it) { +// is Class<*> -> it +// is ParameterizedType -> it.rawType as Class<*> +// else -> Any::class.java +// } +// }, isNullable +// ) +// } +// } +// else -> generateSchema(rawType, isNullable) +// } +// } +// // 处理普通类型 +// is Class<*> -> generateSchema(returnType, isNullable) +// null -> Schema().type("null") +// else -> Schema().type("object") +// } + } + /** * 获取方法的参数列表 * @@ -329,7 +376,7 @@ class OpenApiSpecGenerator { * @param type 参数类型 * @return OpenAPI Schema 对象 */ - private fun generateSchema(type: Class<*>): Schema { + private fun generateSchema(type: Class<*>, isNullable: Boolean = false): Schema { // 如果该类型已经处理过,则返回一个空的 Schema,避免循环引用 if (processedTypes.contains(type)) { return Schema().apply { @@ -344,30 +391,66 @@ class OpenApiSpecGenerator { PRIMITIVE_TYPE_MAPPING.containsKey(type) -> Schema().apply { this.type = PRIMITIVE_TYPE_MAPPING[type] deprecated = false + nullable = isNullable } // 处理枚举类型 type.isEnum -> Schema().apply { - this.type = "string" - enum = type.enumConstants?.map { it.toString() } + val enumValueMethod = type.methods.find { method -> + method.isAnnotationPresent(EnumValue::class.java) + } + this.type = if (enumValueMethod != null) { + when (enumValueMethod.returnType) { + String::class.java -> "string" + Int::class.java, java.lang.Integer::class.java -> "integer" + Long::class.java, java.lang.Long::class.java -> "integer" + Double::class.java, java.lang.Double::class.java -> "number" + Float::class.java, java.lang.Float::class.java -> "number" + Boolean::class.java, java.lang.Boolean::class.java -> "boolean" + else -> "string" // 默认情况下使用 string + } + } else { + "string" // 如果没有 enumValueMethod,默认使用 string + } + enum = type.enumConstants?.map { + if (enumValueMethod != null) { + enumValueMethod.invoke(it) + } else { + it.toString() + } + } + nullable = isNullable } type.name.startsWith("java.lang") || type.name.startsWith("java.time") || type.name.startsWith("java.sql") -> Schema().apply { this.type = type.simpleName.lowercase() deprecated = false + nullable = isNullable } type.name.startsWith("java") -> Schema().apply { this.type = type.simpleName.lowercase() deprecated = false + nullable = isNullable } - // 处理自定义对象 else -> Schema().apply { this.type = "object" - properties = type.declaredFields - .filter { !it.isSynthetic } - .associate { field -> - field.isAccessible = true - field.name to generateSchema(field.type) + properties = type.kotlin.declaredMemberProperties + .associate { property -> + val field = property.javaField + field?.isAccessible = true + val nullable = property.returnType.isMarkedNullable + val fieldType = field?.type ?: Any::class.java + property.name to generateSchema(fieldType, nullable) } } + // 处理自定义对象 +// else -> Schema().apply { +// this.type = "object" +// properties = type.declaredFields +// .filter { !it.isSynthetic } +// .associate { field -> +// field.isAccessible = true +// field.name to generateSchema(field.type, ) +// } +// } }.also { // 处理完后,从已处理集合中移除当前类型 processedTypes.remove(type)