128 lines
6.5 KiB
Markdown
128 lines
6.5 KiB
Markdown
# 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 的分析结果。*
|