S8MasterDataAdapter.cs 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. using Admin.NET.Plugin.AiDOP.Entity.S0.Manufacturing;
  2. using Admin.NET.Plugin.AiDOP.Entity.S0.Warehouse;
  3. using Admin.NET.Plugin.AiDOP.Infrastructure;
  4. using Admin.NET.Plugin.AiDOP.Infrastructure.S8;
  5. namespace Admin.NET.Plugin.AiDOP.Service.S8;
  6. /// <summary>
  7. /// S8 主数据下拉:优先复用 S0 已落库主数据语义(部门等),无则返回空列表,不把「下拉查询」误建成独立业务表。
  8. ///
  9. /// <para><b>S8-P0-3-MASTERDATA-TRUSTED-SCOPE-1 作用域合同</b>:三个入口的工厂边界
  10. /// **一律由 <see cref="S8TrustedScopeResolver"/> 从认证身份解析**,
  11. /// 客户端传入的 <c>factoryRefId</c> 只被视为筛选意图,**当前实现直接忽略、不承担授权作用**。</para>
  12. ///
  13. /// <para><b>S8-SYSUSER-ONLY-1</b>:<c>GetEmployeesAsync</c> 已退役,由
  14. /// <see cref="GetOperatorUsersAsync"/> 取代 —— S8 的"人"只有系统账号一种。</para>
  15. /// </summary>
  16. public class S8MasterDataAdapter : ITransient
  17. {
  18. private readonly SqlSugarRepository<AdoS0DepartmentMaster> _deptRep;
  19. private readonly SqlSugarRepository<AdoS0LineMaster> _lineRep;
  20. private readonly SqlSugarRepository<SysUser> _sysUserRep;
  21. private readonly IS8UserScopeValidator _userScope;
  22. private readonly IS8ExceptionActionAuthorizer _actionAuthorizer;
  23. public S8MasterDataAdapter(
  24. SqlSugarRepository<AdoS0DepartmentMaster> deptRep,
  25. SqlSugarRepository<AdoS0LineMaster> lineRep,
  26. SqlSugarRepository<SysUser> sysUserRep,
  27. IS8UserScopeValidator userScope,
  28. IS8ExceptionActionAuthorizer actionAuthorizer)
  29. {
  30. _deptRep = deptRep;
  31. _lineRep = lineRep;
  32. _sysUserRep = sysUserRep;
  33. _userScope = userScope;
  34. _actionAuthorizer = actionAuthorizer;
  35. }
  36. /// <summary>
  37. /// S8-TENANT-ONLY-BATCH6:主数据下拉的边界由 <c>factory_ref_id</c> 改为 <c>tenant_id</c>。
  38. ///
  39. /// <para><b>这不是等价改写,是修一个真实越权。</b>真库取证(aidopdev,只读):
  40. /// <c>factory_ref_id = 1000</c> 同时属于租户 <c>824585161322565</c> 与 <c>797403760988229</c>——
  41. /// 按工厂过滤会把另一租户的 613 名员工 / 210 个部门直接列进本租户的下拉框。
  42. /// 工厂号从来就不是隔离维度,之前只是没人对着数据看过。</para>
  43. ///
  44. /// <para>可用性同样已验证:三张表 <c>tenant_id</c> 100% 非空
  45. /// (EmployeeMaster 1105 行 / DepartmentMaster 239 行 / LineMaster 169 行,NULL 均为 0),
  46. /// 每个租户都有非空主数据,改判据不会把下拉打空。</para>
  47. /// </summary>
  48. public async Task<object> GetDepartmentsAsync(S8TrustedScope scope) =>
  49. await _deptRep.AsQueryable()
  50. .Where(x => x.TenantId == scope.TenantId)
  51. .Take(500)
  52. .Select(x => new { id = x.Id, code = x.Department, name = x.Descr ?? x.Department })
  53. .ToListAsync();
  54. /// <summary>
  55. /// S8 人员选择器的<b>唯一</b>数据源:当前租户的<b>启用系统账号</b>。
  56. ///
  57. /// <para>S8-SYSUSER-ONLY-1:替代原 <c>GetEmployeesAsync</c>。原实现列的是
  58. /// <c>EmployeeMaster</c>,每行还挂一个 <c>bindStatus: BOUND/UNBOUND</c> —— 也就是说,
  59. /// 下拉框里绝大多数选项<b>选了也没用</b>:真库 1105 名员工只有 8 个绑了账号,
  60. /// 选中未绑定的员工后通知发不出去、审批流转办静默跳过,而页面上没有任何提示。
  61. /// 现在列的就是账号本身,<b>不存在"选了但用不了"的选项</b>。</para>
  62. ///
  63. /// <para>边界只有租户,与 <see cref="GetDepartmentsAsync"/> 同因:
  64. /// <c>factory_ref_id = 1000</c> 横跨两个租户,工厂从来不是隔离维度。</para>
  65. /// </summary>
  66. /// <param name="requiredAction">
  67. /// S8-UAT-APPROVAL-REPAIR-1:只列具备该动作权限的账号(如挑选检验人时传
  68. /// <c>EXCEPTION_VERIFY</c>)。为空则返回全部启用账号。
  69. /// <para>存在的意义是让「页面上能选到的人」与「后端会放行的人」一致 ——
  70. /// 否则用户会选中一个看起来正常、提交却被拒的账号。</para>
  71. /// </param>
  72. public async Task<object> GetOperatorUsersAsync(S8TrustedScope scope, string? requiredAction = null)
  73. {
  74. var users = await _userScope.ValidateUsersAsync(
  75. scope.TenantId,
  76. await _sysUserRep.AsQueryable().ClearFilter()
  77. .Where(u => u.TenantId == scope.TenantId)
  78. .Take(S8UserScopeValidator.MaxHydrationKeys)
  79. .Select(u => u.Id)
  80. .ToListAsync());
  81. if (!string.IsNullOrWhiteSpace(requiredAction))
  82. {
  83. var allowed = new List<S8OperatorUser>();
  84. foreach (var u in users)
  85. {
  86. var auth = await _actionAuthorizer.AuthorizeAsync(scope.TenantId, u.UserId, requiredAction!);
  87. if (auth.Allowed) allowed.Add(u);
  88. }
  89. users = allowed;
  90. }
  91. return users
  92. .OrderBy(u => u.DisplayName, StringComparer.Ordinal)
  93. .Select(u => new { id = u.UserId, name = u.DisplayName, account = u.Account })
  94. .ToList();
  95. }
  96. /// <summary>产线下拉,边界同 <see cref="GetDepartmentsAsync"/>。</summary>
  97. public async Task<object> GetLinesAsync(S8TrustedScope scope) =>
  98. await _lineRep.AsQueryable()
  99. .Where(x => x.TenantId == scope.TenantId)
  100. .Take(500)
  101. .Select(x => new { id = x.Id, code = x.Line, name = x.Describe ?? x.Line })
  102. .ToListAsync();
  103. }