feat: 优化配置报错

This commit is contained in:
叁玖领域 2026-06-09 13:43:00 +08:00
parent f3af0b7134
commit aafd7059bc
7 changed files with 94 additions and 32 deletions

View File

@ -3,5 +3,5 @@ org.gradle.downloadSources=false
org.gradle.parallel=true org.gradle.parallel=true
org.gradle.degree_of_parallelism=16 org.gradle.degree_of_parallelism=16
project_group=top.r3944realms.ltdmanager project_group=top.r3944realms.ltdmanager
project_version=1.22-SNAPSHOT project_version=1.22
dg_lab_version=4.4.14.19 dg_lab_version=4.4.14.19

View File

@ -3,34 +3,34 @@ package top.r3944realms.ltdmanager.core.config
import org.yaml.snakeyaml.LoaderOptions import org.yaml.snakeyaml.LoaderOptions
import org.yaml.snakeyaml.Yaml import org.yaml.snakeyaml.Yaml
import org.yaml.snakeyaml.constructor.Constructor import org.yaml.snakeyaml.constructor.Constructor
import org.yaml.snakeyaml.error.YAMLException
import org.yaml.snakeyaml.introspector.Property import org.yaml.snakeyaml.introspector.Property
import org.yaml.snakeyaml.introspector.PropertyUtils import org.yaml.snakeyaml.introspector.PropertyUtils
import top.r3944realms.ltdmanager.utils.ConfigInitializer import top.r3944realms.ltdmanager.utils.ConfigInitializer
import top.r3944realms.ltdmanager.utils.LoggerUtil
import top.r3944realms.ltdmanager.utils.NamingConventionUtil import top.r3944realms.ltdmanager.utils.NamingConventionUtil
import java.nio.file.Files import java.nio.file.Files
import java.nio.file.Path import java.nio.file.Path
import java.nio.file.Paths import java.nio.file.Paths
object YamlConfigLoader { object YamlConfigLoader {
val appConfigFilePath: Path = Paths.get("config/application.yaml") // 配置文件路径 val appConfigFilePath: Path = Paths.get("config/application.yaml")
val moduleConfigFilePath: Path = Paths.get("config/module.yaml") // 配置文件路径 val moduleConfigFilePath: Path = Paths.get("config/module.yaml")
private val _app_config by lazy { loadAppConfigWrapper() } // 延迟初始化 private val _app_config by lazy { loadAppConfigWrapper() }
val appConfig: AppConfigWrapper get() = _app_config 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 val moduleConfig: ModuleConfigWrapper get() = _module_config
init { init {
// 第一次启动确保配置文件存在
ConfigInitializer.initConfig("module.yaml", "config", false) ConfigInitializer.initConfig("module.yaml", "config", false)
ConfigInitializer.initConfig("application.yaml", "config") ConfigInitializer.initConfig("application.yaml", "config")
// 初始化后加密(确保只执行一次)
runCatching { runCatching {
ensureConfigEncrypted(_app_config) ensureConfigEncrypted(_app_config)
}.onFailure { e -> }.onFailure { e ->
println("初始化加密失败: ${e.message}") LoggerUtil.syncError("配置加密失败: ${e.message?.lineSequence()?.firstOrNull() ?: e.message}")
e.printStackTrace()
} }
} }
private fun ensureConfigEncrypted(config: AppConfigWrapper?) { private fun ensureConfigEncrypted(config: AppConfigWrapper?) {
config?.database?.encryptPassword() config?.database?.encryptPassword()
config?.websocket?.encryptToken() config?.websocket?.encryptToken()
@ -43,47 +43,90 @@ object YamlConfigLoader {
config?.imgTu?.encryptPassword() config?.imgTu?.encryptPassword()
config?.whitelistSystem?.encryptToken() config?.whitelistSystem?.encryptToken()
} }
private fun loadAppConfigWrapper(): AppConfigWrapper { private fun loadAppConfigWrapper(): AppConfigWrapper {
if (!Files.exists(appConfigFilePath)) { if (!Files.exists(appConfigFilePath)) {
throw RuntimeException("应用配置文件未找到: $appConfigFilePath") throw RuntimeException("应用配置文件未找到: $appConfigFilePath")
} }
return loadYaml(appConfigFilePath, AppConfigWrapper::class.java)
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)
}
} }
private fun loadModuleConfigWrapper(): ModuleConfigWrapper { private fun loadModuleConfigWrapper(): ModuleConfigWrapper {
if (!Files.exists(moduleConfigFilePath)) { if (!Files.exists(moduleConfigFilePath)) {
throw RuntimeException("模块配置文件未找到: $moduleConfigFilePath") throw RuntimeException("模块配置文件未找到: $moduleConfigFilePath")
} }
return loadYaml(moduleConfigFilePath, ModuleConfigWrapper::class.java)
}
try { private fun <T> loadYaml(filePath: Path, clazz: Class<T>): T {
val yamlContent = Files.readString(moduleConfigFilePath) val yamlContent = Files.readString(filePath)
return try {
return Yaml(getConstructor(ModuleConfigWrapper::class.java)).load(yamlContent) Yaml(getConstructor(clazz)).load(yamlContent)
?: throw RuntimeException("YAML解析返回null") ?: throw RuntimeException("YAML 解析返回 null: $filePath")
} catch (e: ConfigParseException) {
throw e
} catch (e: Exception) { } 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 { private fun getConstructor(clazz: Class<*>): Constructor {
val propertyUtils = object : PropertyUtils() { val propertyUtils = object : PropertyUtils() {
override fun getProperty(type: Class<*>, name: String): Property { override fun getProperty(type: Class<*>, name: String): Property {
val processedName = if (name.contains("-")) { val processedName = if (name.contains("-")) {
NamingConventionUtil.hyphenToCamel(name) // 连字符转驼峰 NamingConventionUtil.hyphenToCamel(name)
} else { } else {
name 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 loadTuImgConfig(): ImgTuConfig = appConfig.imgTu
fun loadWhitelistSystemConfig(): WhitelistSystemConfig = appConfig.whitelistSystem fun loadWhitelistSystemConfig(): WhitelistSystemConfig = appConfig.whitelistSystem
fun loadModuleConfig(): ModuleConfig = moduleConfig.module fun loadModuleConfig(): ModuleConfig = moduleConfig.module
data class AppConfigWrapper( data class AppConfigWrapper(
var database: DatabaseConfig = DatabaseConfig(), var database: DatabaseConfig = DatabaseConfig(),
var crypto: CryptoConfig = CryptoConfig(), var crypto: CryptoConfig = CryptoConfig(),
@ -118,9 +162,16 @@ object YamlConfigLoader {
var dgLab: DgLabConfig = DgLabConfig(), var dgLab: DgLabConfig = DgLabConfig(),
var imgTu: ImgTuConfig = ImgTuConfig(), var imgTu: ImgTuConfig = ImgTuConfig(),
var whitelistSystem: WhitelistSystemConfig = WhitelistSystemConfig(), var whitelistSystem: WhitelistSystemConfig = WhitelistSystemConfig(),
) ) {
override fun toString(): String =
"AppConfig(loaded=${database.url != null})"
}
data class ModuleConfigWrapper( data class ModuleConfigWrapper(
var module: ModuleConfig = ModuleConfig(), var module: ModuleConfig = ModuleConfig(),
) )
} }
/** 配置解析专用异常 — 不递归打印 cause 避免海量堆栈 */
class ConfigParseException(message: String, cause: Throwable? = null) :
RuntimeException(message, cause, true, false)

View File

@ -0,0 +1,3 @@
SELECT i.id, i.code
FROM blessingskin.invitation_codes i
WHERE i.code IN (${placeholders})

View File

@ -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})

View 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})

View 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)

View File

@ -0,0 +1 @@
SELECT status FROM minecraft_manager_ltd_9.players WHERE qq = ${placeholder}