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核查与闭环测试计划
127 lines
6.7 KiB
Markdown
127 lines
6.7 KiB
Markdown
## 当前情况
|
||
- 管理端“工作台”路由:`web/admin/src/router/modules/dashboard.ts:3-24`
|
||
- 工作台页面:`web/admin/src/views/dashboard/console/index.vue`
|
||
- 工作台数据源:`web/admin/src/api/dashboard.ts` 全为本地 Mock,无真实后端调用
|
||
- 后端已存在管理端认证路由分组:`internal/router/router.go:55-151`,但未注册任何“dashboard”端点
|
||
|
||
## 技术栈与约束
|
||
- 后端:Gin + GORM Gen,入口 `main.go:52-67`,路由 `internal/router/router.go`
|
||
- 数据模型可用:`internal/repository/mysql/model/*.gen.go`(如 `users`、`activity_draw_logs`、`user_item_cards`、`user_points`、`guild_members`)
|
||
- 认证:管理端统一走 `Authorization` 头(`LoginVerifyToken`),同组复用:`internal/router/router.go:56`
|
||
|
||
## 端点设计(前缀 `/api/admin/dashboard`)
|
||
1) `GET /cards`
|
||
- 入参:`rangeType=today|7d|30d|custom`,可选 `start=YYYY-MM-DD&end=YYYY-MM-DD`(custom 时)
|
||
- 出参:`CardStat`(与 `web/admin/src/api/dashboard.ts` 一致)
|
||
- 统计口径:
|
||
- `itemCardSales`:`user_item_cards.created_at` 范围新增数(`user_item_cards`)
|
||
- `drawCount`:`activity_draw_logs.created_at` 范围次数(`activity_draw_logs`)
|
||
- `newUsers`:`users.created_at` 范围新增数(`users`)
|
||
- `totalPoints`:全量有效积分总和(`user_points.points` 过滤 `valid_end` 未过期),实现参考 `internal/service/user/points_balance.go:8-21`
|
||
- 环比字段 `*_Change`:与上一等长时间窗口对比百分比(向下取整),无前窗则返回 `+0%`
|
||
|
||
2) `GET /user_trend`
|
||
- 入参:`rangeType`、`granularity=day|week|month`
|
||
- 出参:`UserTrendResp`
|
||
- 统计:按粒度聚合 `users.created_at` 计数生成时间序列
|
||
|
||
3) `GET /draw_trend`
|
||
- 入参:同上
|
||
- 出参:`DrawTrendResp`
|
||
- 统计:按粒度聚合 `activity_draw_logs.created_at` 计数生成时间序列
|
||
|
||
4) `GET /new_users`
|
||
- 入参:`page`、`page_size`
|
||
- 出参:`NewUserListResp`
|
||
- 明细项字段:
|
||
- `pointsBalance`:用户有效积分余额(复用 `GetPointsBalance` 逻辑 `internal/service/user/points_balance.go:8-21`)
|
||
- `inventoryCount`:`user_inventory` 记录数(状态过滤视业务)
|
||
- `itemCardCount`:`user_item_cards` 有效未使用数量(`status=1`)
|
||
|
||
5) `GET /draw_stream`
|
||
- 入参:`since_id`(可选)、`limit`(默认 50,最大 100)
|
||
- 出参:`DrawStreamResp`
|
||
- 统计:按 `id` 递减拉取最近抽奖日志,关联 `users.nickname`、`activity_issues.name` 生成展示项;返回 `sinceId = max(id)` 以便前端轮询增量
|
||
|
||
6) `GET /todos`
|
||
- 入参:`limit`(默认 50,最大 100)
|
||
- 出参:`TodoListResp`
|
||
- 规则:
|
||
- `bind_mobile`:`users.mobile IS NULL OR ''`
|
||
- `join_guild`:`NOT EXISTS guild_members WHERE user_id=users.id AND status=1`
|
||
- 返回 `avatar`、`nickname`、`taskLabel` 友好文案
|
||
|
||
## 后端改动点
|
||
- 新增处理器:`internal/api/admin/dashboard_admin.go`
|
||
- 采用现有 handler 模式:`internal/api/admin/admin.go:16-42`
|
||
- 为每个端点提供 `core.HandlerFunc`,入参校验使用 `ShouldBindForm/JSON` 与 `internal/pkg/validation`
|
||
- 新增服务层:`internal/service/admin/dashboard_*.go`
|
||
- `GetCardStats(ctx, range)`, `GetUserTrend(ctx, range, granularity)`, `GetDrawTrend(...)`
|
||
- `ListNewUsersWithStats(ctx, page, pageSize)`:多表聚合(users + user_points + user_item_cards + user_inventory)
|
||
- `ListDrawStream(ctx, sinceID, limit)`:抽奖日志联表 users、activity_issues
|
||
- `ListTodos(ctx, limit)`:基于 users 与 guild_members 规则
|
||
- 路由注册:在管理端认证组添加
|
||
- 文件:`internal/router/router.go`
|
||
- 组:`adminAuthApiRouter`(`internal/router/router.go:55-151`)
|
||
- 注册形如:
|
||
- `adminAuthApiRouter.GET("/dashboard/cards", adminHandler.DashboardCards())`
|
||
- `adminAuthApiRouter.GET("/dashboard/user_trend", adminHandler.DashboardUserTrend())`
|
||
- `adminAuthApiRouter.GET("/dashboard/draw_trend", adminHandler.DashboardDrawTrend())`
|
||
- `adminAuthApiRouter.GET("/dashboard/new_users", adminHandler.DashboardNewUsers())`
|
||
- `adminAuthApiRouter.GET("/dashboard/draw_stream", adminHandler.DashboardDrawStream())`
|
||
- `adminAuthApiRouter.GET("/dashboard/todos", adminHandler.DashboardTodos())`
|
||
|
||
## 前端改动点
|
||
- 更新 `web/admin/src/api/dashboard.ts`
|
||
- 用 `request.get` 替换 Mock:
|
||
- `fetchCardStats(range)` → `GET admin/dashboard/cards`
|
||
- `fetchUserTrend(range, granularity)` → `GET admin/dashboard/user_trend`
|
||
- `fetchDrawTrend(range, granularity)` → `GET admin/dashboard/draw_trend`
|
||
- `fetchNewUsers(page, pageSize)` → `GET admin/dashboard/new_users`
|
||
- `fetchDrawStream(sinceId, limit)` → `GET admin/dashboard/draw_stream`
|
||
- `fetchTodos(limit)` → `GET admin/dashboard/todos`
|
||
- 保持 TS 接口不变,便于无缝替换
|
||
|
||
## 数据来源与代码参考
|
||
- 路由分组:`internal/router/router.go:55-61`
|
||
- 抽奖日志分页示例:`internal/service/activity/draw_logs_list.go:9-28`
|
||
- 用户积分余额:`internal/service/user/points_balance.go:8-21`
|
||
- 用户综合统计:`internal/service/user/stats.go:13-38`
|
||
- 数据模型表:`internal/repository/mysql/model/*.gen.go`(如 `users.gen.go:15-29`、`activity_draw_logs.gen.go:13-24`、`user_item_cards.gen.go:13-28`、`guild_members.gen.go:13-23`)
|
||
|
||
## 接口示例返回
|
||
- `GET /api/admin/dashboard/cards`:
|
||
```json
|
||
{
|
||
"itemCardSales": 1234,
|
||
"drawCount": 5678,
|
||
"newUsers": 321,
|
||
"totalPoints": 98765,
|
||
"itemCardChange": "+12%",
|
||
"drawChange": "+8%",
|
||
"newUserChange": "+5%",
|
||
"pointsChange": "+3%"
|
||
}
|
||
```
|
||
- `GET /api/admin/dashboard/user_trend` / `draw_trend`:`{ "granularity": "day", "list": [{"date":"2025-11-01","value":100}, ...] }`
|
||
- 其他返回与 `web/admin/src/api/dashboard.ts` 对齐
|
||
|
||
## 验证与测试
|
||
- Swagger 注解与分组标签:沿用现有风格(参考 `internal/api/admin/users_admin.go:30-46`)
|
||
- 集成测试:参考 `internal/api/admin/titles_admin_test.go` 新增 API 测试用例(登录获取 Token 后调用)
|
||
- 手动验证:
|
||
- 后端:`curl -H "Authorization: <token>" "http://localhost:<port>/api/admin/dashboard/cards?rangeType=7d"`
|
||
- 前端:工作台各模块正常展示且不再使用随机数据
|
||
|
||
## 性能与索引建议
|
||
- 为 `users.created_at`、`activity_draw_logs.created_at`、`user_item_cards.created_at` 建立索引
|
||
- 汇总接口注意分页与时间窗口限制,默认范围 7 天,可配置上限 30 天
|
||
|
||
## 交付范围与验收
|
||
- 完成 6 个端点后,前端工作台所有模块均有真实数据源
|
||
- 验收:页面展示与接口返回满足 TS 类型;Swagger 文档可用;管理员认证校验生效
|
||
|
||
确认后我将:
|
||
- 在后端新增处理器与服务实现并注册路由
|
||
- 替换前端 `dashboard.ts` 的 Mock 为真实请求
|
||
- 补充必要的 Swagger 注释与最小测试用例 |