6.5 KiB
6.5 KiB
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_specflag?还是重建 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)。
最小修复路径(不破坏现有数据):
- 方案甲(最小侵入):在
plugins_service_goods_save_endHook 中,检测商品有venue_data且$vr-spec 存在时,强制将is_exist_many_spec设为 1,但不写 spec_base 表(绕过 ShopXO spec 机制,完全走插件自定义逻辑) - 方案乙(规范做法):调用
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)
理由:
- 安全性:ShopXO 原生原子扣库存防超卖,经过大量生产验证;方案 B 的自建 FOR UPDATE 锁在高并发下有死锁风险
- 数据一致性:方案 A 的 stock = 1,ShopXO 购买流程自带事务保护;方案 B 的 Zone stock 需要插件自己维护一致性和并发安全
- 多 Zone 混买:方案 A 前端每 Zone 一个 goods_params 行,后端按 seat_id 原子购买,体验流畅;方案 B 前端分组但后端共享 Zone stock,反而增加了前端分组逻辑的复杂度(与我们"多 Zone 混买前端分组"的初衷不符)
- 维护性:方案 A 依赖 ShopXO 原生机制,故障排查有据可查;方案 B 是"黑盒",出问题只能靠插件自己
- $vr- 前缀:spec_base_id_map 的 key 可以是 seat_id,无需改 ShopXO spec name 存储
方案 B 的唯一优势:SKU 数量少(Zone 数量 vs 座位数量),后台管理简单。但这个优势在演唱会 10000 座场景下不如安全和一致性重要。
行动项(优先级排序)
- 【P0】紧急修复商品 112 broken state:Hook 注入
is_exist_many_spec=1,使插件能正常识别票务商品(推荐方案甲,最小侵入) - 【P1】实现方案 A 批量 SKU 生成:在
SeatTemplateService::BindToGoods()中,座位模板绑定商品时,批量 INSERT spec_base 记录(inventory=1, price 从 seat_type 读取) - 【P2】隔离 ShopXO 规格管理页面:Hook 隐藏票务商品的原生规格列表,建立独立座位 SKU 管理视图
共识投票
[CONSENSUS: NO] — 本轮仅完成分析,执行待后续阶段
Round 1 完成,输出存档。等待 BackendArchitect 和 SecurityEngineer 的分析结果。