using System.Text.Json; using Admin.NET.Plugin.ApprovalFlow.Service; using Furion.Schedule; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; namespace Admin.NET.Plugin.ApprovalFlow; /// /// 审批超时自动处理作业 — 每 5 分钟扫描一次超时的待办任务 /// [JobDetail("job_flow_timeout", Description = "审批超时自动处理", GroupName = "default", Concurrent = false)] [Period(300000, TriggerId = "trigger_flow_timeout", Description = "每5分钟执行")] public class FlowTimeoutJob : IJob { private readonly IServiceScopeFactory _scopeFactory; private readonly ILogger _logger; public FlowTimeoutJob(IServiceScopeFactory scopeFactory, ILoggerFactory loggerFactory) { _scopeFactory = scopeFactory; _logger = loggerFactory.CreateLogger("FlowTimeoutJob"); } public async Task ExecuteAsync(JobExecutingContext context, CancellationToken stoppingToken) { using var scope = _scopeFactory.CreateScope(); var db = scope.ServiceProvider.GetRequiredService().CopyNew(); var engine = scope.ServiceProvider.GetRequiredService(); var pendingTasks = await db.Queryable() .InnerJoin((t, i) => t.InstanceId == i.Id) .Where((t, i) => t.Status == FlowTaskStatusEnum.Pending && i.Status == FlowInstanceStatusEnum.Running) .Select((t, i) => new { TaskId = t.Id, TaskCreatedAt = t.CreateTime, NodeId = t.NodeId, FlowJsonSnapshot = i.FlowJsonSnapshot, }) .ToListAsync(); var now = DateTime.Now; var processed = 0; foreach (var item in pendingTasks) { if (stoppingToken.IsCancellationRequested) break; try { if (string.IsNullOrWhiteSpace(item.FlowJsonSnapshot)) continue; var flowData = JsonSerializer.Deserialize(item.FlowJsonSnapshot); var node = flowData?.Nodes?.FirstOrDefault(n => n.Id == item.NodeId); var props = node?.Properties; if (props?.TimeoutHours == null || props.TimeoutHours <= 0) continue; if (string.IsNullOrWhiteSpace(props.TimeoutAction)) continue; var deadline = item.TaskCreatedAt.AddHours(props.TimeoutHours.Value); if (now < deadline) continue; if (props.TimeoutAction == "Notify") { var alreadyNotified = await db.Queryable() .AnyAsync(log => log.TaskId == item.TaskId && log.Action == FlowLogActionEnum.AutoTimeout); if (alreadyNotified) continue; } await engine.HandleTimeoutTask(item.TaskId); processed++; } catch (Exception ex) { _logger.LogError(ex, "处理超时任务 {TaskId} 失败", item.TaskId); } } if (processed > 0) _logger.LogInformation("FlowTimeoutJob 本轮处理了 {Count} 个超时任务", processed); } }