S8OperatorBindingService.cs 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. using Admin.NET.Plugin.AiDOP.Dto.S8;
  2. using Admin.NET.Plugin.AiDOP.Entity.S0.Warehouse;
  3. using Admin.NET.Plugin.AiDOP.Infrastructure.S8;
  4. namespace Admin.NET.Plugin.AiDOP.Service.S8;
  5. /// <summary>
  6. /// S8 配置页:操作员(员工)↔ 系统账号绑定。
  7. /// 仅维护 EmployeeMaster.SysUserId 的 1:1 弱关联;不动 SysUser/SysRole/SysUserRole。
  8. /// </summary>
  9. public class S8OperatorBindingService : ITransient
  10. {
  11. private readonly SqlSugarRepository<AdoS0EmployeeMaster> _empRep;
  12. private readonly SqlSugarRepository<SysUser> _sysUserRep;
  13. private readonly UserManager _userManager;
  14. public S8OperatorBindingService(
  15. SqlSugarRepository<AdoS0EmployeeMaster> empRep,
  16. SqlSugarRepository<SysUser> sysUserRep,
  17. UserManager userManager)
  18. {
  19. _empRep = empRep;
  20. _sysUserRep = sysUserRep;
  21. _userManager = userManager;
  22. }
  23. public async Task<List<AdoS8OperatorBindingRowDto>> ListAsync(long? factoryRefId, string? bindStatus, string? keyword)
  24. {
  25. // ClearFilter:EmployeeMaster.tenant_id 属 S0 域租户,与登录 token TenantId 不一致(同 BUG-S8-EMPLOYEES-TENANT-FILTER-001 第 3 处)。
  26. // 安全边界:保留 factoryRefId 显式过滤;仅放开租户全局过滤器;不影响 SysUser 查询。
  27. var emps = await _empRep.AsQueryable().ClearFilter()
  28. .WhereIF(factoryRefId.HasValue, x => x.FactoryRefId == factoryRefId!.Value)
  29. .WhereIF(!string.IsNullOrWhiteSpace(keyword),
  30. x => x.Employee.Contains(keyword!) || (x.Name != null && x.Name.Contains(keyword!)))
  31. .Take(500)
  32. .Select(x => new { x.Id, x.Name, x.Employee, x.FactoryRefId, x.SysUserId })
  33. .ToListAsync();
  34. var sysUserIds = emps.Where(e => e.SysUserId.HasValue && e.SysUserId.Value > 0)
  35. .Select(e => e.SysUserId!.Value).Distinct().ToList();
  36. var userMap = sysUserIds.Count == 0
  37. ? new Dictionary<long, string?>()
  38. : (await _sysUserRep.AsQueryable()
  39. .Where(u => sysUserIds.Contains(u.Id) && u.TenantId == _userManager.TenantId)
  40. .Select(u => new { u.Id, u.RealName, u.Account })
  41. .ToListAsync())
  42. .ToDictionary(u => u.Id, u => string.IsNullOrWhiteSpace(u.RealName) ? u.Account : u.RealName);
  43. var rows = emps.Select(e =>
  44. {
  45. var bound = e.SysUserId.HasValue && e.SysUserId.Value > 0 && userMap.ContainsKey(e.SysUserId.Value);
  46. return new AdoS8OperatorBindingRowDto
  47. {
  48. EmployeeId = e.Id,
  49. EmpCode = e.Employee,
  50. Name = e.Name,
  51. FactoryRefId = e.FactoryRefId,
  52. SysUserId = e.SysUserId,
  53. SysUserName = bound ? userMap[e.SysUserId!.Value] : null,
  54. BindStatus = bound ? "BOUND" : "UNBOUND"
  55. };
  56. });
  57. if (!string.IsNullOrWhiteSpace(bindStatus) && (bindStatus == "BOUND" || bindStatus == "UNBOUND"))
  58. rows = rows.Where(r => r.BindStatus == bindStatus);
  59. return rows
  60. .OrderBy(r => r.BindStatus == "BOUND" ? 0 : 1)
  61. .ThenBy(r => r.EmpCode)
  62. .ToList();
  63. }
  64. public async Task<List<AdoS8ConfigSysUserRowDto>> ListSysUsersAsync(string? keyword, long? excludeEmployeeId)
  65. {
  66. var tenantId = _userManager.TenantId;
  67. var users = await _sysUserRep.AsQueryable()
  68. .Where(u => u.TenantId == tenantId)
  69. .WhereIF(!string.IsNullOrWhiteSpace(keyword),
  70. u => u.Account.Contains(keyword!) || u.RealName.Contains(keyword!))
  71. .Take(200)
  72. .Select(u => new { u.Id, u.Account, u.RealName, u.Status })
  73. .ToListAsync();
  74. if (users.Count == 0) return new();
  75. // 标注哪些 sysUser 已被其它 employee 绑定(用于前端禁选/隐藏);
  76. // 当前 employee 自己绑定的 sysUser 在 alreadyBoundEmployeeId == excludeEmployeeId 时会被前端放行。
  77. var ids = users.Select(u => u.Id).ToList();
  78. var bound = await _empRep.AsQueryable()
  79. .Where(e => e.SysUserId.HasValue && ids.Contains(e.SysUserId!.Value))
  80. .Select(e => new { e.Id, e.SysUserId })
  81. .ToListAsync();
  82. var boundMap = bound.GroupBy(b => b.SysUserId!.Value)
  83. .ToDictionary(g => g.Key, g => g.First().Id);
  84. return users.Select(u => new AdoS8ConfigSysUserRowDto
  85. {
  86. Id = u.Id,
  87. Account = u.Account,
  88. RealName = u.RealName,
  89. Status = (int)u.Status,
  90. AlreadyBoundEmployeeId = boundMap.TryGetValue(u.Id, out var empId) ? empId : null
  91. }).ToList();
  92. }
  93. public async Task<AdoS8OperatorBindingRowDto> BindAsync(AdoS8OperatorBindingCreateDto dto)
  94. {
  95. if (dto.EmployeeId <= 0) throw new S8BizException("员工 ID 不能为空");
  96. if (dto.SysUserId <= 0) throw new S8BizException("系统账号 ID 不能为空");
  97. var emp = await _empRep.GetFirstAsync(x => x.Id == dto.EmployeeId)
  98. ?? throw new S8BizException("员工不存在");
  99. var tenantId = _userManager.TenantId;
  100. var user = await _sysUserRep.GetFirstAsync(x => x.Id == dto.SysUserId && x.TenantId == tenantId)
  101. ?? throw new S8BizException("系统账号不存在或不在当前租户");
  102. // 同一 sysUser 不允许绑定到另一个 employee
  103. var conflict = await _empRep.GetFirstAsync(x => x.SysUserId == dto.SysUserId && x.Id != dto.EmployeeId);
  104. if (conflict != null)
  105. throw new S8BizException($"系统账号 {user.Account} 已绑定至其它员工 {conflict.Name ?? conflict.Employee}");
  106. emp.SysUserId = dto.SysUserId;
  107. emp.UpdateTime = DateTime.Now;
  108. emp.UpdateUser = _userManager.Account;
  109. await _empRep.AsUpdateable(emp)
  110. .UpdateColumns(it => new { it.SysUserId, it.UpdateTime, it.UpdateUser })
  111. .ExecuteCommandAsync();
  112. return new AdoS8OperatorBindingRowDto
  113. {
  114. EmployeeId = emp.Id,
  115. EmpCode = emp.Employee,
  116. Name = emp.Name,
  117. FactoryRefId = emp.FactoryRefId,
  118. SysUserId = dto.SysUserId,
  119. SysUserName = string.IsNullOrWhiteSpace(user.RealName) ? user.Account : user.RealName,
  120. BindStatus = "BOUND"
  121. };
  122. }
  123. public async Task UnbindAsync(long employeeId)
  124. {
  125. if (employeeId <= 0) throw new S8BizException("员工 ID 不能为空");
  126. var emp = await _empRep.GetFirstAsync(x => x.Id == employeeId)
  127. ?? throw new S8BizException("员工不存在");
  128. if (!emp.SysUserId.HasValue) return;
  129. emp.SysUserId = null;
  130. emp.UpdateTime = DateTime.Now;
  131. emp.UpdateUser = _userManager.Account;
  132. await _empRep.AsUpdateable(emp)
  133. .UpdateColumns(it => new { it.SysUserId, it.UpdateTime, it.UpdateUser })
  134. .ExecuteCommandAsync();
  135. }
  136. }