FlowEngineService.cs 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639
  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 UserManager _userManager;
  17. private readonly SysOrgService _sysOrgService;
  18. private readonly FlowNotifyService _notifyService;
  19. public FlowEngineService(
  20. SqlSugarRepository<ApprovalFlow> flowRep,
  21. SqlSugarRepository<ApprovalFlowInstance> instanceRep,
  22. SqlSugarRepository<ApprovalFlowTask> taskRep,
  23. SqlSugarRepository<ApprovalFlowLog> logRep,
  24. SqlSugarRepository<SysUserRole> userRoleRep,
  25. SqlSugarRepository<SysUser> userRep,
  26. UserManager userManager,
  27. SysOrgService sysOrgService,
  28. FlowNotifyService notifyService)
  29. {
  30. _flowRep = flowRep;
  31. _instanceRep = instanceRep;
  32. _taskRep = taskRep;
  33. _logRep = logRep;
  34. _userRoleRep = userRoleRep;
  35. _userRep = userRep;
  36. _userManager = userManager;
  37. _sysOrgService = sysOrgService;
  38. _notifyService = notifyService;
  39. }
  40. /// <summary>
  41. /// 机构数据权限依赖实体的 OrgId;插入时必须与当前用户机构一致,否则非超管在「我发起的/待办」中查不到(OrgId=0 会被过滤)。
  42. /// </summary>
  43. private async Task<long> ResolveOrgIdForNewFlowEntityAsync()
  44. {
  45. var oid = _userManager.OrgId;
  46. if (oid > 0) return oid;
  47. var list = await _sysOrgService.GetUserOrgIdList();
  48. return list is { Count: > 0 } ? list[0] : 0;
  49. }
  50. // ═══════════════════════════════════════════
  51. // 核心生命周期
  52. // ═══════════════════════════════════════════
  53. /// <summary>
  54. /// 发起流程
  55. /// </summary>
  56. public async Task<long> StartFlow(StartFlowInput input)
  57. {
  58. var flow = await _flowRep.AsQueryable()
  59. .Where(u => u.BizType == input.BizType && u.IsPublished && !u.IsDelete)
  60. .OrderByDescending(u => u.Version)
  61. .FirstAsync() ?? throw Oops.Oh($"未找到业务类型 [{input.BizType}] 的已发布流程定义");
  62. if (string.IsNullOrWhiteSpace(flow.FlowJson))
  63. throw Oops.Oh("流程定义的 FlowJson 为空,请先设计流程图");
  64. var flowData = JsonSerializer.Deserialize<ApprovalFlowItem>(flow.FlowJson)
  65. ?? throw Oops.Oh("FlowJson 反序列化失败");
  66. var orgId = await ResolveOrgIdForNewFlowEntityAsync();
  67. var instance = new ApprovalFlowInstance
  68. {
  69. FlowId = flow.Id,
  70. FlowVersion = flow.Version,
  71. BizType = input.BizType,
  72. BizId = input.BizId,
  73. BizNo = input.BizNo,
  74. Title = input.Title ?? $"{flow.Name}-{input.BizNo}",
  75. InitiatorId = _userManager.UserId,
  76. InitiatorName = _userManager.RealName,
  77. Status = FlowInstanceStatusEnum.Running,
  78. FlowJsonSnapshot = flow.FlowJson,
  79. StartTime = DateTime.Now,
  80. OrgId = orgId,
  81. };
  82. var startNode = flowData.Nodes.FirstOrDefault(n =>
  83. n.Type is "bpmn:startEvent" or "start-node")
  84. ?? throw Oops.Oh("流程图中未找到开始节点");
  85. var firstTaskNodeId = FindNextNodeId(flowData, startNode.Id);
  86. instance.CurrentNodeId = firstTaskNodeId;
  87. await _instanceRep.InsertAsync(instance);
  88. await WriteLog(instance.Id, null, startNode.Id, FlowLogActionEnum.Submit, input.Comment);
  89. await CreateTasksForNode(instance, flowData, firstTaskNodeId);
  90. await InvokeHandler(input.BizType, h => h.OnFlowStarted(input.BizId, instance.Id));
  91. return instance.Id;
  92. }
  93. /// <summary>
  94. /// 同意
  95. /// </summary>
  96. public async Task Approve(long taskId, string? comment)
  97. {
  98. var task = await GetPendingTask(taskId);
  99. task.Status = FlowTaskStatusEnum.Approved;
  100. task.Comment = comment;
  101. task.ActionTime = DateTime.Now;
  102. await _taskRep.AsUpdateable(task).ExecuteCommandAsync();
  103. await WriteLog(task.InstanceId, taskId, task.NodeId, FlowLogActionEnum.Approve, comment);
  104. var instance = await _instanceRep.GetByIdAsync(task.InstanceId)
  105. ?? throw Oops.Oh("流程实例不存在");
  106. if (await IsNodeCompleted(instance, task.NodeId))
  107. {
  108. await InvokeHandler(instance.BizType,
  109. h => h.OnNodeCompleted(instance.BizId, task.NodeId, task.NodeName ?? ""));
  110. var flowData = DeserializeFlowJson(instance.FlowJsonSnapshot);
  111. await AdvanceToNext(instance, flowData, task.NodeId);
  112. }
  113. }
  114. /// <summary>
  115. /// 拒绝
  116. /// </summary>
  117. public async Task Reject(long taskId, string? comment)
  118. {
  119. var task = await GetPendingTask(taskId);
  120. task.Status = FlowTaskStatusEnum.Rejected;
  121. task.Comment = comment;
  122. task.ActionTime = DateTime.Now;
  123. await _taskRep.AsUpdateable(task).ExecuteCommandAsync();
  124. await CancelPendingTasks(task.InstanceId, task.NodeId, task.Id);
  125. var instance = await _instanceRep.GetByIdAsync(task.InstanceId)
  126. ?? throw Oops.Oh("流程实例不存在");
  127. instance.Status = FlowInstanceStatusEnum.Rejected;
  128. instance.EndTime = DateTime.Now;
  129. await _instanceRep.AsUpdateable(instance).ExecuteCommandAsync();
  130. await WriteLog(instance.Id, taskId, task.NodeId, FlowLogActionEnum.Reject, comment);
  131. await InvokeHandler(instance.BizType,
  132. h => h.OnFlowCompleted(instance.BizId, FlowInstanceStatusEnum.Rejected));
  133. await _notifyService.NotifyFlowCompleted(instance.InitiatorId, instance.Id, instance.Title, FlowInstanceStatusEnum.Rejected);
  134. }
  135. // ═══════════════════════════════════════════
  136. // 扩展操作
  137. // ═══════════════════════════════════════════
  138. /// <summary>
  139. /// 转办
  140. /// </summary>
  141. public async Task Transfer(long taskId, long targetUserId, string? comment)
  142. {
  143. var task = await GetPendingTask(taskId);
  144. task.Status = FlowTaskStatusEnum.Transferred;
  145. task.Comment = comment;
  146. task.ActionTime = DateTime.Now;
  147. task.TransferToId = targetUserId;
  148. await _taskRep.AsUpdateable(task).ExecuteCommandAsync();
  149. var targetUser = await _userRep.GetByIdAsync(targetUserId);
  150. var instForOrg = await _instanceRep.GetByIdAsync(task.InstanceId);
  151. var newTask = new ApprovalFlowTask
  152. {
  153. InstanceId = task.InstanceId,
  154. NodeId = task.NodeId,
  155. NodeName = task.NodeName,
  156. AssigneeId = targetUserId,
  157. AssigneeName = targetUser?.RealName,
  158. Status = FlowTaskStatusEnum.Pending,
  159. OrgId = instForOrg?.OrgId ?? 0,
  160. };
  161. await _taskRep.InsertAsync(newTask);
  162. await WriteLog(task.InstanceId, taskId, task.NodeId, FlowLogActionEnum.Transfer,
  163. $"{comment} → 转办给 {targetUser?.RealName}");
  164. var instance = await _instanceRep.GetByIdAsync(task.InstanceId);
  165. await _notifyService.NotifyTransferred(targetUserId, task.InstanceId, instance?.Title ?? "", _userManager.RealName);
  166. }
  167. /// <summary>
  168. /// 撤回(发起人撤回)
  169. /// </summary>
  170. public async Task Withdraw(long instanceId)
  171. {
  172. var instance = await _instanceRep.GetByIdAsync(instanceId)
  173. ?? throw Oops.Oh("流程实例不存在");
  174. if (instance.InitiatorId != _userManager.UserId)
  175. throw Oops.Oh("只有发起人可以撤回");
  176. if (instance.Status != FlowInstanceStatusEnum.Running)
  177. throw Oops.Oh("当前流程状态不允许撤回");
  178. var pendingTasks = await _taskRep.AsQueryable()
  179. .Where(t => t.InstanceId == instanceId && t.Status == FlowTaskStatusEnum.Pending)
  180. .ToListAsync();
  181. var doneTasks = await _taskRep.AsQueryable()
  182. .Where(t => t.InstanceId == instanceId &&
  183. t.Status != FlowTaskStatusEnum.Pending &&
  184. t.Status != FlowTaskStatusEnum.Cancelled)
  185. .CountAsync();
  186. if (doneTasks > 0)
  187. throw Oops.Oh("已有人审批过,不可撤回");
  188. var cancelledUserIds = pendingTasks.Select(t => t.AssigneeId).Distinct().ToList();
  189. foreach (var t in pendingTasks)
  190. {
  191. t.Status = FlowTaskStatusEnum.Cancelled;
  192. t.ActionTime = DateTime.Now;
  193. }
  194. await _taskRep.AsUpdateable(pendingTasks).ExecuteCommandAsync();
  195. instance.Status = FlowInstanceStatusEnum.Cancelled;
  196. instance.EndTime = DateTime.Now;
  197. await _instanceRep.AsUpdateable(instance).ExecuteCommandAsync();
  198. await WriteLog(instanceId, null, instance.CurrentNodeId, FlowLogActionEnum.Withdraw, null);
  199. await InvokeHandler(instance.BizType,
  200. h => h.OnFlowCompleted(instance.BizId, FlowInstanceStatusEnum.Cancelled));
  201. await _notifyService.NotifyWithdrawn(cancelledUserIds, instanceId, instance.Title, instance.InitiatorName);
  202. }
  203. /// <summary>
  204. /// 退回上一步
  205. /// </summary>
  206. public async Task ReturnToPrev(long taskId, string? comment)
  207. {
  208. var task = await GetPendingTask(taskId);
  209. var instance = await _instanceRep.GetByIdAsync(task.InstanceId)
  210. ?? throw Oops.Oh("流程实例不存在");
  211. var flowData = DeserializeFlowJson(instance.FlowJsonSnapshot);
  212. var prevNodeId = FindPrevUserTaskNodeId(flowData, task.NodeId);
  213. if (prevNodeId == null)
  214. throw Oops.Oh("已是第一个审批节点,无法退回");
  215. await CancelPendingTasks(task.InstanceId, task.NodeId);
  216. task.Status = FlowTaskStatusEnum.Returned;
  217. task.Comment = comment;
  218. task.ActionTime = DateTime.Now;
  219. await _taskRep.AsUpdateable(task).ExecuteCommandAsync();
  220. instance.CurrentNodeId = prevNodeId;
  221. await _instanceRep.AsUpdateable(instance).ExecuteCommandAsync();
  222. await CreateTasksForNode(instance, flowData, prevNodeId);
  223. await WriteLog(instance.Id, taskId, task.NodeId, FlowLogActionEnum.Return, comment);
  224. var returnedTasks = await _taskRep.AsQueryable()
  225. .Where(t => t.InstanceId == instance.Id && t.NodeId == prevNodeId && t.Status == FlowTaskStatusEnum.Pending)
  226. .ToListAsync();
  227. var returnedUserIds = returnedTasks.Select(t => t.AssigneeId).Distinct().ToList();
  228. await _notifyService.NotifyReturned(returnedUserIds, instance.Id, instance.Title, _userManager.RealName);
  229. }
  230. /// <summary>
  231. /// 加签
  232. /// </summary>
  233. public async Task AddSign(long taskId, long targetUserId, string? comment)
  234. {
  235. var task = await GetPendingTask(taskId);
  236. var targetUser = await _userRep.GetByIdAsync(targetUserId);
  237. var instForAddSign = await _instanceRep.GetByIdAsync(task.InstanceId);
  238. var newTask = new ApprovalFlowTask
  239. {
  240. InstanceId = task.InstanceId,
  241. NodeId = task.NodeId,
  242. NodeName = task.NodeName,
  243. AssigneeId = targetUserId,
  244. AssigneeName = targetUser?.RealName,
  245. Status = FlowTaskStatusEnum.Pending,
  246. IsAddSign = true,
  247. AddSignById = _userManager.UserId,
  248. OrgId = instForAddSign?.OrgId ?? 0,
  249. };
  250. await _taskRep.InsertAsync(newTask);
  251. await WriteLog(task.InstanceId, taskId, task.NodeId, FlowLogActionEnum.AddSign,
  252. $"{comment} → 加签给 {targetUser?.RealName}");
  253. var instance = await _instanceRep.GetByIdAsync(task.InstanceId);
  254. await _notifyService.NotifyAddSign(targetUserId, task.InstanceId, instance?.Title ?? "", _userManager.RealName);
  255. }
  256. /// <summary>
  257. /// 催办
  258. /// </summary>
  259. public async Task Urge(long instanceId)
  260. {
  261. var instance = await _instanceRep.GetByIdAsync(instanceId)
  262. ?? throw Oops.Oh("流程实例不存在");
  263. if (instance.Status != FlowInstanceStatusEnum.Running)
  264. throw Oops.Oh("当前流程不在审批中");
  265. await WriteLog(instanceId, null, instance.CurrentNodeId, FlowLogActionEnum.Urge, "催办");
  266. var pendingTasks = await _taskRep.AsQueryable()
  267. .Where(t => t.InstanceId == instanceId && t.Status == FlowTaskStatusEnum.Pending)
  268. .ToListAsync();
  269. var userIds = pendingTasks.Select(t => t.AssigneeId).Distinct().ToList();
  270. await _notifyService.NotifyUrge(userIds, instanceId, instance.Title);
  271. }
  272. // ═══════════════════════════════════════════
  273. // 内部引擎方法
  274. // ═══════════════════════════════════════════
  275. private async Task AdvanceToNext(ApprovalFlowInstance instance, ApprovalFlowItem flowData, string currentNodeId)
  276. {
  277. var nextNodeId = FindNextNodeId(flowData, currentNodeId);
  278. if (nextNodeId == null)
  279. {
  280. await CompleteInstance(instance, FlowInstanceStatusEnum.Approved);
  281. return;
  282. }
  283. var nextNode = flowData.Nodes.FirstOrDefault(n => n.Id == nextNodeId);
  284. if (nextNode == null)
  285. {
  286. await CompleteInstance(instance, FlowInstanceStatusEnum.Approved);
  287. return;
  288. }
  289. if (nextNode.Type is "bpmn:endEvent" or "end-node")
  290. {
  291. await CompleteInstance(instance, FlowInstanceStatusEnum.Approved);
  292. return;
  293. }
  294. if (nextNode.Type is "bpmn:exclusiveGateway")
  295. {
  296. var bizData = await GetBizData(instance.BizType, instance.BizId);
  297. var targetNodeId = EvaluateGateway(nextNode.Properties?.Conditions, flowData, nextNode.Id, bizData);
  298. instance.CurrentNodeId = targetNodeId;
  299. await _instanceRep.AsUpdateable(instance).UpdateColumns(i => new { i.CurrentNodeId }).ExecuteCommandAsync();
  300. await AdvanceToNext(instance, flowData, nextNode.Id);
  301. return;
  302. }
  303. instance.CurrentNodeId = nextNodeId;
  304. await _instanceRep.AsUpdateable(instance).UpdateColumns(i => new { i.CurrentNodeId }).ExecuteCommandAsync();
  305. await CreateTasksForNode(instance, flowData, nextNodeId);
  306. }
  307. private async Task CompleteInstance(ApprovalFlowInstance instance, FlowInstanceStatusEnum status)
  308. {
  309. instance.Status = status;
  310. instance.EndTime = DateTime.Now;
  311. await _instanceRep.AsUpdateable(instance)
  312. .UpdateColumns(i => new { i.Status, i.EndTime })
  313. .ExecuteCommandAsync();
  314. await InvokeHandler(instance.BizType,
  315. h => h.OnFlowCompleted(instance.BizId, status));
  316. await _notifyService.NotifyFlowCompleted(instance.InitiatorId, instance.Id, instance.Title, status);
  317. }
  318. private async Task CreateTasksForNode(ApprovalFlowInstance instance, ApprovalFlowItem flowData, string nodeId)
  319. {
  320. var node = flowData.Nodes.FirstOrDefault(n => n.Id == nodeId)
  321. ?? throw Oops.Oh($"FlowJson 中未找到节点 [{nodeId}]");
  322. var approvers = await ResolveApprovers(node.Properties, instance.InitiatorId);
  323. if (approvers.Count == 0)
  324. throw Oops.Oh($"节点 [{node.Properties?.NodeName ?? nodeId}] 未配置审批人或审批人列表为空");
  325. var tasks = approvers.Select(a => new ApprovalFlowTask
  326. {
  327. InstanceId = instance.Id,
  328. NodeId = nodeId,
  329. NodeName = node.Properties?.NodeName ?? node.Text?.Value,
  330. AssigneeId = a.userId,
  331. AssigneeName = a.userName,
  332. Status = FlowTaskStatusEnum.Pending,
  333. OrgId = instance.OrgId,
  334. }).ToList();
  335. await _taskRep.AsInsertable(tasks).ExecuteCommandAsync();
  336. var assigneeIds = tasks.Select(t => t.AssigneeId).Distinct().ToList();
  337. await _notifyService.NotifyNewTask(assigneeIds, instance.Id, instance.Title, node.Properties?.NodeName ?? node.Text?.Value);
  338. }
  339. private async Task<List<(long userId, string userName)>> ResolveApprovers(FlowProperties? props, long initiatorId)
  340. {
  341. if (props == null || string.IsNullOrWhiteSpace(props.ApproverType))
  342. return new List<(long, string)>();
  343. var approverType = props.ApproverType;
  344. if (approverType == nameof(ApproverTypeEnum.Initiator))
  345. {
  346. var initiator = await _userRep.GetByIdAsync(initiatorId);
  347. return initiator != null
  348. ? new List<(long, string)> { (initiator.Id, initiator.RealName ?? "") }
  349. : new List<(long, string)>();
  350. }
  351. if (string.IsNullOrWhiteSpace(props.ApproverIds))
  352. return new List<(long, string)>();
  353. var ids = props.ApproverIds.Split(',', StringSplitOptions.RemoveEmptyEntries)
  354. .Select(s => long.TryParse(s.Trim(), out var v) ? v : 0).Where(id => id > 0).ToList();
  355. if (approverType == nameof(ApproverTypeEnum.SpecificUser))
  356. {
  357. var users = await _userRep.AsQueryable()
  358. .Where(u => ids.Contains(u.Id)).ToListAsync();
  359. return users.Select(u => (u.Id, u.RealName ?? "")).ToList();
  360. }
  361. if (approverType == nameof(ApproverTypeEnum.Role))
  362. {
  363. var userIds = await _userRoleRep.AsQueryable()
  364. .Where(ur => ids.Contains(ur.RoleId))
  365. .Select(ur => ur.UserId)
  366. .ToListAsync();
  367. var users = await _userRep.AsQueryable()
  368. .Where(u => userIds.Contains(u.Id)).ToListAsync();
  369. return users.Select(u => (u.Id, u.RealName ?? "")).ToList();
  370. }
  371. if (approverType == nameof(ApproverTypeEnum.Department))
  372. {
  373. var users = await _userRep.AsQueryable()
  374. .Where(u => ids.Contains(u.OrgId)).ToListAsync();
  375. return users.Select(u => (u.Id, u.RealName ?? "")).ToList();
  376. }
  377. return new List<(long, string)>();
  378. }
  379. /// <summary>
  380. /// 评估排他网关 — 依次尝试各非默认分支的条件表达式,首个匹配的获胜;
  381. /// 全不匹配则走默认分支;无默认则走第一条出边
  382. /// 支持简单比较表达式:variable op value(op: ==,!=,>,>=,&lt;,&lt;=)
  383. /// </summary>
  384. private string EvaluateGateway(List<GatewayCondition>? conditions, ApprovalFlowItem flowData, string gatewayNodeId, Dictionary<string, object>? bizData)
  385. {
  386. if (conditions != null && conditions.Count > 0 && bizData != null && bizData.Count > 0)
  387. {
  388. foreach (var cond in conditions.Where(c => !c.IsDefault))
  389. {
  390. if (!string.IsNullOrWhiteSpace(cond.Expression) && EvalSimpleExpression(cond.Expression, bizData))
  391. return cond.TargetNodeId;
  392. }
  393. var defaultBranch = conditions.FirstOrDefault(c => c.IsDefault);
  394. if (defaultBranch != null)
  395. return defaultBranch.TargetNodeId;
  396. }
  397. else if (conditions != null && conditions.Count > 0)
  398. {
  399. var defaultBranch = conditions.FirstOrDefault(c => c.IsDefault);
  400. if (defaultBranch != null) return defaultBranch.TargetNodeId;
  401. return conditions.First().TargetNodeId;
  402. }
  403. var edge = flowData.Edges.FirstOrDefault(e => e.SourceNodeId == gatewayNodeId);
  404. return edge?.TargetNodeId ?? throw Oops.Oh("排他网关没有出边");
  405. }
  406. /// <summary>
  407. /// 简单表达式求值:支持 "field op value" 格式(如 "urgent == 1", "customLevel >= 3", "amount > 10000")
  408. /// 多条件用 &amp;&amp; 连接
  409. /// </summary>
  410. private static bool EvalSimpleExpression(string expression, Dictionary<string, object> bizData)
  411. {
  412. var parts = expression.Split("&&", StringSplitOptions.TrimEntries);
  413. foreach (var part in parts)
  414. {
  415. if (!EvalSingleComparison(part.Trim(), bizData))
  416. return false;
  417. }
  418. return true;
  419. }
  420. private static bool EvalSingleComparison(string expr, Dictionary<string, object> bizData)
  421. {
  422. string[] ops = { ">=", "<=", "!=", "==", ">", "<" };
  423. foreach (var op in ops)
  424. {
  425. var idx = expr.IndexOf(op, StringComparison.Ordinal);
  426. if (idx < 0) continue;
  427. var fieldName = expr[..idx].Trim();
  428. var valueStr = expr[(idx + op.Length)..].Trim().Trim('"', '\'');
  429. if (!bizData.TryGetValue(fieldName, out var fieldValue))
  430. return false;
  431. if (decimal.TryParse(fieldValue?.ToString(), out var numLeft) && decimal.TryParse(valueStr, out var numRight))
  432. {
  433. return op switch
  434. {
  435. "==" => numLeft == numRight,
  436. "!=" => numLeft != numRight,
  437. ">" => numLeft > numRight,
  438. ">=" => numLeft >= numRight,
  439. "<" => numLeft < numRight,
  440. "<=" => numLeft <= numRight,
  441. _ => false,
  442. };
  443. }
  444. var strLeft = fieldValue?.ToString() ?? "";
  445. return op switch
  446. {
  447. "==" => strLeft.Equals(valueStr, StringComparison.OrdinalIgnoreCase),
  448. "!=" => !strLeft.Equals(valueStr, StringComparison.OrdinalIgnoreCase),
  449. _ => false,
  450. };
  451. }
  452. return false;
  453. }
  454. private async Task<Dictionary<string, object>?> GetBizData(string bizType, long bizId)
  455. {
  456. var handlers = App.GetServices<IFlowBizHandler>();
  457. var handler = handlers?.FirstOrDefault(h => h.BizType == bizType);
  458. if (handler == null) return null;
  459. return await handler.GetBizData(bizId);
  460. }
  461. private string? FindNextNodeId(ApprovalFlowItem flowData, string currentNodeId)
  462. {
  463. var edge = flowData.Edges.FirstOrDefault(e => e.SourceNodeId == currentNodeId);
  464. return edge?.TargetNodeId;
  465. }
  466. private string? FindPrevUserTaskNodeId(ApprovalFlowItem flowData, string currentNodeId)
  467. {
  468. var inEdge = flowData.Edges.FirstOrDefault(e => e.TargetNodeId == currentNodeId);
  469. if (inEdge == null) return null;
  470. var prevNode = flowData.Nodes.FirstOrDefault(n => n.Id == inEdge.SourceNodeId);
  471. if (prevNode == null) return null;
  472. if (prevNode.Type is "bpmn:userTask" or "user-node" or "task-node")
  473. return prevNode.Id;
  474. // 递归跳过网关等非用户任务节点
  475. return FindPrevUserTaskNodeId(flowData, prevNode.Id);
  476. }
  477. private async Task<bool> IsNodeCompleted(ApprovalFlowInstance instance, string nodeId)
  478. {
  479. var flowData = DeserializeFlowJson(instance.FlowJsonSnapshot);
  480. var node = flowData.Nodes.FirstOrDefault(n => n.Id == nodeId);
  481. var mode = node?.Properties?.MultiApproveMode;
  482. if (mode == nameof(MultiApproveModeEnum.All))
  483. {
  484. var pendingCount = await _taskRep.AsQueryable()
  485. .Where(t => t.InstanceId == instance.Id && t.NodeId == nodeId && t.Status == FlowTaskStatusEnum.Pending)
  486. .CountAsync();
  487. return pendingCount == 0;
  488. }
  489. // 默认或签(Any):一人通过即完成,取消其他 Pending
  490. await CancelPendingTasks(instance.Id, nodeId);
  491. return true;
  492. }
  493. private async Task CancelPendingTasks(long instanceId, string nodeId, long? excludeTaskId = null)
  494. {
  495. var tasks = await _taskRep.AsQueryable()
  496. .Where(t => t.InstanceId == instanceId && t.NodeId == nodeId && t.Status == FlowTaskStatusEnum.Pending)
  497. .WhereIF(excludeTaskId.HasValue, t => t.Id != excludeTaskId!.Value)
  498. .ToListAsync();
  499. foreach (var t in tasks)
  500. {
  501. t.Status = FlowTaskStatusEnum.Cancelled;
  502. t.ActionTime = DateTime.Now;
  503. }
  504. if (tasks.Count > 0)
  505. await _taskRep.AsUpdateable(tasks).ExecuteCommandAsync();
  506. }
  507. private async Task<ApprovalFlowTask> GetPendingTask(long taskId)
  508. {
  509. var task = await _taskRep.GetByIdAsync(taskId)
  510. ?? throw Oops.Oh("审批任务不存在");
  511. if (task.Status != FlowTaskStatusEnum.Pending)
  512. throw Oops.Oh("该任务已处理");
  513. if (task.AssigneeId != _userManager.UserId)
  514. throw Oops.Oh("当前用户不是该任务的审批人");
  515. return task;
  516. }
  517. private async Task WriteLog(long instanceId, long? taskId, string? nodeId, FlowLogActionEnum action, string? comment)
  518. {
  519. await _logRep.InsertAsync(new ApprovalFlowLog
  520. {
  521. InstanceId = instanceId,
  522. TaskId = taskId,
  523. NodeId = nodeId,
  524. Action = action,
  525. OperatorId = _userManager.UserId,
  526. OperatorName = _userManager.RealName,
  527. Comment = comment,
  528. });
  529. }
  530. private static ApprovalFlowItem DeserializeFlowJson(string? json)
  531. {
  532. if (string.IsNullOrWhiteSpace(json))
  533. throw Oops.Oh("FlowJson 快照为空");
  534. return JsonSerializer.Deserialize<ApprovalFlowItem>(json)
  535. ?? throw Oops.Oh("FlowJson 反序列化失败");
  536. }
  537. private async Task InvokeHandler(string bizType, Func<IFlowBizHandler, Task> action)
  538. {
  539. var handlers = App.GetServices<IFlowBizHandler>();
  540. var handler = handlers?.FirstOrDefault(h => h.BizType == bizType);
  541. if (handler != null)
  542. await action(handler);
  543. }
  544. }