using Admin.NET.Plugin.AiDOP.Entity.S8; using Admin.NET.Plugin.AiDOP.Infrastructure.S8; using Admin.NET.Plugin.ApprovalFlow.Service; namespace Admin.NET.Plugin.AiDOP.Service.S8; public class S8TaskFlowService : ITransient { private readonly SqlSugarRepository _rep; private readonly SqlSugarRepository _timelineRep; private readonly FlowEngineService _flowEngine; private readonly UserManager _userManager; public S8TaskFlowService( SqlSugarRepository rep, SqlSugarRepository timelineRep, FlowEngineService flowEngine, UserManager userManager) { _rep = rep; _timelineRep = timelineRep; _flowEngine = flowEngine; _userManager = userManager; } public async Task ClaimAsync(long id, long tenantId, long factoryId, long assigneeId, string? remark) { if (assigneeId <= 0) throw new S8BizException("认领需指定处理人 AssigneeId"); var e = await LoadAsync(id, tenantId, factoryId) ?? throw new S8BizException("异常不存在"); if (!S8StatusRules.IsAllowedTransition(e.Status, "ASSIGNED")) throw new S8BizException($"状态 {e.Status} 不可认领"); var fromStatus = e.Status; e.Status = "ASSIGNED"; e.AssigneeId = assigneeId; e.AssignedAt = DateTime.Now; e.UpdatedAt = DateTime.Now; await _rep.AsTenant().UseTranAsync(async () => { await _rep.UpdateAsync(e); await InsertTimelineAsync(e.Id, "CLAIM", "认领", fromStatus, "ASSIGNED", assigneeId, null, remark); }, ex => throw ex); return e; } public async Task TransferAsync(long id, long tenantId, long factoryId, long newAssigneeId, string? remark) { var allowedStatuses = new HashSet { "ASSIGNED", "IN_PROGRESS" }; if (newAssigneeId <= 0) throw new S8BizException("转派目标无效"); var e = await LoadAsync(id, tenantId, factoryId) ?? throw new S8BizException("异常不存在"); if (e.ActiveFlowInstanceId.HasValue) throw new S8BizException("审批进行中不可转派"); if (e.Status == "ESCALATED") throw new S8BizException("升级审批中不可转派"); if (S8StatusRules.IsTerminal(e.Status)) throw new S8BizException("已关闭不可转派"); if (!allowedStatuses.Contains(e.Status)) throw new S8BizException($"状态 {e.Status} 不可转派"); e.AssigneeId = newAssigneeId; e.UpdatedAt = DateTime.Now; await _rep.AsTenant().UseTranAsync(async () => { await _rep.UpdateAsync(e); await InsertTimelineAsync(e.Id, "TRANSFER", "转派", e.Status, e.Status, newAssigneeId, null, remark); }, ex => throw ex); return e; } public async Task StartProgressAsync(long id, long tenantId, long factoryId, string? remark) { var currentUserId = GetCurrentUserId(); var e = await LoadAsync(id, tenantId, factoryId) ?? throw new S8BizException("异常不存在"); if (!S8StatusRules.IsAllowedTransition(e.Status, "IN_PROGRESS")) throw new S8BizException($"状态 {e.Status} 不可开始处理"); var fromStatus = e.Status; e.Status = "IN_PROGRESS"; e.UpdatedAt = DateTime.Now; await _rep.AsTenant().UseTranAsync(async () => { await _rep.UpdateAsync(e); await InsertTimelineAsync(e.Id, "START_PROGRESS", "开始处理", fromStatus, "IN_PROGRESS", currentUserId, null, remark); }, ex => throw ex); return e; } public async Task UpgradeAsync(long id, long tenantId, long factoryId, string? remark) { var e = await LoadAsync(id, tenantId, factoryId) ?? throw new S8BizException("异常不存在"); if (e.ActiveFlowInstanceId.HasValue) throw new S8BizException("该异常已有进行中的审批流程,请等待审批完成"); if (!S8StatusRules.IsAllowedTransition(e.Status, "ESCALATED")) throw new S8BizException($"状态 {e.Status} 不可升级"); await _flowEngine.StartFlow(new StartFlowInput { BizType = "EXCEPTION_ESCALATION", BizId = e.Id, Title = $"异常升级审批 - {e.ExceptionCode}", Comment = remark, BizData = new Dictionary { ["severity"] = e.Severity, ["sceneCode"] = e.SceneCode, ["priorityLevel"] = e.PriorityLevel, } }); // 状态和时间线由 ExceptionEscalationBizHandler.OnFlowStarted 回调更新 return await LoadAsync(id, tenantId, factoryId) ?? e; } public async Task RejectAsync(long id, long tenantId, long factoryId, string? remark) { var e = await LoadAsync(id, tenantId, factoryId) ?? throw new S8BizException("异常不存在"); if (!S8StatusRules.IsAllowedTransition(e.Status, "REJECTED")) throw new S8BizException($"状态 {e.Status} 不可驳回"); var from = e.Status; e.Status = "REJECTED"; e.UpdatedAt = DateTime.Now; await _rep.AsTenant().UseTranAsync(async () => { await _rep.UpdateAsync(e); await InsertTimelineAsync(e.Id, "REJECT", "驳回", from, "REJECTED", null, null, remark); }, ex => throw ex); return e; } public async Task CloseAsync(long id, long tenantId, long factoryId, string? remark) { var e = await LoadAsync(id, tenantId, factoryId) ?? throw new S8BizException("异常不存在"); if (e.ActiveFlowInstanceId.HasValue) throw new S8BizException("该异常已有进行中的审批流程,请等待审批完成"); if (!S8StatusRules.IsAllowedTransition(e.Status, "CLOSED")) throw new S8BizException($"状态 {e.Status} 不可关闭"); await _flowEngine.StartFlow(new StartFlowInput { BizType = "EXCEPTION_CLOSURE", BizId = e.Id, Title = $"异常关闭确认 - {e.ExceptionCode}", Comment = remark, BizData = new Dictionary { ["sceneCode"] = e.SceneCode, } }); // 状态和时间线由 ExceptionClosureBizHandler.OnFlowStarted 回调更新 return await LoadAsync(id, tenantId, factoryId) ?? e; } public async Task SubmitVerificationAsync( long id, long tenantId, long factoryId, long verifierId, string? remark) { var currentUserId = GetCurrentUserId(); var e = await LoadAsync(id, tenantId, factoryId) ?? throw new S8BizException("异常不存在"); if (e.AssigneeId != currentUserId) throw new S8BizException("只有当前处理人才能提交复检"); if (verifierId <= 0) throw new S8BizException("请选择检验人"); if (!S8StatusRules.IsAllowedTransition(e.Status, "PENDING_VERIFICATION")) throw new S8BizException($"状态 {e.Status} 不可提交复检"); var from = e.Status; e.Status = "PENDING_VERIFICATION"; e.VerifierId = verifierId; e.VerificationAssignedAt = DateTime.Now; e.UpdatedAt = DateTime.Now; await _rep.AsTenant().UseTranAsync(async () => { await _rep.UpdateAsync(e); await InsertTimelineAsync(e.Id, "VERIFY_SUBMITTED", "提交复检", from, "PENDING_VERIFICATION", currentUserId, null, remark); }, ex => throw ex); return e; } public async Task ApproveVerificationAsync( long id, long tenantId, long factoryId, string? remark) { var currentUserId = GetCurrentUserId(); var e = await LoadAsync(id, tenantId, factoryId) ?? throw new S8BizException("异常不存在"); if (e.VerifierId != currentUserId) throw new S8BizException("只有指定检验人才能检验通过"); if (!S8StatusRules.IsAllowedTransition(e.Status, "RESOLVED")) throw new S8BizException($"状态 {e.Status} 不可检验通过"); var from = e.Status; e.Status = "RESOLVED"; e.VerifiedAt = DateTime.Now; e.VerificationResult = "APPROVED"; e.VerificationRemark = remark; e.UpdatedAt = DateTime.Now; await _rep.AsTenant().UseTranAsync(async () => { await _rep.UpdateAsync(e); await InsertTimelineAsync(e.Id, "VERIFY_APPROVED", "检验通过", from, "RESOLVED", currentUserId, null, remark); }, ex => throw ex); return e; } public async Task RejectVerificationAsync( long id, long tenantId, long factoryId, string remark) { var currentUserId = GetCurrentUserId(); var e = await LoadAsync(id, tenantId, factoryId) ?? throw new S8BizException("异常不存在"); if (e.VerifierId != currentUserId) throw new S8BizException("只有指定检验人才能检验退回"); if (!S8StatusRules.IsAllowedTransition(e.Status, "IN_PROGRESS")) throw new S8BizException($"状态 {e.Status} 不可检验退回"); if (string.IsNullOrWhiteSpace(remark)) throw new S8BizException("检验退回必须填写退回原因"); var from = e.Status; e.Status = "IN_PROGRESS"; e.VerifiedAt = DateTime.Now; e.VerificationResult = "REJECTED"; e.VerificationRemark = remark; e.UpdatedAt = DateTime.Now; await _rep.AsTenant().UseTranAsync(async () => { await _rep.UpdateAsync(e); await InsertTimelineAsync(e.Id, "VERIFY_REJECTED", "检验退回", from, "IN_PROGRESS", currentUserId, null, remark); }, ex => throw ex); return e; } public async Task CommentAsync(long id, string? remark) { var e = await _rep.GetFirstAsync(x => x.Id == id && !x.IsDeleted) ?? throw new S8BizException("异常不存在"); await InsertTimelineAsync(e.Id, "COMMENT", "补充说明", e.Status, e.Status, null, null, remark); } private Task LoadAsync(long id, long tenantId, long factoryId) => _rep.GetFirstAsync(x => x.Id == id && x.TenantId == tenantId && x.FactoryId == factoryId && !x.IsDeleted); // 统一复用框架登录上下文,避免业务身份继续信任前端传参。 private long GetCurrentUserId() { var currentUserId = _userManager.UserId; if (currentUserId <= 0) throw new S8BizException("未获取到当前登录用户"); return currentUserId; } 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 { ExceptionId = exceptionId, ActionCode = code, ActionLabel = label, FromStatus = from, ToStatus = to, OperatorId = operatorId, OperatorName = operatorName, ActionRemark = remark, CreatedAt = DateTime.Now }); }