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;
}
}
}