vr-shopxo-plugin/docs/PHASE_B_2026-04-25_PLAN.md

171 lines
5.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# Phase BB端核销开发计划
> 创建时间2026-04-25 10:26 CST
> 创建人:西莉雅
> 状态:规划中,待 Council 执行
---
## 一、背景
C端票夹 + 出票链路已完成Phase 4.1-4.3)。
下一步B端核销管理后台 + 安全修复。
**已知安全债务Issue #7**
- M-01verifyTicket TOCTOU → ✅ 已修复FOR UPDATE + 事务)
- M-02手动核销无鉴权 → 🔴 B端开发前必须修复
- M-03ALTER TABLE 条件永不成立 → 🟢 快速修复2行
- M-04loadSoldSeats 未实现 → ✅ 已实现
- M-05verifier_id 客户端伪造 → 🔴 B端开发前必须修复
- M-06Admin 控制器无权限校验 → 🔴 B端安全基线
- M-07QR 明文暴露 ticket_code → 🟢 低风险,可后置
- M-08issueTicket 二次写入 → ✅ 已修复
---
## 二、分阶段计划
### Phase B0 — 安全基线(动手前必须完成)
**目标**:修复 M-06、M-02、M-05、M-03建立 B端安全防线。
#### B0-1Admin.php 权限校验M-06
在 Admin.php 所有操作方法TicketVerify/TicketList/TicketExport/VerifierSave/VerifierDelete 等)开头加:
```php
public function TicketVerify()
{
// M-06: 强制管理员登录校验
if (empty(session('admin_id'))) {
return DataReturn('无权限访问,请先登录后台', -1);
}
// ...原有逻辑
}
```
#### B0-2verifier_id 改为 session 来源M-05
**现状**`TicketVerify` 接收客户端传入的 `$verifier_id`,攻击者可伪造任意身份。
**修复**:改为从 session 获取当前 admin 对应的 ShopXO 用户ID`vr_verifiers` 表获取 verifier_id。
```php
// 错误(可伪造):
$verifier_id = input('verifier_id', 0, 'intval');
// 正确(从 session 获取):
$admin_user_id = session('admin_user_info.id'); // ShopXO 后台登录用户ID
$verifier = \think\facade\Db::name('vr_verifiers')
->where('user_id', $admin_user_id)
->where('status', 1)
->find();
if (empty($verifier)) {
return DataReturn('你不是核销员,无权核销', -1);
}
$verifier_id = $verifier['id'];
```
#### B0-3ALTER TABLE bug 修复M-03
EventListener.php 中 `empty($result)` 对 PDOStatement 永远返回 falseALTER TABLE 永不执行。
**文件**`shopxo/app/plugins/vr_ticket/Event.php`(或 EventListener.php
**修复**:改用 `count($result) === 0``empty($result->fetchAll())`
---
### Phase B1 — 核心 B端核销页
**目标**:建一个可用的扫码核销页面,优先满足现场核销需求。
#### B1-1admin/view/ticket/verify.html扫码核销主页面
**功能**
- 扫码按钮HTML5 MediaDevices API兼容 PC 摄像头)
- 手动输入短码/票码文本框
- 调用 `PluginsAdminUrl('vr_ticket', 'admin', 'TicketVerify')` API
- 展示核销结果(座位号、观演人姓名、商品名)
- 统计栏:今日核销数 / 待核销数 / 已核销总数
**注意**:需要先在 Hook.php 的 `AdminSidebarInit` 注册菜单项 `ticketVerify`
#### B1-2admin/view/ticket/list.html电子票列表
- 搜索:订单号/票码/观演人姓名/手机号
- 状态筛选:全部/未核销/已核销/已退款
- 商品筛选
- 每行显示:票码、观演人、座位信息、商品名、状态、时间
- 操作:查看详情
#### B1-3admin/view/ticket/detail.html票详情
- 显示票完整信息(座位/观演人/QR码/发放时间)
- 显示核销记录(如已核销)
---
### Phase B2 — 辅助管理页
#### B2-1核销员管理
- `admin/view/verifier/list.html`:核销员列表(关联 ShopXO 用户)
- `admin/view/verifier/save.html`:添加/编辑核销员(选择 ShopXO 用户作为关联)
#### B2-2核销记录
- `admin/view/verification/list.html`:核销历史记录,支持按核销员/日期筛选
#### B2-3座位模板管理
- `admin/view/seat_template/list.html`:座位模板列表
- `admin/view/seat_template/save.html`:座位模板编辑(参考现有的 view/venue/save.html 风格)
---
## 三、实施顺序
```
B0-1 → B0-2 → B0-3 (安全基线,并行)
B1-1 → B1-2 → B1-3 (核心核销,并行或串行)
B2-1 → B2-2 → B2-3 (辅助管理,并行)
```
---
## 四、技术约束(来自 EXPERIENCES.md
1. **视图路径**`MyView('../../../plugins/vr_ticket/admin/view/ticket/list', [...])`
2. **public_host**:控制器必须显式传递 `public_host` 给模板
3. **header/footer**:所有 admin 页面必须有 `{{:ModuleInclude('public/header')}}``{{:ModuleInclude('public/footer')}}`
4. **Vue 3 textarea**:禁止 `<textarea>[[ value ]]</textarea>`,用隐藏 input
5. **CDN**:禁止 unpkg/jsdelivr`cdn.staticfile.net` 或 ShopXO 自带文件
6. **双目录陷阱**:修改 `app/plugins/vr_ticket/static/` 后必须同步到 `public/plugins/vr_ticket/static/`
7. **AmazeUI 类名**:列表页用 `am-table am-table-striped am-table-hover am-text-middle`
8. **PHP 注释**:确保所有 `/* ... */` 闭合,避免语法错误
---
## 五、验收标准
- [ ] M-06无 session('admin_id') 无法调用任何 Admin.php 接口
- [ ] M-05核销记录中的 verifier_id 来自 session不可伪造
- [ ] B1-1PC 摄像头扫码可成功核销,显示票信息
- [ ] B1-1手动输入短码可成功核销
- [ ] B1-2电子票列表正常展示支持搜索和状态筛选
- [ ] B1-3票详情页正常展示
- [ ] B2-1可添加/禁用核销员
- [ ] B2-2核销记录正常展示
- [ ] 所有页面 header/footer 完整,无无限加载问题
---
## 六、待确认
1. **ShopXO 后台 admin_user_info 结构**session('admin_user_info.id') 是否正确?需确认 ShopXO 后台登录后 session key。
2. **HTML5 扫码兼容**PC 端推荐 `navigator.mediaDevices.getUserMedia`,是否有更好的 ShopXO 原生方案?
3. **B1-1 的扫码入口**:是在"电子票列表"页加一个"扫码核销"按钮,还是独立菜单项?