diff --git a/.workbuddy/memory/2026-10-25.md b/.workbuddy/memory/2026-10-25.md new file mode 100644 index 0000000..18b73d4 --- /dev/null +++ b/.workbuddy/memory/2026-10-25.md @@ -0,0 +1,29 @@ +# 2026-10-25 + +## /stats 文案清理:把「写给运营者看」的话删掉 + +用户指出「数据速览」下面那句「下面几句是从上面的数字直接读出来的,没有推测」怪 —— +那是对他自己说的(作者视角的免责),不是对访客说的。顺着扫了页面全部 40 余条文案。 + +改动: + +- **删 `stats.insight.note`**(四语言 + 模板那行 `

`)。 + 卡片留「数据速览」+ 列表即可;`stats.insight.empty` 保留。 + 删掉说明行后标题与列表之间只剩 4px,给 `.tb-insights` 补 `margin: 8px 0 0`。 +- **`stats.foot.cacheNote` 去掉实现细节**:「当天重算一次的快照(当天第一次访问时算, + 服务重启也会重算)」→「累计数字与图表每天更新一次」。访客不需要知道缓存是懒重算、 + 也不需要知道重启会触发重算;准确的新鲜度由「快照时间」那行给出。 +- 顺手修了 `templates/stats.html` 里一条过期注释(还写着「四条时间」「基准日」, + 上一轮已改成三行「快照时间」)。 + +保留未动(判断过,不属于这个问题): + +- `stats.foot.privacy` 与顶部 `stats.lead` 的后半句近乎同义(隐私声明重复一次)—— + 页脚隐私声明是惯例,重复不算噪音,故保留。 +- `stats.today.tag` 的「(实时)」在 60s TTL 下仍成立,不改。 + +## 约定(已写进 topics/stats.md) + +页面文案一律以**访客**为第二人称对象:不写实现细节(缓存、重启、索引), +不写作者视角的免责(「没有推测」「这里只有」「本文」这类把读者当审稿人的话)。 +数据来源/隐私/新鲜度这些确实要对访客交代的事,放 `stats.lead` 与页脚注。 diff --git a/.workbuddy/memory/topics/stats.md b/.workbuddy/memory/topics/stats.md index 65db92b..3976b6c 100644 --- a/.workbuddy/memory/topics/stats.md +++ b/.workbuddy/memory/topics/stats.md @@ -118,3 +118,13 @@ totals + 字数分布 + 回答耗时合并成一条 SQL(都是同一张表的 公开页只给聚合数字。不显示 IP、不显示单条内容、不做箱主排行(会招骚扰)。 「收到过提问的箱子」这类计数可以,具体是谁不行。 + +## 文案(读者是访客,不是站长) + +- 不写实现细节:缓存策略、懒重算、"服务重启也会重算"、索引、TTL —— 访客要的只是 + 「多久更新一次」。准确的新鲜度交给页脚「快照时间」那行。 +- 不写作者视角的免责:「下面几句…没有推测」「本文」「这里只有」这类,读起来像在跟审稿人 + 解释,而不是在给访客看。已删 `stats.insight.note`。 +- 确实要对访客交代的只有三件:数据来源/隐私(`stats.lead` + `stats.foot.privacy`)、 + 新鲜度(`stats.foot.cacheNote`)、口径例外(`stats.trend.monthlyNote` 的"当月还没过完")。 +- 删说明行会改变间距:`.tb-insights` 自己补 `margin-top`,别指望 `tb-card-note` 的 16px。 diff --git a/internal/i18n/dict_en.go b/internal/i18n/dict_en.go index f936946..63ac79a 100644 --- a/internal/i18n/dict_en.go +++ b/internal/i18n/dict_en.go @@ -970,7 +970,6 @@ var enDict = map[string]string{ "stats.kpi.messages": "Direct messages", "stats.insight.title": "At a glance", - "stats.insight.note": "Each line below is read straight from the numbers above — no guesswork.", "stats.insight.empty": "Not enough question data yet.", "stats.insight.daily30": "%s questions in the last 30 days, about %.1f per day.", "stats.insight.peak": "Busiest day was %s with %s questions.", @@ -1033,5 +1032,5 @@ var enDict = map[string]string{ "stats.foot.prevMonth": "Last question:", "stats.foot.prevAnswer": "Last answer:", "stats.foot.privacy": "All figures are aggregates; no individual question, asker, or source address is shown.", - "stats.foot.cacheNote": "Totals and charts are recomputed once a day, on the first visit after midnight (and again after a restart); the two \"today\" figures are live.", + "stats.foot.cacheNote": "Totals and charts are updated once a day; the two \"today\" figures are live.", } diff --git a/internal/i18n/dict_ja.go b/internal/i18n/dict_ja.go index eca7f1a..bfa415a 100644 --- a/internal/i18n/dict_ja.go +++ b/internal/i18n/dict_ja.go @@ -969,7 +969,6 @@ var jaDict = map[string]string{ "stats.kpi.messages": "DM の総数", "stats.insight.title": "ひと目でわかる数字", - "stats.insight.note": "以下の文はすべて上の数値から直接読み取ったもので、推測は含みません。", "stats.insight.empty": "まだ分析できるほどの質問データがありません。", "stats.insight.daily30": "直近 30 日で %s 件の質問、1 日あたり約 %.1f 件。", "stats.insight.peak": "最も多かった日は %s で %s 件でした。", @@ -1032,5 +1031,5 @@ var jaDict = map[string]string{ "stats.foot.prevMonth": "最後の質問:", "stats.foot.prevAnswer": "最後の回答:", "stats.foot.privacy": "数値はすべて集計値です。個々の質問内容・質問者の身元・送信元アドレスは表示しません。", - "stats.foot.cacheNote": "累計値とグラフはその日の最初のアクセス時に 1 回だけ再計算されます(再起動時にも再計算)。「今日」の 2 項目はリアルタイムです。", + "stats.foot.cacheNote": "累計値とグラフは 1 日 1 回更新されます。「今日」の 2 項目はリアルタイムです。", } diff --git a/internal/i18n/dict_zh_cn.go b/internal/i18n/dict_zh_cn.go index cea5124..54c60ad 100644 --- a/internal/i18n/dict_zh_cn.go +++ b/internal/i18n/dict_zh_cn.go @@ -970,7 +970,6 @@ var zhCNDict = map[string]string{ "stats.kpi.messages": "累计私聊消息", "stats.insight.title": "数据速览", - "stats.insight.note": "下面几句是从上面的数字直接读出来的,没有推测。", "stats.insight.empty": "提问数据还太少,暂时看不出什么。", "stats.insight.daily30": "近 30 天共收到 %s 条提问,日均 %.1f 条。", "stats.insight.peak": "最忙的一天是 %s,收到 %s 条提问。", @@ -1033,5 +1032,5 @@ var zhCNDict = map[string]string{ "stats.foot.prevMonth": "最后一条提问:", "stats.foot.prevAnswer": "最后一条回答:", "stats.foot.privacy": "所有数字均为聚合结果,不展示单条提问内容、提问者身份或来源地址。", - "stats.foot.cacheNote": "累计数字与图表是当天重算一次的快照(当天第一次访问时算,服务重启也会重算);「今日」两项为实时数字。", + "stats.foot.cacheNote": "累计数字与图表每天更新一次;「今日」两项实时。", } diff --git a/internal/i18n/dict_zh_tw.go b/internal/i18n/dict_zh_tw.go index 82ea723..e24a184 100644 --- a/internal/i18n/dict_zh_tw.go +++ b/internal/i18n/dict_zh_tw.go @@ -969,7 +969,6 @@ var zhTWDict = map[string]string{ "stats.kpi.messages": "累計私聊訊息", "stats.insight.title": "數據速覽", - "stats.insight.note": "下面幾句是直接從上面的數字讀出來的,沒有推測。", "stats.insight.empty": "提問數據還太少,暫時看不出什麼。", "stats.insight.daily30": "近 30 天共收到 %s 則提問,日均 %.1f 則。", "stats.insight.peak": "最忙的一天是 %s,收到 %s 則提問。", @@ -1032,5 +1031,5 @@ var zhTWDict = map[string]string{ "stats.foot.prevMonth": "最後一則提問:", "stats.foot.prevAnswer": "最後一則回答:", "stats.foot.privacy": "所有數字均為彙總結果,不展示單則提問內容、提問者身分或來源位址。", - "stats.foot.cacheNote": "累計數字與圖表是當天重算一次的快照(當天第一次造訪時算,服務重啟也會重算);「今日」兩項為即時數字。", + "stats.foot.cacheNote": "累計數字與圖表每天更新一次;「今日」兩項即時。", } diff --git a/templates/stats.html b/templates/stats.html index 81c875d..13c4c49 100644 --- a/templates/stats.html +++ b/templates/stats.html @@ -100,9 +100,9 @@ color: var(--tb-text-muted); } - /* 洞察 */ + /* 洞察:这组卡片没有说明行,靠 margin-top 补出与标题的间隔 */ .tb-insights { - margin: 0; + margin: 8px 0 0; padding: 0; list-style: none; } @@ -390,8 +390,8 @@ line-height: 1.8; color: var(--tb-text-muted); } - /* 四条时间各自成行、标签左列对齐:挤在「A:x · B:y」一行里,两组日期 - 靠一个间隔号分开,扫一眼分不清哪个是基准日哪个是最后提问。 */ + /* 三条时间各自成行、标签左列对齐:挤在「A:x · B:y」一行里,几个日期 + 靠间隔号分开,扫一眼分不清哪个是快照时间哪个是最后提问。 */ .tb-foot-times { display: grid; grid-template-columns: max-content minmax(0, 1fr); @@ -458,7 +458,6 @@ {{ if .Insights }}

{{t $.LangCode "stats.insight.title"}}

-

{{t $.LangCode "stats.insight.note"}}