using System; using System.Collections.Generic; using System.IO; using BetterGenshinImpact.Core.Config; using BetterGenshinImpact.Core.Script.Group; using BetterGenshinImpact.Helpers; using Newtonsoft.Json; namespace BetterGenshinImpact.GameTask.LogParse; public class ExecutionRecordStorage { private static readonly string StorageDirectory = Path.Combine(Global.Absolute(@"log"), "ExecutionRecords"); /// /// 保存执行记录到对应日期的文件中 /// public static void SaveExecutionRecord(ExecutionRecord record) { // 创建存储目录 Directory.CreateDirectory(StorageDirectory); // 获取基于StartTime的日期文件名 string dateKey = record.StartTime.ToString("yyyyMMdd"); string fileName = $"{dateKey}.json"; string filePath = Path.Combine(StorageDirectory, fileName); // 读取或创建当天的记录 DailyExecutionRecord dailyRecord; if (File.Exists(filePath)) { string json = File.ReadAllText(filePath); dailyRecord = JsonConvert.DeserializeObject(json); } else { dailyRecord = new DailyExecutionRecord { Name = dateKey }; } // 更新或添加记录 var existingIndex = dailyRecord.ExecutionRecords.FindIndex(r => r.Id == record.Id); if (existingIndex >= 0) { dailyRecord.ExecutionRecords[existingIndex] = record; } else { dailyRecord.ExecutionRecords.Add(record); } // 保存更新后的文件 string updatedJson = JsonConvert.SerializeObject(dailyRecord, Formatting.Indented); File.WriteAllText(filePath, updatedJson); } public static List GetRecentExecutionRecordsByConfig(TaskCompletionSkipRuleConfig config) { // 确定边界时间是否有效(0-23之间) bool boundaryTimeEnable = config.BoundaryTime >= 0 && config.BoundaryTime <= 23; // 默认获取最近1天的执行记录 int dayCount = 1; // 如果边界时间有效,需要获取2天的记录(可能跨越边界) if (boundaryTimeEnable) { dayCount = 2; } // 如果配置了有效的间隔秒数,根据秒数计算需要获取的天数(向上取整) if (config.LastRunGapSeconds >= 0) { dayCount = ConvertSecondsToDaysUp(config.LastRunGapSeconds); } return GetRecentExecutionRecords(dayCount); } /// /// 读取最近N天的执行记录 /// public static List GetRecentExecutionRecords(int days) { if (days <= 0) throw new ArgumentException("Days must be a positive integer", nameof(days)); var results = new List(); var storageDir = new DirectoryInfo(StorageDirectory); // 确保目录存在 if (!storageDir.Exists) return results; // 计算日期范围 var endDate = DateTime.Today; var startDate = endDate.AddDays(-days + 1); // 遍历日期范围 for (var date = startDate; date <= endDate; date = date.AddDays(1)) { string fileName = $"{date:yyyyMMdd}.json"; string filePath = Path.Combine(StorageDirectory, fileName); if (File.Exists(filePath)) { string json = File.ReadAllText(filePath); var record = JsonConvert.DeserializeObject(json); results.Add(record); } } //实际使用中,使用倒序,反转记录列表,变为倒序 results.Reverse(); foreach (var dailyRecord in results) { var records = dailyRecord.ExecutionRecords; // 反转执行记录,变为倒序 records.Reverse(); } return results; } /// /// 将秒数转换为天数(向上取整) /// /// 总秒数 /// 向上取整后的天数 private static int ConvertSecondsToDaysUp(int seconds) { if (seconds <= 0) return 0; const int secondsPerDay = 86400; // 24 * 60 * 60 double days = (double)seconds / secondsPerDay; return (int)Math.Ceiling(days); } /// /// 根据自定义的一天开始时间判断日期是否属于"今天" /// /// 分界时间(小时),0-23之间,基于参数决定的时间参考系 /// 要判断的日期(本地时间) /// true表示使用服务器时间计算,false表示使用本地时间计算 /// 如果目标日期在根据分界时间定义的"今天"范围内则返回true,否则返回false private static bool IsTodayByBoundary(int boundaryHour, DateTimeOffset targetDate, bool isBoundaryTimeBasedOnServerTime) { // 验证分界时间是否有效 if (boundaryHour < 0 || boundaryHour > 23) throw new ArgumentOutOfRangeException(nameof(boundaryHour), "分界时间必须在0-23之间"); DateTimeOffset now = isBoundaryTimeBasedOnServerTime ? ServerTimeHelper.GetServerTimeNow() : DateTimeOffset.Now; // 计算今天的开始时间(根据分界时间) DateTime todayStart; if (now.Hour >= boundaryHour) { // 今天已经过了分界时间,今天的开始是今天的分界时间 todayStart = new DateTime(now.Year, now.Month, now.Day, boundaryHour, 0, 0); } else { // 今天还没过分界时间,今天的开始是昨天的分界时间 todayStart = new DateTime(now.Year, now.Month, now.Day, boundaryHour, 0, 0).AddDays(-1); } // 计算今天的结束时间(明天的开始时间) DateTime todayEnd = todayStart.AddDays(1); // 判断目标日期是否在今天的范围内 return targetDate >= todayStart && targetDate < todayEnd; } public static bool IsSkipTask(ScriptGroupProject project, out string message,List? dailyRecords=null) { // 初始化消息字符串 message = ""; // 获取任务完成跳过规则配置 var config = project.GroupInfo?.Config?.PathingConfig.TaskCompletionSkipRuleConfig; // 检查配置是否有效:配置不存在、未启用、边界时间无效且间隔时间无效 if (config == null || !config.Enable || (config.BoundaryTime < 0 || config.BoundaryTime > 23) && config.LastRunGapSeconds < 0) { return false; // 配置无效,不执行跳过检查 } // 确定边界时间是否有效(0-23之间) bool boundaryTimeEnable = config.BoundaryTime >= 0 && config.BoundaryTime <= 23; // 获取项目相关信息 var groupName = project.GroupInfo?.Name ?? ""; var folderName = project.FolderName; var projectName = project.Name; var projectType = project.Type; // 获取最近指定天数的执行记录 dailyRecords ??= GetRecentExecutionRecordsByConfig(config); // 遍历每日记录 foreach (var dailyRecord in dailyRecords) { var records = dailyRecord.ExecutionRecords; // 反转执行记录,变为倒序 // records.Reverse(); // 遍历每条执行记录 foreach (var record in records) { // 跳过未成功的执行记录 if (!record.IsSuccessful) continue; // 跳过类型或项目名称不匹配的记录 if (record.Type != projectType || record.ProjectName != projectName) continue; var calcTime = record.EndTime; if (config.ReferencePoint == "StartTime") { calcTime = record.StartTime; } // 如果配置了间隔时间,检查记录是否在时间间隔内 if (config.LastRunGapSeconds >= 0) { double secondsSinceLastRun = (DateTime.Now - calcTime).TotalSeconds; // 跳过超过配置间隔时间的记录 if (secondsSinceLastRun > config.LastRunGapSeconds) continue; } // 检查记录是否在"今天"(根据边界时间定义) if (boundaryTimeEnable) { // 如果记录不在"今天",则跳过 if (!IsTodayByBoundary(config.BoundaryTime, record.ServerStartTime ?? new DateTimeOffset(record.StartTime).ToOffset(ServerTimeHelper.GetServerTimeOffset()), config.IsBoundaryTimeBasedOnServerTime)) continue; } bool isMatchFound = false; string matchReason = ""; // 检查匹配策略 if (config.SkipPolicy == "GroupPhysicalPathSkipPolicy" && groupName == record.GroupName && folderName == record.FolderName) { // 组和物理路径匹配策略 matchReason = "组和物理路径匹配一致"; isMatchFound = true; } else if (config.SkipPolicy == "PhysicalPathSkipPolicy" && folderName == record.FolderName) { // 物理路径匹配策略 matchReason = "物理路径相同"; isMatchFound = true; } else if (config.SkipPolicy == "SameNameSkipPolicy") { // 名称匹配策略(只需要项目名称相同) matchReason = "名称相同"; isMatchFound = true; } else { // 未知的跳过策略 Console.WriteLine("ExecutionRecordStorage: 未预期的跳过策略!"); continue; // 继续检查下一条记录 } if (isMatchFound) { // 构建匹配消息 message = $"检查出满足跳过条件: {matchReason}"; // 添加时间相关信息 if (config.LastRunGapSeconds >= 0) { // 计算下次可执行时间 DateTime nextExecutionTime = calcTime.AddSeconds(config.LastRunGapSeconds); message += $", 需在 {nextExecutionTime:yyyy-M-d H:mm:ss} 之后才能开始执行"; } else if (boundaryTimeEnable) { message += $", 需在下一日 {config.BoundaryTime} 点后才能开始执行"; } // 添加匹配记录的ID message += $", 匹配记录 GUID={record.Id}"; return true; // 找到匹配记录,返回true } } } // 未找到匹配记录 return false; } }