AppConfigHelper.cs 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. using System;
  2. using System.Configuration;
  3. namespace ivf_tl_Operate.Helpers
  4. {
  5. /// <summary>
  6. /// M5-01-2 / M5-02:统一配置读写封装(operate 单一数据源 App.config 的集中接管点)。
  7. /// - 强类型容错读取(缺键返回默认值而非 NPE,缓解 R8"改一处漏一处起不来")。
  8. /// - 加密项读写经 <see cref="CryptoHelper"/>(passWord / engineerPwd 等本机凭据)。
  9. /// - <see cref="MigratePlaintextCredentials"/>:启动一次性明文→密文迁移(幂等)。
  10. /// 分层标注(配置收敛后):
  11. /// 共享层(tl-shared.config,operate/control 经 <appSettings file=> 合并读):urlIp/urlPort/mqttIp/mqttPort/kfkaIP/kfkaPort/outInter
  12. /// operate 本地层(本 config):cacheDisk/Language/tlNum/houseEnabled/userName/passWord(加密)/engineerPwd(加密)/autoFocus
  13. /// 数据库层(TLSetting,不在此封装):autoFocusTime/videoFps/cropNum/heapDate/keepDate/cleanSurfaceData
  14. /// 换气/CCD 类业务键(csTime/gbTime/VentNum/...):配置收敛已从 operate 移除,仅 control 本地 config 持有
  15. /// [M7] 容错与迁移随合并进程运行验证(本地不可构建/运行)。
  16. /// </summary>
  17. public static class AppConfigHelper
  18. {
  19. /// <summary>工程师口令的 config 键(M5-02-3 治理 tl13579)。</summary>
  20. public const string EngineerPwdKey = "engineerPwd";
  21. /// <summary>工程师口令缺省值(首次无值回退;迁移时加密回写)。</summary>
  22. public const string DefaultEngineerPwd = "tl13579";
  23. /// <summary>容错读取字符串,缺键/空返回默认值。</summary>
  24. public static string GetString(string key, string defaultValue = "")
  25. {
  26. try
  27. {
  28. var v = ConfigurationManager.AppSettings[key];
  29. return string.IsNullOrEmpty(v) ? defaultValue : v;
  30. }
  31. catch
  32. {
  33. return defaultValue;
  34. }
  35. }
  36. /// <summary>容错读取整数,缺键/解析失败返回默认值。</summary>
  37. public static int GetInt(string key, int defaultValue = 0)
  38. {
  39. return int.TryParse(GetString(key, null), out var r) ? r : defaultValue;
  40. }
  41. /// <summary>
  42. /// 读取加密项并解密(旧明文原样返回,由迁移逻辑负责回写)。缺键返回默认值。
  43. /// </summary>
  44. public static string GetDecrypted(string key, string defaultValue = "")
  45. {
  46. var raw = GetString(key, null);
  47. if (string.IsNullOrEmpty(raw)) return defaultValue;
  48. return CryptoHelper.Decrypt(raw);
  49. }
  50. /// <summary>配置收敛:operate↔control 共享的连接键(唯一数据源 tl-shared.config)。</summary>
  51. private static readonly System.Collections.Generic.HashSet<string> SharedKeys =
  52. new System.Collections.Generic.HashSet<string>(System.StringComparer.OrdinalIgnoreCase)
  53. { "urlIp", "urlPort", "mqttIp", "mqttPort", "kfkaIP", "kfkaPort", "outInter" };
  54. /// <summary>共享文件路径 = operate 输出根目录\tl-shared.config(control 经 ..\ 指此)。</summary>
  55. public static string SharedConfigPath =>
  56. System.IO.Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, "tl-shared.config");
  57. /// <summary>
  58. /// 写回普通键。配置收敛:共享键落唯一数据源 tl-shared.config(SharedConfigStore),
  59. /// 非共享键沿用 OpenExeConfiguration 落 operate 自己的 config。写后刷新本进程缓存。
  60. /// </summary>
  61. public static void Save(string key, string value)
  62. {
  63. try
  64. {
  65. if (SharedKeys.Contains(key))
  66. {
  67. SharedConfigStore.Write(SharedConfigPath, key, value);
  68. }
  69. else
  70. {
  71. Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
  72. if (config.AppSettings.Settings[key] == null)
  73. config.AppSettings.Settings.Add(key, value);
  74. else
  75. config.AppSettings.Settings[key].Value = value;
  76. config.Save(ConfigurationSaveMode.Modified);
  77. }
  78. ConfigurationManager.RefreshSection("appSettings");
  79. }
  80. catch
  81. {
  82. // 写失败不抛出(避免阻断 UI/启动);[M7] 运行环境核查落盘。
  83. }
  84. }
  85. /// <summary>加密后写回(用于 passWord / engineerPwd 等凭据)。</summary>
  86. public static void SaveEncrypted(string key, string plain)
  87. {
  88. Save(key, CryptoHelper.Encrypt(plain));
  89. }
  90. /// <summary>
  91. /// M5-02-5:启动一次性凭据迁移(幂等)。
  92. /// (a) passWord:明文(旧值,如 123456)→密文回写;已是密文则跳过。
  93. /// (b) engineerPwd:键缺失→以默认 tl13579 加密建键;明文→密文回写。
  94. /// 仅当存在需迁移的明文时才写盘,已全部加密则不触碰文件(幂等)。
  95. /// </summary>
  96. public static void MigratePlaintextCredentials()
  97. {
  98. try
  99. {
  100. Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
  101. bool changed = false;
  102. // (a) passWord 明文 → 密文
  103. var pw = config.AppSettings.Settings["passWord"];
  104. if (pw != null && !string.IsNullOrEmpty(pw.Value) && !CryptoHelper.IsEncrypted(pw.Value))
  105. {
  106. pw.Value = CryptoHelper.Encrypt(pw.Value);
  107. changed = true;
  108. }
  109. // (b) engineerPwd 缺键或明文 → 加密建键/回写(治理 tl13579)
  110. var ep = config.AppSettings.Settings[EngineerPwdKey];
  111. if (ep == null)
  112. {
  113. config.AppSettings.Settings.Add(EngineerPwdKey, CryptoHelper.Encrypt(DefaultEngineerPwd));
  114. changed = true;
  115. }
  116. else if (!string.IsNullOrEmpty(ep.Value) && !CryptoHelper.IsEncrypted(ep.Value))
  117. {
  118. ep.Value = CryptoHelper.Encrypt(ep.Value);
  119. changed = true;
  120. }
  121. if (changed)
  122. {
  123. config.Save(ConfigurationSaveMode.Modified);
  124. ConfigurationManager.RefreshSection("appSettings");
  125. }
  126. }
  127. catch
  128. {
  129. // 迁移失败不阻断启动;[M7] 运行环境核查首次迁移。
  130. }
  131. }
  132. /// <summary>读取工程师口令明文(解密;键缺失/解密失败回退默认 tl13579)。</summary>
  133. public static string GetEngineerPwd()
  134. {
  135. var v = GetDecrypted(EngineerPwdKey, DefaultEngineerPwd);
  136. return string.IsNullOrEmpty(v) ? DefaultEngineerPwd : v;
  137. }
  138. }
  139. }