using Admin.NET.Core; using Admin.NET.Plugin.AiDOP.Entity.DataPlatform; using SqlSugar; namespace Admin.NET.Plugin.AiDOP.DataPlatform; /// /// 按 mdp_source 动态获取只读 SqlSugar 连接域(方式甲底座)。 /// ConfigId 约定:mdp-src-{sourceCode};兼容已注册的 t8_v5(source_code=T8_ERP/T8_V5)。 /// public sealed class MdpSourceScopeFactory : ITransient { private const string ConfigIdPrefix = "mdp-src-"; private readonly ISqlSugarClient _db; public MdpSourceScopeFactory(ISqlSugarClient db) { _db = db; } /// 按 sourceCode 获取连接域;优先复用已注册 ConfigId。 public async Task GetScopeAsync(string sourceCode, CancellationToken cancellationToken = default) { if (string.IsNullOrWhiteSpace(sourceCode)) throw new ArgumentException("sourceCode 不能为空", nameof(sourceCode)); var code = sourceCode.Trim(); // 兼容既有硬编码 T8:未在 mdp_source 登记前也可工作 if (IsT8Alias(code) && _db.AsTenant().IsAnyConnection("t8_v5")) return _db.AsTenant().GetConnectionScope("t8_v5"); // 本库样板源:直接复用主库连接(mdp_source 可不落账号密文) if (IsLocalMysqlAlias(code)) return _db; var configId = ConfigIdPrefix + code; if (_db.AsTenant().IsAnyConnection(configId)) return _db.AsTenant().GetConnectionScope(configId); var source = await _db.Queryable() .Where(x => x.SourceCode == code && x.Status == 1) .FirstAsync(cancellationToken) ?? throw new InvalidOperationException($"mdp_source 未找到启用源:{code}"); if (!string.Equals(source.SourceType, "DB", StringComparison.OrdinalIgnoreCase)) throw new InvalidOperationException($"源 {code} 的 source_type={source.SourceType},不是 DB,无法建 SqlSugar 连接"); // 未配置账号时视为与主库同库,避免样板源建空 Uid 连接失败 if (string.IsNullOrWhiteSpace(source.DbUser)) return _db; var connStr = BuildConnectionString(source); var dbType = MapDbType(source.DbType); _db.AsTenant().AddConnection(new ConnectionConfig { ConfigId = configId, DbType = dbType, ConnectionString = connStr, InitKeyType = InitKeyType.Attribute, IsAutoCloseConnection = true, MoreSettings = new ConnMoreSettings { IsAutoRemoveDataCache = true } }); return _db.AsTenant().GetConnectionScope(configId); } public ISqlSugarClient GetScope(string sourceCode) => GetScopeAsync(sourceCode).GetAwaiter().GetResult(); private static bool IsT8Alias(string code) => code.Equals("T8_ERP", StringComparison.OrdinalIgnoreCase) || code.Equals("T8_V5", StringComparison.OrdinalIgnoreCase) || code.Equals("T8_V5_SQLSERVER", StringComparison.OrdinalIgnoreCase) || code.Equals("t8_v5", StringComparison.OrdinalIgnoreCase); private static bool IsLocalMysqlAlias(string code) => code.Equals("AIDOPDEV_MYSQL", StringComparison.OrdinalIgnoreCase) || code.Equals("LOCAL_MYSQL", StringComparison.OrdinalIgnoreCase) || code.Equals("AIDOP_MYSQL", StringComparison.OrdinalIgnoreCase); private static DbType MapDbType(string? dbType) { if (string.IsNullOrWhiteSpace(dbType)) return DbType.MySql; return dbType.Trim().ToUpperInvariant() switch { "MYSQL" => DbType.MySql, "SQLSERVER" or "MSSQL" => DbType.SqlServer, "ORACLE" => DbType.Oracle, "POSTGRES" or "POSTGRESQL" => DbType.PostgreSQL, _ => throw new NotSupportedException($"不支持的 db_type:{dbType}") }; } private static string BuildConnectionString(MdpSource source) { if (string.IsNullOrWhiteSpace(source.DbHost) || string.IsNullOrWhiteSpace(source.DbName)) throw new InvalidOperationException($"源 {source.SourceCode} 缺少 db_host/db_name"); var password = DecryptPassword(source.DbPasswordEnc); var port = source.DbPort; var user = source.DbUser ?? ""; var extra = string.IsNullOrWhiteSpace(source.DbExtraParams) ? "" : ";" + source.DbExtraParams.Trim().TrimStart(';'); return MapDbType(source.DbType) switch { DbType.SqlServer => $"Server={source.DbHost}{(port is > 0 ? $",{port}" : "")};Database={source.DbName};User Id={user};Password={password};TrustServerCertificate=true;Encrypt=false{extra}", DbType.MySql => $"Server={source.DbHost};Port={(port is > 0 ? port : 3306)};Database={source.DbName};Uid={user};Pwd={password};CharSet=utf8mb4;AllowLoadLocalInfile=true{extra}", DbType.PostgreSQL => $"Host={source.DbHost};Port={(port is > 0 ? port : 5432)};Database={source.DbName};Username={user};Password={password}{extra}", DbType.Oracle => $"Data Source={source.DbHost}:{(port is > 0 ? port : 1521)}/{source.DbName};User Id={user};Password={password}{extra}", _ => throw new NotSupportedException($"不支持的 db_type:{source.DbType}") }; } private static string DecryptPassword(string? enc) { if (string.IsNullOrEmpty(enc)) return ""; try { var plain = CryptogramUtil.Decrypt(enc); return string.IsNullOrEmpty(plain) ? enc : plain; } catch { // 明文或非本系统密文时原样使用(与 Database.json EnableConnEncrypt=false 一致) return enc; } } }