const db = require('../plugin/DataBase/db') const Redis = require('../plugin/DataBase/Redis') const CONFIG_KEYS = ['pay', 'email', 'unilogin', 'proxyForwardServer'] const CACHE_TTL_SECONDS = 300 class RuntimeConfigNotFoundError extends Error { constructor(key) { super(`未配置或未启用 ${key}`) this.name = 'RuntimeConfigNotFoundError' this.code = 'RUNTIME_CONFIG_NOT_FOUND' this.key = key } } function cacheKey(key) { return `runtimeConfig:${key}` } async function getRuntimeConfig(key, options = {}) { const { required = true, defaultValue = undefined } = options if (!CONFIG_KEYS.includes(key)) throw new Error('不支持的运行时配置') const cached = await Redis.get(cacheKey(key)) if (cached) return JSON.parse(cached) const rows = await db.query( `SELECT config_value FROM runtime_configs WHERE config_key = ? AND enabled = 1 ORDER BY is_default DESC, updated_at DESC LIMIT 1`, [key] ) if (!rows?.[0]) { if (!required) return defaultValue throw new RuntimeConfigNotFoundError(key) } const value = typeof rows[0].config_value === 'object' ? rows[0].config_value : JSON.parse(rows[0].config_value) await Redis.set(cacheKey(key), JSON.stringify(value), { EX: CACHE_TTL_SECONDS }) return value } async function getAllRuntimeConfigs() { const rows = await db.query( `SELECT config_key, config_name, config_value, enabled, is_default, updated_at, updated_by FROM runtime_configs ORDER BY config_key, is_default DESC, updated_at DESC` ) || [] return rows.map(row => ({ ...row, config_value: typeof row.config_value === 'object' ? row.config_value : JSON.parse(row.config_value) })) } function validateRuntimeConfig(key, value) { if (key === 'email') { if (!Array.isArray(value)) throw new Error('邮件配置必须为数组') return value.map((item, index) => ({ ...item, priority: Number(item.priority ?? index) })) .sort((a, b) => a.priority - b.priority) } if (!value || typeof value !== 'object' || Array.isArray(value)) throw new Error(`${key} 配置必须为对象`) if (key === 'pay' && (!Array.isArray(value.methods) || value.methods.some(item => !item?.type))) { throw new Error('付款方式配置无效') } return value } async function saveRuntimeConfig({ key, name = 'default', value, enabled = true, isDefault = false, updatedBy }) { if (!CONFIG_KEYS.includes(key)) throw new Error('不支持的运行时配置') const normalized = validateRuntimeConfig(key, value) const result = await db.query( `INSERT INTO runtime_configs (config_key, config_name, config_value, enabled, is_default, updated_at, updated_by) VALUES (?, ?, ?, ?, ?, ?, ?) ON DUPLICATE KEY UPDATE config_value = VALUES(config_value), enabled = VALUES(enabled), is_default = VALUES(is_default), updated_at = VALUES(updated_at), updated_by = VALUES(updated_by)`, [key, String(name).trim(), JSON.stringify(normalized), enabled ? 1 : 0, isDefault ? 1 : 0, Date.now(), updatedBy || null] ) if (result === undefined) throw new Error('保存运行时配置失败') if (isDefault) { await db.query('UPDATE runtime_configs SET is_default = IF(config_name = ?, 1, 0) WHERE config_key = ?', [String(name).trim(), key]) } await Redis.del(cacheKey(key)) return normalized } async function selectRuntimeConfig({ key, name, updatedBy }) { if (!CONFIG_KEYS.includes(key) || !name) throw new Error('配置类型或名称无效') const result = await db.query( 'UPDATE runtime_configs SET is_default = IF(config_name = ?, 1, 0), updated_at = ?, updated_by = ? WHERE config_key = ?', [name, Date.now(), updatedBy || null, key] ) if (result === undefined) throw new Error('切换运行时配置失败') await Redis.del(cacheKey(key)) } module.exports = { CONFIG_KEYS, RuntimeConfigNotFoundError, getRuntimeConfig, getAllRuntimeConfigs, saveRuntimeConfig, selectRuntimeConfig }