using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using BetterGenshinImpact.Model.Gear.Tasks;
using Microsoft.Extensions.Logging;
namespace BetterGenshinImpact.Service.GearTask.Execution;
///
/// GearTask 任务树执行器。
/// 只负责遍历节点、执行任务以及向外发布业务事件。
///
public interface IGearTaskExecutionRunner
{
Task RunAsync(BaseGearTask rootTask, GearTaskExecutionRunContext context, CancellationToken ct);
}
///
/// 默认 GearTask 执行器实现。
///
public sealed class GearTaskExecutionRunner : IGearTaskExecutionRunner
{
private readonly ILogger _logger;
private readonly IGearTaskEventBus _eventBus;
public GearTaskExecutionRunner(ILogger logger, IGearTaskEventBus eventBus)
{
_logger = logger;
_eventBus = eventBus;
}
public async Task RunAsync(BaseGearTask rootTask, GearTaskExecutionRunContext context, CancellationToken ct)
{
var rootContext = CreateNodeContext(rootTask, context, "0", null, 0, 0);
var startedEvent = rootContext.CreateEvent();
startedEvent.SourceRecordId = context.ResumePlan?.SourceRecordId;
startedEvent.TotalRunnableNodeCount = CountTotalNodes(rootTask);
await _eventBus.PublishAsync(startedEvent, ct);
try
{
await ExecuteNodeAsync(rootTask, context, "0", null, 0, 0, ct);
var completedEvent = rootContext.CreateEvent();
await _eventBus.PublishAsync(completedEvent, ct);
}
catch (OperationCanceledException)
{
var cancelledEvent = rootContext.CreateEvent();
cancelledEvent.Reason = "用户取消执行";
await _eventBus.PublishAsync(cancelledEvent, CancellationToken.None);
throw;
}
catch (Exception ex)
{
var failedEvent = rootContext.CreateEvent();
failedEvent.ErrorMessage = ex.Message;
await _eventBus.PublishAsync(failedEvent, CancellationToken.None);
throw;
}
}
private async Task ExecuteNodeAsync(
BaseGearTask task,
GearTaskExecutionRunContext runContext,
string nodeId,
string? parentNodeId,
int depth,
int order,
CancellationToken ct)
{
var nodeContext = CreateNodeContext(task, runContext, nodeId, parentNodeId, depth, order);
task.SetExecutionContext(nodeContext);
if (!task.Enabled)
{
var skippedEvent = nodeContext.CreateEvent();
skippedEvent.Reason = "任务未启用";
await nodeContext.PublishAsync(skippedEvent, ct);
return;
}
if (ShouldSkipByResume(runContext.ResumePlan, nodeId))
{
var skippedEvent = nodeContext.CreateEvent();
skippedEvent.Reason = "恢复执行时跳过已完成节点";
await nodeContext.PublishAsync(skippedEvent, ct);
return;
}
var startedEvent = nodeContext.CreateEvent();
await nodeContext.PublishAsync(startedEvent, ct);
try
{
if (task is IGearTaskResumable resumable)
{
await resumable.ApplyResumeTokenAsync(nodeContext.ResumeTokenJson, ct);
}
await task.Execute(ct);
var childOrder = 0;
foreach (var child in task.Children)
{
await ExecuteNodeAsync(child, runContext, $"{nodeId}/{childOrder}", nodeId, depth + 1, childOrder, ct);
childOrder++;
}
var completedEvent = nodeContext.CreateEvent();
completedEvent.Message = "节点执行完成";
await nodeContext.PublishAsync(completedEvent, ct);
}
catch (OperationCanceledException)
{
throw;
}
catch (Exception ex)
{
_logger.LogError(ex, "执行节点失败: {TaskName} ({NodeId})", task.Name, nodeId);
var failedEvent = nodeContext.CreateEvent();
failedEvent.ErrorMessage = ex.Message;
await nodeContext.PublishAsync(failedEvent, CancellationToken.None);
throw;
}
}
private static string? GetResumeToken(GearTaskResumePlan? plan, string nodeId)
{
if (plan == null)
{
return null;
}
return plan.NodeResumeTokens.TryGetValue(nodeId, out var token) ? token : null;
}
private static bool ShouldSkipByResume(GearTaskResumePlan? plan, string nodeId)
{
if (plan == null || string.IsNullOrWhiteSpace(plan.ResumeNodeId))
{
return false;
}
if (nodeId == plan.ResumeNodeId)
{
return false;
}
if (plan.ResumeNodeId.StartsWith(nodeId + "/", StringComparison.Ordinal))
{
// 恢复节点的祖先链仍然需要执行,否则无法走到真正的恢复节点。
return false;
}
return IsNodeBefore(nodeId, plan.ResumeNodeId);
}
///
/// 比较两个节点路径的顺序,供恢复逻辑判断当前节点是否位于恢复点之前。
///
private static bool IsNodeBefore(string leftNodeId, string rightNodeId)
{
var left = leftNodeId.Split('/').Select(int.Parse).ToArray();
var right = rightNodeId.Split('/').Select(int.Parse).ToArray();
var minLength = Math.Min(left.Length, right.Length);
for (var i = 0; i < minLength; i++)
{
if (left[i] != right[i])
{
return left[i] < right[i];
}
}
return left.Length < right.Length;
}
///
/// 统计任务树节点总数,用于生成执行进度的总量基线。
///
private static int CountTotalNodes(BaseGearTask task)
{
return 1 + task.Children.Sum(CountTotalNodes);
}
private GearTaskNodeExecutionContext CreateNodeContext(
BaseGearTask task,
GearTaskExecutionRunContext runContext,
string nodeId,
string? parentNodeId,
int depth,
int order)
{
return GearTaskNodeExecutionContext.Create(
_eventBus,
task,
runContext,
nodeId,
parentNodeId,
depth,
order,
GetResumeToken(runContext.ResumePlan, nodeId));
}
}