feat: 优化配置报错
This commit is contained in:
parent
f3af0b7134
commit
aafd7059bc
|
|
@ -3,5 +3,5 @@ org.gradle.downloadSources=false
|
|||
org.gradle.parallel=true
|
||||
org.gradle.degree_of_parallelism=16
|
||||
project_group=top.r3944realms.ltdmanager
|
||||
project_version=1.22-SNAPSHOT
|
||||
project_version=1.22
|
||||
dg_lab_version=4.4.14.19
|
||||
|
|
|
|||
|
|
@ -3,34 +3,34 @@ package top.r3944realms.ltdmanager.core.config
|
|||
import org.yaml.snakeyaml.LoaderOptions
|
||||
import org.yaml.snakeyaml.Yaml
|
||||
import org.yaml.snakeyaml.constructor.Constructor
|
||||
import org.yaml.snakeyaml.error.YAMLException
|
||||
import org.yaml.snakeyaml.introspector.Property
|
||||
import org.yaml.snakeyaml.introspector.PropertyUtils
|
||||
import top.r3944realms.ltdmanager.utils.ConfigInitializer
|
||||
import top.r3944realms.ltdmanager.utils.LoggerUtil
|
||||
import top.r3944realms.ltdmanager.utils.NamingConventionUtil
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.Paths
|
||||
|
||||
object YamlConfigLoader {
|
||||
val appConfigFilePath: Path = Paths.get("config/application.yaml") // 配置文件路径
|
||||
val moduleConfigFilePath: Path = Paths.get("config/module.yaml") // 配置文件路径
|
||||
private val _app_config by lazy { loadAppConfigWrapper() } // 延迟初始化
|
||||
val appConfigFilePath: Path = Paths.get("config/application.yaml")
|
||||
val moduleConfigFilePath: Path = Paths.get("config/module.yaml")
|
||||
private val _app_config by lazy { loadAppConfigWrapper() }
|
||||
val appConfig: AppConfigWrapper get() = _app_config
|
||||
private val _module_config by lazy { loadModuleConfigWrapper() } // 延迟初始化
|
||||
private val _module_config by lazy { loadModuleConfigWrapper() }
|
||||
val moduleConfig: ModuleConfigWrapper get() = _module_config
|
||||
|
||||
init {
|
||||
// 第一次启动确保配置文件存在
|
||||
ConfigInitializer.initConfig("module.yaml", "config", false)
|
||||
ConfigInitializer.initConfig("application.yaml", "config")
|
||||
// 初始化后加密(确保只执行一次)
|
||||
runCatching {
|
||||
ensureConfigEncrypted(_app_config)
|
||||
}.onFailure { e ->
|
||||
println("初始化加密失败: ${e.message}")
|
||||
e.printStackTrace()
|
||||
LoggerUtil.syncError("配置加密失败: ${e.message?.lineSequence()?.firstOrNull() ?: e.message}")
|
||||
}
|
||||
}
|
||||
|
||||
private fun ensureConfigEncrypted(config: AppConfigWrapper?) {
|
||||
config?.database?.encryptPassword()
|
||||
config?.websocket?.encryptToken()
|
||||
|
|
@ -43,47 +43,90 @@ object YamlConfigLoader {
|
|||
config?.imgTu?.encryptPassword()
|
||||
config?.whitelistSystem?.encryptToken()
|
||||
}
|
||||
|
||||
private fun loadAppConfigWrapper(): AppConfigWrapper {
|
||||
if (!Files.exists(appConfigFilePath)) {
|
||||
throw RuntimeException("应用配置文件未找到: $appConfigFilePath")
|
||||
}
|
||||
|
||||
try {
|
||||
val yamlContent = Files.readString(appConfigFilePath)
|
||||
|
||||
return Yaml(getConstructor(AppConfigWrapper::class.java)).load(yamlContent)
|
||||
?: throw RuntimeException("YAML解析返回null")
|
||||
|
||||
} catch (e: Exception) {
|
||||
throw RuntimeException("YAML解析失败: ${e.message}", e)
|
||||
return loadYaml(appConfigFilePath, AppConfigWrapper::class.java)
|
||||
}
|
||||
|
||||
}
|
||||
private fun loadModuleConfigWrapper(): ModuleConfigWrapper {
|
||||
if (!Files.exists(moduleConfigFilePath)) {
|
||||
throw RuntimeException("模块配置文件未找到: $moduleConfigFilePath")
|
||||
}
|
||||
return loadYaml(moduleConfigFilePath, ModuleConfigWrapper::class.java)
|
||||
}
|
||||
|
||||
try {
|
||||
val yamlContent = Files.readString(moduleConfigFilePath)
|
||||
|
||||
return Yaml(getConstructor(ModuleConfigWrapper::class.java)).load(yamlContent)
|
||||
?: throw RuntimeException("YAML解析返回null")
|
||||
|
||||
private fun <T> loadYaml(filePath: Path, clazz: Class<T>): T {
|
||||
val yamlContent = Files.readString(filePath)
|
||||
return try {
|
||||
Yaml(getConstructor(clazz)).load(yamlContent)
|
||||
?: throw RuntimeException("YAML 解析返回 null: $filePath")
|
||||
} catch (e: ConfigParseException) {
|
||||
throw e
|
||||
} catch (e: Exception) {
|
||||
throw RuntimeException("YAML解析失败: ${e.message}", e)
|
||||
throw ConfigParseException(formatYamlError(e, filePath), e)
|
||||
}
|
||||
}
|
||||
|
||||
/** 提取 SnakeYAML 异常中的关键信息并格式化为可读的错误 */
|
||||
private fun formatYamlError(e: Throwable, filePath: Path): String {
|
||||
val sb = StringBuilder()
|
||||
sb.appendLine("配置文件解析失败: $filePath")
|
||||
sb.appendLine("─".repeat(48))
|
||||
|
||||
// 提取所有嵌套的 YAMLException 信息 (逆序,最深层优先)
|
||||
val causes = generateSequence(e) { it.cause }.toList().reversed()
|
||||
for (cause in causes) {
|
||||
val msg = cause.message ?: continue
|
||||
// 只保留以 YAML 行号/列号开头的错误和 "Unable to find" 错误
|
||||
if (msg.contains("Unable to find") || msg.startsWith("Cannot create") ||
|
||||
msg.contains("in 'string', line") || msg.contains("Unable to find enum")
|
||||
) {
|
||||
// 提取行号信息
|
||||
val lineMatch = Regex("line (\\d+)").find(msg)
|
||||
val lineInfo = lineMatch?.let { " (第${it.groupValues[1]}行)" } ?: ""
|
||||
// 提取关键描述
|
||||
val desc = when {
|
||||
msg.contains("Unable to find property") -> {
|
||||
val prop = Regex("property '(\\S+)'").find(msg)?.groupValues?.get(1) ?: "?"
|
||||
"未知配置项: '$prop'$lineInfo"
|
||||
}
|
||||
msg.contains("Unable to find enum value") -> {
|
||||
val value = Regex("enum value '(\\S+)'").find(msg)?.groupValues?.get(1) ?: "?"
|
||||
val enumClass = Regex("enum class: (\\S+)").find(msg)?.groupValues?.get(1)?.substringAfterLast('.') ?: ""
|
||||
"未知枚举值: '$value' (有效值见 $enumClass 定义)$lineInfo"
|
||||
}
|
||||
else -> msg.lines().first().trim()
|
||||
}
|
||||
sb.appendLine(" • $desc")
|
||||
}
|
||||
}
|
||||
|
||||
if (sb.lines().count() <= 3) {
|
||||
// 没有提取到结构化信息,回退到原始摘要
|
||||
sb.appendLine(" 详情: ${e.message?.lineSequence()?.firstOrNull()?.take(120) ?: e.javaClass.simpleName}")
|
||||
}
|
||||
return sb.toString().trimEnd()
|
||||
}
|
||||
|
||||
private fun getConstructor(clazz: Class<*>): Constructor {
|
||||
val propertyUtils = object : PropertyUtils() {
|
||||
override fun getProperty(type: Class<*>, name: String): Property {
|
||||
val processedName = if (name.contains("-")) {
|
||||
NamingConventionUtil.hyphenToCamel(name) // 连字符转驼峰
|
||||
NamingConventionUtil.hyphenToCamel(name)
|
||||
} else {
|
||||
name
|
||||
}
|
||||
return super.getProperty(type, processedName)
|
||||
return try {
|
||||
super.getProperty(type, processedName)
|
||||
} catch (e: YAMLException) {
|
||||
// 阻止 SnakeYAML 在错误信息中 dump 完整 JavaBean
|
||||
throw ConfigParseException(
|
||||
"未知配置项 '$name' (映射为 '$processedName'),类型 '${type.simpleName}' 中无此字段"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -105,6 +148,7 @@ object YamlConfigLoader {
|
|||
fun loadTuImgConfig(): ImgTuConfig = appConfig.imgTu
|
||||
fun loadWhitelistSystemConfig(): WhitelistSystemConfig = appConfig.whitelistSystem
|
||||
fun loadModuleConfig(): ModuleConfig = moduleConfig.module
|
||||
|
||||
data class AppConfigWrapper(
|
||||
var database: DatabaseConfig = DatabaseConfig(),
|
||||
var crypto: CryptoConfig = CryptoConfig(),
|
||||
|
|
@ -118,9 +162,16 @@ object YamlConfigLoader {
|
|||
var dgLab: DgLabConfig = DgLabConfig(),
|
||||
var imgTu: ImgTuConfig = ImgTuConfig(),
|
||||
var whitelistSystem: WhitelistSystemConfig = WhitelistSystemConfig(),
|
||||
)
|
||||
) {
|
||||
override fun toString(): String =
|
||||
"AppConfig(loaded=${database.url != null})"
|
||||
}
|
||||
|
||||
data class ModuleConfigWrapper(
|
||||
var module: ModuleConfig = ModuleConfig(),
|
||||
)
|
||||
}
|
||||
|
||||
/** 配置解析专用异常 — 不递归打印 cause 避免海量堆栈 */
|
||||
class ConfigParseException(message: String, cause: Throwable? = null) :
|
||||
RuntimeException(message, cause, true, false)
|
||||
|
|
|
|||
3
src/main/resources/sql/invitation/query_code_ids.sql
Normal file
3
src/main/resources/sql/invitation/query_code_ids.sql
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
SELECT i.id, i.code
|
||||
FROM blessingskin.invitation_codes i
|
||||
WHERE i.code IN (${placeholders})
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
SELECT q.player_id, q.effective, q.is_used, q.qq, q.status
|
||||
FROM ltd_manager_bot.qualified_user_info q
|
||||
WHERE q.qq IN (${placeholders})
|
||||
3
src/main/resources/sql/invitation/query_token_info.sql
Normal file
3
src/main/resources/sql/invitation/query_token_info.sql
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
SELECT q.player_id, q.player_name, q.token, q.expires_at
|
||||
FROM ltd_manager_bot.qualified_user_info q
|
||||
WHERE q.player_id IN (${placeholders})
|
||||
1
src/main/resources/sql/invitation/upsert_ascription.sql
Normal file
1
src/main/resources/sql/invitation/upsert_ascription.sql
Normal file
|
|
@ -0,0 +1 @@
|
|||
INSERT INTO ltd_manager_bot.invitation_code_ascription (id, token_id) VALUES ${values} ON DUPLICATE KEY UPDATE token_id = VALUES(token_id)
|
||||
|
|
@ -0,0 +1 @@
|
|||
SELECT status FROM minecraft_manager_ltd_9.players WHERE qq = ${placeholder}
|
||||
Loading…
Reference in New Issue
Block a user