diff --git a/docs/superpowers/specs/2026-09-01-traffic-ops-admin-design.md b/docs/superpowers/specs/2026-09-01-traffic-ops-admin-design.md new file mode 100644 index 0000000..8ce1f69 --- /dev/null +++ b/docs/superpowers/specs/2026-09-01-traffic-ops-admin-design.md @@ -0,0 +1,177 @@ +# 流量运营管理页设计 + +日期:2026-09-01 +状态:已确认(待实现计划) + +## 背景 + +用户账户中心已能购买云媒体流量套餐并拉起微信支付二维码,但套餐数据目前只能通过 Admin API / 数据库维护,前端没有管理入口。运营还需要查看平台资源池、录入采购批次、核对账务流水。 + +后端 `/v1/admin/traffic/*` 已具备: + +- 套餐分页 / 创建 / 更新 / 下架(DELETE → `inactive`) +- 资源池查询、采购批次创建与分页 +- 账务流水分页(只读) + +## 目标 + +为管理员新增独立页面「流量运营」,页内三个 Tab: + +1. **套餐管理**:完整 CRUD(删除语义=下架) +2. **采购入池**:展示资源池 + 录入采购批次 +3. **账务流水**:只读筛选与分页 + +用户侧账户购买流程不变;仅 `status=active` 的套餐对用户可见。 + +## 非目标 + +- 不改后端 API 契约 +- 不做采购批次编辑/删除(后端无接口) +- 不做流水写入/冲正 +- 不把该能力塞进「系统管理」或「账户中心」 +- 本轮不做通信卡套餐管理 + +## 方案 + +采用独立页面 `TrafficOpsView`: + +- 路由:`/traffic-ops`,`meta: { admin: true }` +- 侧栏:管理员菜单新增「流量运营」,与「固件管理」同级 +- 顶栏标题:流量运营 +- UI 模式对齐 `FirmwaresView`:`operation-page` + 工具栏 + `t-table` + `t-dialog` + `ui.confirm` + +## 信息架构 + +```text +侧栏(管理员) +└─ 流量运营 /traffic-ops + ├─ Tab 套餐管理 + ├─ Tab 采购入池 + └─ Tab 账务流水 +``` + +## Tab 1:套餐管理 + +### 列表 + +- 筛选:`status` = 全部 / `active` / `inactive` +- 操作:刷新、新建套餐 +- 列:编码、名称、额度(GB)、价格、有效期、状态、排序、更新时间、操作 + +### 表单(新建/编辑同一弹窗) + +| 字段 | 新建 | 编辑 | 说明 | +|---|---|---|---| +| code | 必填 | 只读展示 | 唯一;后端不可改 | +| name | 必填 | 可改 | | +| amountGb | 必填 | 可改 | UI 用 GB,提交转 `amountBytes = GB * 1024^3` | +| price | 必填,≥0 | 可改,但避免改成 0 | 后端 Update 仅在 `price > 0` 时写入 | +| validityDays | ≥0 | 可改,但避免把已有值改成 0 | `0` 表示长期有效;Update 仅在 `>0` 时写入 | +| sort | 可选 | 可改 | | +| status | 默认 active | 可改 active/inactive | | + +### 操作语义 + +- **下架**:调用 `DELETE /v1/admin/traffic/packages/:id`,confirm 文案明确「下架后用户侧不可见,之后可再上架」 +- **重新上架**:编辑弹窗把 `status` 改回 `active`,或等价更新 +- 创建成功 / 更新成功 / 下架成功后刷新当前列表 + +## Tab 2:采购入池 + +### 资源池卡片 + +展示:`totalBytes`、`availableBytes`、`status`(同时换算 GB)。 + +若 `GET /v1/admin/traffic/pool` 返回 404 / 资源不存在: + +- 不视为页面失败 +- 展示空态:「尚未建立资源池」 +- 提示:首次成功录入采购后自动建池 + +### 采购列表 + +- 筛选:provider、status(对接现有 query) +- 操作:刷新、录入采购 +- 列:批次号、供应商、总量、成本、到期、状态、备注、创建时间 + +### 新建采购弹窗 + +对齐 `PlatformPurchaseCreateReq`: + +- 必填:`batchNo`、`provider`、`totalBytes`(UI 可用 GB 输入) +- 选填:`unitCost`、`totalCost`、`expiresAt`、`remark` + +仅支持创建,不提供编辑/删除。创建成功后刷新 pool + purchases。 + +## Tab 3:账务流水(只读) + +### 筛选 + +- `accountType`:全部 / user / platform +- `accountId`:可选 +- 分页、刷新 + +### 列 + +时间、账户类型、账户 ID、方向(credit/debit)、金额(GB + 原始字节)、变动前/后余额、来源类型、来源 ID、幂等键。 + +无写操作。空态:「暂无流水」。 + +## 权限与错误处理 + +- 路由 `meta.admin` + 侧栏 `isAdmin` 双控;非管理员不可见、不可进入 +- API 401/403:沿用全局拦截 toast +- 套餐 code 冲突:展示后端「套餐编码已存在」 +- 下架必须二次确认 +- pool 404:引导录入采购,不弹致命错误 + +## 前端改动面 + +1. `src/views/TrafficOpsView/TrafficOpsView.vue`(新) +2. `src/router/index.js`:注册 `/traffic-ops` +3. `src/layout/components/SideMenu.vue`:管理员菜单项 +4. `src/layout/components/TopBar.vue`:标题映射 +5. `src/config/urls.js`:补充 + - `ADMIN_TRAFFIC_POOL` + - `ADMIN_TRAFFIC_PURCHASES` + - `ADMIN_TRAFFIC_PACKAGES` + - `ADMIN_TRAFFIC_PACKAGE(id)` + - `ADMIN_TRAFFIC_LEDGER` + +## 数据流 + +```text +管理员打开 /traffic-ops + ├─ 套餐 Tab → GET /v1/admin/traffic/packages + │ ├─ 新建 → POST /packages + │ ├─ 编辑 → PUT /packages/:id + │ └─ 下架 → DELETE /packages/:id + ├─ 采购 Tab → GET /pool + GET /purchases + │ └─ 录入 → POST /purchases → 刷新 pool/purchases + └─ 流水 Tab → GET /ledger +用户账户页仍只读 GET /v1/account/traffic-packages(active) +``` + +## 验收标准 + +1. 管理员侧栏可见「流量运营」,普通用户不可见 +2. 可新建套餐;账户中心购买区立即出现(active) +3. 可下架套餐;账户中心不再展示 +4. 可编辑名称/价格/额度/排序/状态 +5. 采购录入成功后,资源池从 404 变为有余额,或可用字节增加 +6. 流水 Tab 可按账户类型筛选并分页 +7. 非管理员访问 `/traffic-ops` 被重定向到 `/monitor` + +## 风险与约束 + +- 后端 Update 对 `price`、`validityDays` 使用 `>0` 判断,UI 需避免误把有效值写成 0 导致“看似提交成功但未变更” +- 当前生产 pool 可能尚未初始化;采购 Tab 必须兼容 404 +- 本页不处理微信支付回调;入账仍走既有 `/pay/wx/callback` + +## 实现顺序建议 + +1. urls + 路由 + 菜单骨架 +2. 套餐 Tab CRUD +3. 采购入池 Tab +4. 流水 Tab +5. 管理员权限冒烟 + 用户侧套餐可见性回归