using System; using System.Configuration; namespace ivf_tl_Operate.Helpers { /// /// M5-01-2 / M5-02:统一配置读写封装(operate 单一数据源 App.config 的集中接管点)。 /// - 强类型容错读取(缺键返回默认值而非 NPE,缓解 R8"改一处漏一处起不来")。 /// - 加密项读写经 (passWord / engineerPwd 等本机凭据)。 /// - :启动一次性明文→密文迁移(幂等)。 /// 分层标注(配置收敛后): /// 共享层(tl-shared.config,operate/control 经 合并读):urlIp/urlPort/mqttIp/mqttPort/kfkaIP/kfkaPort/outInter /// operate 本地层(本 config):cacheDisk/Language/tlNum/houseEnabled/userName/passWord(加密)/engineerPwd(加密)/autoFocus /// 数据库层(TLSetting,不在此封装):autoFocusTime/videoFps/cropNum/heapDate/keepDate/cleanSurfaceData /// 换气/CCD 类业务键(csTime/gbTime/VentNum/...):配置收敛已从 operate 移除,仅 control 本地 config 持有 /// [M7] 容错与迁移随合并进程运行验证(本地不可构建/运行)。 /// public static class AppConfigHelper { /// 工程师口令的 config 键(M5-02-3 治理 tl13579)。 public const string EngineerPwdKey = "engineerPwd"; /// 工程师口令缺省值(首次无值回退;迁移时加密回写)。 public const string DefaultEngineerPwd = "tl13579"; /// 容错读取字符串,缺键/空返回默认值。 public static string GetString(string key, string defaultValue = "") { try { var v = ConfigurationManager.AppSettings[key]; return string.IsNullOrEmpty(v) ? defaultValue : v; } catch { return defaultValue; } } /// 容错读取整数,缺键/解析失败返回默认值。 public static int GetInt(string key, int defaultValue = 0) { return int.TryParse(GetString(key, null), out var r) ? r : defaultValue; } /// /// 读取加密项并解密(旧明文原样返回,由迁移逻辑负责回写)。缺键返回默认值。 /// public static string GetDecrypted(string key, string defaultValue = "") { var raw = GetString(key, null); if (string.IsNullOrEmpty(raw)) return defaultValue; return CryptoHelper.Decrypt(raw); } /// 配置收敛:operate↔control 共享的连接键(唯一数据源 tl-shared.config)。 private static readonly System.Collections.Generic.HashSet SharedKeys = new System.Collections.Generic.HashSet(System.StringComparer.OrdinalIgnoreCase) { "urlIp", "urlPort", "mqttIp", "mqttPort", "kfkaIP", "kfkaPort", "outInter" }; /// 共享文件路径 = operate 输出根目录\tl-shared.config(control 经 ..\ 指此)。 public static string SharedConfigPath => System.IO.Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, "tl-shared.config"); /// /// 写回普通键。配置收敛:共享键落唯一数据源 tl-shared.config(SharedConfigStore), /// 非共享键沿用 OpenExeConfiguration 落 operate 自己的 config。写后刷新本进程缓存。 /// public static void Save(string key, string value) { try { if (SharedKeys.Contains(key)) { SharedConfigStore.Write(SharedConfigPath, key, value); } else { Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None); if (config.AppSettings.Settings[key] == null) config.AppSettings.Settings.Add(key, value); else config.AppSettings.Settings[key].Value = value; config.Save(ConfigurationSaveMode.Modified); } ConfigurationManager.RefreshSection("appSettings"); } catch { // 写失败不抛出(避免阻断 UI/启动);[M7] 运行环境核查落盘。 } } /// 加密后写回(用于 passWord / engineerPwd 等凭据)。 public static void SaveEncrypted(string key, string plain) { Save(key, CryptoHelper.Encrypt(plain)); } /// /// M5-02-5:启动一次性凭据迁移(幂等)。 /// (a) passWord:明文(旧值,如 123456)→密文回写;已是密文则跳过。 /// (b) engineerPwd:键缺失→以默认 tl13579 加密建键;明文→密文回写。 /// 仅当存在需迁移的明文时才写盘,已全部加密则不触碰文件(幂等)。 /// public static void MigratePlaintextCredentials() { try { Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None); bool changed = false; // (a) passWord 明文 → 密文 var pw = config.AppSettings.Settings["passWord"]; if (pw != null && !string.IsNullOrEmpty(pw.Value) && !CryptoHelper.IsEncrypted(pw.Value)) { pw.Value = CryptoHelper.Encrypt(pw.Value); changed = true; } // (b) engineerPwd 缺键或明文 → 加密建键/回写(治理 tl13579) var ep = config.AppSettings.Settings[EngineerPwdKey]; if (ep == null) { config.AppSettings.Settings.Add(EngineerPwdKey, CryptoHelper.Encrypt(DefaultEngineerPwd)); changed = true; } else if (!string.IsNullOrEmpty(ep.Value) && !CryptoHelper.IsEncrypted(ep.Value)) { ep.Value = CryptoHelper.Encrypt(ep.Value); changed = true; } if (changed) { config.Save(ConfigurationSaveMode.Modified); ConfigurationManager.RefreshSection("appSettings"); } } catch { // 迁移失败不阻断启动;[M7] 运行环境核查首次迁移。 } } /// 读取工程师口令明文(解密;键缺失/解密失败回退默认 tl13579)。 public static string GetEngineerPwd() { var v = GetDecrypted(EngineerPwdKey, DefaultEngineerPwd); return string.IsNullOrEmpty(v) ? DefaultEngineerPwd : v; } } }