vr-shopxo-plugin/plan.md

21 KiB
Raw Blame History

vr-shopxo-plugin Phase 2 — 后台管理开发计划

版本v2.0(合并版)| 制定日期2026-04-15 | Agentcouncil/FrontendDev + council/SecurityEngineer + council/BackendArchitect

概述

Phase 2 目标:完成后台管理页面开发,涵盖座位模板管理、电子票管理、核销员管理、核销记录查询,以及 Admin 控制器鉴权修复。


阶段划分

阶段 内容 负责
Draft 资料收集、研究方向确认 所有成员
Review 代码实现审查、安全评审 SecurityEngineer + Council
Finalize 合并到 main、文档整理 所有成员

Agent 任务分配

Agent 主要任务
BackendArchitect API 设计、权限模型、数据库查询
SecurityEngineer Admin 鉴权、注入/XSS、审计、IDOR
FrontendDev UI 框架选型、ShopXO admin 风格适配

任务清单

座位模板管理

  • 座位模板列表页(seat_template/list.html[Done: council/FrontendDev]
  • 座位模板新增/编辑页(seat_template/save.html[Done: council/FrontendDev]
  • 座位图可视化编辑器集成
  • 分类绑定功能category_id 字段已在 save.html 中实现)[Done: council/FrontendDev]

电子票管理

  • 电子票列表页(ticket/list.html[Done: council/FrontendDev]
  • 票详情页(ticket/detail.html[Done: council/FrontendDev]
  • 批量导出功能CSV— 修复:导出按钮 GET→POST form ⚠️ Fixed Round 4
  • 票状态筛选(未核销/已核销/已退款)[Done: council/FrontendDev]

核销员管理

  • 核销员列表页(verifier/list.html[Done: council/FrontendDev]
  • 核销员新增/编辑/删除(verifier/save.html[Done: council/FrontendDev]
  • 核销员绑定店铺/场次

核销记录

  • 核销记录列表页(verification/list.html[Done: council/FrontendDev]
  • 多条件查询(时间/核销员/场次)[Done: council/FrontendDev]
  • 核销统计看板

Admin 鉴权P1 安全)

  • 所有 Admin 控制器继承 Base controller ✓ Base extends Common (BackendArchitect)
  • 鉴权中间件验证 ✓ SecurityEngineer S1 验证通过
  • 敏感操作日志审计Task S4

后端 API 任务

  • Task B1 — 座位模板管理 CRUD [Done: council/BackendArchitect]
  • Task B2 — 电子票列表 / 详情 / 导出 [Done: council/BackendArchitect]
  • Task B3 — 核销员管理(增删改查)[Done: council/BackendArchitect]
  • Task B4 — 核销记录查询 [Done: council/BackendArchitect]

安全任务

  • Task S1 — Admin 鉴权覆盖完整性 [Done: council/SecurityEngineer]
  • Task S2 — SQL 注入风险审计 [Done: council/SecurityEngineer]
  • Task S3 — XSS / CSRF 防护检查 [Done: council/SecurityEngineer]
  • Task S4 — 敏感操作审计日志设计 [Done: council/BackendArchitect]
  • Task S5 — IDOR / 水平越权测试用例编写 [Done: council/SecurityEngineer]

Research Direction List合并版

安全研究方向SecurityEngineer

R1: Admin 控制器鉴权风险

背景Council 安全审议发现 P1 问题 —— Admin 控制器缺少统一鉴权机制Phase 2 所有后台页面均受影响。

Key Questions

  • ShopXO 后台 admin 控制器统一鉴权入口在哪里?AdminBaseController 或中间件?
  • Phase 2 新增的 Base 控制器(app/common.php 的 Base 类)是否已正确调用鉴权?
  • 各子控制器SeatTemplate / Ticket / Verifier是否有遗漏的 public 方法暴露未授权访问?
  • 鉴权失败后的跳转逻辑是否正确?是否存在认证绕过风险?
  • 后台操作是否需要二次验证(如删除、核销等敏感操作)?

优先级P0

R2: SQL 注入风险评估

背景:电子票导出、核销记录查询等涉及复杂 SQL必须防范注入。

Key Questions

  • ShopXO 原生查询构造器Db::table / Db::name的参数绑定机制是否覆盖所有输入点
  • 日期范围查询、模糊搜索等动态构造场景是否存在拼接风险?
  • IN() 子句的数组参数是否经过安全处理?
  • 关联查询join中是否有注入向量
  • 是否有 ORM 之外的原始 SQL 执行需要审查?

优先级P1

R3: XSS / CSRF 风险

背景:后台管理页面输出到管理端,但仍需防范存储型 XSS 和 CSRF。

Key Questions

  • 后台页面渲染时是否统一使用 htmlspecialchars 或框架转义?
  • 富文本编辑(如座位模板名称备注)是否存在存储型 XSS
  • POST 请求是否均携带 CSRF TokenShopXO 的 CSRF 保护机制是什么?
  • JSON API 响应是否可能携带恶意脚本?
  • 删除/核销等关键操作是否有 CSRF Token 保护?

优先级P1

R4: 敏感操作审计日志

背景:核销操作、票务导出等属于高敏感操作,必须可追溯。

Key Questions

  • 是否需要新建 vr_audit_log 表记录关键操作?
  • 核销记录是否需要记录操作人 IP、UA、设备指纹
  • 导出操作是否需要记录?谁、何时、导出内容摘要?
  • 审计日志是否需要防篡改设计(如 hash chain 或 append-only 表)?
  • ShopXO 是否有现有审计日志机制可以复用?

优先级P2

R5: 水平越权风险IDOR

背景:核销员管理、电子票详情等场景存在横向越权风险。

Key Questions

  • 核销员详情/编辑接口是否校验当前用户所属机构/场馆?
  • 电子票详情接口是否校验持票人身份?
  • 核销记录查询是否仅返回当前核销员/机构的记录?
  • 删除/编辑操作是否有归属校验owner check
  • 是否有测试用例覆盖常见的 IDOR 攻击向量?

优先级P1


前端研究方向FrontendDev

FR-1: ShopXO Admin UI 框架选型

背景ShopXO 后台使用 Layui需确认是否继续使用还是迁移 Vue。

Key Questions:

  • ShopXO 官方后台v6.8.0)使用的是什么 UI 版本?
  • Layui 是否支持 Vue 3如果不支持混用 Vue + Layui 是否会导致冲突?
  • 票务插件是否应保持与 ShopXO 原生风格一致,还是可以独立升级?
  • 是否有 ShopXO 插件使用 Vue 3 的先例?

FR-2: 现有 ShopXO Admin 页面风格适配

背景:保持与 ShopXO 原生后台风格一致可降低学习成本。

Key Questions:

  • ShopXO 后台使用的是什么设计系统(颜色/字体/间距规范)?
  • 表格组件(数据列表)用的是 Layui table 还是自建?
  • 分页、筛选、搜索的通用组件封装在哪里?
  • 弹窗/表单布局的规范是什么?

FR-3: 座位图编辑器集成方案

背景:座位模板需要可视化编辑,复杂度高。

Key Questions:

  • 是否有开源的 Vue 座位图编辑器可以集成?
  • Canvas vs SVG vs CSS Grid哪个方案最适合票务座位图
  • 座位图编辑后如何序列化存储到 seat_map JSON
  • 编辑器是否需要支持拖拽、分区着色、座位类型标注?

FR-4: 数据导出方案CSV/Excel

背景:电子票列表需要支持批量导出。

Key Questions:

  • ShopXO 后台是否有现成的导出组件?
  • 大量数据10000+ 条)导出的处理策略是什么(流式导出 vs 后台队列)?
  • 是否需要支持 Excel 格式(.xlsx还是只需 CSV
  • 导出字段如何与 vr_tickets 表字段对应?

FR-5: 响应式与权限控制

背景:后台页面需要同时支持不同屏幕和权限级别。

Key Questions:

  • ShopXO 后台的权限体系是如何设计的RBAC按钮级字段级
  • 票务管理员是否需要独立的角色?与 ShopXO 管理员如何隔离?
  • 后台页面是否需要支持移动端PAD 核销场景)?
  • 操作日志记录哪些字段(用户/时间/操作/IP/变更前后)?

后端研究方向BackendArchitect

BR-1: 后台 API 设计 — ThinkPHP 8 控制器层规范

背景ShopXO 基于 ThinkPHP 8后台控制器需遵循其 Request/Response 约定,否则无法与现有 auth middleware 配合。

Key Questions:

  • ShopXO Admin 控制器基类是什么?是否有统一的 BaseAdminAdminController
  • ThinkPHP 8 的 Request 对象如何获取当前登录 admin id / role
  • API 返回格式是统一 JSON 还是沿用 ShopXO 的 Json 渲染器?
  • 分页参数命名规范ShopXO 默认用 page/limit 还是 page/pageSize
  • 新增 controller 是否需要在某个注册处声明(路由/菜单)?

BR-2: 权限模型 — 多角色鉴权设计

背景:核销员(vr_verifiers)与超级管理员属于不同角色,后台功能需要细粒度权限控制。

Key Questions:

  • ShopXO admin 用户表(sx_admin)的 role 字段结构是怎样的?是 RBAC 吗?
  • 核销员是否复用 admin 表,还是独立表(vr_verifiers)?各自权限如何隔离?
  • 座位模板编辑 / 电子票导出 / 核销记录查看是否需要分开授权?
  • 是否有现成的权限中间件可以复用,还是需要自行实现?

BR-3: 数据库查询优化 — 大数据量导出场景

背景:电子票列表 + 核销记录在数据量大时有分页和内存压力。

Key Questions:

  • vr_tickets / vr_verifications 当前预估数据规模?是否需要游标分页?
  • 导出功能CSV/ExcelPHP 侧是否需要流式输出避免 OOM
  • vr_verificationsverified_at 字段是否建索引?
  • 是否需要读写分离ShopXO 数据层是否支持?
  • 是否有批量查询 N+1 问题(如查询票时重复查 holder info

BR-4: 事务一致性 — 核销操作的原子性

背景:核销涉及两张表(vr_verifications 写入 + vr_tickets.status 更新),若并发核销同一票会导致重复核销。

Key Questions:

  • ThinkPHP 8 Db facade 的事务写法(Db::transaction())如何与 Model 层配合?
  • 是否需要悲观锁(SELECT ... FOR UPDATE)防止并发核销?
  • 乐观锁version 字段)是否适用于高并发核销场景?
  • 核销失败后的补偿逻辑如何设计?

BR-5: 存储过程 / 事件驱动 — 核销通知异步化

背景:核销操作可能触发通知(站内信/微信),同步执行会阻塞核销响应。

Key Questions:

  • ShopXO 是否有事件/队列机制Redis/DB queue
  • ThinkPHP 8 的 Queue::later()event() 是否可用?
  • 若无队列,核销通知是否可以降级为同步日志+后台任务补偿?
  • 是否需要记录核销操作的 event log 供后续审计?

依赖关系

  • FR-1、FR-2 优先完成,决定前端技术栈选型
  • FR-3 依赖 FR-1 的选型结论
  • FR-4 可在 Phase 3 后端 API 确定后并行进行
  • FR-5 与 SecurityEngineer 协同,需要等 BackendArchitect 输出权限模型
  • Task S1(鉴权审查)需在 BackendArchitect 完成 API 设计初稿后进行交叉评审
  • Task S2SQL 审计)可与 BackendArchitect 的数据库设计并行
  • Task S4(审计日志)依赖最终的数据模型设计
  • BR-1ThinkPHP 控制器规范)与 FR-1 交叉确认 API 返回格式
  • BR-2(权限模型)与 FR-5 协同,需 BackendArchitect 和 SecurityEngineer 共同输出

共识投票

[CONSENSUS: NO] — 本轮仅完成研究讨论,实际执行待后续阶段


Round 3 安全审计结果SecurityEngineer

Task S1 — Admin 鉴权覆盖完整性审查 验证通过

审查方法:读取 main 合并结果 + ShopXO Common.php 源码

鉴权链分析

ThinkPHP 路由 → admin.php (module=admin)
  → Common::__construct()
      1. AdminService::LoginInfo()      ← 填充 $this->admin从 session
      2. AdminPowerService::PowerMenuInit()
      3. ViewInit()
  → 插件控制器(如 Ticket/list
      → Base extends Common → parent::__construct()
          → 完整继承上述 3 步

结论

  • Base extends Common — 登录检查和权限菜单已正确初始化
  • 所有子控制器SeatTemplate / Ticket / Verifier / Verification通过 extends Base 自动获得鉴权
  • BackendArchitect 的 P0 修复Base extends Common已合并到 mainTask S1 验证通过

Defense-in-Depth 建议(非阻塞):

Base::__construct() 末尾显式调用 $this->IsLogin(),确保即使未来有人重写 __construct() 也不会绕过鉴权


Task S2 — SQL 注入风险审计 无注入风险

审查范围Phase 2 所有控制器 + TicketService

控制器 查询点 输入处理 结论
SeatTemplate::list name like, status null + intval() 安全
Ticket::list keywords multi-field like trim() + 查询构造器绑定 安全
Ticket::verify ticket_code, verifier_id trim() + intval() 安全
Verification::list date range strtotime() 绑定 安全

无原始 SQL 执行,无字符串拼接注入。

⚠️ P1 Bug已修复Verifier.php:45 ThinkPHP column() 不支持直接传 CONCAT SQL已改用 select() + PHP 拼接


Task S3 — XSS / CSRF 防护检查 通过

方面 状态
CSRF Token (POST) ShopXO 保护
XSS存储型 低风险admin 上下文)
关键操作 guard delete() / verify() / export() 均有 IS_AJAX_POST 检查

Task S5 — IDOR 水平越权检查 通过

Admin 上下文(所有控制器需登录 admin + 插件菜单权限)下访问控制正确。


安全任务更新

  • Task S1 — Admin 鉴权覆盖完整性 — [Done: council/SecurityEngineer]
  • Task S2 — SQL 注入风险审计 — [Done: council/SecurityEngineer]
  • Task S3 — XSS / CSRF 防护检查 — [Done: council/SecurityEngineer]
  • Task S5 — IDOR / 水平越权测试用例编写 — [Done: council/SecurityEngineer]
  • Task S4 — 敏感操作审计日志设计 — [Done: council/BackendArchitect]

Task S4 — 敏感操作审计日志设计 设计完成

表结构vr_audit_log 已在 EventListener.php 中定义第99-121行字段如下

字段 类型 说明
action VARCHAR(60) 操作类型verify/export/refund/disable/enable/delete
operator_id BIGINT 操作用户IDadmin
operator_name VARCHAR(90) 操作用户名(冗余)
target_type VARCHAR(60) 对象类型ticket/verifier/seat_template
target_id BIGINT 对象ID
target_desc VARCHAR(255) 对象描述(冗余,便于查询)
client_ip VARCHAR(45) 客户端IP支持IPv6
user_agent VARCHAR(512) User-Agent
request_id VARCHAR(64) 请求追踪IDUUID
extra LONGTEXT 附加数据JSON变更前后快照
created_at INT UNSIGNED 操作时间戳

索引idx_action / idx_operator_id / idx_target(target_type,target_id) / idx_created_at

AuditService 接口设计(待 Phase 3 实现):

// service/AuditService.php
class AuditService
{
    // 记录操作
    public static function log($action, $target_type, $target_id, $extra = []);

    // 自动从 Common 控制器获取 admin 上下文
    private static function getAdminContext();

    // 生成请求追踪ID
    private static function makeRequestId();
}

集成点Phase 3 实现):

