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