namespace Admin.NET.Plugin.AiDOP.Dto.S8; /// /// S8-RULE-GOVERNANCE-BATCH4:监控规则配置页的**只读行模型**。 /// /// 为什么需要它:在此之前 GET /config/watch-rules 直接返回实体 /// AdoS8WatchRule,前端只能拿到数据库投影列。而规则的业务语义 /// (规则名称、业务说明、异常类型、判定口径)自 Batch 1 起只存在于代码定义里, /// 数据库上根本没有对应列 —— 页面想把「基础定义」做成只读展示,就无处取数。 /// 唯一的替代是前端按 ruleCode 硬编码,那等于把代码定义抄了第二份,必然漂移。 /// /// 结构刻意分三段,与页面的三个区一一对应,让「谁能改什么」在类型上就看得见: /// /// Definition:代码定义投影,全只读; /// Parameters:租户可调的白名单参数; /// Runtime:调度运行态,系统写、页面只读。 /// /// /// 刻意不含的字段LockToken / LockedBy / LockUntil / /// RunningStartedAt —— 调度租约是内部实现细节,规则管理员不需要, /// 暴露出去只会让人以为可以手动干预。ParamsJson 原文同理不外发: /// 页面拿到的是解析后的强类型参数,不是那段自由文本。 /// public sealed class S8WatchRuleRowDto { // ─────────────── 身份与归属 ─────────────── public long Id { get; init; } public long TenantId { get; init; } /// 历史兼容列。Tenant-only 改造(Batch 5/6)后将退出全部隔离语义。 public long FactoryId { get; init; } /// 规则编码。定义与运行策略共同的身份。 public string RuleCode { get; init; } = string.Empty; // ─────────────── 代码定义投影(全只读)─────────────── /// /// 该规则在当前代码版本中没有定义(孤儿行)。 /// 页面据此禁用参数保存 / 启用 / 预演,并提示"当前版本未找到规则定义", /// 而不是拿着一堆 null 崩掉或假装它是条正常规则。 /// public bool DefinitionMissing { get; init; } /// 规则名称。孤儿行为 null,页面回落显示 public string? DisplayName { get; init; } /// 业务说明。 public string? Description { get; init; } /// 数据集编码(内部编码,详情页可见)。 public string? DatasetCode { get; init; } /// 数据集业务名称。优先展示它而不是编码。 public string? DatasetDisplayName { get; init; } public string? RuleType { get; init; } public string? RuleMechanism { get; init; } /// 业务对象类型。 public string? SourceObjectType { get; init; } public string? SceneCode { get; init; } public string? StageCode { get; init; } public string? OrderFlowCode { get; init; } /// 命中后建立的异常类型。自 Batch 1 起只存在于代码定义,数据库上已无此信息。 public string? ExceptionTypeCode { get; init; } /// 判定口径的业务语言说明。不含表名 / 列名 / SQL。 public string? JudgementSummary { get; init; } /// 视为「已完成」的状态集合。 public IReadOnlyList CompletedStates { get; init; } = Array.Empty(); /// 去重身份的业务语言说明。 public string? DedupIdentitySummary { get; init; } /// 该规则开放调整的参数取值域。孤儿行为 null。 public S8RuleParameterPolicyDto? AllowedParameters { get; init; } // ─────────────── 租户运行参数(可调)─────────────── public bool Enabled { get; init; } public string Severity { get; init; } = string.Empty; public int PollIntervalSeconds { get; init; } public int TriggerCountRequired { get; init; } public int RecoverCountRequired { get; init; } /// 宽限分钟。解析自 params_json 的 B 类参数,页面无需接触原文。 public int GraceMinutes { get; init; } public long? DefaultOccurrenceDeptId { get; init; } public long? DefaultResponsibleDeptId { get; init; } // ─────────────── 运行态(只读)─────────────── public DateTime? NextRunAt { get; init; } public DateTime? LastRunAt { get; init; } public string? LastStatus { get; init; } public string? LastError { get; init; } public int? LastDurationMs { get; init; } public string? LastRunId { get; init; } public int ConsecutiveFailureCount { get; init; } public DateTime? PausedUntil { get; init; } public string? PauseReason { get; init; } public DateTime CreatedAt { get; init; } public DateTime? UpdatedAt { get; init; } } /// /// 参数取值域,供页面渲染输入控件的 min / max / 默认值与可选项。 /// /// 下发取值域而不是让前端写死:取值域随规则定义走 /// (不同规则对"宽限多久算合理"的判断本就不同),前端写死会在新增规则时静默不一致。 /// 服务端仍然是唯一权威 —— 这里只是让 UI 能提前拦住明显越界,不是把校验外包给前端。 /// public sealed class S8RuleParameterPolicyDto { public int PollIntervalSecondsMin { get; init; } public int PollIntervalSecondsMax { get; init; } public int TriggerCountRequiredMin { get; init; } public int TriggerCountRequiredMax { get; init; } public int RecoverCountRequiredMin { get; init; } public int RecoverCountRequiredMax { get; init; } public int GraceMinutesMin { get; init; } /// 宽限分钟无业务上限;下发 int.MaxValue 时前端不应渲染成有意义的上界。 public int GraceMinutesMax { get; init; } public IReadOnlyList AllowedSeverities { get; init; } = Array.Empty(); /// 是否允许配置部门兜底。 public bool AllowsDepartmentDefaults { get; init; } /// /// S8-RULE-READINESS-1:两个默认部门是否为启用前置条件。 /// 前端据此在启用前拦截并提示具体缺项;**后端仍独立校验**,前端只是 UX。 /// public bool RequiresDepartmentDefaultsForEnable { get; init; } }