FlowEngineService.cs 45 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078
  1. using System.Text.Json;
  2. using Admin.NET.Core.Service;
  3. namespace Admin.NET.Plugin.ApprovalFlow.Service;
  4. /// <summary>
  5. /// 流程推进引擎 — 核心状态机
  6. /// 不暴露为 API,由其他 Service 内部调用
  7. /// </summary>
  8. public class FlowEngineService : ITransient
  9. {
  10. private readonly SqlSugarRepository<ApprovalFlow> _flowRep;
  11. private readonly SqlSugarRepository<ApprovalFlowInstance> _instanceRep;
  12. private readonly SqlSugarRepository<ApprovalFlowTask> _taskRep;
  13. private readonly SqlSugarRepository<ApprovalFlowLog> _logRep;
  14. private readonly SqlSugarRepository<SysUserRole> _userRoleRep;
  15. private readonly SqlSugarRepository<SysUser> _userRep;
  16. private readonly SqlSugarRepository<SysOrg> _orgRep;
  17. private readonly SqlSugarRepository<ApprovalFlowDelegate> _delegateRep;
  18. private readonly SqlSugarRepository<ApprovalFlowCompletedNode> _completedNodeRep;
  19. private readonly UserManager _userManager;
  20. private readonly SysOrgService _sysOrgService;
  21. private readonly FlowNotifyService _notifyService;
  22. public FlowEngineService(
  23. SqlSugarRepository<ApprovalFlow> flowRep,
  24. SqlSugarRepository<ApprovalFlowInstance> instanceRep,
  25. SqlSugarRepository<ApprovalFlowTask> taskRep,
  26. SqlSugarRepository<ApprovalFlowLog> logRep,
  27. SqlSugarRepository<SysUserRole> userRoleRep,
  28. SqlSugarRepository<SysUser> userRep,
  29. SqlSugarRepository<SysOrg> orgRep,
  30. SqlSugarRepository<ApprovalFlowDelegate> delegateRep,
  31. SqlSugarRepository<ApprovalFlowCompletedNode> completedNodeRep,
  32. UserManager userManager,
  33. SysOrgService sysOrgService,
  34. FlowNotifyService notifyService)
  35. {
  36. _flowRep = flowRep;
  37. _instanceRep = instanceRep;
  38. _taskRep = taskRep;
  39. _logRep = logRep;
  40. _userRoleRep = userRoleRep;
  41. _userRep = userRep;
  42. _orgRep = orgRep;
  43. _delegateRep = delegateRep;
  44. _completedNodeRep = completedNodeRep;
  45. _userManager = userManager;
  46. _sysOrgService = sysOrgService;
  47. _notifyService = notifyService;
  48. }
  49. /// <summary>
  50. /// 机构数据权限依赖实体的 OrgId;插入时必须与当前用户机构一致,否则非超管在「我发起的/待办」中查不到(OrgId=0 会被过滤)。
  51. /// </summary>
  52. private async Task<long> ResolveOrgIdForNewFlowEntityAsync()
  53. {
  54. var oid = _userManager.OrgId;
  55. if (oid > 0) return oid;
  56. var list = await _sysOrgService.GetUserOrgIdList();
  57. return list is { Count: > 0 } ? list[0] : 0;
  58. }
  59. // ═══════════════════════════════════════════
  60. // 核心生命周期
  61. // ═══════════════════════════════════════════
  62. /// <summary>
  63. /// 发起流程
  64. /// </summary>
  65. public async Task<long> StartFlow(StartFlowInput input)
  66. {
  67. var flow = await _flowRep.AsQueryable()
  68. .Where(u => u.BizType == input.BizType && u.IsPublished && !u.IsDelete)
  69. .OrderByDescending(u => u.Version)
  70. .FirstAsync() ?? throw Oops.Oh($"未找到业务类型 [{input.BizType}] 的已发布流程定义");
  71. if (string.IsNullOrWhiteSpace(flow.FlowJson))
  72. throw Oops.Oh("流程定义的 FlowJson 为空,请先设计流程图");
  73. var flowData = JsonSerializer.Deserialize<ApprovalFlowItem>(flow.FlowJson)
  74. ?? throw Oops.Oh("FlowJson 反序列化失败");
  75. var orgId = await ResolveOrgIdForNewFlowEntityAsync();
  76. var instance = new ApprovalFlowInstance
  77. {
  78. FlowId = flow.Id,
  79. FlowVersion = flow.Version,
  80. BizType = input.BizType,
  81. BizId = input.BizId,
  82. BizNo = input.BizNo,
  83. Title = input.Title ?? $"{flow.Name}-{input.BizNo}",
  84. InitiatorId = _userManager.UserId,
  85. InitiatorName = _userManager.RealName,
  86. Status = FlowInstanceStatusEnum.Running,
  87. FlowJsonSnapshot = flow.FlowJson,
  88. StartTime = DateTime.Now,
  89. OrgId = orgId,
  90. };
  91. var startNode = flowData.Nodes.FirstOrDefault(n =>
  92. n.Type is "bpmn:startEvent" or "start-node")
  93. ?? throw Oops.Oh("流程图中未找到开始节点");
  94. await _instanceRep.InsertAsync(instance);
  95. await WriteLog(instance.Id, null, startNode.Id, FlowLogActionEnum.Submit, input.Comment);
  96. // 开始节点登记为已完成,随后沿出边推进(兼容首节点即为并行网关 Fork 的拓扑)
  97. await MarkNodeCompleted(instance.Id, startNode);
  98. var firstOutgoing = flowData.Edges.Where(e => e.SourceNodeId == startNode.Id).Select(e => e.TargetNodeId).ToList();
  99. if (firstOutgoing.Count == 0)
  100. throw Oops.Oh("流程图开始节点未连接任何后继节点");
  101. foreach (var target in firstOutgoing)
  102. {
  103. await ProcessNextNode(instance, flowData, target);
  104. }
  105. await InvokeHandler(input.BizType, h => h.OnFlowStarted(input.BizId, instance.Id));
  106. return instance.Id;
  107. }
  108. /// <summary>
  109. /// 同意
  110. /// </summary>
  111. public async Task Approve(long taskId, string? comment)
  112. {
  113. var task = await GetPendingTask(taskId);
  114. task.Status = FlowTaskStatusEnum.Approved;
  115. task.Comment = comment;
  116. task.ActionTime = DateTime.Now;
  117. await _taskRep.AsUpdateable(task).ExecuteCommandAsync();
  118. // P1-6 审批代理:同步取消配对的本人/代理任务
  119. await CancelPairedDelegateTask(task);
  120. await WriteLog(task.InstanceId, taskId, task.NodeId, FlowLogActionEnum.Approve, comment);
  121. var instance = await _instanceRep.GetByIdAsync(task.InstanceId)
  122. ?? throw Oops.Oh("流程实例不存在");
  123. if (await IsNodeCompleted(instance, task.NodeId))
  124. {
  125. await InvokeHandler(instance.BizType,
  126. h => h.OnNodeCompleted(instance.BizId, task.NodeId, task.NodeName ?? ""));
  127. var flowData = DeserializeFlowJson(instance.FlowJsonSnapshot);
  128. await AdvanceToNext(instance, flowData, task.NodeId);
  129. }
  130. }
  131. /// <summary>
  132. /// 拒绝
  133. /// </summary>
  134. public async Task Reject(long taskId, string? comment)
  135. {
  136. var task = await GetPendingTask(taskId);
  137. task.Status = FlowTaskStatusEnum.Rejected;
  138. task.Comment = comment;
  139. task.ActionTime = DateTime.Now;
  140. await _taskRep.AsUpdateable(task).ExecuteCommandAsync();
  141. // Reject 导致流程终止:取消整个实例所有剩余 Pending 任务(含并行分支、配对代理任务等)
  142. await CancelAllPendingTasks(task.InstanceId, task.Id);
  143. var instance = await _instanceRep.GetByIdAsync(task.InstanceId)
  144. ?? throw Oops.Oh("流程实例不存在");
  145. instance.Status = FlowInstanceStatusEnum.Rejected;
  146. instance.EndTime = DateTime.Now;
  147. await _instanceRep.AsUpdateable(instance).ExecuteCommandAsync();
  148. await WriteLog(instance.Id, taskId, task.NodeId, FlowLogActionEnum.Reject, comment);
  149. await InvokeHandler(instance.BizType,
  150. h => h.OnFlowCompleted(instance.BizId, FlowInstanceStatusEnum.Rejected));
  151. await _notifyService.NotifyFlowCompleted(instance.InitiatorId, instance.Id, instance.Title, FlowInstanceStatusEnum.Rejected);
  152. }
  153. // ═══════════════════════════════════════════
  154. // 扩展操作
  155. // ═══════════════════════════════════════════
  156. /// <summary>
  157. /// 转办
  158. /// </summary>
  159. public async Task Transfer(long taskId, long targetUserId, string? comment)
  160. {
  161. var task = await GetPendingTask(taskId);
  162. task.Status = FlowTaskStatusEnum.Transferred;
  163. task.Comment = comment;
  164. task.ActionTime = DateTime.Now;
  165. task.TransferToId = targetUserId;
  166. await _taskRep.AsUpdateable(task).ExecuteCommandAsync();
  167. var targetUser = await _userRep.GetByIdAsync(targetUserId);
  168. var instForOrg = await _instanceRep.GetByIdAsync(task.InstanceId);
  169. var newTask = new ApprovalFlowTask
  170. {
  171. InstanceId = task.InstanceId,
  172. NodeId = task.NodeId,
  173. NodeName = task.NodeName,
  174. AssigneeId = targetUserId,
  175. AssigneeName = targetUser?.RealName,
  176. Status = FlowTaskStatusEnum.Pending,
  177. OrgId = instForOrg?.OrgId ?? 0,
  178. };
  179. await _taskRep.InsertAsync(newTask);
  180. await WriteLog(task.InstanceId, taskId, task.NodeId, FlowLogActionEnum.Transfer,
  181. $"{comment} → 转办给 {targetUser?.RealName}");
  182. var instance = await _instanceRep.GetByIdAsync(task.InstanceId);
  183. await _notifyService.NotifyTransferred(targetUserId, task.InstanceId, instance?.Title ?? "", _userManager.RealName);
  184. }
  185. /// <summary>
  186. /// 撤回(发起人撤回)
  187. /// </summary>
  188. public async Task Withdraw(long instanceId)
  189. {
  190. var instance = await _instanceRep.GetByIdAsync(instanceId)
  191. ?? throw Oops.Oh("流程实例不存在");
  192. if (instance.InitiatorId != _userManager.UserId)
  193. throw Oops.Oh("只有发起人可以撤回");
  194. if (instance.Status != FlowInstanceStatusEnum.Running)
  195. throw Oops.Oh("当前流程状态不允许撤回");
  196. var pendingTasks = await _taskRep.AsQueryable()
  197. .Where(t => t.InstanceId == instanceId && t.Status == FlowTaskStatusEnum.Pending)
  198. .ToListAsync();
  199. var doneTasks = await _taskRep.AsQueryable()
  200. .Where(t => t.InstanceId == instanceId &&
  201. t.Status != FlowTaskStatusEnum.Pending &&
  202. t.Status != FlowTaskStatusEnum.Cancelled)
  203. .CountAsync();
  204. if (doneTasks > 0)
  205. throw Oops.Oh("已有人审批过,不可撤回");
  206. var cancelledUserIds = pendingTasks.Select(t => t.AssigneeId).Distinct().ToList();
  207. foreach (var t in pendingTasks)
  208. {
  209. t.Status = FlowTaskStatusEnum.Cancelled;
  210. t.ActionTime = DateTime.Now;
  211. }
  212. await _taskRep.AsUpdateable(pendingTasks).ExecuteCommandAsync();
  213. instance.Status = FlowInstanceStatusEnum.Cancelled;
  214. instance.EndTime = DateTime.Now;
  215. await _instanceRep.AsUpdateable(instance).ExecuteCommandAsync();
  216. await WriteLog(instanceId, null, instance.CurrentNodeId, FlowLogActionEnum.Withdraw, null);
  217. await InvokeHandler(instance.BizType,
  218. h => h.OnFlowCompleted(instance.BizId, FlowInstanceStatusEnum.Cancelled));
  219. await _notifyService.NotifyWithdrawn(cancelledUserIds, instanceId, instance.Title, instance.InitiatorName);
  220. }
  221. /// <summary>
  222. /// 退回上一步
  223. /// </summary>
  224. public async Task ReturnToPrev(long taskId, string? comment)
  225. {
  226. var task = await GetPendingTask(taskId);
  227. var instance = await _instanceRep.GetByIdAsync(task.InstanceId)
  228. ?? throw Oops.Oh("流程实例不存在");
  229. var flowData = DeserializeFlowJson(instance.FlowJsonSnapshot);
  230. var prevNodeId = FindPrevUserTaskNodeId(flowData, task.NodeId);
  231. if (prevNodeId == null)
  232. throw Oops.Oh("已是第一个审批节点,无法退回");
  233. await CancelPendingTasks(task.InstanceId, task.NodeId);
  234. task.Status = FlowTaskStatusEnum.Returned;
  235. task.Comment = comment;
  236. task.ActionTime = DateTime.Now;
  237. await _taskRep.AsUpdateable(task).ExecuteCommandAsync();
  238. instance.CurrentNodeId = prevNodeId;
  239. await _instanceRep.AsUpdateable(instance).ExecuteCommandAsync();
  240. await CreateTasksForNode(instance, flowData, prevNodeId);
  241. await WriteLog(instance.Id, taskId, task.NodeId, FlowLogActionEnum.Return, comment);
  242. var returnedTasks = await _taskRep.AsQueryable()
  243. .Where(t => t.InstanceId == instance.Id && t.NodeId == prevNodeId && t.Status == FlowTaskStatusEnum.Pending)
  244. .ToListAsync();
  245. var returnedUserIds = returnedTasks.Select(t => t.AssigneeId).Distinct().ToList();
  246. await _notifyService.NotifyReturned(returnedUserIds, instance.Id, instance.Title, _userManager.RealName);
  247. }
  248. /// <summary>
  249. /// 加签
  250. /// </summary>
  251. public async Task AddSign(long taskId, long targetUserId, string? comment)
  252. {
  253. var task = await GetPendingTask(taskId);
  254. var targetUser = await _userRep.GetByIdAsync(targetUserId);
  255. var instForAddSign = await _instanceRep.GetByIdAsync(task.InstanceId);
  256. var newTask = new ApprovalFlowTask
  257. {
  258. InstanceId = task.InstanceId,
  259. NodeId = task.NodeId,
  260. NodeName = task.NodeName,
  261. AssigneeId = targetUserId,
  262. AssigneeName = targetUser?.RealName,
  263. Status = FlowTaskStatusEnum.Pending,
  264. IsAddSign = true,
  265. AddSignById = _userManager.UserId,
  266. OrgId = instForAddSign?.OrgId ?? 0,
  267. };
  268. await _taskRep.InsertAsync(newTask);
  269. await WriteLog(task.InstanceId, taskId, task.NodeId, FlowLogActionEnum.AddSign,
  270. $"{comment} → 加签给 {targetUser?.RealName}");
  271. var instance = await _instanceRep.GetByIdAsync(task.InstanceId);
  272. await _notifyService.NotifyAddSign(targetUserId, task.InstanceId, instance?.Title ?? "", _userManager.RealName);
  273. }
  274. /// <summary>
  275. /// 手动升级 — 当前审批人主动将任务升级到更高层级
  276. /// </summary>
  277. public async Task Escalate(long taskId, string? comment)
  278. {
  279. var task = await GetPendingTask(taskId);
  280. var instance = await _instanceRep.GetByIdAsync(task.InstanceId)
  281. ?? throw Oops.Oh("流程实例不存在");
  282. var flowData = DeserializeFlowJson(instance.FlowJsonSnapshot);
  283. var node = flowData.Nodes.FirstOrDefault(n => n.Id == task.NodeId)
  284. ?? throw Oops.Oh("节点不存在");
  285. var props = node.Properties;
  286. if (props?.EnableManualEscalation != true
  287. || string.IsNullOrWhiteSpace(props.EscalationApproverType)
  288. || string.IsNullOrWhiteSpace(props.EscalationApproverIds))
  289. throw Oops.Oh("该节点未配置升级目标,无法升级");
  290. task.Status = FlowTaskStatusEnum.Escalated;
  291. task.Comment = comment;
  292. task.ActionTime = DateTime.Now;
  293. await _taskRep.AsUpdateable(task).ExecuteCommandAsync();
  294. await CancelPendingTasks(task.InstanceId, task.NodeId, task.Id);
  295. var escalationApprovers = await ResolveApprovers(
  296. new FlowProperties
  297. {
  298. ApproverType = props.EscalationApproverType,
  299. ApproverIds = props.EscalationApproverIds,
  300. ApproverNames = props.EscalationApproverNames,
  301. },
  302. instance.InitiatorId);
  303. if (escalationApprovers.Count == 0)
  304. throw Oops.Oh("升级目标审批人列表为空");
  305. var newTasks = escalationApprovers.Select(a => new ApprovalFlowTask
  306. {
  307. InstanceId = instance.Id,
  308. NodeId = task.NodeId,
  309. NodeName = task.NodeName,
  310. AssigneeId = a.userId,
  311. AssigneeName = a.userName,
  312. Status = FlowTaskStatusEnum.Pending,
  313. OrgId = instance.OrgId,
  314. }).ToList();
  315. await _taskRep.AsInsertable(newTasks).ExecuteCommandAsync();
  316. var targetNames = string.Join(", ", escalationApprovers.Select(a => a.userName));
  317. await WriteLog(instance.Id, taskId, task.NodeId, FlowLogActionEnum.Escalate,
  318. $"{comment} → 升级给 {targetNames}");
  319. var targetUserIds = escalationApprovers.Select(a => a.userId).Distinct().ToList();
  320. await _notifyService.NotifyEscalated(targetUserIds, instance.Id, instance.Title,
  321. _userManager.RealName, task.NodeName);
  322. }
  323. /// <summary>
  324. /// 催办
  325. /// </summary>
  326. public async Task Urge(long instanceId)
  327. {
  328. var instance = await _instanceRep.GetByIdAsync(instanceId)
  329. ?? throw Oops.Oh("流程实例不存在");
  330. if (instance.Status != FlowInstanceStatusEnum.Running)
  331. throw Oops.Oh("当前流程不在审批中");
  332. await WriteLog(instanceId, null, instance.CurrentNodeId, FlowLogActionEnum.Urge, "催办");
  333. var pendingTasks = await _taskRep.AsQueryable()
  334. .Where(t => t.InstanceId == instanceId && t.Status == FlowTaskStatusEnum.Pending)
  335. .ToListAsync();
  336. var userIds = pendingTasks.Select(t => t.AssigneeId).Distinct().ToList();
  337. await _notifyService.NotifyUrge(userIds, instanceId, instance.Title);
  338. }
  339. // ═══════════════════════════════════════════
  340. // 超时自动处理(由 FlowTimeoutJob 调用,无 UserManager 上下文)
  341. // ═══════════════════════════════════════════
  342. /// <summary>
  343. /// 处理单个超时任务(由定时任务调用)
  344. /// </summary>
  345. public async Task HandleTimeoutTask(long taskId)
  346. {
  347. var task = await _taskRep.GetByIdAsync(taskId);
  348. if (task == null || task.Status != FlowTaskStatusEnum.Pending) return;
  349. var instance = await _instanceRep.GetByIdAsync(task.InstanceId);
  350. if (instance == null || instance.Status != FlowInstanceStatusEnum.Running) return;
  351. var flowData = DeserializeFlowJson(instance.FlowJsonSnapshot);
  352. var node = flowData.Nodes.FirstOrDefault(n => n.Id == task.NodeId);
  353. var props = node?.Properties;
  354. if (props == null) return;
  355. switch (props.TimeoutAction)
  356. {
  357. case "Notify":
  358. await _notifyService.NotifyTimeout(
  359. new List<long> { task.AssigneeId }, instance.Id, instance.Title);
  360. await WriteSystemLog(instance.Id, task.Id, task.NodeId,
  361. FlowLogActionEnum.AutoTimeout, "审批超时,已发送提醒通知");
  362. break;
  363. case "AutoApprove":
  364. await AutoApproveTask(task, instance);
  365. break;
  366. case "AutoReject":
  367. await AutoRejectTask(task, instance);
  368. break;
  369. case "AutoEscalate":
  370. await AutoEscalateTask(task, props, instance);
  371. break;
  372. }
  373. }
  374. private async Task AutoApproveTask(ApprovalFlowTask task, ApprovalFlowInstance instance)
  375. {
  376. task.Status = FlowTaskStatusEnum.Approved;
  377. task.Comment = "系统自动通过(超时)";
  378. task.ActionTime = DateTime.Now;
  379. await _taskRep.AsUpdateable(task).ExecuteCommandAsync();
  380. await WriteSystemLog(instance.Id, task.Id, task.NodeId,
  381. FlowLogActionEnum.AutoTimeout, "审批超时,系统自动通过");
  382. if (await IsNodeCompleted(instance, task.NodeId))
  383. {
  384. await InvokeHandler(instance.BizType,
  385. h => h.OnNodeCompleted(instance.BizId, task.NodeId, task.NodeName ?? ""));
  386. var flowData = DeserializeFlowJson(instance.FlowJsonSnapshot);
  387. await AdvanceToNext(instance, flowData, task.NodeId);
  388. }
  389. }
  390. private async Task AutoRejectTask(ApprovalFlowTask task, ApprovalFlowInstance instance)
  391. {
  392. task.Status = FlowTaskStatusEnum.Rejected;
  393. task.Comment = "系统自动拒绝(超时)";
  394. task.ActionTime = DateTime.Now;
  395. await _taskRep.AsUpdateable(task).ExecuteCommandAsync();
  396. await CancelPendingTasks(task.InstanceId, task.NodeId, task.Id);
  397. instance.Status = FlowInstanceStatusEnum.Rejected;
  398. instance.EndTime = DateTime.Now;
  399. await _instanceRep.AsUpdateable(instance).ExecuteCommandAsync();
  400. await WriteSystemLog(instance.Id, task.Id, task.NodeId,
  401. FlowLogActionEnum.AutoTimeout, "审批超时,系统自动拒绝");
  402. await InvokeHandler(instance.BizType,
  403. h => h.OnFlowCompleted(instance.BizId, FlowInstanceStatusEnum.Rejected));
  404. await _notifyService.NotifyFlowCompleted(instance.InitiatorId, instance.Id,
  405. instance.Title, FlowInstanceStatusEnum.Rejected);
  406. }
  407. private async Task AutoEscalateTask(ApprovalFlowTask task, FlowProperties nodeProps, ApprovalFlowInstance instance)
  408. {
  409. if (string.IsNullOrWhiteSpace(nodeProps.EscalationApproverType)
  410. || string.IsNullOrWhiteSpace(nodeProps.EscalationApproverIds))
  411. return;
  412. task.Status = FlowTaskStatusEnum.Escalated;
  413. task.Comment = "系统自动升级(超时)";
  414. task.ActionTime = DateTime.Now;
  415. await _taskRep.AsUpdateable(task).ExecuteCommandAsync();
  416. await CancelPendingTasks(task.InstanceId, task.NodeId, task.Id);
  417. var approvers = await ResolveApprovers(
  418. new FlowProperties
  419. {
  420. ApproverType = nodeProps.EscalationApproverType,
  421. ApproverIds = nodeProps.EscalationApproverIds,
  422. },
  423. instance.InitiatorId);
  424. if (approvers.Count == 0) return;
  425. var newTasks = approvers.Select(a => new ApprovalFlowTask
  426. {
  427. InstanceId = instance.Id,
  428. NodeId = task.NodeId,
  429. NodeName = task.NodeName,
  430. AssigneeId = a.userId,
  431. AssigneeName = a.userName,
  432. Status = FlowTaskStatusEnum.Pending,
  433. OrgId = instance.OrgId,
  434. }).ToList();
  435. await _taskRep.AsInsertable(newTasks).ExecuteCommandAsync();
  436. var targetNames = string.Join(", ", approvers.Select(a => a.userName));
  437. await WriteSystemLog(instance.Id, task.Id, task.NodeId,
  438. FlowLogActionEnum.AutoTimeout, $"审批超时,自动升级给 {targetNames}");
  439. var targetUserIds = approvers.Select(a => a.userId).Distinct().ToList();
  440. await _notifyService.NotifyEscalated(targetUserIds, instance.Id, instance.Title,
  441. "系统", task.NodeName);
  442. }
  443. private async Task WriteSystemLog(long instanceId, long? taskId, string? nodeId,
  444. FlowLogActionEnum action, string? comment)
  445. {
  446. await _logRep.InsertAsync(new ApprovalFlowLog
  447. {
  448. InstanceId = instanceId,
  449. TaskId = taskId,
  450. NodeId = nodeId,
  451. Action = action,
  452. OperatorId = 0,
  453. OperatorName = "系统",
  454. Comment = comment,
  455. });
  456. }
  457. // ═══════════════════════════════════════════
  458. // 内部引擎方法
  459. // ═══════════════════════════════════════════
  460. /// <summary>
  461. /// 推进到下一节点。支持并行网关(Fork / Join)。
  462. /// 行为约定:
  463. /// - 进入本方法前,调用方(如 <see cref="Approve"/>)应已将 <paramref name="currentNodeId"/>(userTask)标记为完成节点;
  464. /// 本方法会将途经的网关节点也写入 <see cref="ApprovalFlowCompletedNode"/>。
  465. /// - 并行网关 Fork(出边&gt;=2):沿每条出边递归推进;
  466. /// 并行网关 Join(入边&gt;=2):校验所有前驱节点是否都在"已完成"集合中,
  467. /// 任一尚未完成则**静默等待**(不报错、不推进),由后续分支完成后再次触发 Join 校验。
  468. /// </summary>
  469. private async Task AdvanceToNext(ApprovalFlowInstance instance, ApprovalFlowItem flowData, string currentNodeId)
  470. {
  471. // 当前节点可能是 userTask(完成记录由 Approve 写入)或网关(入口处已写入);此处统一确保幂等入库
  472. var currentNode = flowData.Nodes.FirstOrDefault(n => n.Id == currentNodeId);
  473. await MarkNodeCompleted(instance.Id, currentNode);
  474. var outgoingEdges = flowData.Edges.Where(e => e.SourceNodeId == currentNodeId).ToList();
  475. if (outgoingEdges.Count == 0)
  476. {
  477. await CompleteInstance(instance, FlowInstanceStatusEnum.Approved);
  478. return;
  479. }
  480. // 非并行网关场景:当前节点一般只有 1 条出边
  481. foreach (var edge in outgoingEdges)
  482. {
  483. await ProcessNextNode(instance, flowData, edge.TargetNodeId);
  484. }
  485. }
  486. /// <summary>
  487. /// 处理某个"下一节点"。根据节点类型分发:
  488. /// - endEvent:所有分支任务都结束时触发实例完成
  489. /// - exclusiveGateway:按条件选择分支
  490. /// - parallelGateway:Fork 并行分发;Join 等待所有前驱完成
  491. /// - userTask / 其他:创建任务
  492. /// </summary>
  493. private async Task ProcessNextNode(ApprovalFlowInstance instance, ApprovalFlowItem flowData, string nextNodeId)
  494. {
  495. var nextNode = flowData.Nodes.FirstOrDefault(n => n.Id == nextNodeId);
  496. if (nextNode == null)
  497. {
  498. await CompleteInstance(instance, FlowInstanceStatusEnum.Approved);
  499. return;
  500. }
  501. if (nextNode.Type is "bpmn:endEvent" or "end-node")
  502. {
  503. // 所有并行分支都已结束(无其它 Pending 任务)才真正完成实例
  504. var hasOtherPending = await _taskRep.AsQueryable()
  505. .AnyAsync(t => t.InstanceId == instance.Id && t.Status == FlowTaskStatusEnum.Pending);
  506. if (hasOtherPending) return;
  507. await CompleteInstance(instance, FlowInstanceStatusEnum.Approved);
  508. return;
  509. }
  510. if (nextNode.Type is "bpmn:exclusiveGateway")
  511. {
  512. await MarkNodeCompleted(instance.Id, nextNode);
  513. var bizData = await GetBizData(instance.BizType, instance.BizId);
  514. var targetNodeId = EvaluateGateway(nextNode.Properties?.Conditions, flowData, nextNode.Id, bizData);
  515. instance.CurrentNodeId = targetNodeId;
  516. await _instanceRep.AsUpdateable(instance).UpdateColumns(i => new { i.CurrentNodeId }).ExecuteCommandAsync();
  517. await ProcessNextNode(instance, flowData, targetNodeId);
  518. return;
  519. }
  520. if (nextNode.Type is "bpmn:parallelGateway")
  521. {
  522. var incoming = flowData.Edges.Where(e => e.TargetNodeId == nextNode.Id).Select(e => e.SourceNodeId).ToList();
  523. var outgoing = flowData.Edges.Where(e => e.SourceNodeId == nextNode.Id).Select(e => e.TargetNodeId).ToList();
  524. // Join 语义:入边 >= 2,需等所有前驱都已完成
  525. if (incoming.Count >= 2)
  526. {
  527. var completedSet = await GetCompletedNodeIdSet(instance.Id);
  528. if (!incoming.All(p => completedSet.Contains(p)))
  529. {
  530. // 未汇合,静默等待后续分支抵达
  531. return;
  532. }
  533. }
  534. // Fork 或 Join 通过:标记网关完成,沿所有出边推进
  535. await MarkNodeCompleted(instance.Id, nextNode);
  536. foreach (var target in outgoing)
  537. {
  538. await ProcessNextNode(instance, flowData, target);
  539. }
  540. return;
  541. }
  542. // userTask 或其他:创建任务
  543. instance.CurrentNodeId = nextNodeId;
  544. await _instanceRep.AsUpdateable(instance).UpdateColumns(i => new { i.CurrentNodeId }).ExecuteCommandAsync();
  545. await CreateTasksForNode(instance, flowData, nextNodeId);
  546. }
  547. /// <summary>
  548. /// 标记节点已完成(幂等:重复写入被唯一索引拦截后忽略)
  549. /// </summary>
  550. private async Task MarkNodeCompleted(long instanceId, ApprovalFlowNodeItem? node)
  551. {
  552. if (node == null) return;
  553. try
  554. {
  555. await _completedNodeRep.InsertAsync(new ApprovalFlowCompletedNode
  556. {
  557. InstanceId = instanceId,
  558. NodeId = node.Id,
  559. NodeName = node.Properties?.NodeName ?? node.Text?.Value,
  560. NodeType = node.Type,
  561. CompletedTime = DateTime.Now,
  562. });
  563. }
  564. catch
  565. {
  566. // 并发场景下可能触发唯一索引冲突,忽略(已存在即可)
  567. }
  568. }
  569. /// <summary>
  570. /// 查询实例已完成节点 Id 集合
  571. /// </summary>
  572. private async Task<HashSet<string>> GetCompletedNodeIdSet(long instanceId)
  573. {
  574. var ids = await _completedNodeRep.AsQueryable()
  575. .Where(c => c.InstanceId == instanceId)
  576. .Select(c => c.NodeId)
  577. .ToListAsync();
  578. return new HashSet<string>(ids);
  579. }
  580. private async Task CompleteInstance(ApprovalFlowInstance instance, FlowInstanceStatusEnum status)
  581. {
  582. // 幂等:并行分支同时到达 end 时避免重复完成
  583. var latest = await _instanceRep.GetByIdAsync(instance.Id);
  584. if (latest == null || latest.Status != FlowInstanceStatusEnum.Running) return;
  585. instance.Status = status;
  586. instance.EndTime = DateTime.Now;
  587. await _instanceRep.AsUpdateable(instance)
  588. .UpdateColumns(i => new { i.Status, i.EndTime })
  589. .ExecuteCommandAsync();
  590. await InvokeHandler(instance.BizType,
  591. h => h.OnFlowCompleted(instance.BizId, status));
  592. await _notifyService.NotifyFlowCompleted(instance.InitiatorId, instance.Id, instance.Title, status);
  593. }
  594. private async Task CreateTasksForNode(ApprovalFlowInstance instance, ApprovalFlowItem flowData, string nodeId)
  595. {
  596. var node = flowData.Nodes.FirstOrDefault(n => n.Id == nodeId)
  597. ?? throw Oops.Oh($"FlowJson 中未找到节点 [{nodeId}]");
  598. var approvers = await ResolveApprovers(node.Properties, instance.InitiatorId);
  599. if (approvers.Count == 0)
  600. throw Oops.Oh($"节点 [{node.Properties?.NodeName ?? nodeId}] 未配置审批人或审批人列表为空");
  601. var nodeName = node.Properties?.NodeName ?? node.Text?.Value;
  602. var tasks = approvers.Select(a => new ApprovalFlowTask
  603. {
  604. InstanceId = instance.Id,
  605. NodeId = nodeId,
  606. NodeName = nodeName,
  607. AssigneeId = a.userId,
  608. AssigneeName = a.userName,
  609. Status = FlowTaskStatusEnum.Pending,
  610. OrgId = instance.OrgId,
  611. }).ToList();
  612. // P1-6 审批代理:为每个原审批人检查是否存在有效代理,是则并行创建一条代理任务
  613. var delegateTasks = new List<ApprovalFlowTask>();
  614. foreach (var a in approvers)
  615. {
  616. var del = await FindEffectiveDelegate(a.userId, instance.BizType);
  617. if (del == null) continue;
  618. // 代理人和原审批人不能重复,代理人也不能是原审批人列表中其他人(避免同一人两条任务)
  619. if (approvers.Any(x => x.userId == del.DelegateUserId)) continue;
  620. delegateTasks.Add(new ApprovalFlowTask
  621. {
  622. InstanceId = instance.Id,
  623. NodeId = nodeId,
  624. NodeName = nodeName,
  625. AssigneeId = del.DelegateUserId,
  626. AssigneeName = del.DelegateUserName,
  627. Status = FlowTaskStatusEnum.Pending,
  628. OrgId = instance.OrgId,
  629. IsDelegate = true,
  630. DelegateForUserId = a.userId,
  631. DelegateForUserName = a.userName,
  632. });
  633. }
  634. var allTasks = tasks.Concat(delegateTasks).ToList();
  635. await _taskRep.AsInsertable(allTasks).ExecuteCommandAsync();
  636. var assigneeIds = allTasks.Select(t => t.AssigneeId).Distinct().ToList();
  637. await _notifyService.NotifyNewTask(assigneeIds, instance.Id, instance.Title, nodeName);
  638. }
  639. /// <summary>
  640. /// 查找指定用户当前生效的审批代理(时间窗口内 + 已启用 + BizType 匹配或全局)
  641. /// </summary>
  642. private async Task<ApprovalFlowDelegate?> FindEffectiveDelegate(long userId, string? bizType)
  643. {
  644. var now = DateTime.Now;
  645. return await _delegateRep.AsQueryable()
  646. .Where(d => d.UserId == userId
  647. && d.IsEnabled
  648. && d.StartTime <= now
  649. && d.EndTime >= now
  650. && (string.IsNullOrEmpty(d.BizType) || d.BizType == bizType))
  651. .OrderBy(d => d.BizType == null ? 1 : 0) // 优先匹配指定 BizType 的代理
  652. .OrderByDescending(d => d.CreateTime)
  653. .FirstAsync();
  654. }
  655. /// <summary>
  656. /// 取消与已完成任务配对的代理任务(P1-6)
  657. /// - 本人任务完成:取消对应的代理任务
  658. /// - 代理任务完成:取消对应的本人任务
  659. /// </summary>
  660. private async Task CancelPairedDelegateTask(ApprovalFlowTask completedTask)
  661. {
  662. ApprovalFlowTask? paired;
  663. if (completedTask.IsDelegate)
  664. {
  665. var originalUserId = completedTask.DelegateForUserId ?? 0;
  666. if (originalUserId == 0) return;
  667. paired = await _taskRep.AsQueryable()
  668. .Where(t => t.InstanceId == completedTask.InstanceId
  669. && t.NodeId == completedTask.NodeId
  670. && t.Status == FlowTaskStatusEnum.Pending
  671. && t.AssigneeId == originalUserId
  672. && !t.IsDelegate)
  673. .FirstAsync();
  674. }
  675. else
  676. {
  677. paired = await _taskRep.AsQueryable()
  678. .Where(t => t.InstanceId == completedTask.InstanceId
  679. && t.NodeId == completedTask.NodeId
  680. && t.Status == FlowTaskStatusEnum.Pending
  681. && t.IsDelegate
  682. && t.DelegateForUserId == completedTask.AssigneeId)
  683. .FirstAsync();
  684. }
  685. if (paired == null) return;
  686. paired.Status = FlowTaskStatusEnum.Cancelled;
  687. paired.ActionTime = DateTime.Now;
  688. await _taskRep.AsUpdateable(paired).ExecuteCommandAsync();
  689. }
  690. private async Task<List<(long userId, string userName)>> ResolveApprovers(FlowProperties? props, long initiatorId)
  691. {
  692. if (props == null || string.IsNullOrWhiteSpace(props.ApproverType))
  693. return new List<(long, string)>();
  694. var approverType = props.ApproverType;
  695. if (approverType == nameof(ApproverTypeEnum.Initiator))
  696. {
  697. var initiator = await _userRep.GetByIdAsync(initiatorId);
  698. return initiator != null
  699. ? new List<(long, string)> { (initiator.Id, initiator.RealName ?? "") }
  700. : new List<(long, string)>();
  701. }
  702. if (string.IsNullOrWhiteSpace(props.ApproverIds))
  703. return new List<(long, string)>();
  704. var ids = props.ApproverIds.Split(',', StringSplitOptions.RemoveEmptyEntries)
  705. .Select(s => long.TryParse(s.Trim(), out var v) ? v : 0).Where(id => id > 0).ToList();
  706. if (approverType == nameof(ApproverTypeEnum.SpecificUser))
  707. {
  708. var users = await _userRep.AsQueryable()
  709. .Where(u => ids.Contains(u.Id)).ToListAsync();
  710. return users.Select(u => (u.Id, u.RealName ?? "")).ToList();
  711. }
  712. if (approverType == nameof(ApproverTypeEnum.Role))
  713. {
  714. var userIds = await _userRoleRep.AsQueryable()
  715. .Where(ur => ids.Contains(ur.RoleId))
  716. .Select(ur => ur.UserId)
  717. .ToListAsync();
  718. var users = await _userRep.AsQueryable()
  719. .Where(u => userIds.Contains(u.Id)).ToListAsync();
  720. return users.Select(u => (u.Id, u.RealName ?? "")).ToList();
  721. }
  722. if (approverType == nameof(ApproverTypeEnum.Department))
  723. {
  724. var users = await _userRep.AsQueryable()
  725. .Where(u => ids.Contains(u.OrgId)).ToListAsync();
  726. return users.Select(u => (u.Id, u.RealName ?? "")).ToList();
  727. }
  728. if (approverType == nameof(ApproverTypeEnum.DepartmentLeader))
  729. {
  730. var initiator = await _userRep.GetByIdAsync(initiatorId);
  731. if (initiator == null || initiator.OrgId <= 0)
  732. return new List<(long, string)>();
  733. var org = await _orgRep.GetByIdAsync(initiator.OrgId);
  734. if (org?.DirectorId != null && org.DirectorId > 0)
  735. {
  736. var director = await _userRep.GetByIdAsync(org.DirectorId.Value);
  737. if (director != null)
  738. return new List<(long, string)> { (director.Id, director.RealName ?? "") };
  739. }
  740. if (initiator.ManagerUserId != null && initiator.ManagerUserId > 0)
  741. {
  742. var manager = await _userRep.GetByIdAsync(initiator.ManagerUserId.Value);
  743. if (manager != null)
  744. return new List<(long, string)> { (manager.Id, manager.RealName ?? "") };
  745. }
  746. return new List<(long, string)>();
  747. }
  748. return new List<(long, string)>();
  749. }
  750. /// <summary>
  751. /// 评估排他网关 — 依次尝试各非默认分支的条件表达式,首个匹配的获胜;
  752. /// 全不匹配则走默认分支;无默认则走第一条出边
  753. /// 支持简单比较表达式:variable op value(op: ==,!=,>,>=,&lt;,&lt;=)
  754. /// </summary>
  755. private string EvaluateGateway(List<GatewayCondition>? conditions, ApprovalFlowItem flowData, string gatewayNodeId, Dictionary<string, object>? bizData)
  756. {
  757. if (conditions != null && conditions.Count > 0 && bizData != null && bizData.Count > 0)
  758. {
  759. foreach (var cond in conditions.Where(c => !c.IsDefault))
  760. {
  761. if (!string.IsNullOrWhiteSpace(cond.Expression) && EvalSimpleExpression(cond.Expression, bizData))
  762. return cond.TargetNodeId;
  763. }
  764. var defaultBranch = conditions.FirstOrDefault(c => c.IsDefault);
  765. if (defaultBranch != null)
  766. return defaultBranch.TargetNodeId;
  767. }
  768. else if (conditions != null && conditions.Count > 0)
  769. {
  770. var defaultBranch = conditions.FirstOrDefault(c => c.IsDefault);
  771. if (defaultBranch != null) return defaultBranch.TargetNodeId;
  772. return conditions.First().TargetNodeId;
  773. }
  774. var edge = flowData.Edges.FirstOrDefault(e => e.SourceNodeId == gatewayNodeId);
  775. return edge?.TargetNodeId ?? throw Oops.Oh("排他网关没有出边");
  776. }
  777. /// <summary>
  778. /// 简单表达式求值:支持 "field op value" 格式(如 "urgent == 1", "customLevel >= 3", "amount > 10000")
  779. /// 多条件用 &amp;&amp; 连接
  780. /// </summary>
  781. private static bool EvalSimpleExpression(string expression, Dictionary<string, object> bizData)
  782. {
  783. var parts = expression.Split("&&", StringSplitOptions.TrimEntries);
  784. foreach (var part in parts)
  785. {
  786. if (!EvalSingleComparison(part.Trim(), bizData))
  787. return false;
  788. }
  789. return true;
  790. }
  791. private static bool EvalSingleComparison(string expr, Dictionary<string, object> bizData)
  792. {
  793. string[] ops = { ">=", "<=", "!=", "==", ">", "<" };
  794. foreach (var op in ops)
  795. {
  796. var idx = expr.IndexOf(op, StringComparison.Ordinal);
  797. if (idx < 0) continue;
  798. var fieldName = expr[..idx].Trim();
  799. var valueStr = expr[(idx + op.Length)..].Trim().Trim('"', '\'');
  800. if (!bizData.TryGetValue(fieldName, out var fieldValue))
  801. return false;
  802. if (decimal.TryParse(fieldValue?.ToString(), out var numLeft) && decimal.TryParse(valueStr, out var numRight))
  803. {
  804. return op switch
  805. {
  806. "==" => numLeft == numRight,
  807. "!=" => numLeft != numRight,
  808. ">" => numLeft > numRight,
  809. ">=" => numLeft >= numRight,
  810. "<" => numLeft < numRight,
  811. "<=" => numLeft <= numRight,
  812. _ => false,
  813. };
  814. }
  815. var strLeft = fieldValue?.ToString() ?? "";
  816. return op switch
  817. {
  818. "==" => strLeft.Equals(valueStr, StringComparison.OrdinalIgnoreCase),
  819. "!=" => !strLeft.Equals(valueStr, StringComparison.OrdinalIgnoreCase),
  820. _ => false,
  821. };
  822. }
  823. return false;
  824. }
  825. private async Task<Dictionary<string, object>?> GetBizData(string bizType, long bizId)
  826. {
  827. var handlers = App.GetServices<IFlowBizHandler>();
  828. var handler = handlers?.FirstOrDefault(h => h.BizType == bizType);
  829. if (handler == null) return null;
  830. return await handler.GetBizData(bizId);
  831. }
  832. private string? FindNextNodeId(ApprovalFlowItem flowData, string currentNodeId)
  833. {
  834. var edge = flowData.Edges.FirstOrDefault(e => e.SourceNodeId == currentNodeId);
  835. return edge?.TargetNodeId;
  836. }
  837. private string? FindPrevUserTaskNodeId(ApprovalFlowItem flowData, string currentNodeId)
  838. {
  839. var inEdge = flowData.Edges.FirstOrDefault(e => e.TargetNodeId == currentNodeId);
  840. if (inEdge == null) return null;
  841. var prevNode = flowData.Nodes.FirstOrDefault(n => n.Id == inEdge.SourceNodeId);
  842. if (prevNode == null) return null;
  843. if (prevNode.Type is "bpmn:userTask" or "user-node" or "task-node")
  844. return prevNode.Id;
  845. // 递归跳过网关等非用户任务节点
  846. return FindPrevUserTaskNodeId(flowData, prevNode.Id);
  847. }
  848. private async Task<bool> IsNodeCompleted(ApprovalFlowInstance instance, string nodeId)
  849. {
  850. var flowData = DeserializeFlowJson(instance.FlowJsonSnapshot);
  851. var node = flowData.Nodes.FirstOrDefault(n => n.Id == nodeId);
  852. var mode = node?.Properties?.MultiApproveMode;
  853. if (mode == nameof(MultiApproveModeEnum.All))
  854. {
  855. var pendingCount = await _taskRep.AsQueryable()
  856. .Where(t => t.InstanceId == instance.Id && t.NodeId == nodeId && t.Status == FlowTaskStatusEnum.Pending)
  857. .CountAsync();
  858. return pendingCount == 0;
  859. }
  860. // 默认或签(Any):一人通过即完成,取消其他 Pending
  861. await CancelPendingTasks(instance.Id, nodeId);
  862. return true;
  863. }
  864. private async Task CancelPendingTasks(long instanceId, string nodeId, long? excludeTaskId = null)
  865. {
  866. var tasks = await _taskRep.AsQueryable()
  867. .Where(t => t.InstanceId == instanceId && t.NodeId == nodeId && t.Status == FlowTaskStatusEnum.Pending)
  868. .WhereIF(excludeTaskId.HasValue, t => t.Id != excludeTaskId!.Value)
  869. .ToListAsync();
  870. foreach (var t in tasks)
  871. {
  872. t.Status = FlowTaskStatusEnum.Cancelled;
  873. t.ActionTime = DateTime.Now;
  874. }
  875. if (tasks.Count > 0)
  876. await _taskRep.AsUpdateable(tasks).ExecuteCommandAsync();
  877. }
  878. /// <summary>
  879. /// 取消整个实例下所有剩余 Pending 任务(Reject 时跨并行分支使用)
  880. /// </summary>
  881. private async Task CancelAllPendingTasks(long instanceId, long? excludeTaskId = null)
  882. {
  883. var tasks = await _taskRep.AsQueryable()
  884. .Where(t => t.InstanceId == instanceId && t.Status == FlowTaskStatusEnum.Pending)
  885. .WhereIF(excludeTaskId.HasValue, t => t.Id != excludeTaskId!.Value)
  886. .ToListAsync();
  887. foreach (var t in tasks)
  888. {
  889. t.Status = FlowTaskStatusEnum.Cancelled;
  890. t.ActionTime = DateTime.Now;
  891. }
  892. if (tasks.Count > 0)
  893. await _taskRep.AsUpdateable(tasks).ExecuteCommandAsync();
  894. }
  895. private async Task<ApprovalFlowTask> GetPendingTask(long taskId)
  896. {
  897. var task = await _taskRep.GetByIdAsync(taskId)
  898. ?? throw Oops.Oh("审批任务不存在");
  899. if (task.Status != FlowTaskStatusEnum.Pending)
  900. throw Oops.Oh("该任务已处理");
  901. if (task.AssigneeId != _userManager.UserId)
  902. throw Oops.Oh("当前用户不是该任务的审批人");
  903. return task;
  904. }
  905. private async Task WriteLog(long instanceId, long? taskId, string? nodeId, FlowLogActionEnum action, string? comment)
  906. {
  907. await _logRep.InsertAsync(new ApprovalFlowLog
  908. {
  909. InstanceId = instanceId,
  910. TaskId = taskId,
  911. NodeId = nodeId,
  912. Action = action,
  913. OperatorId = _userManager.UserId,
  914. OperatorName = _userManager.RealName,
  915. Comment = comment,
  916. });
  917. }
  918. private static ApprovalFlowItem DeserializeFlowJson(string? json)
  919. {
  920. if (string.IsNullOrWhiteSpace(json))
  921. throw Oops.Oh("FlowJson 快照为空");
  922. return JsonSerializer.Deserialize<ApprovalFlowItem>(json)
  923. ?? throw Oops.Oh("FlowJson 反序列化失败");
  924. }
  925. private async Task InvokeHandler(string bizType, Func<IFlowBizHandler, Task> action)
  926. {
  927. var handlers = App.GetServices<IFlowBizHandler>();
  928. var handler = handlers?.FirstOrDefault(h => h.BizType == bizType);
  929. if (handler != null)
  930. await action(handler);
  931. }
  932. }