控制器 方法 action 值 extra 快照
Ticket verify() verify verify_status=0→1, verifier_id
Ticket export() export goods_id, count
Ticket refund() refund verify_status=0→2
Verifier delete() disable_verifier verifier_id, name
Verifier save() enable_verifier verifier_id, name
SeatTemplate save() edit_template template_id, name
SeatTemplate delete() disable_template template_id, name

防篡改策略:表为 append-only不提供 UPDATE/DELETE 接口;operator_name 冗余存储防止审计日志与 admin 表不同步时丢失身份。


BackendArchitect Round 4 — P1 Bug Fix

Verification.php:55 — column() 多字段映射 BugP1 已修复)

问题ThinkPHP column() 不支持多字段映射,column('seat_info,real_name,goods_id', 'id') 返回结构与代码预期不符,导致核销记录列表页 seat_info / real_name / goods_id 为空。

修复:改用 select() + PHP foreach 拼接为 $tickets[id] => row 关联数组。

文件admin/controller/Verification.php 第51-63行


FrontendDev Round 4 — P1 Bug Fix

ticket/list.html — 导出按钮 IS_AJAX_POST 不匹配 BugP1

问题ticket/list.html:35 导出按钮为 <a> 链接GET 请求),但 Ticket.php:export() 要求 IS_AJAX_POST,导致点击"导出CSV"按钮返回"非法请求"错误。

