| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152 |
- using Admin.NET.Plugin.AiDOP.Entity.S0.Manufacturing;
- using Admin.NET.Plugin.AiDOP.Entity.S0.Sales;
- using Admin.NET.Plugin.AiDOP.Entity.S0.Supply;
- using Admin.NET.Plugin.AiDOP.Entity.S0.Warehouse;
- using Microsoft.Extensions.Logging;
- using SqlSugar;
- namespace Admin.NET.Plugin.AiDOP.Infrastructure;
- /// <summary>
- /// S0 删除前下游引用检查。
- /// 每个主数据主表在 DELETE 前调用对应方法,若存在下游引用则拒绝删除。
- /// 返回 null 表示无引用可安全删除;非空表示 (引用数, 下游表语义描述)。
- /// </summary>
- public sealed class AdoS0ReferenceChecker
- {
- private readonly ISqlSugarClient _db;
- private readonly ILogger<AdoS0ReferenceChecker> _logger;
- public AdoS0ReferenceChecker(ISqlSugarClient db, ILogger<AdoS0ReferenceChecker> logger)
- {
- _db = db;
- _logger = logger;
- }
- /// <summary>
- /// 容错包装:对已知 schema 不对齐表(如 srm_purchase)的查询失败时不阻断删除,只记日志。
- /// 独立问题由 BUG-S0-SRMPURCHASE-SCHEMA-MISMATCH 单独修。
- /// </summary>
- private async Task<int> SafeCountAsync<T>(Func<ISugarQueryable<T>> queryFactory, string tag) where T : class, new()
- {
- try
- {
- return await queryFactory().CountAsync();
- }
- catch (Exception ex)
- {
- _logger.LogWarning(ex, "[AdoS0ReferenceChecker] skip {Tag} check due to schema mismatch", tag);
- return 0;
- }
- }
- /// <summary>
- /// 保存端:物料(ItemMaster)存在性检查。
- /// 仅校验"目标主表存在该 itemNum",不做 factoryRefId/domain 作用域校验(B2 归口)。
- /// 空值视为"未填"直接放行,由调用方先做必填校验。
- /// </summary>
- public async Task<bool> MaterialExistsAsync(string? itemNum)
- {
- if (string.IsNullOrWhiteSpace(itemNum)) return true;
- return await _db.Queryable<AdoS0ItemMaster>()
- .Where(x => x.ItemNum == itemNum)
- .AnyAsync();
- }
- /// <summary>
- /// 保存端:工作中心(WorkCtrMaster)存在性检查。
- /// 注意跨命名:业务键是 WorkCtrMaster.WorkCtr(非 WorkCenterCode)。
- /// 空值视为"未填"直接放行;作用域(Domain/Factory)一致性归 B2。
- /// </summary>
- public async Task<bool> WorkCenterExistsAsync(string? workCenterCode)
- {
- if (string.IsNullOrWhiteSpace(workCenterCode)) return true;
- return await _db.Queryable<AdoS0WorkCtrMaster>()
- .Where(x => x.WorkCtr == workCenterCode)
- .AnyAsync();
- }
- /// <summary>
- /// 物料(ItemMaster)删除前引用检查。
- /// 覆盖下游:工艺路线明细 / 物料替代关系 / 货源清单(SRM 采购)。
- /// </summary>
- public async Task<(int Count, string Table)?> MaterialReferencesAsync(string? itemNum)
- {
- if (string.IsNullOrWhiteSpace(itemNum)) return null;
- var routing = await _db.Queryable<AdoS0MfgRoutingOpDetail>()
- .Where(x => x.MaterialCode == itemNum)
- .CountAsync();
- if (routing > 0) return (routing, "工艺路线明细 (RoutingOpDetail)");
- var sub = await _db.Queryable<AdoS0ItemSubstituteDetail>()
- .Where(x => x.ItemNum == itemNum || x.SubstituteItem == itemNum)
- .CountAsync();
- if (sub > 0) return (sub, "物料替代关系 (ItemSubstituteDetail)");
- var srm = await SafeCountAsync(
- () => _db.Queryable<AdoS0SrmPurchase>().Where(x => x.MaterialCode == itemNum),
- "SrmPurchase.MaterialCode");
- if (srm > 0) return (srm, "货源清单 (SrmPurchase)");
- return null;
- }
- /// <summary>
- /// 供应商(SuppMaster)删除前引用检查。
- /// 覆盖下游:货源清单(SRM 采购)。
- /// </summary>
- public async Task<(int Count, string Table)?> SupplierReferencesAsync(string? supp)
- {
- if (string.IsNullOrWhiteSpace(supp)) return null;
- var srm = await SafeCountAsync(
- () => _db.Queryable<AdoS0SrmPurchase>().Where(x => x.Supplier == supp || x.SupplierNumber == supp),
- "SrmPurchase.Supplier");
- if (srm > 0) return (srm, "货源清单 (SrmPurchase)");
- return null;
- }
- /// <summary>
- /// 库位(LocationMaster)删除前引用检查。
- /// 覆盖下游:货架 / 物料默认库位。
- /// </summary>
- public async Task<(int Count, string Table)?> LocationReferencesAsync(string? location)
- {
- if (string.IsNullOrWhiteSpace(location)) return null;
- var shelf = await _db.Queryable<AdoS0LocationShelfMaster>()
- .Where(x => x.Location == location)
- .CountAsync();
- if (shelf > 0) return (shelf, "货架 (LocationShelfMaster)");
- var item = await _db.Queryable<AdoS0ItemMaster>()
- .Where(x => x.Location == location)
- .CountAsync();
- if (item > 0) return (item, "物料默认库位 (ItemMaster.Location)");
- return null;
- }
- /// <summary>
- /// 部门(DepartmentMaster)删除前引用检查。
- /// 覆盖下游:员工所属部门 / 工作中心所属部门。
- /// </summary>
- public async Task<(int Count, string Table)?> DepartmentReferencesAsync(string? department)
- {
- if (string.IsNullOrWhiteSpace(department)) return null;
- var emp = await _db.Queryable<AdoS0EmployeeMaster>()
- .Where(x => x.Department == department)
- .CountAsync();
- if (emp > 0) return (emp, "员工 (EmployeeMaster)");
- var wc = await _db.Queryable<AdoS0WorkCtrMaster>()
- .Where(x => x.Department == department)
- .CountAsync();
- if (wc > 0) return (wc, "工作中心 (WorkCtrMaster)");
- return null;
- }
- }
|