mirror of
https://github.com/babalae/better-genshin-impact.git
synced 2026-05-25 10:05:49 +08:00
- 新增 IGearTaskEventBus 接口及默认实现,用于解耦执行器与记录器、UI 投影等消费者 - 新增 IGearTaskResumable 接口,支持任务节点内部恢复(如 Pathing 任务可恢复至特定路径点) - 重构任务执行流程,使用 GearTaskExecutionRunner 替代旧的 GearTaskExecutionManager - 实现基于磁盘 JSON 的历史记录存储(IGearTaskHistoryStore),支持执行记录的保存、加载与清理 - 为 PathingGearTask 添加恢复能力,通过 PathingGearTaskResumeState 记录断点状态 - 在 PathExecutor 中集成运行时事件通知,支持路径点进入、完成、传送等事件的发布 - 统一执行事件模型(GearTaskExecutionEvent),包含任务定义、节点路径、时间戳等元数据 - 服务注册更新,使用新的执行器、事件总线、历史记录器等组件
133 lines
4.3 KiB
C#
133 lines
4.3 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Threading.Tasks;
|
|
using Newtonsoft.Json;
|
|
|
|
namespace BetterGenshinImpact.Service.GearTask.Execution;
|
|
|
|
/// <summary>
|
|
/// GearTask 历史记录存储接口。
|
|
/// </summary>
|
|
public interface IGearTaskHistoryStore
|
|
{
|
|
Task SaveAsync(GearTaskExecutionRecord record);
|
|
|
|
Task<GearTaskExecutionRecord?> LoadAsync(string taskDefinitionFileKey, string recordId);
|
|
|
|
Task<IReadOnlyList<GearTaskExecutionRecord>> LoadLatestAsync(string taskDefinitionFileKey, int count);
|
|
|
|
Task TrimAsync(string taskDefinitionFileKey, int keepCount);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 基于磁盘 JSON 文件的历史记录存储。
|
|
/// 一个执行记录对应一个文件,按任务定义分目录存放。
|
|
/// </summary>
|
|
public sealed class GearTaskHistoryStore : IGearTaskHistoryStore
|
|
{
|
|
private readonly JsonSerializerSettings _jsonSettings = new()
|
|
{
|
|
Formatting = Formatting.Indented,
|
|
NullValueHandling = NullValueHandling.Ignore,
|
|
DateFormatString = "yyyy-MM-dd HH:mm:ss.fff",
|
|
};
|
|
|
|
private readonly string _historyRootPath = Path.Combine(AppContext.BaseDirectory, "User", "task_v2", "history");
|
|
|
|
public GearTaskHistoryStore()
|
|
{
|
|
Directory.CreateDirectory(_historyRootPath);
|
|
}
|
|
|
|
public async Task SaveAsync(GearTaskExecutionRecord record)
|
|
{
|
|
var filePath = GetRecordFilePath(record.TaskDefinitionFileKey, record.RecordId, record.StartTime);
|
|
Directory.CreateDirectory(Path.GetDirectoryName(filePath)!);
|
|
|
|
// 先写临时文件再覆盖正式文件,避免进程中断时把历史记录写坏。
|
|
var json = JsonConvert.SerializeObject(record, _jsonSettings);
|
|
var tempPath = filePath + ".tmp";
|
|
await File.WriteAllTextAsync(tempPath, json);
|
|
File.Move(tempPath, filePath, true);
|
|
}
|
|
|
|
public async Task<GearTaskExecutionRecord?> LoadAsync(string taskDefinitionFileKey, string recordId)
|
|
{
|
|
var directory = GetDefinitionDirectory(taskDefinitionFileKey);
|
|
if (!Directory.Exists(directory))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
var filePath = Directory.GetFiles(directory, $"*_{recordId}.json").OrderByDescending(f => f).FirstOrDefault();
|
|
if (filePath == null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
var json = await File.ReadAllTextAsync(filePath);
|
|
return JsonConvert.DeserializeObject<GearTaskExecutionRecord>(json, _jsonSettings);
|
|
}
|
|
|
|
public async Task<IReadOnlyList<GearTaskExecutionRecord>> LoadLatestAsync(string taskDefinitionFileKey, int count)
|
|
{
|
|
var directory = GetDefinitionDirectory(taskDefinitionFileKey);
|
|
if (!Directory.Exists(directory))
|
|
{
|
|
return [];
|
|
}
|
|
|
|
var files = Directory.GetFiles(directory, "*.json")
|
|
.OrderByDescending(Path.GetFileNameWithoutExtension)
|
|
.Take(count)
|
|
.ToList();
|
|
|
|
var list = new List<GearTaskExecutionRecord>(files.Count);
|
|
foreach (var file in files)
|
|
{
|
|
var json = await File.ReadAllTextAsync(file);
|
|
var record = JsonConvert.DeserializeObject<GearTaskExecutionRecord>(json, _jsonSettings);
|
|
if (record != null)
|
|
{
|
|
list.Add(record);
|
|
}
|
|
}
|
|
|
|
return list;
|
|
}
|
|
|
|
public Task TrimAsync(string taskDefinitionFileKey, int keepCount)
|
|
{
|
|
var directory = GetDefinitionDirectory(taskDefinitionFileKey);
|
|
if (!Directory.Exists(directory))
|
|
{
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
// 文件名以开始时间开头,按文件名倒序即可近似得到最近执行记录。
|
|
var files = Directory.GetFiles(directory, "*.json")
|
|
.OrderByDescending(Path.GetFileNameWithoutExtension)
|
|
.Skip(keepCount)
|
|
.ToList();
|
|
|
|
foreach (var file in files)
|
|
{
|
|
File.Delete(file);
|
|
}
|
|
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
public string GetDefinitionDirectory(string taskDefinitionFileKey)
|
|
{
|
|
return Path.Combine(_historyRootPath, taskDefinitionFileKey);
|
|
}
|
|
|
|
private string GetRecordFilePath(string taskDefinitionFileKey, string recordId, DateTime startTime)
|
|
{
|
|
return Path.Combine(GetDefinitionDirectory(taskDefinitionFileKey), $"{startTime:yyyyMMddHHmmss}_{recordId}.json");
|
|
}
|
|
}
|