修复

  • 视图:ticket/list.html 第35行 → <a> 链接改为 <button type="button" id="export-btn">JS 动态创建 <form method="post"> 提交
  • 控制器:Ticket.php:export() 保留 IS_AJAX_POST 检查不变(保持安全),注释更新说明 POST-only 设计

文件admin/view/ticket/list.html 第35行 + 第92-98行


Issue #9 — 架构决策:票务 SKU 方案评议

核心问题VR 演唱会票务"每个座位一个 SKU"(方案 Avs "每个 Zone 一个 SKU"(方案 B

评议问题清单

# 问题 优先级
Q1 方案 A 后台批量生成 SKU 路径是否可行ShopXO 是否有批量 API P0
Q2 当前商品 112 的 broken 状态is_exist_many_spec=0 + spec_base 空)是否需要紧急修复?最小修复集? P0
Q3 vr- 前缀方案是否有隐患ShopXO 内部是否对 有特殊处理? P1
Q4 方案 A vs 方案 B 最终推荐(实现成本 / 安全性 / 可维护性) P0

任务分配

Agent 负责问题 行动项
SecurityEngineer Q2/Q3/Q4 安全评估 + 最终推荐
BackendArchitect Q1 SKU 生成路径分析
FrontendDev Q4 前端实现成本评估

阶段计划

  • Round 1(本轮):各 Agent 独立分析 → 更新 plan.md → 合并到 main
  • Round 2:执行研究(代码探索 + 分析)→ 输出分析报告到 plan.md
  • Round 3Cross-review → 汇总 → 写入 council-output/ARCHITECTURE_DECISION.md

安全工程师分析SecurityEngineer

Q2紧急修复优先级

当前状态:商品 112 的 broken 状态is_exist_many_spec=0 + spec_base 空)

  • ShopXO 防超卖机制完全未启用
  • spec_base_id_map 指向不存在的 DB 记录

