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

5.9 KiB
Raw Blame History

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 等)开头加:

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 用户IDvr_verifiers 表获取 verifier_id。

// 错误(可伪造):
$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) === 0empty($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/jsdelivrcdn.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 的扫码入口:是在"电子票列表"页加一个"扫码核销"按钮,还是独立菜单项?