{"record":{"id":"0bba68d0005cb6c8","repo":"babalae/better-genshin-impact","slug":"text","errorCode":null,"errorMessage":"未识别到全部恢复时间：{text}","messagePattern":"未识别到全部恢复时间：(.+?)","errorType":"exception","errorClass":"FormatException","httpStatus":null,"severity":"error","filePath":"BetterGenshinImpact/GameTask/AutoBoss/AutoBossTask.cs","lineNumber":411,"sourceCode":"    }\n\n    private static TimeSpan ExtractFullRecoveryTime(string text)\n    {\n        if (text.Contains(\"原粹树脂已完全恢复\", StringComparison.Ordinal))\n        {\n            return TimeSpan.Zero;\n        }\n\n        var fullRecoveryMatch = Regex.Match(text, @\"全部恢复(?<time>\\d{1,3}:\\d{2}:\\d{2})\");\n        if (fullRecoveryMatch.Success)\n        {\n            return ParseRecoveryTime(fullRecoveryMatch.Groups[\"time\"].Value);\n        }\n\n        var timeMatches = Regex.Matches(text, @\"\\d{1,3}:\\d{2}:\\d{2}\");\n        if (timeMatches.Count == 0)\n        {\n            throw new FormatException($\"未识别到全部恢复时间：{text}\");\n        }\n\n        return ParseRecoveryTime(timeMatches[timeMatches.Count - 1].Value);\n    }\n\n    private static TimeSpan ParseRecoveryTime(string timeText)\n    {\n        var parts = timeText.Split(':');\n        if (parts.Length != 3\n            || !int.TryParse(parts[0], NumberStyles.None, CultureInfo.InvariantCulture, out var hours)\n            || !int.TryParse(parts[1], NumberStyles.None, CultureInfo.InvariantCulture, out var minutes)\n            || !int.TryParse(parts[2], NumberStyles.None, CultureInfo.InvariantCulture, out var seconds)\n            || minutes < 0\n            || minutes > 59\n            || seconds < 0\n            || seconds > 59)\n        {\n            throw new FormatException($\"树脂恢复时间格式无效：{timeText}\");","sourceCodeStart":393,"sourceCodeEnd":429,"githubUrl":"https://github.com/babalae/better-genshin-impact/blob/a7cb36712dcb409be610257d877fcea3597e9d6b/BetterGenshinImpact/GameTask/AutoBoss/AutoBossTask.cs#L393-L429","documentation":"ExtractFullRecoveryTime 先判断 '已完全恢复'（返回零），再用 '全部恢复H:MM:SS' 正则，最后兜底用任意 H:MM:SS 正则。三者都失败时说明弹窗 OCR 文本里没有任何可识别的恢复时间，抛 FormatException。","triggerScenarios":"树脂详情弹窗未打开或内容被遮挡；OCR 文本里冒号是全角 '：' 未被 NormalizeRecoveryText 处理（注意代码已 Replace('：',':')，但若为其他分隔符则失败）；时间格式被 OCR 拆断（如 '12:30:00' 被识别为 '12:30 OO'）。","commonSituations":"点击图标后 Delay(500) 不足，弹窗动画未完成；detailRect（220×150 固定像素）漂移；游戏语言/字体变化导致 '全部恢复' 文案变化；PaddleOCR 在小字号下漏识冒号。","solutions":["加大点击后等待并轮询弹窗出现再截图。","记录 normalizedText 原文（已有 debug 日志）确认 OCR 内容。","扩展正则容忍：允许半角/全角冒号、允许 'HH:MM' 缺秒、允许中文 '小时/分/秒' 描述。","校准 detailRect 偏移（resinIconLeft-13, resinIconBottom+29）是否随分辨率正确。"],"exampleFix":"// before\nvar timeMatches = Regex.Matches(text, @\"\\d{1,3}:\\d{2}:\\d{2}\");\nif (timeMatches.Count == 0)\n{\n    throw new FormatException($\"未识别到全部恢复时间：{text}\");\n}\n\n// after\nvar timeMatches = Regex.Matches(text, @\"\\d{1,3}[:：]\\d{2}[:：]\\d{2}\");\nif (timeMatches.Count == 0)\n{\n    throw new FormatException($\"未识别到全部恢复时间：{text}\");\n}","handlingStrategy":"retry","validationCode":"// RecognizeFullRecoveryTime 调用前确认 detailRegion 非空且 OCR 有文本\nusing var detailRegion = clickedCapture.DeriveCrop(detailRect);\nvar result = OcrFactory.Paddle.OcrResult(detailRegion.SrcMat);\nif (result.Regions.Count == 0)\n{\n    throw new FormatException(\"树脂详情 OCR 区域无文本，弹窗可能未打开\");\n}","typeGuard":null,"tryCatchPattern":"try { return ExtractFullRecoveryTime(normalizedText); }\ncatch (FormatException) { _logger.LogDebug(\"{Name}：恢复时间文本=[{Text}]\", Name, normalizedText); throw; }","preventionTips":["点击图标后等待弹窗稳定再截图，必要时重试。","正则容忍全角冒号与缺秒格式。","输出 normalizedText 到 debug 日志便于排查。"],"tags":["ocr","resin","regex","genshin","autoboss"],"backgroundTag":null,"analyzedSha":"a7cb36712dcb409be610257d877fcea3597e9d6b","analyzedAt":"2026-08-13T16:44:57.548Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}