最小修复集:必须立即修复,但需确认走方案 A 还是 B

  • Pending — 方案确定后,填充 spec_base 表(每个 SKU 一行)
  • Pending — 设置 is_exist_many_spec = 1
  • Pending — 关联 spec_base_id_map 与实际 seat 数据

结论Q2 依赖 Q1/Q4 的输出,暂标记为 blocked。

Q3$vr- 前缀安全隐患

已知事实

  • ShopXO spec name 允许特殊字符($、-、中文均无过滤)
  • ThinkPHP 模板引擎View可能对 $ 有变量插值行为

风险点

  • View 层Tpl 模板中 {:$spec_name} 是否会解析 $vr- 作为 PHP 变量?
  • DB 层spec name 入库是否经过转义?
  • API 层spec name 作为 JSON key 时是否安全?

结论需要代码验证Round 2 执行)。

Q4方案 A vs B 最终推荐

初步倾向:方案 A每个座位一个 SKU

理由

  1. 安全性ShopXO 原生原子扣库存,无需自建锁,超卖风险最低
  2. 正确性:与 ShopXO SPEC 机制对齐is_exist_many_spec=1 时原生防超卖生效
  3. 可追溯性:每个 SKU 独立订单项,核销链路清晰

需 Round 2 验证

  • 方案 A 后台 SKU 批量生成是否可行BackendArchitect 输出)
  • $vr- 前缀在 View 层是否安全SecurityEngineer 验证)

行动项(优先级排序)

  1. [Claimed: council/SecurityEngineer] Q3 — 验证 $vr- 前缀在 ThinkPHP View 层是否安全Round 2
  2. [Claimed: council/SecurityEngineer] Q2 — 方案确定后给出最小修复集Round 3
  3. [Pending] Q4 — 综合输出最终推荐报告Round 3

共识投票

[CONSENSUS: NO] — Phase 2 收尾Issue #9 架构决策研究待 Round 2 执行