using Admin.NET.Plugin.AiDOP.Entity.S8;
using Admin.NET.Plugin.AiDOP.Service.S8.Rules;
namespace Admin.NET.Plugin.AiDOP.Service.S8;
///
/// S8-STEP6E-CFG-WATCH-ORCHESTRATION-DEDUPE-UI-CERT-1:Watch 主链的**业务建单边界**。
///
/// 只抽象一件事——「就一次命中提交一次异常报告」。这是
/// S8WatchSchedulerService 调用 S8ManualReportService 的**唯一最窄位置**
/// (抗抖门通过之后、计数回写/检测日志/通知派发之前)。
///
/// 为什么需要它:Watch 主链的编排(数据源加载 → evaluator → 检测状态 → 抗抖 →
/// dedupe)必须能在认证中真实跑通,但真实建单会连带写 ado_s8_exception、
/// ado_s8_exception_timeline,并触发 ApprovalFlow 建实例/任务/日志——这些既不可回滚
/// (只有 exception + timeline 在同一事务内,其余全是自动提交),也依赖部门主数据。
/// 把边界抽出来后,认证 Host 可以只替换这一个接口,从而做到:
/// 上游全真实、建单意图可核对、业务副作用为零。
///
/// ⚠️ 生产语义不得改变:正式实现只做一次纯委派,不改参数、不改事务、
/// 不改异常处理、不改 dedupe、不改通知、不改返回值语义。
///
public interface IS8ExceptionReportDispatcher
{
/// 把一次规则命中提交为标准异常单;参数与返回值同 。
Task CreateFromHitAsync(long tenantId, long factoryId, S8RuleHit hit);
}
///
/// 正式实现:**纯委派**,零业务逻辑。Furion 按 ITransient 自动注册到
/// ,无需改动 Startup。
///
public sealed class S8ExceptionReportDispatcher : IS8ExceptionReportDispatcher, ITransient
{
private readonly S8ManualReportService _manualReportService;
public S8ExceptionReportDispatcher(S8ManualReportService manualReportService) =>
_manualReportService = manualReportService;
public Task CreateFromHitAsync(long tenantId, long factoryId, S8RuleHit hit) =>
_manualReportService.CreateFromHitAsync(tenantId, factoryId, hit);
}