using Admin.NET.Plugin.AiDOP.Entity.S8; using Admin.NET.Plugin.AiDOP.Infrastructure; namespace Admin.NET.Plugin.AiDOP.Service.S8; public class S8AlertRuleService : ITransient { private readonly SqlSugarRepository _rep; public S8AlertRuleService(SqlSugarRepository rep) => _rep = rep; public async Task> ListAsync(long tenantId, long factoryId) => await _rep.AsQueryable() .Where(x => x.TenantId == tenantId && x.FactoryId == factoryId) .ToListAsync(); // ================================================================================ // S8-STEP6D-CFG-ALERT-LEGACY-WRITE-GUARD-CERT-1:写入口已退役(LEGACY_SHELL 契约)。 // // 产品裁决:ado_s8_alert_rule = 历史兼容只读数据;运行时告警判定权威 = ado_s8_watch_rule。 // 依据(运行时实证):三个 evaluator 的 EvaluateAsync 都接收 IReadOnlyList, // 但该形参在每个文件中只出现一次(声明本身),方法体读取次数 = 0;生产 tick 在 // S8WatchSchedulerService.cs:1294 查一次后转手丢弃(NO-OP READ);唯一真正读取其字段的 // LoadExecutionRulesAsync 只能经 AdoS8WatchDebugController 抵达,而后者生产默认 404。 // // 因此继续开放写入 = 让调用方保存一份**永远不会生效**的配置。仅在前端隐藏按钮不够: // API 仍可直接写入。故在此服务层(写入的唯一收敛点)统一拒绝。 // // ⚠️ 抛出发生在**任何 DB 访问之前**:不做存在性查询、不做 LoadScopedAsync。 // 这既保证零 DB 触碰,也保证 PUT/DELETE 对任意 id(含越权 id)一律返回 410 而非 404 // —— 「这个能力没了」优先于「这条记录不属于你」,避免用越权探测反推他租户数据是否存在。 // // 保留三个方法签名(而非删除)是**硬约束**:S8TenantIsolationContractTests // 用反射断言带 S8TrustedScope 的写入口存在、且无 scope 的旧重载不存在。 // // 读路径完全保留(ListAsync);调度器与 debug 链走 SqlSugarRepository // 直连仓储、不经本服务,故不受影响。 // ================================================================================ public Task CreateAsync(AdoS8AlertRule body, S8TrustedScope scope) => throw new S8WriteRetiredException(); public Task UpdateAsync(long id, AdoS8AlertRule body, S8TrustedScope scope) => throw new S8WriteRetiredException(); public Task DeleteAsync(long id, S8TrustedScope scope) => throw new S8WriteRetiredException(); }