Bläddra i källkod

fix(s8): map task operators to bound system users

S8 task-flow auth previously compared assignee/verifier (employee.Id) to
the JWT user id (sysUser.Id), which live in different namespaces and
made submit/approve/reject-verification reject every legitimate caller.

Add EmployeeMaster.SysUserId column and resolve assignee/verifier through
that mapping before comparing against UserManager.UserId. Error message
includes "或当前账号未绑定员工主数据" so unbound employees fail visibly.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
YY968XX 3 månader sedan
förälder
incheckning
afca5fece8

+ 3 - 0
server/Plugins/Admin.NET.Plugin.AiDOP/Entity/S0/Warehouse/AdoS0EmployeeMaster.cs

@@ -79,6 +79,9 @@ public class AdoS0EmployeeMaster
     [SugarColumn(ColumnName = "UpdateTime", ColumnDescription = "更新时间", IsNullable = true)]
     public DateTime? UpdateTime { get; set; }
 
+    [SugarColumn(ColumnName = "sys_user_id", ColumnDescription = "关联系统账号ID(SysUser.Id;为空表示未绑定)", ColumnDataType = "bigint", IsNullable = true)]
+    public long? SysUserId { get; set; }
+
     /// <summary>列表展示:部门说明(不落库,来自 DepartmentMaster 关联)</summary>
     [SugarColumn(IsIgnore = true)]
     public string? DepartDescr { get; set; }

+ 26 - 6
server/Plugins/Admin.NET.Plugin.AiDOP/Service/S8/S8TaskFlowService.cs

@@ -1,3 +1,4 @@
+using Admin.NET.Plugin.AiDOP.Entity.S0.Warehouse;
 using Admin.NET.Plugin.AiDOP.Entity.S8;
 using Admin.NET.Plugin.AiDOP.Infrastructure.S8;
 using Admin.NET.Plugin.ApprovalFlow.Service;
@@ -8,17 +9,20 @@ public class S8TaskFlowService : ITransient
 {
     private readonly SqlSugarRepository<AdoS8Exception> _rep;
     private readonly SqlSugarRepository<AdoS8ExceptionTimeline> _timelineRep;
+    private readonly SqlSugarRepository<AdoS0EmployeeMaster> _employeeRep;
     private readonly FlowEngineService _flowEngine;
     private readonly UserManager _userManager;
 
     public S8TaskFlowService(
         SqlSugarRepository<AdoS8Exception> rep,
         SqlSugarRepository<AdoS8ExceptionTimeline> timelineRep,
+        SqlSugarRepository<AdoS0EmployeeMaster> employeeRep,
         FlowEngineService flowEngine,
         UserManager userManager)
     {
         _rep = rep;
         _timelineRep = timelineRep;
+        _employeeRep = employeeRep;
         _flowEngine = flowEngine;
         _userManager = userManager;
     }
@@ -170,8 +174,8 @@ public class S8TaskFlowService : ITransient
     {
         var currentUserId = GetCurrentUserId();
         var e = await LoadAsync(id, tenantId, factoryId) ?? throw new S8BizException("异常不存在");
-        if (e.AssigneeId != currentUserId)
-            throw new S8BizException("只有当前处理人才能提交复检");
+        await EnsureCurrentUserIsOperatorAsync(e.AssigneeId, currentUserId,
+            "只有当前处理人才能提交复检(或当前账号未绑定员工主数据)");
         if (verifierId <= 0)
             throw new S8BizException("请选择检验人");
         if (!S8StatusRules.IsAllowedTransition(e.Status, "PENDING_VERIFICATION"))
@@ -199,8 +203,8 @@ public class S8TaskFlowService : ITransient
     {
         var currentUserId = GetCurrentUserId();
         var e = await LoadAsync(id, tenantId, factoryId) ?? throw new S8BizException("异常不存在");
-        if (e.VerifierId != currentUserId)
-            throw new S8BizException("只有指定检验人才能检验通过");
+        await EnsureCurrentUserIsOperatorAsync(e.VerifierId, currentUserId,
+            "只有指定检验人才能检验通过(或当前账号未绑定员工主数据)");
         if (!S8StatusRules.IsAllowedTransition(e.Status, "RESOLVED"))
             throw new S8BizException($"状态 {e.Status} 不可检验通过");
 
@@ -227,8 +231,8 @@ public class S8TaskFlowService : ITransient
     {
         var currentUserId = GetCurrentUserId();
         var e = await LoadAsync(id, tenantId, factoryId) ?? throw new S8BizException("异常不存在");
-        if (e.VerifierId != currentUserId)
-            throw new S8BizException("只有指定检验人才能检验退回");
+        await EnsureCurrentUserIsOperatorAsync(e.VerifierId, currentUserId,
+            "只有指定检验人才能检验退回(或当前账号未绑定员工主数据)");
         if (!S8StatusRules.IsAllowedTransition(e.Status, "IN_PROGRESS"))
             throw new S8BizException($"状态 {e.Status} 不可检验退回");
         if (string.IsNullOrWhiteSpace(remark))
@@ -271,6 +275,22 @@ public class S8TaskFlowService : ITransient
         return currentUserId;
     }
 
+    // 把异常上的处理人/检验人(employeeId) 经 EmployeeMaster.SysUserId 解析到系统账号 ID。
+    private async Task<long?> GetEmployeeSysUserIdAsync(long? employeeId)
+    {
+        if (!employeeId.HasValue || employeeId.Value <= 0) return null;
+        var emp = await _employeeRep.GetFirstAsync(x => x.Id == employeeId.Value);
+        return emp?.SysUserId;
+    }
+
+    // 鉴权统一入口:要求当前登录用户必须是 employeeId 解析后的 SysUserId。
+    private async Task EnsureCurrentUserIsOperatorAsync(long? employeeId, long currentUserId, string failMessage)
+    {
+        var ownerSysUserId = await GetEmployeeSysUserIdAsync(employeeId);
+        if (ownerSysUserId != currentUserId)
+            throw new S8BizException(failMessage);
+    }
+
     private async Task InsertTimelineAsync(long exceptionId, string code, string label, string? from, string? to,
         long? operatorId, string? operatorName, string? remark) =>
         await _timelineRep.InsertAsync(new AdoS8ExceptionTimeline