using Admin.NET.Plugin.AiDOP.MaterialWarehouse.InventoryPosting.Entity; namespace Admin.NET.Plugin.AiDOP.MaterialWarehouse.InventoryPosting.Idempotency; /// /// IQC 过账头幂等存储抽象。唯一性由 (tenant_id, domain_code, fbillno) 的 DB UNIQUE 保证; /// TryInsertAsync 依赖唯一约束原子占位——**不是** SELECT-不存在-INSERT(无并发窗口)。 /// 生产实现走 SqlSugar + duplicate-key(1062) 捕获;测试实现走线程安全内存。 /// public interface IIqcPostingStore { /// 尝试插入过账头(PROCESSING)。UNIQUE 冲突 → Inserted=false 并尽量带出既有记录。 Task TryInsertAsync(AdoIqcInventoryPosting posting); /// 按幂等键读取既有过账头。 Task GetAsync(long tenantId, string domainCode, string fbillNo); /// 业务提交成功/失败后回写过账状态(POSTED / FAILED)。 Task UpdateStatusAsync(AdoIqcInventoryPosting posting, string status); /// /// 原子抢占 stale PROCESSING(Phase 7C):仅当 status=PROCESSING 且 update_time < /// 时把 update_time 刷新为 并返回 true(心跳续租=抢占成功)。**单条件更新原子完成**, /// 不做 SELECT-then-UPDATE,杜绝竞态窗口——并发多路仅一路 rows=1。lease 未过期或已被他路抢占 → false。 /// Task TryRecoverStaleAsync(long tenantId, string domainCode, string fbillNo, DateTime staleBefore, DateTime now); }