diff --git a/README.md b/README.md index 27abbeb..3adc5a5 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,7 @@ |------|------| | **[docs/VR_GOODS_CONFIG_SPEC.md](docs/VR_GOODS_CONFIG_SPEC.md)** | ⚠️ **vr_goods_config JSON 格式 v3.0 完整规格**(商品配置核心) | | **[docs/PHASE2_PLAN.md](docs/PHASE2_PLAN.md)** | Phase 2 当前状态 + 下一步工作计划 | +| **[docs/PHASE_B_2026-04-25_PLAN.md](docs/PHASE_B_2026-04-25_PLAN.md)** | 🔴 **Phase B:B端核销开发计划**(安全基线 + 核心核销页 + 辅助管理页) | | **[docs/EXPERIENCES.md](docs/EXPERIENCES.md)** | ⚠️ **踩坑经验(必读)** — 18条核心教训 | | **[docs/DEVELOPMENT_LOG.md](docs/DEVELOPMENT_LOG.md)** | 开发日志(完整变更记录) | @@ -95,11 +96,10 @@ | 阶段 | 状态 | 说明 | |------|------|------| | Phase 1 | ✅ 完成 | 商品详情页座位图 + 观演人表单 + 模板渲染 | -| **Phase 2** | ⚠️ 进行中 | Issue #13:vr_goods_config v3.0 落地 + Task 1: C端票夹 ✅ | -| Phase 3 | ⏳ 开发中 | B端核销 API + 页面 | -| Phase 4 | 🔜 下一步 | 后台 4 控制器联调 | +| Phase 2 | ✅ 完成 | vr_goods_config v3.0 + C端票夹 + 出票链路 | +| **Phase 3** | 🔜 下一步 | B端核销开发(docs/PHASE_B_2026-04-25_PLAN.md)| -**Phase 2 当前工作**:[Issue #13](http://xmhome.gitop.top:3000/sileya-ai/vr-shopxo-plugin/issues/13) — vr_goods_config v3.0 落地实现 +**当前主线**:[docs/PHASE_B_2026-04-25_PLAN.md](docs/PHASE_B_2026-04-25_PLAN.md) — B端核销 + 安全修复(Council 执行中) --- diff --git a/docs/PHASE_B_2026-04-25_PLAN.md b/docs/PHASE_B_2026-04-25_PLAN.md new file mode 100644 index 0000000..1e6fa32 --- /dev/null +++ b/docs/PHASE_B_2026-04-25_PLAN.md @@ -0,0 +1,170 @@ +# Phase B:B端核销开发计划 + +> 创建时间:2026-04-25 10:26 CST +> 创建人:西莉雅 +> 状态:规划中,待 Council 执行 + +--- + +## 一、背景 + +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 二次写入 → ✅ 已修复 + +--- + +## 二、分阶段计划 + +### Phase B0 — 安全基线(动手前必须完成) + +**目标**:修复 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())`。 + +--- + +### Phase B1 — 核心 B端核销页 + +**目标**:建一个可用的扫码核销页面,优先满足现场核销需求。 + +#### 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码/发放时间) +- 显示核销记录(如已核销) + +--- + +### 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**:禁止 ``,用隐藏 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-1:PC 摄像头扫码可成功核销,显示票信息 +- [ ] 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 的扫码入口**:是在"电子票列表"页加一个"扫码核销"按钮,还是独立菜单项?