fix(stats): 回答耗时改用 answered_at,修测试环境实测出的三处问题
在测试环境(box-dev-api)上逐项验算后发现「回答耗时」这一口径是错的。 1. 回答耗时不能用 updated_at - created_at(P0) GORM 的 Update 会自动刷新 updated_at,而已读、置顶、公开/私密切换全走 Update。 实测:一条 10 天前的提问被 SetRead 标记已读后,updated_at 被推到当下(delta=240h)。 于是「回答耗时」实际测的是「提问到最后一次被碰」:线上那 6 条「超过 7 天」 和偏低的 24 小时回复占比都是这么来的。 修法:新增 questions.answered_at(首次回答时间,可空),只在 answer 由空变非空时 写一次,改写回答不重置;聚合改用 COALESCE(answered_at, updated_at), 首次加列时用 updated_at 冻结历史一次(Migrator().HasColumn 判断是否刚加列), 否则历史值会随着后续的已读/置顶继续变大。 2. 环比句式读不通 中文「上月提问量比前一个月 -69.6%」、英文「...: -69.6% questions.」都是病句。 改为「上月提问量环比 %s%%」/「Month-over-month change for last month: %s%%」。 3. 脚注说「当日 0 点的快照」不实 实际是当天第一次访问时算、服务重启也会重算(实测算出时刻 01:11:50, 基准日却写 2026-10-24,说明它含了 0 点之后的数据)。改成准确描述。 4. 数据库故障时请求会排成 N×30s ovRefresh 串行化 + 30 秒超时意味着数据库挂起时第 N 个请求要等 N×30 秒。 加 30 秒失败冷却:冷却期内直接降级(有旧快照用旧快照),不再碰数据库。 todayNumbers 失败也盖时间戳,不再每个请求都打两条 COUNT。 测试:新增 internal/db/answered_at_test.go(5 例:只记一次、私密路径也记、 分桶以 answered_at 为准、历史行回退、回填),并做过变异验证 —— 把口径改回 updated_at 后测试立即失败。internal/stats 补 3 例冷却测试。全量构建与测试通过 (internal/storage 的构建失败是 overlay 桩的既有问题)。
This commit is contained in:
@@ -975,7 +975,7 @@ var enDict = map[string]string{
|
||||
"stats.insight.daily30": "%s questions in the last 30 days, about %.1f per day.",
|
||||
"stats.insight.peak": "Busiest day was %s with %s questions.",
|
||||
"stats.insight.busiest": "Questions cluster around %s %d:00.",
|
||||
"stats.insight.mom": "Last month versus the month before: %s%% questions.",
|
||||
"stats.insight.mom": "Month-over-month change for last month: %s%%.",
|
||||
"stats.insight.fastReply": "%s%% of answered questions got a reply within 24 hours.",
|
||||
"stats.insight.private": "%s%% of questions were sent privately.",
|
||||
|
||||
@@ -1034,5 +1034,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 a snapshot taken at 00:00 local time, refreshed once a day; the two \"today\" figures are live.",
|
||||
"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.",
|
||||
}
|
||||
|
||||
@@ -974,7 +974,7 @@ var jaDict = map[string]string{
|
||||
"stats.insight.daily30": "直近 30 日で %s 件の質問、1 日あたり約 %.1f 件。",
|
||||
"stats.insight.peak": "最も多かった日は %s で %s 件でした。",
|
||||
"stats.insight.busiest": "質問は %s %d 時ごろに集中しています。",
|
||||
"stats.insight.mom": "先月は前月比 %s%%。",
|
||||
"stats.insight.mom": "先月の質問数は前月比 %s%%。",
|
||||
"stats.insight.fastReply": "回答済みのうち %s%% が 24 時間以内に回答されています。",
|
||||
"stats.insight.private": "質問の %s%% は非公開が選ばれています。",
|
||||
|
||||
@@ -1033,5 +1033,5 @@ var jaDict = map[string]string{
|
||||
"stats.foot.prevMonth": "最後の質問:",
|
||||
"stats.foot.prevAnswer": "最後の回答:",
|
||||
"stats.foot.privacy": "数値はすべて集計値です。個々の質問内容・質問者の身元・送信元アドレスは表示しません。",
|
||||
"stats.foot.cacheNote": "累計値とグラフは当日 0 時のスナップショットで、1 日 1 回だけ更新されます。「今日」の 2 項目はリアルタイムです。",
|
||||
"stats.foot.cacheNote": "累計値とグラフはその日の最初のアクセス時に 1 回だけ再計算されます(再起動時にも再計算)。「今日」の 2 項目はリアルタイムです。",
|
||||
}
|
||||
|
||||
@@ -975,7 +975,7 @@ var zhCNDict = map[string]string{
|
||||
"stats.insight.daily30": "近 30 天共收到 %s 条提问,日均 %.1f 条。",
|
||||
"stats.insight.peak": "最忙的一天是 %s,收到 %s 条提问。",
|
||||
"stats.insight.busiest": "提问最集中在%s %d 点前后。",
|
||||
"stats.insight.mom": "上月提问量比前一个月 %s%%。",
|
||||
"stats.insight.mom": "上月提问量环比 %s%%。",
|
||||
"stats.insight.fastReply": "已回答的提问中,%s%% 在 24 小时内拿到了回复。",
|
||||
"stats.insight.private": "%s%% 的提问选择了私密(不公开)。",
|
||||
|
||||
@@ -1034,5 +1034,5 @@ var zhCNDict = map[string]string{
|
||||
"stats.foot.prevMonth": "最后一条提问:",
|
||||
"stats.foot.prevAnswer": "最后一条回答:",
|
||||
"stats.foot.privacy": "所有数字均为聚合结果,不展示单条提问内容、提问者身份或来源地址。",
|
||||
"stats.foot.cacheNote": "累计数字与图表是当日 0 点的快照,每天只更新一次;「今日」两项为实时数字。",
|
||||
"stats.foot.cacheNote": "累计数字与图表是当天重算一次的快照(当天第一次访问时算,服务重启也会重算);「今日」两项为实时数字。",
|
||||
}
|
||||
|
||||
@@ -974,7 +974,7 @@ var zhTWDict = map[string]string{
|
||||
"stats.insight.daily30": "近 30 天共收到 %s 則提問,日均 %.1f 則。",
|
||||
"stats.insight.peak": "最忙的一天是 %s,收到 %s 則提問。",
|
||||
"stats.insight.busiest": "提問最集中在%s %d 點前後。",
|
||||
"stats.insight.mom": "上月提問量比前一個月 %s%%。",
|
||||
"stats.insight.mom": "上月提問量環比 %s%%。",
|
||||
"stats.insight.fastReply": "已回答的提問中,%s%% 在 24 小時內得到回覆。",
|
||||
"stats.insight.private": "%s%% 的提問選擇了私密(不公開)。",
|
||||
|
||||
@@ -1033,5 +1033,5 @@ var zhTWDict = map[string]string{
|
||||
"stats.foot.prevMonth": "最後一則提問:",
|
||||
"stats.foot.prevAnswer": "最後一則回答:",
|
||||
"stats.foot.privacy": "所有數字均為彙總結果,不展示單則提問內容、提問者身分或來源位址。",
|
||||
"stats.foot.cacheNote": "累計數字與圖表是當日 0 點的快照,每天只更新一次;「今日」兩項為即時數字。",
|
||||
"stats.foot.cacheNote": "累計數字與圖表是當天重算一次的快照(當天第一次造訪時算,服務重啟也會重算);「今日」兩項為即時數字。",
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user