From 5f555b447d207776eafb4cff7318e8e1639d711a Mon Sep 17 00:00:00 2001 From: tamakyi Date: Wed, 9 Sep 2026 19:08:59 +0800 Subject: [PATCH] summary --- engine.go | 44 ++++++++++++++++++++++++++++++++++++++++++++ static/app.js | 1 + 2 files changed, 45 insertions(+) diff --git a/engine.go b/engine.go index f3de816..cb53a12 100644 --- a/engine.go +++ b/engine.go @@ -291,6 +291,7 @@ func (e *Engine) LoadAll() { continue } e.states[name] = &repoState{cfg: cfg, status: "idle"} + e.restoreLastSync(e.states[name]) } } @@ -353,6 +354,44 @@ func rotateStatIfNeeded(path string, maxLines int) bool { return true } +// restoreLastSync 从统计 CSV 的最后一条记录恢复 lastSync,使重启后界面不再显示「从未同步」。 +func (e *Engine) restoreLastSync(s *repoState) { + path := filepath.Join(e.statsDir, s.cfg.Name+".csv") + f, err := os.Open(path) + if err != nil { + return + } + defer f.Close() + recs, err := csv.NewReader(f).ReadAll() + if err != nil || len(recs) < 2 { + return + } + var ts, raw string + for _, rec := range recs[1:] { + if len(rec) < 3 { + continue + } + ts, raw = rec[0], rec[2] + } + if ts == "" { + return + } + result := raw + switch raw { + case "success": + result = "ok" + case "failed": + result = "error" + } + detail := map[string]string{"ok": "同步完成", "error": "同步失败", "conflict": "存在冲突"}[result] + if detail == "" { + detail = "上次同步" + } + s.mu.Lock() + s.lastSync = &SyncResult{Time: ts, Result: result, Detail: detail} + s.mu.Unlock() +} + func firstRemoteName(cfg *RepoConfig) string { if len(cfg.Remotes) > 0 { return cfg.Remotes[0].Name @@ -576,6 +615,7 @@ func (e *Engine) setFail(s *repoState, msg string) { s.lastError = msg s.lastSync = &SyncResult{Time: now, Result: "error", Detail: msg} s.mu.Unlock() + e.stat(s, "sync", "error", 0) e.logf(s, "error", "%s", msg) e.pushStatus(s) } @@ -587,6 +627,7 @@ func (e *Engine) setOK(s *repoState, detail string) { s.lastError = "" s.lastSync = &SyncResult{Time: now, Result: "ok", Detail: detail} s.mu.Unlock() + e.stat(s, "sync", "ok", 0) e.pushStatus(s) } @@ -915,6 +956,7 @@ func (e *Engine) pullRemote(s *repoState, cfg *RepoConfig, remote string, n int) s.lastError = "拉取产生冲突,等待在冲突处理台解决" s.lastSync = &SyncResult{Time: now, Result: "conflict", Detail: s.lastError} s.mu.Unlock() + e.stat(s, "sync", "conflict", 0) e.stat(s, "pull", "conflict", n) e.logf(s, "error", "拉取产生冲突:%s(请在详情 → 冲突中处理)", strings.Join(files, ", ")) e.pushStatus(s) @@ -955,6 +997,7 @@ func (e *Engine) ForcePush(name string) error { s.lastError = "" s.lastSync = &SyncResult{Time: time.Now().Format("2006-01-02 15:04:05"), Result: "ok", Detail: "强制推送完成"} s.mu.Unlock() + e.stat(s, "sync", "ok", 0) e.pushStatus(s) return nil } @@ -975,6 +1018,7 @@ func (e *Engine) AbortPush(name string) error { s.status = "ok" s.lastError = "用户放弃强制推送" s.lastSync = &SyncResult{Time: time.Now().Format("2006-01-02 15:04:05"), Result: "ok", Detail: "放弃强制推送,状态已清除"} + e.stat(s, "sync", "ok", 0) e.logf(s, "warn", "放弃强制推送") e.pushStatus(s) return nil diff --git a/static/app.js b/static/app.js index 63a6460..d96f864 100644 --- a/static/app.js +++ b/static/app.js @@ -961,6 +961,7 @@ function dateStr(d) { function loadStats(name) { api(`/api/repos/${name}/stats`).then(rows => { + rows = rows.filter(r => r.action !== 'sync'); const days = []; for (let i = 29; i >= 0; i--) { const d = new Date();