2026-04-25 02:28:04 +00:00
|
|
|
|
# Phase B:B端核销开发计划
|
|
|
|
|
|
|
|
|
|
|
|
> 创建时间:2026-04-25 10:26 CST
|
|
|
|
|
|
> 创建人:西莉雅
|
2026-04-25 05:15:03 +00:00
|
|
|
|
> 状态:✅ Council 执行 + 审阅完成(2026-04-25 13:13 CST)
|
|
|
|
|
|
> 分支:`feat/phase-b-verification`
|
|
|
|
|
|
> 审阅:3步 subagent 审阅 + 发现4处问题全部修完推送
|
2026-04-25 02:28:04 +00:00
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 一、背景
|
|
|
|
|
|
|
|
|
|
|
|
C端票夹 + 出票链路已完成(Phase 4.1-4.3)。
|
|
|
|
|
|
下一步:B端核销管理后台 + 安全修复。
|
|
|
|
|
|
|
|
|
|
|
|
**已知安全债务(Issue #7):**
|
|
|
|
|
|
- M-01:verifyTicket TOCTOU → ✅ 已修复(FOR UPDATE + 事务)
|
|
|
|
|
|
- M-02:手动核销无鉴权 → 🔴 B端开发前必须修复
|
|
|
|
|
|
- M-03:ALTER TABLE 条件永不成立 → 🟢 快速修复(2行)
|
|
|
|
|
|
- M-04:loadSoldSeats 未实现 → ✅ 已实现
|
|
|
|
|
|
- M-05:verifier_id 客户端伪造 → 🔴 B端开发前必须修复
|
|
|
|
|
|
- M-06:Admin 控制器无权限校验 → 🔴 B端安全基线
|
|
|
|
|
|
- M-07:QR 明文暴露 ticket_code → 🟢 低风险,可后置
|
|
|
|
|
|
- M-08:issueTicket 二次写入 → ✅ 已修复
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 二、分阶段计划
|
|
|
|
|
|
|
2026-04-25 02:41:18 +00:00
|
|
|
|
### Phase B0 — 安全基线 ✅ 完成
|
|
|
|
|
|
|
|
|
|
|
|
> B0-1 + B0-2 + B0-3 由 subagent B0-SecurityFix 完成(commit: `f3d102e7a`)
|
2026-04-25 02:28:04 +00:00
|
|
|
|
|
|
|
|
|
|
**目标**:修复 M-06、M-02、M-05、M-03,建立 B端安全防线。
|
|
|
|
|
|
|
|
|
|
|
|
#### B0-1:Admin.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-2:verifier_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-3:ALTER TABLE bug 修复(M-03)
|
|
|
|
|
|
|
|
|
|
|
|
EventListener.php 中 `empty($result)` 对 PDOStatement 永远返回 false,ALTER TABLE 永不执行。
|
|
|
|
|
|
|
|
|
|
|
|
**文件**:`shopxo/app/plugins/vr_ticket/Event.php`(或 EventListener.php)
|
|
|
|
|
|
|
|
|
|
|
|
**修复**:改用 `count($result) === 0` 或 `empty($result->fetchAll())`。
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
2026-04-25 02:41:18 +00:00
|
|
|
|
### Phase B1 — 核心 B端核销页 ✅ 完成
|
|
|
|
|
|
|
|
|
|
|
|
> B1-1 + B1-2 + B1-3 由 subagent B1-CorePages 完成(commit: `d8c45fbb8`)
|
2026-04-25 02:28:04 +00:00
|
|
|
|
|
|
|
|
|
|
**目标**:建一个可用的扫码核销页面,优先满足现场核销需求。
|
|
|
|
|
|
|
|
|
|
|
|
#### B1-1:admin/view/ticket/verify.html(扫码核销主页面)
|
|
|
|
|
|
|
|
|
|
|
|
**功能**:
|
|
|
|
|
|
- 扫码按钮(HTML5 MediaDevices API,兼容 PC 摄像头)
|
|
|
|
|
|
- 手动输入短码/票码文本框
|
|
|
|
|
|
- 调用 `PluginsAdminUrl('vr_ticket', 'admin', 'TicketVerify')` API
|
|
|
|
|
|
- 展示核销结果(座位号、观演人姓名、商品名)
|
|
|
|
|
|
- 统计栏:今日核销数 / 待核销数 / 已核销总数
|
|
|
|
|
|
|
|
|
|
|
|
**注意**:需要先在 Hook.php 的 `AdminSidebarInit` 注册菜单项 `ticketVerify`。
|
|
|
|
|
|
|
|
|
|
|
|
#### B1-2:admin/view/ticket/list.html(电子票列表)
|
|
|
|
|
|
|
|
|
|
|
|
- 搜索:订单号/票码/观演人姓名/手机号
|
|
|
|
|
|
- 状态筛选:全部/未核销/已核销/已退款
|
|
|
|
|
|
- 商品筛选
|
|
|
|
|
|
- 每行显示:票码、观演人、座位信息、商品名、状态、时间
|
|
|
|
|
|
- 操作:查看详情
|
|
|
|
|
|
|
|
|
|
|
|
#### B1-3:admin/view/ticket/detail.html(票详情)
|
|
|
|
|
|
|
|
|
|
|
|
- 显示票完整信息(座位/观演人/QR码/发放时间)
|
|
|
|
|
|
- 显示核销记录(如已核销)
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
2026-04-25 02:41:18 +00:00
|
|
|
|
### Phase B2 — 辅助管理页 ✅ 完成
|
|
|
|
|
|
|
|
|
|
|
|
> B2-1 + B2-2 + B2-3 由 subagent B2-SupportPages 完成(commit: `a104f16f0`)
|
2026-04-25 02:28:04 +00:00
|
|
|
|
|
|
|
|
|
|
#### 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 注释**:确保所有 `/* ... */` 闭合,避免语法错误
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
2026-04-25 05:15:03 +00:00
|
|
|
|
## 五、验收标准(代码层)
|
|
|
|
|
|
|
|
|
|
|
|
- [x] M-06:无 session('admin_id') 无法调用任何 Admin.php 接口(审阅通过)
|
|
|
|
|
|
- [x] M-05:verifier_id 来自 session,不可伪造(审阅通过)
|
|
|
|
|
|
- [x] M-03:ALTER TABLE bug 修复(审阅通过)
|
|
|
|
|
|
- [x] TicketStats API 存在且权限校验正确(审阅通过)
|
|
|
|
|
|
- [x] B1-1:verify.html 有 stats AJAX + loadStats() + 扫码+核销功能(代码审阅通过,含修复)
|
|
|
|
|
|
- [x] B1-2:list.html 搜索/筛选/分页/操作按钮(审阅通过)
|
|
|
|
|
|
- [x] B1-3:detail.html 基础信息+条形码+核销记录(审阅通过,标签已修)
|
|
|
|
|
|
- [x] B2-1:verifier/list.html + save.html(审阅通过)
|
|
|
|
|
|
- [x] B2-2:verification/list.html(审阅通过)
|
|
|
|
|
|
- [x] B2-3:seat_template/list.html + save.html(审阅通过)
|
|
|
|
|
|
- [x] 所有页面 header/footer 完整(审阅通过)
|
|
|
|
|
|
|
|
|
|
|
|
> ⚠️ 以上均为代码层审阅通过。实际功能需在 ShopXO 后台部署后测试验证。(待实际测试)
|
2026-04-25 02:28:04 +00:00
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
2026-04-25 02:41:18 +00:00
|
|
|
|
## 六、待确认 & 待测试
|
|
|
|
|
|
|
|
|
|
|
|
1. **ShopXO 后台 session key**:需确认 `session('admin_user_info.id')` 是正确的 ShopXO 后台登录用户 ID key。
|
|
|
|
|
|
2. **Hook.php 菜单注册**:`ticket/verify.html` 已注册扫码核销菜单项,需在 ShopXO 后台确认菜单显示。
|
|
|
|
|
|
3. **TicketStats API**:`verify.html` 引用了 `PluginsAdminUrl('vr_ticket', 'admin', 'TicketStats')` 获取统计数据,该方法尚未在 Admin.php 中实现,需补充。
|
|
|
|
|
|
4. **QR 码展示**:`detail.html` 需确认 QR 码图片 URL 路径(`TicketService::getQrCodeUrl` 是否可被 admin 模板调用)。
|
2026-04-25 02:28:04 +00:00
|
|
|
|
|
2026-04-25 02:41:18 +00:00
|
|
|
|
## 七、Commit 记录
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
f3d102e7a feat(B0): M-06/M-05/M-03 security fixes
|
|
|
|
|
|
d8c45fbb8 feat(B1): ticket/verify + list + detail admin views
|
|
|
|
|
|
a104f16f0 feat(B2): verifier + verification + seat_template admin views
|
|
|
|
|
|
```
|