FlowTimeoutJob.cs 3.3 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586
  1. using System.Text.Json;
  2. using Admin.NET.Plugin.ApprovalFlow.Service;
  3. using Furion.Schedule;
  4. using Microsoft.Extensions.DependencyInjection;
  5. using Microsoft.Extensions.Logging;
  6. namespace Admin.NET.Plugin.ApprovalFlow;
  7. /// <summary>
  8. /// 审批超时自动处理作业 — 每 5 分钟扫描一次超时的待办任务
  9. /// </summary>
  10. [JobDetail("job_flow_timeout", Description = "审批超时自动处理",
  11. GroupName = "default", Concurrent = false)]
  12. [Period(300000, TriggerId = "trigger_flow_timeout", Description = "每5分钟执行")]
  13. public class FlowTimeoutJob : IJob
  14. {
  15. private readonly IServiceScopeFactory _scopeFactory;
  16. private readonly ILogger _logger;
  17. public FlowTimeoutJob(IServiceScopeFactory scopeFactory, ILoggerFactory loggerFactory)
  18. {
  19. _scopeFactory = scopeFactory;
  20. _logger = loggerFactory.CreateLogger("FlowTimeoutJob");
  21. }
  22. public async Task ExecuteAsync(JobExecutingContext context, CancellationToken stoppingToken)
  23. {
  24. using var scope = _scopeFactory.CreateScope();
  25. var db = scope.ServiceProvider.GetRequiredService<ISqlSugarClient>().CopyNew();
  26. var engine = scope.ServiceProvider.GetRequiredService<FlowEngineService>();
  27. var pendingTasks = await db.Queryable<ApprovalFlowTask>()
  28. .InnerJoin<ApprovalFlowInstance>((t, i) => t.InstanceId == i.Id)
  29. .Where((t, i) => t.Status == FlowTaskStatusEnum.Pending
  30. && i.Status == FlowInstanceStatusEnum.Running)
  31. .Select((t, i) => new
  32. {
  33. TaskId = t.Id,
  34. TaskCreatedAt = t.CreateTime,
  35. NodeId = t.NodeId,
  36. FlowJsonSnapshot = i.FlowJsonSnapshot,
  37. })
  38. .ToListAsync();
  39. var now = DateTime.Now;
  40. var processed = 0;
  41. foreach (var item in pendingTasks)
  42. {
  43. if (stoppingToken.IsCancellationRequested) break;
  44. try
  45. {
  46. if (string.IsNullOrWhiteSpace(item.FlowJsonSnapshot)) continue;
  47. var flowData = JsonSerializer.Deserialize<ApprovalFlowItem>(item.FlowJsonSnapshot);
  48. var node = flowData?.Nodes?.FirstOrDefault(n => n.Id == item.NodeId);
  49. var props = node?.Properties;
  50. if (props?.TimeoutHours == null || props.TimeoutHours <= 0) continue;
  51. if (string.IsNullOrWhiteSpace(props.TimeoutAction)) continue;
  52. var deadline = item.TaskCreatedAt.AddHours(props.TimeoutHours.Value);
  53. if (now < deadline) continue;
  54. if (props.TimeoutAction == "Notify")
  55. {
  56. var alreadyNotified = await db.Queryable<ApprovalFlowLog>()
  57. .AnyAsync(log => log.TaskId == item.TaskId
  58. && log.Action == FlowLogActionEnum.AutoTimeout);
  59. if (alreadyNotified) continue;
  60. }
  61. await engine.HandleTimeoutTask(item.TaskId);
  62. processed++;
  63. }
  64. catch (Exception ex)
  65. {
  66. _logger.LogError(ex, "处理超时任务 {TaskId} 失败", item.TaskId);
  67. }
  68. }
  69. if (processed > 0)
  70. _logger.LogInformation("FlowTimeoutJob 本轮处理了 {Count} 个超时任务", processed);
  71. }
  72. }