diff --git a/plan.md b/plan.md
index 8092e6f..3531147 100644
--- a/plan.md
+++ b/plan.md
@@ -1,6 +1,6 @@
# vr-shopxo-plugin 架构决策评议 — plan.md
-> 版本:v1.0 | 制定日期:2026-04-15 | Agent:council/BackendArchitect
+> 版本:v1.1(合并版)| 日期:2026-04-15 | Agent:council/FrontendDev + BackendArchitect + SecurityEngineer
> 关联:Issue #9
---
@@ -20,59 +20,34 @@ Phase 0/1/2 已完成基础骨架,暴露了一个 P0 架构问题:VR 演唱
---
+## 核心问题(4问)
+
+| # | 问题 | 负责 |
+|---|------|------|
+| Q1 | 方案 A 后台批量生成 SKU 路径是否可行?ShopXO 是否有批量 API? | BackendArchitect |
+| Q2 | 当前商品 112 的 broken 状态(is_exist_many_spec=0 + spec_base 空)是否需要紧急修复?最小修复集? | BackendArchitect + SecurityEngineer |
+| Q3 | $vr- 前缀方案是否有隐患?ShopXO 内部是否对 $ 有特殊处理? | SecurityEngineer |
+| Q4 | 方案 A vs 方案 B 最终推荐(实现成本 / 安全性 / 可维护性) | 所有成员 |
+
+---
+
## 阶段划分
| 阶段 | 内容 | 负责 |
|------|------|------|
-| Round 1 | 独立评议 + plan.md 合并 | 所有成员 |
+| Round 1(本轮)| 独立评议 + plan.md 合并 | 所有成员 |
| Round 2 | 各成员深入分析(后台实现路径、安全评估、前端方案) | 所有成员 |
-| Round 3 | 综合推荐 + 输出最终决策报告 | 所有成员 |
-
----
-
-## Agent 任务分配
-
-| Agent | 主要评议方向 |
-|-------|------------|
-| BackendArchitect | Q1(Plan A 后台批量 SKU 生成可行性)+ Q4(最终推荐) |
-| SecurityEngineer | Q3($vr- 前缀安全风险)+ Q4(安全性维度) |
-| FrontendDev | 前端方案 A/B 的实现差异 + Q4(前端实现成本) |
+| Round 3 | 综合推荐 + 输出最终决策报告 + `council-output/ARCHITECTURE_DECISION.md` | FrontendDev 主笔 |
---
## 任务清单
-### 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)
-
----
-
-## 依赖关系
-
-- Q1(BackendArchitect)先完成,后 Q4 才能给出完整推荐
-- Q3(SecurityEngineer)可与 Q1 并行
-- Q2 可独立完成,紧急程度由 BackendArchitect 判定
+- [ ] **Q1**: 方案 A 批量生成 SKU 路径 `[Pending: BackendArchitect]`
+- [ ] **Q2**: 商品 112 broken 状态紧急修复 `[Pending: BackendArchitect + SecurityEngineer]`
+- [ ] **Q3**: $vr- 前缀安全评估 `[Pending: SecurityEngineer]`
+- [ ] **Q4**: 方案 A vs 方案 B 最终推荐 `[Pending: all]`
+- [ ] **Final**: `council-output/ARCHITECTURE_DECISION.md` — 汇总三方推荐 + 最终结论
---
@@ -81,14 +56,25 @@ Phase 0/1/2 已完成基础骨架,暴露了一个 P0 架构问题:VR 演唱
| 任务 | Claim 状态 |
|------|-----------|
| Q1 | [Pending: BackendArchitect] |
-| Q2 | [Pending: BackendArchitect] |
+| Q2 | [Pending: BackendArchitect + SecurityEngineer] |
| Q3 | [Pending: SecurityEngineer] |
| Q4 | [Pending: all] |
| 最终输出 | [Pending: all] |
---
-## 本轮(Round 1)初判(BackendArchitect)
+## 依赖关系
+
+- Q1(BackendArchitect)先完成,后 Q4 才能给出完整推荐
+- Q3(SecurityEngineer)可与 Q1 并行
+- Q2 可独立完成,紧急程度由 BackendArchitect 判定
+- 三方分析完成后,FrontendDev 主笔 Round 3 最终报告
+
+---
+
+## 各成员 Round 1 初判
+
+### BackendArchitect 初判
**Q1 初步判断**:Plan A 后台批量生成 SKU **可行**。ShopXO 的 `goods_spec_base` 是标准 MySQL 表,插件可直接 INSERT。但需要确认:
- ShopXO 商品保存时是否校验 spec_base 的 referential integrity
@@ -99,162 +85,64 @@ Phase 0/1/2 已完成基础骨架,暴露了一个 P0 架构问题:VR 演唱
**Q4 初步判断**:倾向 **方案 A**。ShopXO 原生防超卖机制比自建锁更可靠(DB 层面原子操作),且不破坏 ShopXO 生态完整性。
----
+### FrontendDev 初判(Q1-Q4 分析)
-### Task S2 — SQL 注入风险审计 ✅ 无注入风险
+**Q1 分析:方案 A 批量生成 SKU 路径**
-**审查范围**: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()` 绑定 | ✅ 安全 |
+ShopXO spec_base 生成机制:
+- 商品保存时,`GoodsService::Save()` 调用 `SpecService::Save()` 逐条写入 `sxo_goods_spec_base`
+- **没有现成的批量 API** — 需要在插件初始化/商品绑定时,批量调用 `SpecService` 或直接 SQL INSERT
+- 方案 A 的 SKU 数量 = 座位数(一场演唱会可能 10000+ 个座位)
+- **前端配合**:uni-app 需要维护 `seat_id → spec_base_id` 映射(已在 `spec_base_id_map` 中)
+- **关键风险**:商品规格管理页面会显示 10000+ 行 SKU,可能导致 ShopXO 后台崩溃
+- **解决方向**:插件专用规格不出现在 ShopXO 原生规格管理页,通过 Hook 隐藏;建立独立的"座位 SKU 管理"页面
-**无原始 SQL 执行,无字符串拼接注入。**
+**Q2 分析:商品 112 broken state 最小修复集**
-⚠️ **P1 Bug(已修复)**:`Verifier.php:45` ThinkPHP `column()` 不支持直接传 CONCAT SQL,已改用 `select()` + PHP 拼接
+结论:**需要立即修复,推荐最小方案。**
----
+根因:`is_exist_many_spec=0` 意味着 ShopXO 认为此商品无多规格,spec_base 表自然为空(从未生成过 SKU)。
-### Task S3 — XSS / CSRF 防护检查 ✅ 通过
+最小修复路径(不破坏现有数据):
+1. 方案甲(最小侵入):在 `plugins_service_goods_save_end` Hook 中,检测商品有 `venue_data` 且 `$vr-` spec 存在时,强制将 `is_exist_many_spec` 设为 1,但不写 spec_base 表(绕过 ShopXO spec 机制,完全走插件自定义逻辑)
+2. 方案乙(规范做法):调用 `SpecService::Save()` 为每个座位生成一条 spec_base 记录(inventory=1, price 从 seat_type 读取)
-| 方面 | 状态 |
-|------|------|
-| CSRF Token (POST) | ✅ ShopXO 保护 |
-| XSS(存储型) | ✅ 低风险(admin 上下文) |
-| 关键操作 guard | ✅ `delete()` / `verify()` / `export()` 均有 `IS_AJAX_POST` 检查 |
+**推荐方案甲**(最小修复):
+- 优势:无需重建 SKU,不影响现有订单数据
+- 代价:`is_exist_many_spec` 变成"脏 flag",但这是 ShopXO 的内部状态,插件不依赖它做业务
+- 操作:一条 UPDATE + 一条 Hook 注入
----
+**Q3 分析:$vr- 前缀隐患**
-### Task S5 — IDOR 水平越权检查 ✅ 通过
+结论:**低风险,但需实测确认。**
-Admin 上下文(所有控制器需登录 admin + 插件菜单权限)下访问控制正确。
+ShopXO spec name 字段无字符过滤,数据库 `varchar` 类型允许 `$` 字符。潜在风险点:
+- ThinkPHP 的 `__isset()` / 动态属性访问可能对 `$` 敏感(但 spec name 存 DB 而非 PHP 属性,低风险)
+- 前端模板渲染时,`$vr-` 字符串可能触发 Vue/JS 的变量插值解析(`{{ $vr-场馆 }}`)—— **这是真实风险**
+- ShopXO 原生规格管理页面可能将 `$` 视为特殊字符处理
----
+**需要验证**:uni-app 端 spec value 的渲染方式(是纯文本还是模板字符串?)
-### 安全任务更新
+**Q4 最终推荐:方案 A vs 方案 B**
-- [x] **Task S1** — Admin 鉴权覆盖完整性 — `[Done: council/SecurityEngineer]`
-- [x] **Task S2** — SQL 注入风险审计 — `[Done: council/SecurityEngineer]`
-- [x] **Task S3** — XSS / CSRF 防护检查 — `[Done: council/SecurityEngineer]`
-- [x] **Task S5** — IDOR / 水平越权测试用例编写 — `[Done: council/SecurityEngineer]`
-- [x] **Task S4** — 敏感操作审计日志设计 — `[Done: council/BackendArchitect]`
+**推荐:方案 A(每个座位一个 SPEC/SKU)**
----
+理由:
+1. **安全性**:ShopXO 原生原子扣库存防超卖,经过大量生产验证;方案 B 的自建 FOR UPDATE 锁在高并发下有死锁风险
+2. **数据一致性**:方案 A 的 stock = 1,ShopXO 购买流程自带事务保护;方案 B 的 Zone stock 需要插件自己维护一致性和并发安全
+3. **多 Zone 混买**:方案 A 前端每 Zone 一个 goods_params 行,后端按 seat_id 原子购买,体验流畅;方案 B 前端分组但后端共享 Zone stock,反而增加了前端分组逻辑的复杂度
+4. **维护性**:方案 A 依赖 ShopXO 原生机制,故障排查有据可查;方案 B 是"黑盒",出问题只能靠插件自己
+5. **$vr- 前缀**:spec_base_id_map 的 key 可以是 seat_id,无需改 ShopXO spec name 存储
-## Task S4 — 敏感操作审计日志设计 ✅ 设计完成
+**方案 B 的唯一优势**:SKU 数量少(Zone 数量 vs 座位数量),后台管理简单。但这个优势在演唱会 10000 座场景下不如安全和一致性重要。
-**表结构**:`vr_audit_log` 已在 `EventListener.php` 中定义(第99-121行),字段如下:
+### SecurityEngineer 初判(Q2/Q3/Q4)
-| 字段 | 类型 | 说明 |
-|------|------|------|
-| `action` | VARCHAR(60) | 操作类型:verify/export/refund/disable/enable/delete |
-| `operator_id` | BIGINT | 操作用户ID(admin) |
-| `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) | 请求追踪ID(UUID) |
-| `extra` | LONGTEXT | 附加数据JSON(变更前后快照) |
-| `created_at` | INT UNSIGNED | 操作时间戳 |
+**Q2:紧急修复优先级**
-**索引**:`idx_action` / `idx_operator_id` / `idx_target(target_type,target_id)` / `idx_created_at`
-
-**AuditService 接口设计**(待 Phase 3 实现):
-
-```php
-// 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()` 多字段映射 Bug(P1 已修复)
-
-**问题**:`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 不匹配 Bug(P1)
-
-**问题**:`ticket/list.html:35` 导出按钮为 `` 链接(GET 请求),但 `Ticket.php:export()` 要求 `IS_AJAX_POST`,导致点击"导出CSV"按钮返回"非法请求"错误。
-
-**修复**:
-- 视图:`ticket/list.html` 第35行 → `` 链接改为 `