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);
}
}