using System.ComponentModel.DataAnnotations; using System.Linq; using System.Security.Cryptography; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using TakeoutSaaS.Infrastructure.Common.Options; using TakeoutSaaS.Shared.Abstractions.Data; namespace TakeoutSaaS.Infrastructure.Common.Persistence; /// /// 数据库连接工厂,支持读写分离及连接配置校验。 /// public sealed class DatabaseConnectionFactory( IOptionsMonitor optionsMonitor, IConfiguration configuration, ILogger logger) : IDatabaseConnectionFactory { private const int DefaultCommandTimeoutSeconds = 30; private const int DefaultMaxRetryCount = 3; private const int DefaultMaxRetryDelaySeconds = 5; /// /// 获取指定数据源与读写角色的连接信息。 /// /// 逻辑数据源名称。 /// 连接角色。 /// 连接串与超时/重试配置。 public DatabaseConnectionDetails GetConnection(string dataSourceName, DatabaseConnectionRole role) { if (string.IsNullOrWhiteSpace(dataSourceName)) { logger.LogWarning("请求的数据源名称为空,使用默认连接。"); return BuildFallbackConnection(); } var options = optionsMonitor.CurrentValue.Find(dataSourceName); if (options != null) { if (!ValidateOptions(dataSourceName, options)) { return BuildFallbackConnection(); } var connectionString = ResolveConnectionString(options, role); return new DatabaseConnectionDetails( connectionString, options.CommandTimeoutSeconds, options.MaxRetryCount, options.MaxRetryDelaySeconds); } var fallback = configuration.GetConnectionString(dataSourceName); if (string.IsNullOrWhiteSpace(fallback)) { logger.LogError("缺少数据源 {DataSource} 的连接配置,回退到默认本地连接。", dataSourceName); return BuildFallbackConnection(); } logger.LogWarning("未找到数据源 {DataSource} 的 Database 节配置,回退使用 ConnectionStrings。", dataSourceName); return new DatabaseConnectionDetails( fallback, DefaultCommandTimeoutSeconds, DefaultMaxRetryCount, DefaultMaxRetryDelaySeconds); } /// /// 校验数据源配置完整性。 /// /// 数据源名称。 /// 数据源配置。 /// 配置不合法时抛出。 private bool ValidateOptions(string dataSourceName, DatabaseDataSourceOptions options) { var results = new List(); var context = new ValidationContext(options); if (!Validator.TryValidateObject(options, context, results, validateAllProperties: true)) { var errorMessages = string.Join("; ", results.Select(result => result.ErrorMessage)); logger.LogError("数据源 {DataSource} 配置非法:{Errors},回退到默认连接。", dataSourceName, errorMessages); return false; } return true; } /// /// 根据读写角色选择连接串,从读连接随机分配。 /// /// 数据源配置。 /// 连接角色。 /// 可用连接串。 private string ResolveConnectionString(DatabaseDataSourceOptions options, DatabaseConnectionRole role) { if (role == DatabaseConnectionRole.Read && options.Reads.Count > 0) { var index = RandomNumberGenerator.GetInt32(options.Reads.Count); return options.Reads[index]; } if (string.IsNullOrWhiteSpace(options.Write)) { return BuildFallbackConnection().ConnectionString; } return options.Write; } private DatabaseConnectionDetails BuildFallbackConnection() { const string fallback = "Host=120.53.222.17;Port=5432;Database=postgres;Username=postgres;Password=MsuMshk112233;Pooling=true;Minimum Pool Size=1;Maximum Pool Size=20"; logger.LogWarning("使用默认回退连接串:{Connection}", fallback); return new DatabaseConnectionDetails( fallback, DefaultCommandTimeoutSeconds, DefaultMaxRetryCount, DefaultMaxRetryDelaySeconds); } }