## 当前情况
- 管理端“工作台”路由：`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 注释与最小测试用例