feat: finalize core modules and gateway
This commit is contained in:
@@ -0,0 +1,121 @@
|
||||
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;
|
||||
|
||||
/// <summary>
|
||||
/// 数据库连接工厂,支持读写分离及连接配置校验。
|
||||
/// </summary>
|
||||
public sealed class DatabaseConnectionFactory(
|
||||
IOptionsMonitor<DatabaseOptions> optionsMonitor,
|
||||
IConfiguration configuration,
|
||||
ILogger<DatabaseConnectionFactory> logger) : IDatabaseConnectionFactory
|
||||
{
|
||||
private const int DefaultCommandTimeoutSeconds = 30;
|
||||
private const int DefaultMaxRetryCount = 3;
|
||||
private const int DefaultMaxRetryDelaySeconds = 5;
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定数据源与读写角色的连接信息。
|
||||
/// </summary>
|
||||
/// <param name="dataSourceName">逻辑数据源名称。</param>
|
||||
/// <param name="role">连接角色。</param>
|
||||
/// <returns>连接串与超时/重试配置。</returns>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 校验数据源配置完整性。
|
||||
/// </summary>
|
||||
/// <param name="dataSourceName">数据源名称。</param>
|
||||
/// <param name="options">数据源配置。</param>
|
||||
/// <exception cref="InvalidOperationException">配置不合法时抛出。</exception>
|
||||
private bool ValidateOptions(string dataSourceName, DatabaseDataSourceOptions options)
|
||||
{
|
||||
var results = new List<ValidationResult>();
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据读写角色选择连接串,从读连接随机分配。
|
||||
/// </summary>
|
||||
/// <param name="options">数据源配置。</param>
|
||||
/// <param name="role">连接角色。</param>
|
||||
/// <returns>可用连接串。</returns>
|
||||
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=localhost;Port=5432;Database=postgres;Username=postgres;Password=postgres;Pooling=true;Minimum Pool Size=1;Maximum Pool Size=20";
|
||||
logger.LogWarning("使用默认回退连接串:{Connection}", fallback);
|
||||
return new DatabaseConnectionDetails(
|
||||
fallback,
|
||||
DefaultCommandTimeoutSeconds,
|
||||
DefaultMaxRetryCount,
|
||||
DefaultMaxRetryDelaySeconds);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user