feat(db): 修改事务管理
This commit is contained in:
parent
098fa02ca6
commit
dff4e2d0a5
@ -21,10 +21,6 @@ class ResponseHandler: ResponseHandlerInterface {
|
|||||||
val requestId = ctx.get<Long>("requestId") ?: -1L
|
val requestId = ctx.get<Long>("requestId") ?: -1L
|
||||||
val code: Int
|
val code: Int
|
||||||
val resStr = when (responseData) {
|
val resStr = when (responseData) {
|
||||||
is Unit -> {
|
|
||||||
code = HttpStatus.NO_CONTENT
|
|
||||||
null
|
|
||||||
}
|
|
||||||
is RespBean -> {
|
is RespBean -> {
|
||||||
code = responseData.code
|
code = responseData.code
|
||||||
responseData.requestId = requestId
|
responseData.requestId = requestId
|
||||||
@ -54,6 +50,7 @@ class ResponseHandler: ResponseHandlerInterface {
|
|||||||
is Meta -> {
|
is Meta -> {
|
||||||
RespBean.failure("${e.name}:${e.message}", e.data)
|
RespBean.failure("${e.name}:${e.message}", e.data)
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
RespBean.failure("${e.javaClass.simpleName}${if (e.message != null) ":${e.message}" else ""}")
|
RespBean.failure("${e.javaClass.simpleName}${if (e.message != null) ":${e.message}" else ""}")
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import app.domain.account.modle.AccountRoleAccessDTO
|
|||||||
import com.google.inject.Inject
|
import com.google.inject.Inject
|
||||||
import io.vertx.sqlclient.SqlClient
|
import io.vertx.sqlclient.SqlClient
|
||||||
import org.aikrai.vertx.db.RepositoryImpl
|
import org.aikrai.vertx.db.RepositoryImpl
|
||||||
|
import org.aikrai.vertx.db.tx.withTransaction
|
||||||
|
|
||||||
class AccountRepositoryImpl @Inject constructor(
|
class AccountRepositoryImpl @Inject constructor(
|
||||||
sqlClient: SqlClient
|
sqlClient: SqlClient
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import cn.hutool.core.lang.Snowflake
|
|||||||
import cn.hutool.crypto.SecureUtil
|
import cn.hutool.crypto.SecureUtil
|
||||||
import com.google.inject.Inject
|
import com.google.inject.Inject
|
||||||
import io.vertx.ext.web.RoutingContext
|
import io.vertx.ext.web.RoutingContext
|
||||||
|
import mu.KotlinLogging
|
||||||
import org.aikrai.vertx.db.tx.withTransaction
|
import org.aikrai.vertx.db.tx.withTransaction
|
||||||
import org.aikrai.vertx.utlis.IpUtil
|
import org.aikrai.vertx.utlis.IpUtil
|
||||||
import org.aikrai.vertx.utlis.Meta
|
import org.aikrai.vertx.utlis.Meta
|
||||||
@ -18,11 +19,20 @@ class AccountService @Inject constructor(
|
|||||||
private val accountRepository: AccountRepository,
|
private val accountRepository: AccountRepository,
|
||||||
private val tokenService: TokenService,
|
private val tokenService: TokenService,
|
||||||
) {
|
) {
|
||||||
|
private val logger = KotlinLogging.logger { }
|
||||||
|
|
||||||
suspend fun testTransaction() {
|
suspend fun testTransaction() {
|
||||||
withTransaction {
|
withTransaction {
|
||||||
accountRepository.update(1L, mapOf("avatar" to "test001"))
|
accountRepository.update(1L, mapOf("avatar" to "test0001"))
|
||||||
// throw Meta.failure("test transaction", "test transaction")
|
|
||||||
|
try {
|
||||||
|
withTransaction {
|
||||||
accountRepository.update(1L, mapOf("avatar" to "test002"))
|
accountRepository.update(1L, mapOf("avatar" to "test002"))
|
||||||
|
throw Meta.error("test transaction", "test transaction")
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
logger.info { "内层事务失败已处理: ${e.message}" }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -9,7 +9,7 @@ import io.vertx.sqlclient.*
|
|||||||
import io.vertx.sqlclient.templates.SqlTemplate
|
import io.vertx.sqlclient.templates.SqlTemplate
|
||||||
import mu.KotlinLogging
|
import mu.KotlinLogging
|
||||||
import org.aikrai.vertx.db.annotation.*
|
import org.aikrai.vertx.db.annotation.*
|
||||||
import org.aikrai.vertx.db.tx.TxCtx
|
import org.aikrai.vertx.db.tx.TxCtxElem
|
||||||
import org.aikrai.vertx.jackson.JsonUtil
|
import org.aikrai.vertx.jackson.JsonUtil
|
||||||
import org.aikrai.vertx.utlis.Meta
|
import org.aikrai.vertx.utlis.Meta
|
||||||
import java.lang.reflect.Field
|
import java.lang.reflect.Field
|
||||||
@ -171,9 +171,6 @@ open class RepositoryImpl<TId, TEntity : Any>(
|
|||||||
"DELETE FROM $tableName WHERE $idFieldName = #{id}"
|
"DELETE FROM $tableName WHERE $idFieldName = #{id}"
|
||||||
}
|
}
|
||||||
val params = mapOf("id" to id)
|
val params = mapOf("id" to id)
|
||||||
if (logger.isDebugEnabled) {
|
|
||||||
logger.debug { "SQL: $sqlTemplate, PARAMS: $params" }
|
|
||||||
}
|
|
||||||
return execute(sqlTemplate, params)
|
return execute(sqlTemplate, params)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
logger.error(e) { "Error deleting entity with id: $id" }
|
logger.error(e) { "Error deleting entity with id: $id" }
|
||||||
@ -190,7 +187,6 @@ open class RepositoryImpl<TId, TEntity : Any>(
|
|||||||
"UPDATE $tableName SET $setClause WHERE $idFieldName = #{id}"
|
"UPDATE $tableName SET $setClause WHERE $idFieldName = #{id}"
|
||||||
}
|
}
|
||||||
val params = getNonNullFields(t) + mapOf("id" to idField.get(t))
|
val params = getNonNullFields(t) + mapOf("id" to idField.get(t))
|
||||||
logger.debug { "SQL: $sqlTemplate, PARAMS: $params" }
|
|
||||||
return execute(sqlTemplate, params)
|
return execute(sqlTemplate, params)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
logger.error(e) { "Error updating entity: $t" }
|
logger.error(e) { "Error updating entity: $t" }
|
||||||
@ -206,7 +202,6 @@ open class RepositoryImpl<TId, TEntity : Any>(
|
|||||||
"UPDATE $tableName SET $setClause WHERE $idFieldName = #{id}"
|
"UPDATE $tableName SET $setClause WHERE $idFieldName = #{id}"
|
||||||
}
|
}
|
||||||
val params = parameters + mapOf("id" to id)
|
val params = parameters + mapOf("id" to id)
|
||||||
logger.debug { "SQL: $sqlTemplate, PARAMS: $params" }
|
|
||||||
return execute(sqlTemplate, params)
|
return execute(sqlTemplate, params)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
logger.error(e) { "Error updating entity with id: $id" }
|
logger.error(e) { "Error updating entity with id: $id" }
|
||||||
@ -236,7 +231,6 @@ open class RepositoryImpl<TId, TEntity : Any>(
|
|||||||
"SELECT $columns FROM $tableName WHERE $field = #{value}"
|
"SELECT $columns FROM $tableName WHERE $field = #{value}"
|
||||||
}
|
}
|
||||||
val params = mapOf("value" to value)
|
val params = mapOf("value" to value)
|
||||||
logger.debug { "SQL: $sqlTemplate, PARAMS: $params" }
|
|
||||||
return get(sqlTemplate, params, clazz)
|
return get(sqlTemplate, params, clazz)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
logger.error(e) { "Error getting entity by field: $field = $value" }
|
logger.error(e) { "Error getting entity by field: $field = $value" }
|
||||||
@ -252,7 +246,6 @@ open class RepositoryImpl<TId, TEntity : Any>(
|
|||||||
"SELECT $columns FROM $tableName WHERE ${fieldMappings[field.name]} = #{value}"
|
"SELECT $columns FROM $tableName WHERE ${fieldMappings[field.name]} = #{value}"
|
||||||
}
|
}
|
||||||
val params = mapOf("value" to value)
|
val params = mapOf("value" to value)
|
||||||
logger.debug { "SQL: $sql, PARAMS: $params" }
|
|
||||||
return get(sql, params, clazz)
|
return get(sql, params, clazz)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
logger.error(e) { "Error getting entity by field: ${field.name} = $value" }
|
logger.error(e) { "Error getting entity by field: ${field.name} = $value" }
|
||||||
@ -308,7 +301,7 @@ open class RepositoryImpl<TId, TEntity : Any>(
|
|||||||
.execute(params)
|
.execute(params)
|
||||||
.coAwait()
|
.coAwait()
|
||||||
.rowCount()
|
.rowCount()
|
||||||
} catch (e: Exception) {
|
} catch (e: Throwable) {
|
||||||
logger.error(e) { "Error executing SQL: $sql, PARAMS: $params" }
|
logger.error(e) { "Error executing SQL: $sql, PARAMS: $params" }
|
||||||
throw Meta.repository(e.javaClass.simpleName, e.message)
|
throw Meta.repository(e.javaClass.simpleName, e.message)
|
||||||
}
|
}
|
||||||
@ -326,20 +319,14 @@ open class RepositoryImpl<TId, TEntity : Any>(
|
|||||||
|
|
||||||
// 其他工具方法
|
// 其他工具方法
|
||||||
private suspend fun getConnection(): SqlClient {
|
private suspend fun getConnection(): SqlClient {
|
||||||
return if (TxCtx.isTransactionActive(coroutineContext)) {
|
val txElem = coroutineContext[TxCtxElem]
|
||||||
TxCtx.currentSqlConnection(coroutineContext) ?: run {
|
return txElem?.connection ?: sqlClient
|
||||||
logger.error("TransactionContextElement.sqlConnection is null")
|
|
||||||
return sqlClient
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
sqlClient
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 通用获取或创建 SQL 模板的方法
|
// 通用获取或创建 SQL 模板的方法
|
||||||
private fun getOrCreateSql(tableName: String, key: String, sqlProvider: () -> String): String {
|
private fun getOrCreateSql(tableName: String, sqlKey: String, generator: () -> String): String {
|
||||||
val tableSqlMap = baseSqlCache.getOrPut(tableName) { ConcurrentHashMap() }
|
return baseSqlCache.computeIfAbsent(tableName) { ConcurrentHashMap() }
|
||||||
return tableSqlMap.getOrPut(key, sqlProvider)
|
.computeIfAbsent(sqlKey) { generator() }
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取非空字段及其值
|
// 获取非空字段及其值
|
||||||
|
|||||||
@ -1,41 +1,80 @@
|
|||||||
package org.aikrai.vertx.db.tx
|
package org.aikrai.vertx.db.tx
|
||||||
|
|
||||||
|
import io.vertx.kotlin.coroutines.coAwait
|
||||||
|
import io.vertx.sqlclient.SqlClient
|
||||||
import io.vertx.sqlclient.SqlConnection
|
import io.vertx.sqlclient.SqlConnection
|
||||||
import io.vertx.sqlclient.Transaction
|
import io.vertx.sqlclient.Transaction
|
||||||
|
import org.aikrai.vertx.utlis.Meta
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.coroutines.CoroutineContext
|
import kotlin.coroutines.CoroutineContext
|
||||||
|
import kotlin.coroutines.coroutineContext
|
||||||
|
|
||||||
class TxCtxElem(
|
class TxCtxElem(
|
||||||
val sqlConnection: SqlConnection,
|
val connection: SqlConnection,
|
||||||
val transaction: Transaction,
|
val transaction: Transaction?, // 外层事务才有实际transaction对象
|
||||||
val isActive: Boolean = true,
|
val savepointName: String? = null, // 内层事务使用savepoint名称
|
||||||
val isNested: Boolean = false,
|
val depth: Int = 0, // 事务嵌套深度
|
||||||
val transactionStack: Stack<TxCtxElem>,
|
|
||||||
val index: Int = transactionStack.size,
|
|
||||||
val transactionId: String = UUID.randomUUID().toString()
|
|
||||||
) : CoroutineContext.Element {
|
) : CoroutineContext.Element {
|
||||||
companion object Key : CoroutineContext.Key<TxCtxElem>
|
companion object Key : CoroutineContext.Key<TxCtxElem>
|
||||||
override val key: CoroutineContext.Key<*> = Key
|
override val key: CoroutineContext.Key<*> = Key
|
||||||
|
|
||||||
override fun toString(): String {
|
val isRoot: Boolean = depth == 0
|
||||||
return "TransactionContextElement(transactionId=$transactionId, isActive=$isActive, isNested=$isNested)"
|
val isNested: Boolean = depth > 0
|
||||||
}
|
val transactionId: String = UUID.randomUUID().toString().substring(0, 8)
|
||||||
|
|
||||||
|
// 标记是否已回滚或提交
|
||||||
|
var completed: Boolean = false
|
||||||
}
|
}
|
||||||
|
|
||||||
object TxCtx {
|
object TxCtx {
|
||||||
fun getTransactionId(context: CoroutineContext): String? {
|
/**
|
||||||
return context[TxCtxElem.Key]?.transactionId
|
* 判断当前是否在事务上下文中
|
||||||
}
|
*/
|
||||||
|
|
||||||
fun currentTransaction(context: CoroutineContext): Transaction? {
|
|
||||||
return context[TxCtxElem.Key]?.transaction
|
|
||||||
}
|
|
||||||
|
|
||||||
fun currentSqlConnection(context: CoroutineContext): SqlConnection? {
|
|
||||||
return context[TxCtxElem.Key]?.sqlConnection
|
|
||||||
}
|
|
||||||
|
|
||||||
fun isTransactionActive(context: CoroutineContext): Boolean {
|
fun isTransactionActive(context: CoroutineContext): Boolean {
|
||||||
return context[TxCtxElem.Key]?.isActive ?: false
|
return context[TxCtxElem] != null
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前事务的连接
|
||||||
|
*/
|
||||||
|
fun currentSqlConnection(context: CoroutineContext): SqlClient? {
|
||||||
|
return context[TxCtxElem]?.connection
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前事务深度
|
||||||
|
*/
|
||||||
|
fun currentTransactionDepth(context: CoroutineContext): Int {
|
||||||
|
return context[TxCtxElem]?.depth ?: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 手动控制设置当前事务回滚点
|
||||||
|
*/
|
||||||
|
suspend fun setSavepoint(name: String): String {
|
||||||
|
val context = coroutineContext
|
||||||
|
val txElem = context[TxCtxElem] ?: throw Meta.error(
|
||||||
|
"TransactionError",
|
||||||
|
"Cannot set savepoint. No active transaction."
|
||||||
|
)
|
||||||
|
|
||||||
|
val connection = txElem.connection
|
||||||
|
val pointName = "manual_$name"
|
||||||
|
connection.query("SAVEPOINT $pointName").execute().coAwait()
|
||||||
|
return pointName
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 手动回滚到指定保存点
|
||||||
|
*/
|
||||||
|
suspend fun rollbackToSavepoint(name: String) {
|
||||||
|
val context = coroutineContext
|
||||||
|
val txElem = context[TxCtxElem] ?: throw Meta.error(
|
||||||
|
"TransactionError",
|
||||||
|
"Cannot rollback to savepoint. No active transaction."
|
||||||
|
)
|
||||||
|
val connection = txElem.connection
|
||||||
|
connection.query("ROLLBACK TO SAVEPOINT $name").execute().coAwait()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -40,65 +40,90 @@ object TxMgrHolder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class TxMgr(
|
class TxMgr(private val pool: Pool) {
|
||||||
private val pool: Pool
|
|
||||||
) {
|
|
||||||
|
|
||||||
private val logger = KotlinLogging.logger { }
|
private val logger = KotlinLogging.logger { }
|
||||||
private val transactionStackMap = ConcurrentHashMap<CoroutineContext, Stack<TxCtxElem>>()
|
|
||||||
|
|
||||||
/**
|
suspend fun <T> withTransaction(block: suspend CoroutineScope.() -> T): T {
|
||||||
* 在事务上下文中执行一个块。
|
|
||||||
*
|
|
||||||
* @param block 需要在事务中执行的挂起函数。
|
|
||||||
* @return 块的结果。
|
|
||||||
*/
|
|
||||||
suspend fun <T> withTransaction(block: suspend CoroutineScope.() -> T): Any? {
|
|
||||||
val currentContext = coroutineContext
|
val currentContext = coroutineContext
|
||||||
val transactionStack = currentContext[TxCtxElem]?.transactionStack ?: Stack<TxCtxElem>()
|
val currentTx = currentContext[TxCtxElem]
|
||||||
// 外层事务,嵌套事务,都创建新的连接和事务。实现外层事务回滚时所有嵌套事务回滚,嵌套事务回滚不影响外部事务
|
|
||||||
val connection: SqlConnection = pool.connection.coAwait()
|
|
||||||
val transaction: Transaction = connection.begin().coAwait()
|
|
||||||
|
|
||||||
return try {
|
// 已在事务中 - 创建SAVEPOINT
|
||||||
val txCtxElem =
|
if (currentTx != null) {
|
||||||
TxCtxElem(connection, transaction, true, transactionStack.isNotEmpty(), transactionStack)
|
return withSavepoint(currentTx, block)
|
||||||
transactionStack.push(txCtxElem)
|
}
|
||||||
logger.debug { (if (txCtxElem.isNested) "嵌套" else "") + "事务Id:" + txCtxElem.transactionId + "开始" }
|
|
||||||
|
|
||||||
withContext(currentContext + txCtxElem) {
|
// 外层事务 - 创建实际事务
|
||||||
val result = block()
|
val connection = pool.connection.coAwait()
|
||||||
if (txCtxElem.index == 0) {
|
val transaction = connection.begin().coAwait()
|
||||||
while (transactionStack.isNotEmpty()) {
|
val startTime = System.currentTimeMillis()
|
||||||
val txCtx = transactionStack.pop()
|
|
||||||
txCtx.transaction.commit().coAwait()
|
try {
|
||||||
logger.debug { (if (txCtx.isNested) "嵌套" else "") + "事务Id:" + txCtx.transactionId + "提交" }
|
// 创建根事务上下文
|
||||||
|
val txElem = TxCtxElem(connection, transaction, depth = 0)
|
||||||
|
logger.debug { "Root transaction ${txElem.transactionId} started" }
|
||||||
|
|
||||||
|
val result = withContext(currentContext + txElem) {
|
||||||
|
block()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 提交事务
|
||||||
|
if (!txElem.completed) {
|
||||||
|
transaction.commit().coAwait()
|
||||||
|
txElem.completed = true
|
||||||
|
logger.debug { "Root transaction ${txElem.transactionId} committed, took ${System.currentTimeMillis() - startTime}ms" }
|
||||||
}
|
}
|
||||||
result
|
return result
|
||||||
}
|
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
logger.error(e) { "Transaction failed, rollback" }
|
logger.error(e) { "Root transaction failed, rolling back" }
|
||||||
if (transactionStack.isNotEmpty() && !transactionStack.peek().isNested) {
|
|
||||||
// 外层事务失败,回滚所有事务
|
transaction.rollback().coAwait()
|
||||||
logger.error { "Rolling back all transactions" }
|
|
||||||
while (transactionStack.isNotEmpty()) {
|
|
||||||
val txCtxElem = transactionStack.pop()
|
|
||||||
txCtxElem.transaction.rollback().coAwait()
|
|
||||||
logger.debug { (if (txCtxElem.isNested) "嵌套" else "") + "事务Id:" + txCtxElem.transactionId + "回滚" }
|
|
||||||
}
|
|
||||||
throw e
|
throw e
|
||||||
} else {
|
|
||||||
// 嵌套事务失败,只回滚当前事务
|
|
||||||
val txCtxElem = transactionStack.pop()
|
|
||||||
txCtxElem.transaction.rollback().coAwait()
|
|
||||||
logger.debug(e) { (if (txCtxElem.isNested) "嵌套" else "") + "事务Id:" + txCtxElem.transactionId + "回滚" }
|
|
||||||
}
|
|
||||||
} finally {
|
} finally {
|
||||||
if (transactionStack.isEmpty()) {
|
connection.close()
|
||||||
transactionStackMap.remove(currentContext) // 清理上下文
|
}
|
||||||
connection.close() // 仅在外层事务时关闭连接
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
private suspend fun <T> withSavepoint(
|
||||||
|
parentTx: TxCtxElem,
|
||||||
|
block: suspend CoroutineScope.() -> T
|
||||||
|
): T {
|
||||||
|
val connection = parentTx.connection
|
||||||
|
val savepointName = "sp_${UUID.randomUUID().toString().replace("-", "").substring(0, 10)}"
|
||||||
|
val startTime = System.currentTimeMillis()
|
||||||
|
|
||||||
|
// 创建保存点
|
||||||
|
connection.query("SAVEPOINT $savepointName").execute().coAwait()
|
||||||
|
logger.debug { "Nested transaction with savepoint $savepointName started" }
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 创建嵌套事务上下文
|
||||||
|
val nestedTxElem = TxCtxElem(
|
||||||
|
connection = connection,
|
||||||
|
transaction = null, // 嵌套事务没有独立的Transaction对象
|
||||||
|
savepointName = savepointName,
|
||||||
|
depth = parentTx.depth + 1,
|
||||||
|
)
|
||||||
|
|
||||||
|
val result = withContext(coroutineContext + nestedTxElem) {
|
||||||
|
block()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 嵌套事务成功,释放保存点
|
||||||
|
if (!nestedTxElem.completed) {
|
||||||
|
connection.query("RELEASE SAVEPOINT $savepointName").execute().coAwait()
|
||||||
|
nestedTxElem.completed = true
|
||||||
|
logger.debug { "Savepoint $savepointName released, took ${System.currentTimeMillis() - startTime}ms" }
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
|
||||||
|
} catch (e: Exception) {
|
||||||
|
logger.warn(e) { "Nested transaction failed, rolling back to savepoint $savepointName" }
|
||||||
|
// 回滚到保存点,但不影响外层事务
|
||||||
|
connection.query("ROLLBACK TO SAVEPOINT $savepointName").execute().coAwait()
|
||||||
|
throw e
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -113,15 +113,29 @@ class OpenApiSpecGenerator {
|
|||||||
*/
|
*/
|
||||||
private fun generatePaths(): Paths {
|
private fun generatePaths(): Paths {
|
||||||
val paths = Paths()
|
val paths = Paths()
|
||||||
|
val pathInfoMap = mutableMapOf<String, Pair<String, PathItem>>()
|
||||||
// 获取所有带有 @Controller 注解的类
|
// 获取所有带有 @Controller 注解的类
|
||||||
val packageName = ClassUtil.getMainClass()?.packageName
|
val packageName = ClassUtil.getMainClass().packageName
|
||||||
val controllerClassSet = Reflections(packageName).getTypesAnnotatedWith(Controller::class.java)
|
val controllerClassSet = Reflections(packageName).getTypesAnnotatedWith(Controller::class.java)
|
||||||
ClassUtil.getPublicMethods(controllerClassSet).forEach { (controllerClass, methods) ->
|
ClassUtil.getPublicMethods(controllerClassSet).forEach { (controllerClass, methods) ->
|
||||||
val controllerInfo = extractControllerInfo(controllerClass)
|
val controllerInfo = extractControllerInfo(controllerClass)
|
||||||
methods.forEach { method ->
|
methods.forEach { method ->
|
||||||
val pathInfo = generatePathInfo(method, controllerInfo)
|
val pathInfo = generatePathInfo(method, controllerInfo)
|
||||||
paths.addPathItem(pathInfo.path, pathInfo.pathItem)
|
if (!pathInfo.pathItem.post?.tags?.first().isNullOrBlank()) {
|
||||||
|
pathInfoMap[pathInfo.path] = Pair(pathInfo.pathItem.post.tags.first(), pathInfo.pathItem)
|
||||||
}
|
}
|
||||||
|
if (!pathInfo.pathItem.get?.tags?.first().isNullOrBlank()) {
|
||||||
|
pathInfoMap[pathInfo.path] = Pair(pathInfo.pathItem.get.tags.first(), pathInfo.pathItem)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val sortedMap = pathInfoMap.toList()
|
||||||
|
.sortedBy { it.second.second.post?.summary }
|
||||||
|
.sortedBy { it.second.second.get?.summary }
|
||||||
|
.sortedBy { it.second.first }
|
||||||
|
.toMap()
|
||||||
|
for ((key, value) in sortedMap) {
|
||||||
|
paths[key] = value.second
|
||||||
}
|
}
|
||||||
return paths
|
return paths
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user