| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152 |
- namespace Admin.NET.Plugin.AiDOP.Rule;
- /// <summary>
- /// 规则配置只读数据源边界。后续数据库实现只需替换该接口实现。
- /// </summary>
- public interface IRuleConfigRepository
- {
- Task<IReadOnlyList<RuleConfigLayer>> GetEffectiveLayersAsync(
- string moduleCode,
- string scenarioCode,
- long tenantId,
- long? factoryId,
- CancellationToken cancellationToken = default);
- }
- /// <summary>
- /// 默认空数据源,主要用于单元测试或手动回退。
- /// </summary>
- public sealed class EmptyRuleConfigRepository : IRuleConfigRepository
- {
- public Task<IReadOnlyList<RuleConfigLayer>> GetEffectiveLayersAsync(
- string moduleCode,
- string scenarioCode,
- long tenantId,
- long? factoryId,
- CancellationToken cancellationToken = default)
- {
- cancellationToken.ThrowIfCancellationRequested();
- _ = moduleCode;
- _ = scenarioCode;
- _ = tenantId;
- _ = factoryId;
- return Task.FromResult<IReadOnlyList<RuleConfigLayer>>(Array.Empty<RuleConfigLayer>());
- }
- }
- /// <summary>
- /// `ado_rule_*` 数据库只读仓储。研发阶段默认启用,用于验证规则配置链路。
- /// </summary>
- public sealed class DbRuleConfigRepository : IRuleConfigRepository, ITransient
- {
- private readonly ISqlSugarClient _db;
- public DbRuleConfigRepository(ISqlSugarClient db)
- {
- _db = db;
- }
- public async Task<IReadOnlyList<RuleConfigLayer>> GetEffectiveLayersAsync(
- string moduleCode,
- string scenarioCode,
- long tenantId,
- long? factoryId,
- CancellationToken cancellationToken = default)
- {
- cancellationToken.ThrowIfCancellationRequested();
- var now = DateTime.Now;
- var normalizedFactoryId = factoryId.GetValueOrDefault();
- var rows = await _db.Ado.SqlQueryAsync<RuleConfigDbRow>(
- """
- SELECT
- p.id AS ProfileId,
- p.tenant_id AS TenantId,
- p.factory_id AS FactoryId,
- p.profile_code AS ProfileCode,
- i.rule_code AS RuleCode,
- i.value_type AS ValueType,
- i.rule_value AS RuleValue,
- i.is_enabled AS ItemEnabled,
- i.sort_no AS SortNo,
- s.required AS Required,
- s.ui_control AS UiControl
- FROM ado_rule_profile p
- INNER JOIN ado_rule_item i ON i.profile_id = p.id
- LEFT JOIN ado_rule_schema s
- ON s.module_code = p.module_code
- AND s.scenario_code = p.scenario_code
- AND s.rule_code = i.rule_code
- AND s.is_enabled = 1
- WHERE p.module_code = @ModuleCode
- AND p.scenario_code = @ScenarioCode
- AND p.is_enabled = 1
- AND i.is_enabled = 1
- AND (p.effective_from IS NULL OR p.effective_from <= @Now)
- AND (p.effective_to IS NULL OR p.effective_to >= @Now)
- AND (
- (p.tenant_id = 0 AND p.factory_id = 0)
- OR (p.tenant_id = @TenantId AND p.factory_id = 0)
- OR (p.tenant_id = @TenantId AND p.factory_id = @FactoryId)
- )
- ORDER BY p.tenant_id, p.factory_id, p.version, i.sort_no, i.id
- """,
- new SugarParameter("@ModuleCode", moduleCode),
- new SugarParameter("@ScenarioCode", scenarioCode),
- new SugarParameter("@TenantId", tenantId),
- new SugarParameter("@FactoryId", normalizedFactoryId),
- new SugarParameter("@Now", now));
- return rows
- .GroupBy(x => new { x.ProfileId, x.TenantId, x.FactoryId, x.ProfileCode })
- .Select(x => new RuleConfigLayer
- {
- Scope = BuildScope(x.Key.ProfileCode, x.Key.TenantId, x.Key.FactoryId),
- Priority = BuildPriority(x.Key.TenantId, x.Key.FactoryId),
- Items = x
- .OrderBy(item => item.SortNo)
- .Select(item => new RuleConfigItem
- {
- RuleCode = item.RuleCode,
- ValueType = item.ValueType,
- RuleValue = item.RuleValue,
- Required = item.Required == 1,
- IsEnabled = item.ItemEnabled == 1,
- IsHighRiskSwitch = false
- })
- .ToList()
- })
- .OrderBy(x => x.Priority)
- .ToList();
- }
- private static int BuildPriority(long tenantId, long factoryId)
- {
- if (tenantId > 0 && factoryId > 0) return 300;
- if (tenantId > 0) return 200;
- return 100;
- }
- private static string BuildScope(string profileCode, long tenantId, long factoryId)
- {
- if (tenantId > 0 && factoryId > 0) return $"factory:{tenantId}:{factoryId}:{profileCode}";
- if (tenantId > 0) return $"tenant:{tenantId}:{profileCode}";
- return $"global:{profileCode}";
- }
- private sealed class RuleConfigDbRow
- {
- public long ProfileId { get; set; }
- public long TenantId { get; set; }
- public long FactoryId { get; set; }
- public string ProfileCode { get; set; } = string.Empty;
- public string RuleCode { get; set; } = string.Empty;
- public string ValueType { get; set; } = string.Empty;
- public string? RuleValue { get; set; }
- public int ItemEnabled { get; set; }
- public int SortNo { get; set; }
- public int? Required { get; set; }
- public string? UiControl { get; set; }
- }
- }
|