feat(config): 重构配置管理

This commit is contained in:
AiKrai 2025-04-27 15:50:13 +08:00
parent e97f3f5519
commit e7016373c2
15 changed files with 306 additions and 122 deletions

3
.gitignore vendored
View File

@ -5,7 +5,8 @@ build/
!**/src/test/**/build/ !**/src/test/**/build/
/config /config
/gradle /gradle
log/ /logs
/.cursor
### IntelliJ IDEA ### ### IntelliJ IDEA ###
.idea .idea

View File

@ -1,21 +1,20 @@
package app.config package app.config
import app.config.auth.JWTAuthProvider import app.config.auth.JWTAuthProvider
import app.config.db.DbPoolProvider
import cn.hutool.core.lang.Snowflake import cn.hutool.core.lang.Snowflake
import cn.hutool.core.util.IdUtil import cn.hutool.core.util.IdUtil
import com.google.inject.* import com.google.inject.AbstractModule
import com.google.inject.name.Names import com.google.inject.Guice
import com.google.inject.Injector
import com.google.inject.Singleton
import io.vertx.core.Vertx import io.vertx.core.Vertx
import io.vertx.ext.auth.jwt.JWTAuth import io.vertx.ext.auth.jwt.JWTAuth
import io.vertx.pgclient.PgBuilder
import io.vertx.pgclient.PgConnectOptions
import io.vertx.sqlclient.Pool import io.vertx.sqlclient.Pool
import io.vertx.sqlclient.PoolOptions
import io.vertx.sqlclient.SqlClient import io.vertx.sqlclient.SqlClient
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import org.aikrai.vertx.config.Config
import org.aikrai.vertx.config.DefaultScope import org.aikrai.vertx.config.DefaultScope
import org.aikrai.vertx.db.tx.TxMgrHolder.initTxMgr import org.aikrai.vertx.config.FrameworkConfigModule
object InjectConfig { object InjectConfig {
fun configure(vertx: Vertx): Injector { fun configure(vertx: Vertx): Injector {
@ -27,44 +26,21 @@ class InjectorModule(
private val vertx: Vertx, private val vertx: Vertx,
) : AbstractModule() { ) : AbstractModule() {
override fun configure() { override fun configure() {
val pool = getDbPool().also { initTxMgr(it) } // 1. 安装框架提供的配置模块
val coroutineScope = DefaultScope(vertx) install(FrameworkConfigModule())
for ((key, value) in Config.getConfigMap()) { // 2. 绑定 Vertx 实例和 CoroutineScope
bind(String::class.java).annotatedWith(Names.named(key)).toInstance(value.toString())
}
bind(Vertx::class.java).toInstance(vertx) bind(Vertx::class.java).toInstance(vertx)
bind(CoroutineScope::class.java).toInstance(coroutineScope) bind(CoroutineScope::class.java).toInstance(DefaultScope(vertx))
// 3. 绑定 Snowflake
bind(Snowflake::class.java).toInstance(IdUtil.getSnowflake()) bind(Snowflake::class.java).toInstance(IdUtil.getSnowflake())
// 4. 绑定数据库连接池 (使用 Provider 来延迟创建)
bind(Pool::class.java).toProvider(DbPoolProvider::class.java).`in`(Singleton::class.java)
bind(SqlClient::class.java).to(Pool::class.java) // 绑定 SqlClient 到 Pool
// 5. 绑定 JWTAuth
bind(JWTAuth::class.java).toProvider(JWTAuthProvider::class.java).`in`(Singleton::class.java) bind(JWTAuth::class.java).toProvider(JWTAuthProvider::class.java).`in`(Singleton::class.java)
// 绑定 DbPool 为单例
bind(Pool::class.java).toInstance(pool)
bind(SqlClient::class.java).toInstance(pool)
} }
}
private fun getDbPool(): Pool {
// val type = configMap["databases.type"].toString()
// val name = configMap["databases.name"].toString()
// val host = configMap["databases.host"].toString()
// val port = configMap["databases.port"].toString()
// val user = configMap["databases.username"].toString()
// val password = configMap["databases.password"].toString()
// val dbMap = Config.getKey("databases") as Map<String, String>
val name = Config.getKey("databases.name").toString()
val host = Config.getKey("databases.host").toString()
val port = Config.getKey("databases.port").toString()
val user = Config.getKey("databases.username").toString()
val password = Config.getKey("databases.password").toString()
val poolOptions = PoolOptions().setMaxSize(10)
val clientOptions = PgConnectOptions()
.setHost(host)
.setPort(port.toInt())
.setDatabase(name)
.setUser(user)
.setPassword(password)
.setTcpKeepAlive(true)
return PgBuilder.pool().connectingTo(clientOptions).with(poolOptions).using(vertx).build()
}
}

View File

@ -2,22 +2,22 @@ package app.config.auth
import com.google.inject.Inject import com.google.inject.Inject
import com.google.inject.Provider import com.google.inject.Provider
import com.google.inject.name.Named
import io.vertx.core.Vertx import io.vertx.core.Vertx
import io.vertx.ext.auth.PubSecKeyOptions import io.vertx.ext.auth.PubSecKeyOptions
import io.vertx.ext.auth.jwt.JWTAuth import io.vertx.ext.auth.jwt.JWTAuth
import io.vertx.ext.auth.jwt.JWTAuthOptions import io.vertx.ext.auth.jwt.JWTAuthOptions
import org.aikrai.vertx.config.JwtConfig
class JWTAuthProvider @Inject constructor( class JWTAuthProvider @Inject constructor(
private val vertx: Vertx, private val vertx: Vertx,
@Named("jwt.key") private val key: String private val jwtConfig: JwtConfig
) : Provider<JWTAuth> { ) : Provider<JWTAuth> {
override fun get(): JWTAuth { override fun get(): JWTAuth {
val options = JWTAuthOptions() val options = JWTAuthOptions()
.addPubSecKey( .addPubSecKey(
PubSecKeyOptions() PubSecKeyOptions()
.setAlgorithm("HS256") .setAlgorithm(jwtConfig.algorithm)
.setBuffer(key) .setBuffer(jwtConfig.key)
) )
return JWTAuth.create(vertx, options) return JWTAuth.create(vertx, options)
} }

View File

@ -0,0 +1,43 @@
package app.config.db
import com.google.inject.Inject
import com.google.inject.Provider
import com.google.inject.Singleton
import io.vertx.core.Vertx
import io.vertx.pgclient.PgBuilder
import io.vertx.pgclient.PgConnectOptions
import io.vertx.sqlclient.Pool
import io.vertx.sqlclient.PoolOptions
import org.aikrai.vertx.config.DatabaseConfig
import org.aikrai.vertx.db.tx.TxMgrHolder
/**
* 数据库连接池提供者
*/
@Singleton
class DbPoolProvider @Inject constructor(
private val vertx: Vertx,
private val dbConfig: DatabaseConfig
) : Provider<Pool> {
override fun get(): Pool {
val poolOptions = PoolOptions().setMaxSize(dbConfig.maxPoolSize)
val clientOptions = PgConnectOptions()
.setHost(dbConfig.host)
.setPort(dbConfig.port)
.setDatabase(dbConfig.name)
.setUser(dbConfig.username)
.setPassword(dbConfig.password)
.setTcpKeepAlive(true)
val pool = PgBuilder.pool()
.connectingTo(clientOptions)
.with(poolOptions)
.using(vertx)
.build()
// 初始化事务管理器
TxMgrHolder.initTxMgr(pool)
return pool
}
}

View File

@ -59,7 +59,7 @@ class Demo1Controller @Inject constructor(
suspend fun testRetriever( suspend fun testRetriever(
@D("key", "key") key: String @D("key", "key") key: String
) { ) {
val configMap = Config.getKey(key) val configMap = Config.getStringOrNull(key)
println(configMap) println(configMap)
} }
} }

View File

@ -2,24 +2,25 @@ package app.port.aipfox
import app.util.openapi.OpenApiSpecGenerator import app.util.openapi.OpenApiSpecGenerator
import com.google.inject.Inject import com.google.inject.Inject
import com.google.inject.name.Named
import io.vertx.core.Vertx import io.vertx.core.Vertx
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.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.config.Config
class ApifoxClient @Inject constructor( class ApifoxClient @Inject constructor(
private val vertx: Vertx, private val vertx: Vertx,
@Named("apifox.token") private val token: String,
@Named("apifox.projectId") private val projectId: String,
@Named("apifox.folderId") private val folderId: String,
@Named("server.name") private val serverName: String,
@Named("server.port") private val port: String
) { ) {
private val logger = KotlinLogging.logger { } private val logger = KotlinLogging.logger { }
private val token = Config.getString("apifox.token", "")
private val projectId = Config.getString("apifox.projectId", "")
private val folderId = Config.getString("apifox.folderId", "")
private val serverName = Config.getString("server.name", "")
private val port = Config.getString("server.port", "")
fun importOpenapi() { fun importOpenapi() {
val openApiJsonStr = OpenApiSpecGenerator().genOpenApiSpecStr(serverName, "1.0", "http://127.0.0.1:$port/api") val openApiJsonStr = OpenApiSpecGenerator().genOpenApiSpecStr(serverName, "1.0", "http://127.0.0.1:$port/api")
val options = WebClientOptions().setDefaultPort(443).setDefaultHost("api.apifox.com").setSsl(true) val options = WebClientOptions().setDefaultPort(443).setDefaultHost("api.apifox.com").setSsl(true)

View File

@ -6,28 +6,23 @@ import io.vertx.core.Vertx
import io.vertx.kotlin.coroutines.coAwait import io.vertx.kotlin.coroutines.coAwait
import io.vertx.redis.client.* import io.vertx.redis.client.*
import mu.KotlinLogging import mu.KotlinLogging
import org.aikrai.vertx.config.Config import org.aikrai.vertx.config.RedisConfig
@Singleton @Singleton
class RedisClient @Inject constructor( class RedisClient @Inject constructor(
vertx: Vertx vertx: Vertx,
redisConfig: RedisConfig
) { ) {
private val logger = KotlinLogging.logger { } private val logger = KotlinLogging.logger { }
private val host = Config.getKey("redis.host").toString()
private val port = Config.getKey("redis.port").toString()
private val database = Config.getKey("redis.database").toString().toInt()
private val password = Config.getKey("redis.password").toString()
private val maxPoolSize = Config.getKey("redis.maxPoolSize").toString().toInt()
private val maxPoolWaiting = Config.getKey("redis.maxPoolWaiting").toString().toInt()
private var redisClient = Redis.createClient( private var redisClient = Redis.createClient(
vertx, vertx,
RedisOptions() RedisOptions()
.setType(RedisClientType.STANDALONE) .setType(RedisClientType.STANDALONE)
.addConnectionString("redis://$host:$port/$database") .addConnectionString("redis://${redisConfig.host}:${redisConfig.port}/${redisConfig.db}")
.setPassword(password) .setPassword(redisConfig.pass ?: "")
.setMaxPoolSize(maxPoolSize) .setMaxPoolSize(redisConfig.poolSize)
.setMaxPoolWaiting(maxPoolWaiting) .setMaxPoolWaiting(redisConfig.maxPoolWaiting)
) )
// EX秒PX毫秒 // EX秒PX毫秒

View File

@ -9,7 +9,6 @@ import app.port.aipfox.ApifoxClient
import cn.hutool.core.lang.Snowflake import cn.hutool.core.lang.Snowflake
import com.google.inject.Inject import com.google.inject.Inject
import com.google.inject.Injector import com.google.inject.Injector
import com.google.inject.name.Named
import io.vertx.core.Handler import io.vertx.core.Handler
import io.vertx.core.http.HttpHeaders import io.vertx.core.http.HttpHeaders
import io.vertx.core.http.HttpMethod import io.vertx.core.http.HttpMethod
@ -23,7 +22,7 @@ import io.vertx.kotlin.coroutines.coAwait
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import mu.KotlinLogging import mu.KotlinLogging
import org.aikrai.vertx.auth.AuthUser import org.aikrai.vertx.auth.AuthUser
import org.aikrai.vertx.config.Config import org.aikrai.vertx.config.ServerConfig
import org.aikrai.vertx.context.RouterBuilder import org.aikrai.vertx.context.RouterBuilder
import org.aikrai.vertx.jackson.JsonUtil import org.aikrai.vertx.jackson.JsonUtil
import org.aikrai.vertx.utlis.LangUtil.toStringMap import org.aikrai.vertx.utlis.LangUtil.toStringMap
@ -36,8 +35,7 @@ class WebVerticle @Inject constructor(
private val apifoxClient: ApifoxClient, private val apifoxClient: ApifoxClient,
private val snowflake: Snowflake, private val snowflake: Snowflake,
private val responseHandler: ResponseHandler, private val responseHandler: ResponseHandler,
@Named("server.port") private val port: Int, private val serverConfig: ServerConfig
@Named("server.context") private val context: String,
) : CoroutineVerticle() { ) : CoroutineVerticle() {
private val logger = KotlinLogging.logger { } private val logger = KotlinLogging.logger { }
@ -48,30 +46,29 @@ class WebVerticle @Inject constructor(
val options = HttpServerOptions().setMaxFormAttributeSize(1024 * 1024) val options = HttpServerOptions().setMaxFormAttributeSize(1024 * 1024)
val server = vertx.createHttpServer(options) val server = vertx.createHttpServer(options)
.requestHandler(rootRouter) .requestHandler(rootRouter)
.listen(port) .listen(serverConfig.port)
.coAwait() .coAwait()
apifoxClient.importOpenapi() apifoxClient.importOpenapi()
logger.info { "http server start - http://127.0.0.1:${server.actualPort()}/$context" } logger.info { "http server start - http://127.0.0.1:${server.actualPort()}${serverConfig.context}" }
} }
override suspend fun stop() { override suspend fun stop() {
} }
private fun setupRouter(rootRouter: Router, router: Router) { private fun setupRouter(rootRouter: Router, router: Router) {
rootRouter.route("/api" + "*").subRouter(router) rootRouter.route("${serverConfig.context}*").subRouter(router)
router.route() router.route()
.handler(corsHandler) .handler(corsHandler)
.handler(BodyHandler.create()) .handler(BodyHandler.create())
.handler(logHandler) .handler(logHandler)
.failureHandler(errorHandler) .failureHandler(errorHandler)
val authHandler = JwtAuthenticationHandler(coroutineScope, tokenService, context, snowflake) val authHandler = JwtAuthenticationHandler(coroutineScope, tokenService, serverConfig.context, snowflake)
router.route("/*").handler(authHandler) router.route("/*").handler(authHandler)
val scanPath = Config.getKeyAsString("server.package") val routerBuilder = RouterBuilder(coroutineScope, router, serverConfig.scanPackage, responseHandler).build { service ->
val routerBuilder = RouterBuilder(coroutineScope, router, scanPath, responseHandler).build { service ->
getIt.getInstance(service) getIt.getInstance(service)
} }
authHandler.anonymous.addAll(routerBuilder.anonymousPaths) authHandler.anonymous.addAll(routerBuilder.anonymousPaths)

View File

@ -1,15 +1,5 @@
server: server:
name: vtx_demo name: vtx_demo
context: api # 上下文 context: /api
timeout: 120 # eventbus超时时间 # active: dev
http: package: app
header: # header获取到的变量
- x-requested-with
- Access-Control-Allow-Origin
- origin
- Content-Type
- accept
event-bus:
timeout: 10000 # 毫秒
jwt:
key: 123456sdfjasdfjl # jwt加密key

View File

@ -0,0 +1,18 @@
application:
version: 1.0.0
server:
http:
header: # header获取到的变量
- x-requested-with
- Access-Control-Allow-Origin
- origin
- Content-Type
- accept
event-bus:
timeout: 10000 # 毫秒
jwt:
key: 123456sdfjasdfjl
algorithm: HS256
expiresInSeconds: 604800 # 7天

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<configuration <configuration
debug="false" scan="true" scanPeriod="30 second"> debug="false" scan="true" scanPeriod="30 second">
<property name="ROOT" value="./log/"/> <property name="ROOT" value="./logs/"/>
<property name="APPNAME" value="vertx-demo"/> <property name="APPNAME" value="vertx-demo"/>
<property name="FILESIZE" value="500MB"/> <property name="FILESIZE" value="500MB"/>
<property name="MAXHISTORY" value="100"/> <property name="MAXHISTORY" value="100"/>

View File

@ -3,7 +3,6 @@ package app
import org.aikrai.vertx.db.annotation.TableField import org.aikrai.vertx.db.annotation.TableField
import org.aikrai.vertx.db.annotation.TableId import org.aikrai.vertx.db.annotation.TableId
import org.aikrai.vertx.db.annotation.TableName import org.aikrai.vertx.db.annotation.TableName
import org.aikrai.vertx.db.annotation.TableIndex
import org.aikrai.vertx.db.annotation.EnumValue import org.aikrai.vertx.db.annotation.EnumValue
import org.aikrai.vertx.db.annotation.TableFieldComment import org.aikrai.vertx.db.annotation.TableFieldComment
import org.aikrai.vertx.db.migration.AnnotationMapping import org.aikrai.vertx.db.migration.AnnotationMapping
@ -98,10 +97,10 @@ object GenerateMigration {
) )
// 设置索引映射 // 设置索引映射
mapper.indexMapping = AnnotationMapping( // mapper.indexMapping = AnnotationMapping(
annotationClass = TableIndex::class, // annotationClass = TableIndex::class,
propertyName = "name" // propertyName = "name"
) // )
// 设置枚举值映射 // 设置枚举值映射
mapper.enumValueMapping = AnnotationMapping( mapper.enumValueMapping = AnnotationMapping(

View File

@ -0,0 +1,53 @@
package org.aikrai.vertx.config
/**
* 数据库配置
*/
data class DatabaseConfig(
val name: String,
val host: String,
val port: Int,
val username: String,
val password: String,
val maxPoolSize: Int = 10
)
/**
* Redis配置
*/
data class RedisConfig(
val host: String,
val port: Int,
val db: Int,
val pass: String?,
val poolSize: Int = 8,
val maxPoolWaiting: Int = 32
)
/**
* JWT配置
*/
data class JwtConfig(
val key: String,
val algorithm: String = "HS256",
val expiresInSeconds: Int = 60 * 60 * 24 * 7 // 7天
)
/**
* 服务器配置
*/
data class ServerConfig(
val port: Int,
val context: String,
val scanPackage: String
)
/**
* 框架配置
*/
data class FrameworkConfiguration(
val server: ServerConfig,
val database: DatabaseConfig,
val redis: RedisConfig,
val jwt: JwtConfig
)

View File

@ -12,7 +12,7 @@ import java.util.concurrent.atomic.AtomicReference
object Config { object Config {
private val retriever = AtomicReference<ConfigRetriever?>(null) private val retriever = AtomicReference<ConfigRetriever?>(null)
private var configMap = emptyMap<String, Any>() private val configMapRef = AtomicReference<Map<String, Any>>(emptyMap())
suspend fun init(vertx: Vertx) { suspend fun init(vertx: Vertx) {
if (retriever.get() != null) return if (retriever.get() != null) return
@ -20,38 +20,71 @@ object Config {
val cas = retriever.compareAndSet(null, configRetriever) val cas = retriever.compareAndSet(null, configRetriever)
if (cas) { if (cas) {
val configObj = configRetriever.config.coAwait() val configObj = configRetriever.config.coAwait()
configMap = FlattenUtil.flattenJsonObject(configObj) // 存储扁平化的 Map
configMapRef.set(FlattenUtil.flattenJsonObject(configObj))
} }
} }
fun getKey(key: String): Any? { fun getString(key: String, defaultValue: String): String {
if (retriever.get() == null) throw IllegalStateException("Config not initialized") return configMapRef.get()[key]?.toString() ?: defaultValue
// 检查 configMap 中是否存在指定的 key
return if (configMap.containsKey(key)) {
configMap[key]
} else {
// 找到所有以 key 开头的条目
val map = configMap.filterKeys { it.startsWith(key) }
// 如果没有找到任何匹配的条目,返回 null
return map.ifEmpty { null }
}
} }
fun getKeyAsString(key: String): String? { fun getStringOrNull(key: String): String? {
if (retriever.get() == null) throw IllegalStateException("Config not initialized") return configMapRef.get()[key]?.toString()
// 检查 configMap 中是否存在指定的 key }
return if (configMap.containsKey(key)) {
configMap[key].toString() fun getInt(key: String, defaultValue: Int): Int {
} else { return configMapRef.get()[key]?.toString()?.toIntOrNull() ?: defaultValue
// 找到所有以 key 开头的条目 }
val map = configMap.filterKeys { it.startsWith(key) }
// 如果没有找到任何匹配的条目,返回 null fun getIntOrNull(key: String): Int? {
if (map.isEmpty()) return null else map.toString() return configMapRef.get()[key]?.toString()?.toIntOrNull()
} }
fun getLong(key: String, defaultValue: Long): Long {
return configMapRef.get()[key]?.toString()?.toLongOrNull() ?: defaultValue
}
fun getLongOrNull(key: String): Long? {
return configMapRef.get()[key]?.toString()?.toLongOrNull()
}
fun getBoolean(key: String, defaultValue: Boolean): Boolean {
val value = configMapRef.get()[key]
return when (value) {
is Boolean -> value
is String -> value.toBooleanStrictOrNull() ?: defaultValue // toBooleanStrictOrNull 更安全
else -> defaultValue
}
}
fun getBooleanOrNull(key: String): Boolean? {
val value = configMapRef.get()[key]
return when (value) {
is Boolean -> value
is String -> value.toBooleanStrictOrNull()
else -> null
}
}
// 获取嵌套对象或列表
fun getObject(keyPrefix: String): Map<String, Any>? {
val map = configMapRef.get()
val subMap = map.filterKeys { it.startsWith("$keyPrefix.") }
.mapKeys { it.key.removePrefix("$keyPrefix.") }
return if (subMap.isEmpty()) null else subMap
}
fun getStringList(key: String, defaultValue: List<String> = emptyList()): List<String> {
return (configMapRef.get()[key] as? JsonArray)?.mapNotNull { it?.toString() } ?: defaultValue
}
fun getStringListOrNull(key: String): List<String>? {
return (configMapRef.get()[key] as? JsonArray)?.mapNotNull { it?.toString() }
} }
fun getConfigMap(): Map<String, Any> { fun getConfigMap(): Map<String, Any> {
return configMap return configMapRef.get()
} }
private suspend fun load(vertx: Vertx): ConfigRetriever { private suspend fun load(vertx: Vertx): ConfigRetriever {

View File

@ -0,0 +1,78 @@
package org.aikrai.vertx.config
import com.google.inject.AbstractModule
import com.google.inject.Provides
import com.google.inject.Singleton
/**
* 框架配置模块
*
* 负责使用增强后的Config对象读取配置并将其实例化为数据类进行绑定
*/
class FrameworkConfigModule : AbstractModule() {
override fun configure() {
// 这里不需要bind(Config::class.java)因为Config是object
}
@Provides
@Singleton
fun provideDatabaseConfig(): DatabaseConfig {
return DatabaseConfig(
name = Config.getString("databases.name", "default_db"),
host = Config.getString("databases.host", "localhost"),
port = Config.getInt("databases.port", 5432),
username = Config.getString("databases.username", "user"),
password = Config.getString("databases.password", "password"),
maxPoolSize = Config.getInt("databases.maxPoolSize", 10)
)
}
@Provides
@Singleton
fun provideRedisConfig(): RedisConfig {
return RedisConfig(
host = Config.getString("redis.host", "localhost"),
port = Config.getInt("redis.port", 6379),
db = Config.getInt("redis.database", 0),
pass = Config.getStringOrNull("redis.password"),
poolSize = Config.getInt("redis.maxPoolSize", 8),
maxPoolWaiting = Config.getInt("redis.maxPoolWaiting", 32)
)
}
@Provides
@Singleton
fun provideJwtConfig(): JwtConfig {
val key = Config.getStringOrNull("jwt.key")
?: throw IllegalStateException("缺少必要配置: jwt.key")
return JwtConfig(
key = key,
algorithm = Config.getString("jwt.algorithm", "HS256"),
expiresInSeconds = Config.getInt("jwt.expiresInSeconds", 60 * 60 * 24 * 7)
)
}
@Provides
@Singleton
fun provideServerConfig(): ServerConfig {
val scanPackage = Config.getStringOrNull("server.package")
?: throw IllegalStateException("缺少必要配置: server.package")
return ServerConfig(
port = Config.getInt("server.port", 8080),
context = Config.getString("server.context", "/api"),
scanPackage = scanPackage
)
}
@Provides
@Singleton
fun provideFrameworkConfiguration(
server: ServerConfig,
database: DatabaseConfig,
redis: RedisConfig,
jwt: JwtConfig
): FrameworkConfiguration {
return FrameworkConfiguration(server, database, redis, jwt)
}
}