using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using FoodLabeling.Th.Application.Contracts.Options;
using SqlSugar;
namespace FoodLabeling.Th.Application.MultiTenancy;
///
/// 根据配置生成 MySQL 租户库连接串
///
public static class TenantDatabaseConnectionStringBuilder
{
private const int FallbackHashLength = 8;
private static readonly Regex SafeNameRegex = new(@"[^a-z0-9_]", RegexOptions.Compiled);
///
/// 将租户名或显式 DatabaseKey 规范化为 MySQL 库名片段(小写字母、数字、下划线)。
/// 纯中文等非 ASCII 名称在清洗为空时,使用基于名称的稳定 hash 兜底(t + 8 位十六进制)。
///
public static string NormalizeTenantDatabaseKey(string tenantName, string? explicitDatabaseKey = null)
{
if (!string.IsNullOrWhiteSpace(explicitDatabaseKey))
{
return NormalizeSanitizedKey(explicitDatabaseKey.Trim(), "DatabaseKey 无效,无法生成数据库名");
}
var key = SanitizeToDatabaseKey(tenantName);
if (string.IsNullOrEmpty(key))
{
key = BuildStableFallbackKey(tenantName);
}
return key;
}
public static string BuildDatabaseName(
FoodLabelingThTenantDatabaseOptions options,
string tenantName,
string? databaseKey = null)
{
var key = NormalizeTenantDatabaseKey(tenantName, databaseKey);
return options.DatabaseNameTemplate.Replace("{tenant}", key, StringComparison.OrdinalIgnoreCase);
}
public static string BuildMySqlConnectionString(
FoodLabelingThTenantDatabaseOptions options,
string tenantName,
string? databaseKey = null)
{
var database = BuildDatabaseName(options, tenantName, databaseKey);
return
$"server={options.Server};port={options.Port};database={database};uid={options.UserId};pwd={options.Password};CharSet={options.CharSet};";
}
public static DbType ResolveDbType(string? configuredDbType)
{
return Enum.TryParse(configuredDbType, true, out var dbType) ? dbType : DbType.MySql;
}
private static string SanitizeToDatabaseKey(string value)
{
var key = value.Trim().ToLowerInvariant();
key = SafeNameRegex.Replace(key, "_");
return Regex.Replace(key, "_+", "_").Trim('_');
}
private static string NormalizeSanitizedKey(string value, string invalidMessage)
{
var key = SanitizeToDatabaseKey(value);
if (string.IsNullOrEmpty(key))
{
throw new ArgumentException(invalidMessage);
}
return key;
}
///
/// 基于租户名称生成稳定、可重复的短 key,避免纯非 ASCII 名称清洗后为空。
///
private static string BuildStableFallbackKey(string tenantName)
{
var hashBytes = SHA256.HashData(Encoding.UTF8.GetBytes(tenantName.Trim()));
var hex = Convert.ToHexString(hashBytes).ToLowerInvariant();
return "t" + hex[..FallbackHashLength];
}
}