refactor(router): 优化路由构建器

This commit is contained in:
AiKrai 2025-03-13 14:38:33 +08:00
parent fa18c92a02
commit 5eccf7ceed
3 changed files with 251 additions and 219 deletions

View File

@ -1,5 +1,6 @@
package app.port.aipfox package app.port.aipfox
import app.util.openapi.OpenApiSpecGenerator
import com.google.inject.Inject import com.google.inject.Inject
import com.google.inject.name.Named import com.google.inject.name.Named
import io.vertx.core.Vertx import io.vertx.core.Vertx
@ -8,7 +9,6 @@ import io.vertx.core.json.JsonObject
import io.vertx.ext.web.client.WebClient import io.vertx.ext.web.client.WebClient
import io.vertx.ext.web.client.WebClientOptions import io.vertx.ext.web.client.WebClientOptions
import mu.KotlinLogging import mu.KotlinLogging
import org.aikrai.vertx.openapi.OpenApiSpecGenerator
class ApifoxClient @Inject constructor( class ApifoxClient @Inject constructor(
private val vertx: Vertx, private val vertx: Vertx,

View File

@ -1,4 +1,4 @@
package org.aikrai.vertx.openapi package app.util.openapi
import cn.hutool.core.util.StrUtil import cn.hutool.core.util.StrUtil
import io.swagger.v3.core.util.Json import io.swagger.v3.core.util.Json
@ -14,7 +14,6 @@ import io.swagger.v3.oas.models.parameters.Parameter
import io.swagger.v3.oas.models.parameters.RequestBody import io.swagger.v3.oas.models.parameters.RequestBody
import io.swagger.v3.oas.models.responses.ApiResponse import io.swagger.v3.oas.models.responses.ApiResponse
import io.swagger.v3.oas.models.responses.ApiResponses 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 io.swagger.v3.oas.models.servers.Server
import mu.KotlinLogging import mu.KotlinLogging
import org.aikrai.vertx.context.Controller import org.aikrai.vertx.context.Controller
@ -25,7 +24,6 @@ import org.aikrai.vertx.utlis.ClassUtil
import org.reflections.Reflections import org.reflections.Reflections
import java.lang.reflect.Method import java.lang.reflect.Method
import java.lang.reflect.ParameterizedType import java.lang.reflect.ParameterizedType
import java.lang.reflect.Type
import java.sql.Date import java.sql.Date
import java.sql.Time import java.sql.Time
import java.sql.Timestamp import java.sql.Timestamp
@ -219,7 +217,9 @@ class OpenApiSpecGenerator {
private fun buildPath(controllerPrefix: String, methodName: String): String { private fun buildPath(controllerPrefix: String, methodName: String): String {
val classPath = if (controllerPrefix != "/") { val classPath = if (controllerPrefix != "/") {
StrUtil.toCamelCase(StrUtil.toUnderlineCase(controllerPrefix)) StrUtil.toCamelCase(StrUtil.toUnderlineCase(controllerPrefix))
} else "" } else {
""
}
val methodPath = StrUtil.toCamelCase(StrUtil.toUnderlineCase(methodName)) val methodPath = StrUtil.toCamelCase(StrUtil.toUnderlineCase(methodName))
return "/$classPath/$methodPath".replace("//", "/") return "/$classPath/$methodPath".replace("//", "/")
} }

View File

@ -1,7 +1,6 @@
package org.aikrai.vertx.context package org.aikrai.vertx.context
import cn.hutool.core.util.StrUtil import cn.hutool.core.util.StrUtil
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import io.vertx.core.http.HttpMethod import io.vertx.core.http.HttpMethod
import io.vertx.core.json.JsonObject import io.vertx.core.json.JsonObject
import io.vertx.ext.auth.User import io.vertx.ext.auth.User
@ -10,6 +9,7 @@ import io.vertx.ext.web.RoutingContext
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.aikrai.vertx.auth.* import org.aikrai.vertx.auth.*
import org.aikrai.vertx.auth.AuthUser.Companion.validateAuth
import org.aikrai.vertx.config.resp.DefaultResponseHandler import org.aikrai.vertx.config.resp.DefaultResponseHandler
import org.aikrai.vertx.config.resp.ResponseHandlerInterface import org.aikrai.vertx.config.resp.ResponseHandlerInterface
import org.aikrai.vertx.db.annotation.EnumValue import org.aikrai.vertx.db.annotation.EnumValue
@ -19,209 +19,247 @@ import org.aikrai.vertx.utlis.Meta
import org.reflections.Reflections import org.reflections.Reflections
import java.lang.reflect.Method import java.lang.reflect.Method
import java.lang.reflect.ParameterizedType import java.lang.reflect.ParameterizedType
import kotlin.collections.ArrayList
import kotlin.coroutines.Continuation import kotlin.coroutines.Continuation
import kotlin.reflect.KFunction import kotlin.reflect.KFunction
import kotlin.reflect.full.callSuspend import kotlin.reflect.full.callSuspend
import kotlin.reflect.full.declaredFunctions import kotlin.reflect.full.declaredFunctions
import kotlin.reflect.jvm.javaType import kotlin.reflect.jvm.javaType
/**
* RouterBuilder - 基于注解控制器构建Vert.x路由的实用工具
*
* 该类扫描控制器类分析其方法并将其注册为具有适当参数绑定和授权规则的HTTP端点
*/
class RouterBuilder( class RouterBuilder(
private val coroutineScope: CoroutineScope, private val coroutineScope: CoroutineScope,
private val router: Router, private val router: Router,
private val scanPath: String? = null, private val scanPath: String? = null,
private val responseHandler: ResponseHandlerInterface = DefaultResponseHandler() private val responseHandler: ResponseHandlerInterface = DefaultResponseHandler()
) { ) {
var anonymousPaths = ArrayList<String>() // 不需要认证的路径集合
val anonymousPaths = mutableListOf<String>()
/**
* 基于注解控制器类构建路由
*
* @param getIt 解析控制器实例的函数
* @return 当前RouterBuilder实例用于链式调用
*/
fun build(getIt: (clazz: Class<*>) -> Any): RouterBuilder { fun build(getIt: (clazz: Class<*>) -> Any): RouterBuilder {
// 缓存路由信息 // 扫描并缓存控制器路由信息
val routeInfoCache = scanControllerRoutes()
// 注册路由处理器
registerRouteHandlers(routeInfoCache, getIt)
return this
}
/**
* 扫描控制器类并提取路由信息
*/
private fun scanControllerRoutes(): Map<Pair<String, HttpMethod>, RouteInfo> {
val routeInfoCache = mutableMapOf<Pair<String, HttpMethod>, RouteInfo>() val routeInfoCache = mutableMapOf<Pair<String, HttpMethod>, RouteInfo>()
// 获取所有 Controller 类中的公共方法
val packagePath = scanPath ?: ClassUtil.getMainClass().packageName val packagePath = scanPath ?: ClassUtil.getMainClass().packageName
val controllerClassSet = Reflections(packagePath).getTypesAnnotatedWith(Controller::class.java) val controllerClassSet = Reflections(packagePath).getTypesAnnotatedWith(Controller::class.java)
val controllerMethods = ClassUtil.getPublicMethods(controllerClassSet) val controllerMethods = ClassUtil.getPublicMethods(controllerClassSet)
for ((classType, methods) in controllerMethods) { for ((classType, methods) in controllerMethods) {
processControllerClass(classType, methods.toList(), routeInfoCache)
}
return routeInfoCache
}
/**
* 处理控制器类及其方法
*/
private fun processControllerClass(
classType: Class<*>,
methods: List<Method>,
routeInfoCache: MutableMap<Pair<String, HttpMethod>, RouteInfo>
) {
val controllerAnnotation = classType.getDeclaredAnnotationsByType(Controller::class.java).firstOrNull() val controllerAnnotation = classType.getDeclaredAnnotationsByType(Controller::class.java).firstOrNull()
val prefixPath = controllerAnnotation?.prefix ?: "" val prefixPath = controllerAnnotation?.prefix ?: ""
val classAllowAnonymous = classType.getAnnotation(AllowAnonymous::class.java) != null val classAllowAnonymous = classType.getAnnotation(AllowAnonymous::class.java) != null
for (method in methods) { for (method in methods) {
val reqPath = getReqPath(prefixPath, classType, method) val reqPath = getReqPath(prefixPath, classType, method)
val httpMethod = getHttpMethod(method) val httpMethod = getHttpMethod(method)
val allowAnonymous = method.getAnnotation(AllowAnonymous::class.java) != null
if (classAllowAnonymous || allowAnonymous) anonymousPaths.add(reqPath) // 处理匿名访问
if (classAllowAnonymous || method.getAnnotation(AllowAnonymous::class.java) != null) {
anonymousPaths.add(reqPath)
}
// 提取方法元数据
val customizeResp = method.getAnnotation(CustomizeResponse::class.java) != null val customizeResp = method.getAnnotation(CustomizeResponse::class.java) != null
val role = method.getAnnotation(CheckRole::class.java) val role = method.getAnnotation(CheckRole::class.java)
val permissions = method.getAnnotation(CheckPermission::class.java) val permissions = method.getAnnotation(CheckPermission::class.java)
val kFunction = classType.kotlin.declaredFunctions.find { it.name == method.name }
if (kFunction != null) { // 查找对应的Kotlin函数
val parameterInfo = kFunction.parameters.mapNotNull { parameter -> classType.kotlin.declaredFunctions.find { it.name == method.name }?.let { kFunction ->
val parameterInfo = extractParameterInfo(kFunction)
routeInfoCache[reqPath to httpMethod] = RouteInfo(
classType, method, kFunction, parameterInfo,
customizeResp, role, permissions, httpMethod
)
}
}
}
/**
* 从Kotlin函数中提取参数信息
*/
private fun extractParameterInfo(kFunction: KFunction<*>): List<ParameterInfo> {
return kFunction.parameters.mapNotNull { parameter ->
val javaType = parameter.type.javaType val javaType = parameter.type.javaType
// 跳过协程的Continuation参数 // 跳过协程的Continuation参数
if (javaType is Class<*> && Continuation::class.java.isAssignableFrom(javaType)) { if (javaType is Class<*> && Continuation::class.java.isAssignableFrom(javaType)) {
return@mapNotNull null return@mapNotNull null
} }
parameter.name ?: return@mapNotNull null
// 参数必须具有名称
val paramName = parameter.name ?: return@mapNotNull null
// 从D注解获取自定义参数名
val annotation = parameter.annotations.find { it is D } as? D val annotation = parameter.annotations.find { it is D } as? D
val paramName = annotation?.name?.takeIf { it.isNotBlank() } ?: parameter.name ?: "" val finalParamName = annotation?.name?.takeIf { it.isNotBlank() } ?: paramName
// 确定参数类型
val typeClass = when (javaType) { val typeClass = when (javaType) {
is Class<*> -> javaType is Class<*> -> javaType
is ParameterizedType -> javaType.rawType as? Class<*> is ParameterizedType -> javaType.rawType as? Class<*>
else -> null else -> null
} } ?: parameter.type.javaType as Class<*>
// 处理枚举类型参数
val isEnum = typeClass.isEnum || parameter.type.javaType.javaClass.isEnum
val enumValueMethod = if (isEnum) {
typeClass.methods.find { it.isAnnotationPresent(EnumValue::class.java) }
} else null
val enumConstants = if (isEnum) {
typeClass.enumConstants?.associateBy({
enumValueMethod?.invoke(it)?.toString() ?: (it as Enum<*>).name
}, { it })
} else emptyMap()
// 检查是否是复杂类型
val isComplex = !parameter.type.classifier.toString().startsWith("class kotlin.") &&
!parameter.type.classifier.toString().startsWith("class io.vertx") &&
!typeClass.isEnum &&
!parameter.type.javaType.javaClass.isEnum &&
parameter.type.javaType is Class<*>
ParameterInfo( ParameterInfo(
name = paramName, name = finalParamName,
type = typeClass ?: parameter.type.javaType as Class<*>, type = typeClass,
isNullable = parameter.type.isMarkedNullable, isNullable = parameter.type.isMarkedNullable,
isList = parameter.type.classifier == List::class, isList = parameter.type.classifier == List::class,
isComplex = !parameter.type.classifier.toString().startsWith("class kotlin.") && isComplex = isComplex,
!parameter.type.classifier.toString().startsWith("class io.vertx") && isEnum = isEnum,
!(parameter.type.javaType as Class<*>).isEnum && enumValueMethod = enumValueMethod,
!parameter.type.javaType.javaClass.isEnum && enumConstants = enumConstants
parameter.type.javaType is Class<*>,
isEnum = typeClass?.isEnum ?: (parameter.type.javaType as Class<*>).isEnum
) )
} }
routeInfoCache[reqPath to httpMethod] = }
RouteInfo(classType, method, kFunction, parameterInfo, customizeResp, role, permissions)
/**
* 为缓存的路由注册路由处理程序
*/
private fun registerRouteHandlers(
routeInfoCache: Map<Pair<String, HttpMethod>, RouteInfo>,
getIt: (clazz: Class<*>) -> Any
) {
routeInfoCache.forEach { (pathMethod, routeInfo) ->
val (path, _) = pathMethod
router.route(routeInfo.httpMethod, path).handler { ctx ->
handleRequest(ctx, getIt, routeInfo)
} }
} }
} }
// 注册路由处理器 /**
routeInfoCache.forEach { (path, routeInfo) -> * 处理传入的HTTP请求
router.route(routeInfo.httpMethod, path.first).handler { ctx -> */
if (ctx.user() != null) { private fun handleRequest(ctx: RoutingContext, getIt: (clazz: Class<*>) -> Any, routeInfo: RouteInfo) {
val user = ctx.user() as AuthUser // 如果存在用户,检查授权
if (!user.validateAuth(routeInfo)) { ctx.user()?.let { user ->
ctx.fail(403, Meta.unauthorized("unauthorized")) if (user is AuthUser) {
return@handler try {
user.validateAuth(routeInfo.role, routeInfo.permissions)
} catch (e: Throwable) {
ctx.fail(403, Meta.unauthorized("未授权"))
return
} }
} }
}
// 获取控制器实例并执行方法
val instance = getIt(routeInfo.classType) val instance = getIt(routeInfo.classType)
buildLambda(ctx, instance, routeInfo) executeControllerMethod(ctx, instance, routeInfo)
}
}
return this
} }
private fun buildLambda(ctx: RoutingContext, instance: Any, routeInfo: RouteInfo) { /**
* 在协程中执行控制器方法
*/
private fun executeControllerMethod(ctx: RoutingContext, instance: Any, routeInfo: RouteInfo) {
coroutineScope.launch { coroutineScope.launch {
try { try {
val params = getParamsInstance(ctx, routeInfo.parameterInfo) val params = resolveMethodParameters(ctx, routeInfo.parameterInfo)
val resObj = if (routeInfo.kFunction.isSuspend) { val result = if (routeInfo.kFunction.isSuspend) {
routeInfo.kFunction.callSuspend(instance, *params) routeInfo.kFunction.callSuspend(instance, *params)
} else { } else {
routeInfo.kFunction.call(instance, *params) routeInfo.kFunction.call(instance, *params)
} }
responseHandler.normal(ctx, resObj, routeInfo.customizeResp) responseHandler.normal(ctx, result, routeInfo.customizeResp)
} catch (e: Throwable) { } catch (e: Throwable) {
responseHandler.exception(ctx, e) responseHandler.exception(ctx, e)
} }
} }
} }
companion object { /**
private val objectMapper = jacksonObjectMapper() * 从请求中解析方法参数
*/
private fun AuthUser.validateAuth(routeInfo: RouteInfo): Boolean { private fun resolveMethodParameters(ctx: RoutingContext, paramsInfo: List<ParameterInfo>): Array<Any?> {
// 如果没有权限要求直接返回true
if (routeInfo.role == null && routeInfo.permissions == null) return true
// 验证角色
val hasValidRole = routeInfo.role?.let { role ->
val roleSet = attributes().getJsonArray("role").toSet() as Set<String>
if (roleSet.isEmpty()) {
false
} else {
val reqRoleSet = (role.value + role.type).filter { it.isNotBlank() }.toSet()
validateSet(reqRoleSet, roleSet, role.mode)
}
} ?: true
// 验证权限
val hasValidPermission = routeInfo.permissions?.let { permissions ->
val permissionSet = attributes().getJsonArray("permissions").toSet() as Set<String>
val roleSet = attributes().getJsonArray("role").toSet() as Set<String>
if (permissionSet.isEmpty() && roleSet.isEmpty()) {
false
} else {
if (permissions.orRole.isNotEmpty()) {
val roleBoolean = validateSet(permissions.orRole.toSet(), roleSet, Mode.AND)
if (roleBoolean) return true
}
val reqPermissionSet = (permissions.value + permissions.type).filter { it.isNotBlank() }.toSet()
validateSet(reqPermissionSet, permissionSet, permissions.mode)
}
} ?: true
return hasValidRole && hasValidPermission
}
private fun validateSet(
required: Set<String>,
actual: Set<String>,
mode: Mode
): Boolean {
if (required.isEmpty()) return true
return when (mode) {
Mode.AND -> required == actual
Mode.OR -> required.any { it in actual }
}
}
private fun getReqPath(prefix: String, clazz: Class<*>, method: Method): String {
var classPath = if (prefix.isNotBlank()) {
StrUtil.toCamelCase(StrUtil.toUnderlineCase(prefix))
} else {
StrUtil.toCamelCase(StrUtil.toUnderlineCase(clazz.simpleName.removeSuffix("Controller")))
}
if (classPath == "/") classPath = ""
val methodName = StrUtil.toCamelCase(StrUtil.toUnderlineCase(method.name))
return "/$classPath/$methodName".replace("//", "/")
}
private fun getParamsInstance(ctx: RoutingContext, paramsInfo: List<ParameterInfo>): Array<Any?> {
val params = mutableListOf<Any?>() val params = mutableListOf<Any?>()
// 从不同来源收集参数
val formAttributes = ctx.request().formAttributes().associate { it.key to it.value } val formAttributes = ctx.request().formAttributes().associate { it.key to it.value }
val queryParams = ctx.queryParams().entries().associate { it.key to it.value } val queryParams = ctx.queryParams().entries().associate { it.key to it.value }
val combinedParams = formAttributes + queryParams val combinedParams = formAttributes + queryParams
// 解析Body
val bodyObj = if (!ctx.body().isEmpty) ctx.body().asJsonObject() else null // 解析请求体
val bodyObj = ctx.body().takeUnless { it.isEmpty }?.asJsonObject()
val bodyMap = bodyObj?.map ?: emptyMap() val bodyMap = bodyObj?.map ?: emptyMap()
// 处理每个参数
paramsInfo.forEach { param -> paramsInfo.forEach { param ->
if (param.isList) { when {
// 处理List类型参数
param.isList -> {
var value = ctx.queryParams().getAll(param.name) var value = ctx.queryParams().getAll(param.name)
if (value.isEmpty() && bodyMap[param.name] != null) { if (value.isEmpty() && bodyMap[param.name] != null) {
value = (bodyMap[param.name] as Collection<*>).map { it.toString() }.toMutableList() value = (bodyMap[param.name] as? Collection<*>)?.map { it.toString() }?.toMutableList() ?: mutableListOf()
} }
if (value.isEmpty() && !param.isNullable) { if (value.isEmpty() && !param.isNullable) {
throw IllegalArgumentException("Missing required parameter: ${param.name}") throw IllegalArgumentException("缺少必要参数: ${param.name}")
} }
params.add(value.ifEmpty { null }) params.add(value.ifEmpty { null })
return@forEach
} }
if (param.isEnum) { // 处理枚举类型参数
val value = sequenceOf( param.isEnum -> {
combinedParams[param.name], val value = combinedParams[param.name] ?: bodyMap[param.name]?.toString()
bodyMap[param.name] val enumValue = param.enumConstants?.get(value)
).filterNotNull().map { it.toString() }.firstOrNull() params.add(enumValue)
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) { // 处理复杂对象参数
param.isComplex -> {
try { try {
val value = sequenceOf( val value = sequenceOf(
if (paramsInfo.size == 1) bodyObj else null, if (paramsInfo.size == 1) bodyObj else null,
@ -229,68 +267,79 @@ class RouterBuilder(
combinedParams[param.name]?.let { JsonObject(it) }, combinedParams[param.name]?.let { JsonObject(it) },
bodyObj bodyObj
).filterNotNull().firstOrNull { !it.isEmpty } ).filterNotNull().firstOrNull { !it.isEmpty }
if (value?.isEmpty == true && !param.isNullable) { if (value?.isEmpty == true && !param.isNullable) {
throw IllegalArgumentException("Missing required parameter: ${param.name}") throw IllegalArgumentException("缺少必要参数: ${param.name}")
} }
params.add(if (value == null || value.isEmpty) null else JsonUtil.parseObject(value, param.type)) params.add(if (value == null || value.isEmpty) null else JsonUtil.parseObject(value, param.type))
return@forEach
} catch (e: Exception) { } catch (e: Exception) {
throw IllegalArgumentException(e.message, e) throw IllegalArgumentException(e.message, e)
} }
} }
params.add( // 处理特殊或基本类型参数
when (param.type) { else -> {
params.add(when (param.type) {
RoutingContext::class.java -> ctx RoutingContext::class.java -> ctx
User::class.java -> ctx.user() User::class.java -> ctx.user()
else -> { else -> {
val bodyValue = bodyMap[param.name] val bodyValue = bodyMap[param.name]
val paramValue = bodyValue?.toString() ?: combinedParams[param.name] val paramValue = bodyValue?.toString() ?: combinedParams[param.name]
when { when {
paramValue == null -> { paramValue == null -> {
if (!param.isNullable) throw IllegalArgumentException("Missing required parameter: ${param.name}") else null if (!param.isNullable) {
throw IllegalArgumentException("缺少必要参数: ${param.name}")
} else null
} }
else -> { else -> {
val value = getParamValue(paramValue.toString(), param.type) val value = convertStringToType(paramValue.toString(), param.type)
if (!param.isNullable && value == null) { if (!param.isNullable && value == null) {
throw IllegalArgumentException("Missing required parameter: ${param.name}") throw IllegalArgumentException("缺少必要参数: ${param.name}")
} else { } else value
value
} }
} }
} }
})
} }
} }
)
} }
return params.toTypedArray() return params.toTypedArray()
} }
companion object {
/** /**
* 将字符串参数值映射到目标类型 * 根据类和方法信息构造请求路径
*
* @param paramValue 参数的字符串值
* @param type 目标 [Class] 类型
* @return 转换为目标类型的参数值如果转换失败则返回 `null`
*/ */
private fun getParamValue(paramValue: String, type: Class<*>): Any? { private fun getReqPath(prefix: String, clazz: Class<*>, method: Method): String {
val classPath = if (prefix.isNotBlank()) {
StrUtil.toCamelCase(StrUtil.toUnderlineCase(prefix))
} else {
StrUtil.toCamelCase(StrUtil.toUnderlineCase(clazz.simpleName.removeSuffix("Controller")))
}.let { if (it == "/") "" else it }
val methodName = StrUtil.toCamelCase(StrUtil.toUnderlineCase(method.name))
return "/$classPath/$methodName".replace("//", "/")
}
/**
* 将字符串值转换为目标类型
*/
private fun convertStringToType(paramValue: String, type: Class<*>): Any? {
return when (type) { return when (type) {
String::class.java -> paramValue String::class.java -> paramValue
Int::class.java, Integer::class.java -> paramValue.toIntOrNull() Int::class.java, Integer::class.java -> paramValue.toIntOrNull()
Long::class.java, Long::class.java -> paramValue.toLongOrNull() Long::class.java, java.lang.Long::class.java -> paramValue.toLongOrNull()
Double::class.java, Double::class.java -> paramValue.toDoubleOrNull() Double::class.java, java.lang.Double::class.java -> paramValue.toDoubleOrNull()
Boolean::class.java, Boolean::class.java -> paramValue.toBoolean() Boolean::class.java, java.lang.Boolean::class.java -> paramValue.toBoolean()
else -> paramValue else -> paramValue
} }
} }
/** /**
* 根据 [CustomizeRequest] 注解确定给定 REST 方法的 HTTP 方法 * 确定控制器方法的HTTP方法
*
* @param method 目标方法
* @return 对应的 [HttpMethod]
*/ */
fun getHttpMethod(method: Method): HttpMethod { fun getHttpMethod(method: Method): HttpMethod {
val api = method.getAnnotation(CustomizeRequest::class.java) val api = method.getAnnotation(CustomizeRequest::class.java)
@ -306,33 +355,11 @@ class RouterBuilder(
HttpMethod.POST HttpMethod.POST
} }
} }
}
/** /**
* 将对象序列化为 JSON 表示 * 路由元数据数据类
*
* @param obj 要序列化的对象
* @return JSON 字符串
*/ */
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( private data class RouteInfo(
val classType: Class<*>, val classType: Class<*>,
val method: Method, val method: Method,
@ -341,15 +368,20 @@ class RouterBuilder(
val customizeResp: Boolean, val customizeResp: Boolean,
val role: CheckRole? = null, val role: CheckRole? = null,
val permissions: CheckPermission? = null, val permissions: CheckPermission? = null,
val httpMethod: HttpMethod = getHttpMethod(method) val httpMethod: HttpMethod
) )
/**
* 参数元数据数据类
*/
private data class ParameterInfo( private data class ParameterInfo(
val name: String, val name: String,
val type: Class<*>, val type: Class<*>,
val isNullable: Boolean, val isNullable: Boolean,
val isList: Boolean, val isList: Boolean,
val isComplex: Boolean, val isComplex: Boolean,
val isEnum: Boolean val isEnum: Boolean,
val enumValueMethod: Method? = null,
val enumConstants: Map<String, Any>? = null
) )
} }