# vr-shopxo-plugin Issue #9 — 架构决策评议计划 > 版本:v1.0 | 日期:2026-04-15 | Agent:council/FrontendDev(主笔) ## 任务背景 Phase 0/1/2 已完成骨架,但暴露了 P0 架构问题:当前商品 112 的 spec_base 表为空(broken state),spec_base_id_map 中的 ID 在 DB 不存在,ShopXO 原生防超卖机制完全未启用。需要评议方案 A vs B 并给出明确推荐。 --- ## 核心问题(4问) ### Q1: 方案 A 的后台批量生成 SKU 路径是否可行? - 具体实现方式?ShopXO 是否有批量创建 SKU 的 API/代码路径? - 前端(uni-app)如何配合? ### Q2: 当前商品 112 的 broken 状态需要立即修复吗?最小修复集是什么? - `is_exist_many_spec=0` + `spec_base` 空的根因是什么? - 最小修复:不改 spec_base 表,仅改 `is_exist_many_spec` flag?还是重建 SKU? ### Q3: $vr- 前缀方案是否有隐患? - ShopXO 内部逻辑是否对带 `$` 的 spec name 做特殊处理? - spec_value 的 name/label 字段是否允许 `$` 字符? ### Q4: 方案 A vs 方案 B 的最终推荐 - 考虑:实现成本、安全性(防超卖)、可维护性、多 Zone 混买体验 --- ## 阶段划分 | 阶段 | 内容 | 负责 | |------|------|------| | **Round 1**(本轮)| 分析 4 个问题,建立共识框架,输出推荐 | FrontendDev + BackendArchitect + SecurityEngineer 独立输出 | | **Round 2** | 交叉评审其他成员的分析,补充或反驳 | 所有成员 | | **Round 3** | 最终报告合并,输出 `council-output/ARCHITECTURE_DECISION.md` | FrontendDev 主笔 | --- ## 任务清单 - [ ] **Task FD-1**: FrontendDev — 分析 Q1-Q4,输出推荐(plan.md 本文件) - [ ] **Task FD-2**: FrontendDev — 评审 BackendArchitect / SecurityEngineer 的分析 - [ ] **Task FD-3**: FrontendDev — 撰写最终报告 `council-output/ARCHITECTURE_DECISION.md` - [ ] **Task FD-4**: FrontendDev — 更新 plan.md 和 ARCHITECTURE.md,合并到 main --- ## 依赖关系 - BackendArchitect 输出后端视角(ShopXO spec_base 机制、批量生成可能性) - SecurityEngineer 输出安全视角($vr- 前缀风险、防超卖方案安全性) - FrontendDev 输出前端视角(多 Zone 混买 UX、$vr- 前端处理) - 三方分析完成后,合并为最终报告 --- ## 行动项(FrontendDev Round 1 输出) ### Q1 分析:方案 A 批量生成 SKU 路径 **结论:可行,但实现路径复杂。** 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 管理"页面 ### Q2 分析:商品 112 broken state 最小修复集 **结论:需要立即修复,推荐最小方案。** 根因:`is_exist_many_spec=0` 意味着 ShopXO 认为此商品无多规格,spec_base 表自然为空(从未生成过 SKU)。 最小修复路径(不破坏现有数据): 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 读取) **推荐方案甲**(最小修复): - 优势:无需重建 SKU,不影响现有订单数据 - 代价:`is_exist_many_spec` 变成"脏 flag",但这是 ShopXO 的内部状态,插件不依赖它做业务 - 操作:一条 UPDATE + 一条 Hook 注入 ### Q3 分析:$vr- 前缀隐患 **结论:低风险,但需实测确认。** ShopXO spec name 字段无字符过滤,数据库 `varchar` 类型允许 `$` 字符。潜在风险点: - ThinkPHP 的 `__isset()` / 动态属性访问可能对 `$` 敏感(但 spec name 存 DB 而非 PHP 属性,低风险) - 前端模板渲染时,`$vr-` 字符串可能触发 Vue/JS 的变量插值解析(`{{ $vr-场馆 }}`)—— **这是真实风险** - ShopXO 原生规格管理页面可能将 `$` 视为特殊字符处理 **需要验证**:uni-app 端 spec value 的渲染方式(是纯文本还是模板字符串?) ### Q4 最终推荐:方案 A vs 方案 B **推荐:方案 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,反而增加了前端分组逻辑的复杂度(与我们"多 Zone 混买前端分组"的初衷不符) 4. **维护性**:方案 A 依赖 ShopXO 原生机制,故障排查有据可查;方案 B 是"黑盒",出问题只能靠插件自己 5. **$vr- 前缀**:spec_base_id_map 的 key 可以是 seat_id,无需改 ShopXO spec name 存储 **方案 B 的唯一优势**:SKU 数量少(Zone 数量 vs 座位数量),后台管理简单。但这个优势在演唱会 10000 座场景下不如安全和一致性重要。 --- ## 行动项(优先级排序) 1. **【P0】紧急修复商品 112 broken state**:Hook 注入 `is_exist_many_spec=1`,使插件能正常识别票务商品(推荐方案甲,最小侵入) 2. **【P1】实现方案 A 批量 SKU 生成**:在 `SeatTemplateService::BindToGoods()` 中,座位模板绑定商品时,批量 INSERT spec_base 记录(inventory=1, price 从 seat_type 读取) 3. **【P2】隔离 ShopXO 规格管理页面**:Hook 隐藏票务商品的原生规格列表,建立独立座位 SKU 管理视图 --- ## 共识投票 [CONSENSUS: NO] — 本轮仅完成分析,执行待后续阶段 --- *Round 1 完成,输出存档。等待 BackendArchitect 和 SecurityEngineer 的分析结果。*