Some checks failed
Build docker and publish / linux (1.24.5) (push) Failing after 39s
feat(抽奖动态): 修复抽奖动态未渲染问题并优化文案展示 fix(用户概览): 修复用户概览无数据显示问题 feat(新用户列表): 在新用户列表显示称号明细 refactor(待办事项): 移除代办模块并全宽展示实时动态 feat(批量操作): 限制为单用户操作并在批量时提醒 fix(称号分配): 防重复分配称号的改造计划 perf(接口性能): 优化新用户和抽奖动态接口性能 feat(订单漏斗): 优化订单转化漏斗指标计算 docs(测试计划): 完善盲盒运营API核查与闭环测试计划
52 lines
3.7 KiB
Markdown
52 lines
3.7 KiB
Markdown
## 痛点定位
|
||
- 新用户接口在单次请求内对每个用户做多次单表查询(资产、道具卡、优惠券、称号、最后在线时间),形成 N+1 查询,延迟随列表大小线性增加(`internal/api/admin/dashboard_admin.go` 的 `DashboardNewUsers()`)。
|
||
- 实时抽奖动态接口对每条抽奖日志逐条联查用户/期/活动/奖品,亦为 N+1 查询(`DashboardDrawStream()`)。
|
||
|
||
## 后端优化方案
|
||
- 批量聚合替代逐条查询(NewUsers)
|
||
- 第一步:一次查出当前页的 `user_id` 列表
|
||
- 第二步:分别对各表做分组聚合(单次查询):
|
||
- 资产:`SELECT user_id, COUNT(*) FROM user_inventory WHERE user_id IN (...) GROUP BY user_id`
|
||
- 道具卡:`SELECT user_id, COUNT(*) FROM user_item_cards WHERE status=1 AND user_id IN (...) GROUP BY user_id`
|
||
- 优惠券:`SELECT user_id, COUNT(*) FROM user_coupons WHERE user_id IN (...) GROUP BY user_id`
|
||
- 称号:`SELECT ut.user_id, st.id, st.name FROM user_titles ut LEFT JOIN system_titles st ON st.id=ut.title_id WHERE ut.user_id IN (...)`
|
||
- 最后在线:分别取各行为表 `MAX(time)` 按 `user_id` 聚合,再在内存求最大值
|
||
- 积分余额:改为批量查 `user_points` 有效积分 `GROUP BY user_id`,或接入预聚合表(见下)
|
||
- 第三步:用 `map[user_id]value` 合并到用户列表,避免每行多次往返数据库
|
||
- 连接查询替代逐条补全(DrawStream)
|
||
- 单条 SQL 联查:`activity_draw_logs` LEFT JOIN `users`、`activity_issues`、`activities`、`activity_reward_settings`,一次性返回 `nickname/activityName/issueNumber/prizeName`
|
||
- 保留 `since_id + limit` 增量拉取;避免循环内 `First()` 调用
|
||
- 预聚合与缓存
|
||
- 建议增加 `user_stats` 表(或 Redis 缓存)维护:`points_balance`、`inventory_count`、`item_card_count`、`coupon_count`、`title_list`、`last_online_at`
|
||
- 更新策略:
|
||
- 同步:在相关写入路径(发放积分/道具卡/优惠券/称号、抽奖、下单)更新统计
|
||
- 异步:crontab 每 1-5 分钟增量刷新
|
||
- 实时抽奖:为最近 50 条结果加 3-5 秒内存缓存(LRU 或 Redis)
|
||
- 限流与分页
|
||
- 新用户默认 `page_size=20`,最大 50;实时抽奖 `limit<=100`
|
||
- 对于“今年”范围下分页检索控制页大小,避免一次返回过多用户
|
||
|
||
## 数据库与索引
|
||
- 新建/确认索引:
|
||
- `users(created_at)`、`users(id)`
|
||
- `activity_draw_logs(id DESC, user_id, issue_id, reward_id, created_at)`
|
||
- `user_inventory(user_id)`、`user_item_cards(user_id,status)`、`user_coupons(user_id)`、`user_titles(user_id,title_id)`
|
||
- `user_points(user_id, valid_end)`、`user_points_ledger(user_id, created_at)`
|
||
- 可选:为 `log_request(path, created_at)` 增加 `user_id` 字段与索引,精确“最后在线时间”
|
||
|
||
## 前端协同优化
|
||
- 新用户页签切换时:防抖 200ms;保留上次结果并显示加载骨架,避免空白闪烁
|
||
- 实时抽奖轮询:保持 5s;追加条目后裁剪到 100-200 条以保证 DOM 轻量;使用 `ref` 持有列表(已改)
|
||
- 宽度问题:动态项允许换行并分两行展示(已改),避免不可见
|
||
|
||
## 验收指标
|
||
- 新用户接口:在 `page_size=20` 时 P95 响应时间 < 200ms(本地数据量下)
|
||
- 实时抽奖接口:在 `limit=50` 时 P95 响应时间 < 150ms;每轮轮询端到端显示时间 < 300ms
|
||
|
||
## 下一步实现内容(获批后执行)
|
||
1) 重写 `DashboardNewUsers()` 为批量聚合与合并映射
|
||
2) 重写 `DashboardDrawStream()` 为单次 LEFT JOIN 联查
|
||
3) 添加必要索引迁移脚本
|
||
4) 可选:落地 `user_stats` 预聚合与写路径刷新机制
|
||
|
||
确认后我将按上述方案逐条落地并提供压测数据与对比报告。 |