vr-shopxo-plugin/plan.md

12 KiB
Raw Blame History

vr-shopxo-plugin 架构决策评议 — plan.md

版本v1.0 | 制定日期2026-04-15 | Agentcouncil/BackendArchitect 关联Issue #9


任务背景

Phase 0/1/2 已完成基础骨架,暴露了一个 P0 架构问题VR 演唱会票务商品中 ShopXO SPEC 与 SKU 的绑定方案。

已知事实:

  • ShopXO goods_spec_baseSKU表当前为空商品 112 的 is_exist_many_spec=0
  • spec_base_id_map 中的 ID如 1001/1002/1003在 DB 中不存在
  • ShopXO 防超卖机制(原子扣 inventory完全未启用

两种架构方向:

  • 方案 A:每个座位 = 一个 SKUstock=1ShopXO 原生防超卖
  • 方案 B:每个 Zone = 一个 SKUstock=Zone座位数自建 FOR UPDATE 防超卖

阶段划分

阶段 内容 负责
Round 1 独立评议 + plan.md 合并 所有成员
Round 2 各成员深入分析(后台实现路径、安全评估、前端方案) 所有成员
Round 3 综合推荐 + 输出最终决策报告 所有成员

Agent 任务分配

Agent 主要评议方向
BackendArchitect Q1Plan A 后台批量 SKU 生成可行性)+ Q4最终推荐
SecurityEngineer Q3$vr- 前缀安全风险)+ Q4安全性维度
FrontendDev 前端方案 A/B 的实现差异 + Q4前端实现成本

任务清单

Q1 — Plan A 后台批量生成 SKU 路径评估 [Pending: BackendArchitect]

  • 分析 ShopXO spec_base 表写入路径
  • 确认是否需要修改 ShopXO 核心代码还是通过插件可完成
  • 评估批量生成的性能(上万座位场景)
  • 给出可行性结论

Q2 — 商品 112 broken 状态紧急修复 [Pending: BackendArchitect]

  • 评估 is_exist_many_spec=0 + spec_base 空的实际影响
  • 确定最小修复集(是否需要立即修复)
  • 制定修复方案

Q3 — $vr- 前缀安全评估 [Pending: SecurityEngineer]

  • 检查 ShopXO 对带 $ 字符 spec name 的处理逻辑
  • 识别潜在冲突或注入风险
  • 给出安全结论

Q4 — 方案 A vs 方案 B 最终推荐 [Pending: all]

  • BackendArchitect从实现成本、ShopXO 原生机制利用角度评议
  • SecurityEngineer从防超卖安全性角度评议
  • FrontendDev从前端复杂度角度评议

最终输出

  • council-output/ARCHITECTURE_DECISION.md — 汇总三方推荐 + 最终结论Round 3

依赖关系

  • Q1BackendArchitect先完成后 Q4 才能给出完整推荐
  • Q3SecurityEngineer可与 Q1 并行
  • Q2 可独立完成,紧急程度由 BackendArchitect 判定

Claim 状态

任务 Claim 状态
Q1 [Pending: BackendArchitect]
Q2 [Pending: BackendArchitect]
Q3 [Pending: SecurityEngineer]
Q4 [Pending: all]
最终输出 [Pending: all]

本轮Round 1初判BackendArchitect

Q1 初步判断Plan A 后台批量生成 SKU 可行。ShopXO 的 goods_spec_base 是标准 MySQL 表,插件可直接 INSERT。但需要确认

  • ShopXO 商品保存时是否校验 spec_base 的 referential integrity
  • 上万座位时批量 INSERT 的性能
  • spec_base_id_map 中的 ID 是否需要与 ShopXO 内部 ID 对齐

Q2 初步判断:当前 broken 状态暂不需要立即修复。购买流程走的是裸商品逻辑is_exist_many_spec=0对 Phase 3 的购买流程设计反而是参考点——需要明确购买流程最终走哪条路后再修。

Q4 初步判断:倾向 方案 A。ShopXO 原生防超卖机制比自建锁更可靠DB 层面原子操作),且不破坏 ShopXO 生态完整性。


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 执行