新增 /user/insights,让箱主第一次能看到「有多少人来过我的箱、提问处理得怎么样」。 数据来源: - 新表 box_views:一行 = 一访客×一天×一箱,views 累加,唯一索引 (user_id, viewed_on, visitor)。刻意不内嵌 dbutil.Model —— 软删除 + 唯一索引 会让被删行永久占槽,且删过的槽位再也插不进去。 - 访客标识 = HMAC(日盐, IP) 前 32 hex:同日可去重(UV 准)、跨日不可关联(隐私)。 代价是近 7/30 天 UV = 各日 UV 之和,页面上已写明口径。 写入是异步的:buffered channel 512 + 2s 批量 flush,队列满即丢(统计尽力而为, 不该因为记一笔访问就拖慢页面)。过滤箱主本人、空 UA、爬虫 UA、预取请求。 密钥复用 server.xsrf_key;每日清理 400 天前的明细。 页面(纯 CSS 画图,不引图表库): - 今日/近 7 天/近 30 天 KPI + 上周对比 - 30 天 PV/UV 双柱趋势、24 小时时段分布 - 提问处理漏斗 + 回答率进度条 + 自动结论 聚合层 internal/db/box_analytics.go 复用 analyticsDialectFor 的时区处理, 与全站 stats 分开(那个不含单箱维度、按自然日缓存)。 测试:box_views 4 项(含「同批同键先内存合并」——PG 的 ON CONFLICT 不允许 同条 INSERT 二次命中同一行)、insights 的 pct/窗口切分/visitorID/爬虫过滤。
32 lines
1.2 KiB
Go
32 lines
1.2 KiB
Go
// Copyright 2026 tamakyi. All rights reserved.
|
|
// Use of this source code is governed by a MIT-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package user
|
|
|
|
import (
|
|
"time"
|
|
|
|
"github.com/tamakyi/TamaBox/internal/context"
|
|
"github.com/tamakyi/TamaBox/internal/i18n"
|
|
"github.com/tamakyi/TamaBox/internal/insights"
|
|
)
|
|
|
|
// Insights 箱主自己的数据面板。
|
|
//
|
|
// 与 /stats 的区别:那个是全站公开数据(任何人可看、不含单箱维度);
|
|
// 这里只统计当前登录用户自己的箱,且只对本人可见 —— 所以数据里可以出现
|
|
// 「你的箱本周被看了多少次」这种带归属的指标。
|
|
//
|
|
// 隐私边界:库里只存「每 访客×天×箱 的访问次数」,访客标识是带日盐的 IP HMAC,
|
|
// 没有 IP、没有 UA、没有访问路径。因此面板能回答「多少人来了」,
|
|
// 但回答不了「谁来过」——这是刻意的,箱主不需要也不应该拿到提问者与访客的身份。
|
|
func Insights(ctx context.Context) {
|
|
page := insights.Build(ctx.Request().Context(), ctx.Lang(), ctx.User.ID, time.Now())
|
|
page.FormatInsights()
|
|
|
|
ctx.SetTitle(i18n.T(ctx.Lang(), "insights.title"))
|
|
ctx.Data["Insights"] = page
|
|
ctx.Success("user/insights")
|
|
}
|