MdpStdFullReplace.cs 3.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667
  1. using System.Text.RegularExpressions;
  2. using SqlSugar;
  3. namespace Admin.NET.Plugin.AiDOP.DataPlatform;
  4. /// <summary>
  5. /// 标准层「当前 active 源 FULL Replace」局部工具(S5/S6/S7 列表双源 Phase 1 用)。
  6. ///
  7. /// 用途:切源/FULL 刷新时,保证 std = 当前 active source 的完整结果集,消除旧源独有业务键残留。
  8. /// 语义:<b>先由调用方 PullAll 成功灌 stg,再</b>在单事务内 DELETE 当前 tenant(+可选业务条件) 的 std,
  9. /// 随后执行「仅当前 source_system」的 INSERT(由调用方以委托传入其对象专属 transform SQL)。
  10. ///
  11. /// 安全边界:
  12. /// - 仅本工具删除 std;不 TRUNCATE 整表、不跨 tenant、不跨业务类型(extraWhere 由调用方精确限定)。
  13. /// - DELETE + INSERT 同一本库事务;任一失败回滚,旧 std 保持不变。
  14. /// - 必须在 Pull 成功之后调用;Pull 失败应让异常在调用方先抛出,绝不先删 std。
  15. /// - 非数据平台核心(不改 Factory/Executor/Writer),仅一层薄事务封装。
  16. /// </summary>
  17. internal static class MdpStdFullReplace
  18. {
  19. // ⚠️ 适用边界(Phase 2 防御性复核确认):本工具的 DELETE 仅按 tenant_id(+可选 extraWhere) 限定,
  20. // 因此**仅适用于 tenant 独占的业务专表 std**(如 mdp_std_purchase_receipt / _production_receipt / _ipqc_inspection,
  21. // 实测各仅其转换服务写入)。若某 std 与其它业务共享,必须由调用方传入精确 extraWhere 业务分区条件,否则会误删他业务行。
  22. private static readonly Regex IdentifierRe = new(@"^[A-Za-z0-9_]+$", RegexOptions.Compiled);
  23. /// <summary>
  24. /// 单事务内 FULL Replace 当前 tenant 的 std:先 DELETE,再执行 source 过滤的 INSERT 委托。
  25. /// </summary>
  26. /// <param name="db">调用方自身注入的 ISqlSugarClient(同一 scope,事务覆盖委托内的写入)。</param>
  27. /// <param name="stdTable">标准层表名(标识符白名单校验)。</param>
  28. /// <param name="tenantId">精确隔离的租户 id。</param>
  29. /// <param name="extraWhere">可选附加删除条件(如业务类型列),已由调用方保证安全,不含用户输入。</param>
  30. /// <param name="insertScopedAsync">仅写入当前 source_system 结果的 INSERT 委托,返回写入行数。</param>
  31. public static async Task<int> ReplaceAsync(
  32. ISqlSugarClient db,
  33. string stdTable,
  34. long tenantId,
  35. string? extraWhere,
  36. Func<Task<int>> insertScopedAsync,
  37. CancellationToken cancellationToken = default)
  38. {
  39. if (string.IsNullOrWhiteSpace(stdTable) || !IdentifierRe.IsMatch(stdTable))
  40. throw new InvalidOperationException($"非法 std 表名:{stdTable}");
  41. cancellationToken.ThrowIfCancellationRequested();
  42. var where = string.IsNullOrWhiteSpace(extraWhere)
  43. ? "tenant_id=@__tenant"
  44. : $"tenant_id=@__tenant AND {extraWhere}";
  45. await db.Ado.BeginTranAsync();
  46. try
  47. {
  48. await db.Ado.ExecuteCommandAsync(
  49. $"DELETE FROM `{stdTable}` WHERE {where}",
  50. new SugarParameter("@__tenant", tenantId));
  51. var rows = await insertScopedAsync();
  52. await db.Ado.CommitTranAsync();
  53. return rows;
  54. }
  55. catch
  56. {
  57. await db.Ado.RollbackTranAsync();
  58. throw;
  59. }
  60. }
  61. }