v0.18.19:失敗訊息改成人話(leo:「這個錯誤訊息我真的看不懂」)
v0.18.18 雖然把失敗檔案列出來了,但寫的是
「上次失敗(第 5 次),5h38m38s 後重試(上面是自動重試的排程,真正的原因是前一次失敗)」
——**那是我寫給自己看的**:只是把 collector 的原文轉貼、再補一句解釋為什麼看起來怪。
使用者要的是三件事:**現在怎樣、為什麼、我要不要做什麼**。
## 改法
- 解析退避訊息取出「試幾次/多久後再試/原因」,重新組成人話
- **有原因**時以原因為主、排程放後面補充(他要判斷的是原因)
- **沒有原因**時(舊版失敗的檔案沒存 LastError)誠實說:
「已經自動試過 5 次都沒成功,約 5 小時後會再試一次。這個檔是在舊版失敗的,
當時沒有記下原因;如果一直沒好,把紀錄檔傳給我們。」
- 時間不再精確到秒(`5h38m38s` → 「約 5 小時」)——使用者要的是量級不是秒數
## 實測輸出(三種情境都印出來看過)
舊:上次失敗(第 5 次),5h38m38s 後重試(上面是自動重試的排程…)
新:已經自動試過 5 次都沒成功,約 5 小時後會再試一次。這個檔是在舊版失敗的…
新:今天的免費 AI 額度用完了 ⇒ 明天會自動恢復,這些檔案會自己補上,你不用做什麼。
新:這份 PDF 看起來是掃描的圖片 ⇒ 需要先做文字辨識(OCR),或換一份有文字的版本。
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"_algo": 2,
|
||||
"v0.18.17": "cb6e53a052b87482",
|
||||
"v0.18.18": "a495451d9d9da78a"
|
||||
"v0.18.18": "a495451d9d9da78a",
|
||||
"v0.18.19": "b754d7ff97396794"
|
||||
}
|
||||
|
||||
+51
-2
@@ -23,8 +23,10 @@ import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
runtime2 "runtime"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -278,6 +280,39 @@ type UIFailItem struct {
|
||||
// maxFailShown=最多列幾份(多了會洗版;總數仍照實講)。
|
||||
const maxFailShown = 8
|
||||
|
||||
// parseRetryMessage 從退避訊息裡取出「試了幾次、多久後再試、原因是什麼」。
|
||||
// 格式來源=collector 的 retrySkipReason()。解析失敗就回 ok=false,由呼叫端原樣顯示。
|
||||
func parseRetryMessage(raw string) (n int, wait, reason string, ok bool) {
|
||||
m := retryMsgRe.FindStringSubmatch(raw)
|
||||
if m == nil {
|
||||
return 0, "", "", false
|
||||
}
|
||||
n, _ = strconv.Atoi(m[1])
|
||||
wait = humanizeDuration(m[2])
|
||||
if i := strings.Index(raw, "|原因:"); i >= 0 {
|
||||
reason = raw[i+len("|原因:"):]
|
||||
}
|
||||
return n, wait, reason, true
|
||||
}
|
||||
|
||||
var retryMsgRe = regexp.MustCompile(`上次失敗(第 (\d+) 次),([0-9hms.]+) 後重試`)
|
||||
|
||||
// humanizeDuration 把 `5h38m38s` 講成「5 小時」——使用者不需要精確到秒。
|
||||
func humanizeDuration(d string) string {
|
||||
dur, err := time.ParseDuration(d)
|
||||
if err != nil {
|
||||
return d
|
||||
}
|
||||
switch {
|
||||
case dur >= time.Hour:
|
||||
return fmt.Sprintf("%d 小時", int(dur.Hours()))
|
||||
case dur >= time.Minute:
|
||||
return fmt.Sprintf("%d 分鐘", int(dur.Minutes()))
|
||||
default:
|
||||
return "不到 1 分鐘"
|
||||
}
|
||||
}
|
||||
|
||||
// humanizeFailure 把 collector 的技術錯誤翻成使用者看得懂、而且**知道要不要行動**的一句話。
|
||||
//
|
||||
// 🔴 leo 2026-08-06 實測,兩個真實案例(從他 Windows 的 collector.log 挖出來的):
|
||||
@@ -298,8 +333,22 @@ func humanizeFailure(raw string) string {
|
||||
return "這份 PDF 看起來是掃描的圖片,沒有文字可以讀 ⇒ 需要先做文字辨識(OCR),或換一份有文字的版本。"
|
||||
case strings.Contains(raw, "尚未支援的檔案格式"):
|
||||
return "這種檔案格式還讀不了 ⇒ 先另存成 PDF 或 Word(.docx)再放回同一個資料夾。"
|
||||
case strings.Contains(raw, "後重試"):
|
||||
return raw + "(上面是自動重試的排程,真正的原因是前一次失敗)"
|
||||
}
|
||||
|
||||
// 退避訊息:collector 給的是「上次失敗(第 5 次),5h38m38s 後重試|原因:…」。
|
||||
//
|
||||
// 🔴 leo 2026-08-06:「**這個錯誤訊息我真的看不懂,明白說比較好**」
|
||||
// ——我原本只是把原文轉貼、再加一句「上面是自動重試的排程」,那是寫給我自己看的。
|
||||
// 使用者要的是三件事:**現在怎樣、為什麼、我要不要做什麼**。
|
||||
if n, wait, reason, ok := parseRetryMessage(raw); ok {
|
||||
if reason != "" {
|
||||
// 有原因就以原因為主,排程放後面當補充(他真正需要判斷的是原因)
|
||||
return humanizeFailure(reason) +
|
||||
fmt.Sprintf("(已經自動試過 %d 次,約 %s後會再試一次)", n, wait)
|
||||
}
|
||||
return fmt.Sprintf("已經自動試過 %d 次都沒成功,約 %s後會再試一次。"+
|
||||
"這個檔是在舊版失敗的,當時沒有記下原因;如果一直沒好,"+
|
||||
"到「版本與更新」旁邊把紀錄檔傳給我們。", n, wait)
|
||||
}
|
||||
return raw
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user