using System.Data.Common; using System.Security.Cryptography; using System.Text; namespace Admin.NET.Plugin.AiDOP.Service.S8.Rules; /// /// S8-DYNAMIC-SQLSUGAR-SCOPE-FACTORY-1:S8 data_source endpoint 规范化与 hash 派生。 /// 纯字符串变换: /// 1) 用 DbConnectionStringBuilder 解析 raw endpoint 的 key/value(大小写无关); /// 2) 按 OrdinalIgnoreCase 全部 key lower-case 化,避免不同输入 case 导致 hash 漂移; /// 3) 补齐缺失参数(已有 key 不覆盖),与主库 Database.json 池层参数对齐: /// AllowPublicKeyRetrieval / Pooling / Minimum Pool Size / Maximum Pool Size / /// Connection Timeout / Connection Idle Timeout / Connection LifeTime; /// 4) 按 key 字母序输出 normalized canonical connection string; /// 5) 对 normalized 做 SHA256 → 64 字符小写 hex EndpointHash。 /// 不访问数据库、不读写文件、不读 env、不输出连接串原文、不输出密码。 /// 输入为空或空白时抛 ArgumentException,message 不携带 endpoint 原文。 /// internal static class S8DataSourceEndpointNormalizer { public const string KeyAllowPublicKeyRetrieval = "allowpublickeyretrieval"; public const string KeyPooling = "pooling"; public const string KeyMinimumPoolSize = "minimum pool size"; public const string KeyMaximumPoolSize = "maximum pool size"; public const string KeyConnectionTimeout = "connection timeout"; public const string KeyConnectionIdleTimeout = "connection idle timeout"; public const string KeyConnectionLifeTime = "connection lifetime"; private static readonly (string Key, string Value)[] _normalizeDefaults = { (KeyAllowPublicKeyRetrieval, "True"), (KeyPooling, "True"), (KeyMinimumPoolSize, "0"), (KeyMaximumPoolSize, "20"), (KeyConnectionTimeout, "10"), (KeyConnectionIdleTimeout, "180"), (KeyConnectionLifeTime, "300"), }; public static S8DataSourceEndpointNormalizeResult Normalize(string rawEndpoint) { if (string.IsNullOrWhiteSpace(rawEndpoint)) throw new ArgumentException("S8 data_source endpoint 不能为空或空白", nameof(rawEndpoint)); var parsed = new DbConnectionStringBuilder { ConnectionString = rawEndpoint }; // SortedDictionary(Ordinal) + 全部 key 小写化 → 相同语义 endpoint 落到相同 canonical 顺序,hash 稳定。 var canonical = new SortedDictionary(StringComparer.Ordinal); foreach (string key in parsed.Keys) { var canonicalKey = key.ToLowerInvariant(); canonical[canonicalKey] = parsed[key]?.ToString() ?? string.Empty; } // 补齐缺失默认值;已有 key(如自定义 Maximum Pool Size)保留原值,不覆盖。 foreach (var (defaultKey, defaultValue) in _normalizeDefaults) { if (!canonical.ContainsKey(defaultKey)) canonical[defaultKey] = defaultValue; } // 用 DbConnectionStringBuilder 按 sorted key 顺序重组;其 ToString 自动处理 value quoting。 var normalizedBuilder = new DbConnectionStringBuilder(); foreach (var kv in canonical) normalizedBuilder[kv.Key] = kv.Value; var normalized = normalizedBuilder.ConnectionString; var bytes = SHA256.HashData(Encoding.UTF8.GetBytes(normalized)); var hashHex = Convert.ToHexString(bytes).ToLowerInvariant(); return new S8DataSourceEndpointNormalizeResult(normalized, hashHex); } } /// /// S8 data_source endpoint 规范化结果。NormalizedConnectionString 用于建 SqlSugarScope; /// EndpointHash 是 SHA256(normalized) 的 64 字符小写 hex,调用方可取前 N 位派生 ConfigId。 /// public readonly record struct S8DataSourceEndpointNormalizeResult(string NormalizedConnectionString, string EndpointHash);