2026-04-15 11:14:44 +00:00
|
|
|
|
# vr-shopxo-plugin 架构决策评议 — plan.md
|
2026-04-15 05:53:39 +00:00
|
|
|
|
|
2026-04-15 11:16:49 +00:00
|
|
|
|
> 版本:v1.1(合并版)| 日期:2026-04-15 | Agent:council/FrontendDev + BackendArchitect + SecurityEngineer
|
2026-04-15 11:14:44 +00:00
|
|
|
|
> 关联:Issue #9
|
2026-04-15 06:11:43 +00:00
|
|
|
|
|
2026-04-15 11:14:44 +00:00
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 任务背景
|
|
|
|
|
|
|
|
|
|
|
|
Phase 0/1/2 已完成基础骨架,暴露了一个 P0 架构问题:VR 演唱会票务商品中 ShopXO SPEC 与 SKU 的绑定方案。
|
2026-04-15 05:53:39 +00:00
|
|
|
|
|
2026-04-15 11:14:44 +00:00
|
|
|
|
**已知事实:**
|
|
|
|
|
|
- ShopXO `goods_spec_base`(SKU表)当前为空,商品 112 的 `is_exist_many_spec=0`
|
|
|
|
|
|
- `spec_base_id_map` 中的 ID(如 1001/1002/1003)在 DB 中不存在
|
|
|
|
|
|
- ShopXO 防超卖机制(原子扣 inventory)完全未启用
|
|
|
|
|
|
|
|
|
|
|
|
**两种架构方向:**
|
|
|
|
|
|
- **方案 A**:每个座位 = 一个 SKU(stock=1),ShopXO 原生防超卖
|
|
|
|
|
|
- **方案 B**:每个 Zone = 一个 SKU(stock=Zone座位数),自建 FOR UPDATE 防超卖
|
2026-04-15 05:53:39 +00:00
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
2026-04-15 11:16:49 +00:00
|
|
|
|
## 核心问题(4问)
|
2026-04-15 06:11:43 +00:00
|
|
|
|
|
2026-04-15 11:16:49 +00:00
|
|
|
|
| # | 问题 | 负责 |
|
|
|
|
|
|
|---|------|------|
|
|
|
|
|
|
| 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 最终推荐(实现成本 / 安全性 / 可维护性) | 所有成员 |
|
2026-04-15 06:11:43 +00:00
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
2026-04-15 11:15:12 +00:00
|
|
|
|
## 阶段划分
|
2026-04-15 06:11:43 +00:00
|
|
|
|
|
2026-04-15 11:15:12 +00:00
|
|
|
|
| 阶段 | 内容 | 负责 |
|
|
|
|
|
|
|------|------|------|
|
2026-04-15 11:16:49 +00:00
|
|
|
|
| Round 1(本轮)| 独立评议 + plan.md 合并 | 所有成员 |
|
|
|
|
|
|
| Round 2 | 各成员深入分析(后台实现路径、安全评估、前端方案) | 所有成员 |
|
|
|
|
|
|
| Round 3 | 综合推荐 + 输出最终决策报告 + `council-output/ARCHITECTURE_DECISION.md` | FrontendDev 主笔 |
|
2026-04-15 06:11:43 +00:00
|
|
|
|
|
|
|
|
|
|
---
|
2026-04-15 05:53:39 +00:00
|
|
|
|
|
2026-04-15 06:11:43 +00:00
|
|
|
|
## 任务清单
|
2026-04-15 05:53:39 +00:00
|
|
|
|
|
2026-04-15 11:20:22 +00:00
|
|
|
|
- [x] **Q1**: 方案 A 批量生成 SKU 路径 `[Done: BackendArchitect]` ✅
|
|
|
|
|
|
- [x] **Q2**: 商品 112 broken 状态紧急修复 `[Done: BackendArchitect]` ✅
|
2026-04-15 11:21:15 +00:00
|
|
|
|
- [x] **Q3**: $vr- 前缀安全评估 `[Done: SecurityEngineer]` ✅
|
|
|
|
|
|
- [x] **Q4**: 方案 A vs 方案 B 最终推荐 `[Done: FrontendDev]`
|
2026-04-15 11:16:49 +00:00
|
|
|
|
- [ ] **Final**: `council-output/ARCHITECTURE_DECISION.md` — 汇总三方推荐 + 最终结论
|
2026-04-15 06:11:43 +00:00
|
|
|
|
|
2026-04-15 11:16:49 +00:00
|
|
|
|
---
|
2026-04-15 06:11:43 +00:00
|
|
|
|
|
2026-04-15 11:16:49 +00:00
|
|
|
|
## Claim 状态
|
2026-04-15 06:11:43 +00:00
|
|
|
|
|
2026-04-15 11:16:49 +00:00
|
|
|
|
| 任务 | Claim 状态 |
|
|
|
|
|
|
|------|-----------|
|
2026-04-15 11:20:22 +00:00
|
|
|
|
| Q1 | [Done: BackendArchitect] |
|
|
|
|
|
|
| Q2 | [Done: BackendArchitect] |
|
2026-04-15 11:21:15 +00:00
|
|
|
|
| Q3 | [Done: SecurityEngineer] |
|
|
|
|
|
|
| Q4 | [Done: FrontendDev] |
|
2026-04-15 11:16:49 +00:00
|
|
|
|
| 最终输出 | [Pending: all] |
|
2026-04-15 05:53:39 +00:00
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 依赖关系
|
|
|
|
|
|
|
2026-04-15 11:14:44 +00:00
|
|
|
|
- Q1(BackendArchitect)先完成,后 Q4 才能给出完整推荐
|
|
|
|
|
|
- Q3(SecurityEngineer)可与 Q1 并行
|
|
|
|
|
|
- Q2 可独立完成,紧急程度由 BackendArchitect 判定
|
2026-04-15 11:16:49 +00:00
|
|
|
|
- 三方分析完成后,FrontendDev 主笔 Round 3 最终报告
|
2026-04-15 05:53:39 +00:00
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
2026-04-15 11:16:49 +00:00
|
|
|
|
## 各成员 Round 1 初判
|
2026-04-15 05:53:39 +00:00
|
|
|
|
|
2026-04-15 11:16:49 +00:00
|
|
|
|
### BackendArchitect 初判
|
2026-04-15 06:11:43 +00:00
|
|
|
|
|
2026-04-15 11:14:44 +00:00
|
|
|
|
**Q1 初步判断**:Plan A 后台批量生成 SKU **可行**。ShopXO 的 `goods_spec_base` 是标准 MySQL 表,插件可直接 INSERT。但需要确认:
|
|
|
|
|
|
- ShopXO 商品保存时是否校验 spec_base 的 referential integrity
|
|
|
|
|
|
- 上万座位时批量 INSERT 的性能
|
|
|
|
|
|
- spec_base_id_map 中的 ID 是否需要与 ShopXO 内部 ID 对齐
|
2026-04-15 06:11:43 +00:00
|
|
|
|
|
2026-04-15 11:14:44 +00:00
|
|
|
|
**Q2 初步判断**:当前 broken 状态**暂不需要立即修复**。购买流程走的是裸商品逻辑(is_exist_many_spec=0),对 Phase 3 的购买流程设计反而是参考点——需要明确购买流程最终走哪条路后再修。
|
2026-04-15 06:11:43 +00:00
|
|
|
|
|
2026-04-15 11:14:44 +00:00
|
|
|
|
**Q4 初步判断**:倾向 **方案 A**。ShopXO 原生防超卖机制比自建锁更可靠(DB 层面原子操作),且不破坏 ShopXO 生态完整性。
|
2026-04-15 06:00:20 +00:00
|
|
|
|
|
2026-04-15 11:16:49 +00:00
|
|
|
|
### FrontendDev 初判(Q1-Q4 分析)
|
2026-04-15 06:00:20 +00:00
|
|
|
|
|
2026-04-15 11:16:49 +00:00
|
|
|
|
**Q1 分析:方案 A 批量生成 SKU 路径**
|
2026-04-15 06:00:20 +00:00
|
|
|
|
|
2026-04-15 11:16:49 +00:00
|
|
|
|
结论:**可行,但实现路径复杂。**
|
2026-04-15 06:11:43 +00:00
|
|
|
|
|
2026-04-15 11:15:12 +00:00
|
|
|
|
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 管理"页面
|
2026-04-15 06:20:03 +00:00
|
|
|
|
|
2026-04-15 11:16:49 +00:00
|
|
|
|
**Q2 分析:商品 112 broken state 最小修复集**
|
2026-04-15 06:20:03 +00:00
|
|
|
|
|
2026-04-15 11:16:49 +00:00
|
|
|
|
结论:**需要立即修复,推荐最小方案。**
|
2026-04-15 06:20:03 +00:00
|
|
|
|
|
2026-04-15 11:15:12 +00:00
|
|
|
|
根因:`is_exist_many_spec=0` 意味着 ShopXO 认为此商品无多规格,spec_base 表自然为空(从未生成过 SKU)。
|
2026-04-15 06:20:03 +00:00
|
|
|
|
|
2026-04-15 11:15:12 +00:00
|
|
|
|
最小修复路径(不破坏现有数据):
|
|
|
|
|
|
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 读取)
|
2026-04-15 06:20:03 +00:00
|
|
|
|
|
2026-04-15 11:15:12 +00:00
|
|
|
|
**推荐方案甲**(最小修复):
|
|
|
|
|
|
- 优势:无需重建 SKU,不影响现有订单数据
|
|
|
|
|
|
- 代价:`is_exist_many_spec` 变成"脏 flag",但这是 ShopXO 的内部状态,插件不依赖它做业务
|
|
|
|
|
|
- 操作:一条 UPDATE + 一条 Hook 注入
|
2026-04-15 06:20:03 +00:00
|
|
|
|
|
2026-04-15 11:16:49 +00:00
|
|
|
|
**Q3 分析:$vr- 前缀隐患**
|
2026-04-15 06:20:03 +00:00
|
|
|
|
|
2026-04-15 11:16:49 +00:00
|
|
|
|
结论:**低风险,但需实测确认。**
|
2026-04-15 06:20:03 +00:00
|
|
|
|
|
2026-04-15 11:15:12 +00:00
|
|
|
|
ShopXO spec name 字段无字符过滤,数据库 `varchar` 类型允许 `$` 字符。潜在风险点:
|
|
|
|
|
|
- ThinkPHP 的 `__isset()` / 动态属性访问可能对 `$` 敏感(但 spec name 存 DB 而非 PHP 属性,低风险)
|
|
|
|
|
|
- 前端模板渲染时,`$vr-` 字符串可能触发 Vue/JS 的变量插值解析(`{{ $vr-场馆 }}`)—— **这是真实风险**
|
|
|
|
|
|
- ShopXO 原生规格管理页面可能将 `$` 视为特殊字符处理
|
2026-04-15 06:20:03 +00:00
|
|
|
|
|
2026-04-15 11:15:12 +00:00
|
|
|
|
**需要验证**:uni-app 端 spec value 的渲染方式(是纯文本还是模板字符串?)
|
2026-04-15 06:20:03 +00:00
|
|
|
|
|
2026-04-15 11:16:49 +00:00
|
|
|
|
**Q4 最终推荐:方案 A vs 方案 B**
|
2026-04-15 06:20:03 +00:00
|
|
|
|
|
2026-04-15 11:15:12 +00:00
|
|
|
|
**推荐:方案 A(每个座位一个 SPEC/SKU)**
|
2026-04-15 06:20:03 +00:00
|
|
|
|
|
2026-04-15 11:15:12 +00:00
|
|
|
|
理由:
|
|
|
|
|
|
1. **安全性**:ShopXO 原生原子扣库存防超卖,经过大量生产验证;方案 B 的自建 FOR UPDATE 锁在高并发下有死锁风险
|
|
|
|
|
|
2. **数据一致性**:方案 A 的 stock = 1,ShopXO 购买流程自带事务保护;方案 B 的 Zone stock 需要插件自己维护一致性和并发安全
|
2026-04-15 11:16:49 +00:00
|
|
|
|
3. **多 Zone 混买**:方案 A 前端每 Zone 一个 goods_params 行,后端按 seat_id 原子购买,体验流畅;方案 B 前端分组但后端共享 Zone stock,反而增加了前端分组逻辑的复杂度
|
2026-04-15 11:15:12 +00:00
|
|
|
|
4. **维护性**:方案 A 依赖 ShopXO 原生机制,故障排查有据可查;方案 B 是"黑盒",出问题只能靠插件自己
|
|
|
|
|
|
5. **$vr- 前缀**:spec_base_id_map 的 key 可以是 seat_id,无需改 ShopXO spec name 存储
|
2026-04-15 06:20:03 +00:00
|
|
|
|
|
2026-04-15 11:15:12 +00:00
|
|
|
|
**方案 B 的唯一优势**:SKU 数量少(Zone 数量 vs 座位数量),后台管理简单。但这个优势在演唱会 10000 座场景下不如安全和一致性重要。
|
2026-04-15 06:20:03 +00:00
|
|
|
|
|
2026-04-15 11:16:49 +00:00
|
|
|
|
### SecurityEngineer 初判(Q2/Q3/Q4)
|
2026-04-15 06:00:20 +00:00
|
|
|
|
|
2026-04-15 11:16:49 +00:00
|
|
|
|
**Q2:紧急修复优先级**
|
2026-04-15 11:14:39 +00:00
|
|
|
|
|
2026-04-15 11:16:49 +00:00
|
|
|
|
当前状态:商品 112 的 broken 状态(is_exist_many_spec=0 + spec_base 空)
|
2026-04-15 11:14:39 +00:00
|
|
|
|
- ShopXO 防超卖机制完全未启用
|
|
|
|
|
|
- spec_base_id_map 指向不存在的 DB 记录
|
|
|
|
|
|
|
|
|
|
|
|
**最小修复集**:必须立即修复,但需确认走方案 A 还是 B
|
|
|
|
|
|
- [ ] **Pending** — 方案确定后,填充 spec_base 表(每个 SKU 一行)
|
|
|
|
|
|
- [ ] **Pending** — 设置 is_exist_many_spec = 1
|
|
|
|
|
|
- [ ] **Pending** — 关联 spec_base_id_map 与实际 seat 数据
|
|
|
|
|
|
|
2026-04-15 11:16:49 +00:00
|
|
|
|
结论:Q2 依赖 Q1/Q4 的输出,暂标记为 blocked。
|
2026-04-15 11:14:39 +00:00
|
|
|
|
|
2026-04-15 11:16:49 +00:00
|
|
|
|
**Q3:$vr- 前缀安全隐患**
|
2026-04-15 11:14:39 +00:00
|
|
|
|
|
2026-04-15 11:16:49 +00:00
|
|
|
|
已知事实:
|
2026-04-15 11:14:39 +00:00
|
|
|
|
- ShopXO spec name 允许特殊字符($、-、中文均无过滤)
|
|
|
|
|
|
- ThinkPHP 模板引擎(View)可能对 $ 有变量插值行为
|
|
|
|
|
|
|
2026-04-15 11:16:49 +00:00
|
|
|
|
风险点:
|
2026-04-15 11:14:39 +00:00
|
|
|
|
- [ ] View 层:Tpl 模板中 `{:$spec_name}` 是否会解析 $vr- 作为 PHP 变量?
|
|
|
|
|
|
- [ ] DB 层:spec name 入库是否经过转义?
|
|
|
|
|
|
- [ ] API 层:spec name 作为 JSON key 时是否安全?
|
|
|
|
|
|
|
2026-04-15 11:16:49 +00:00
|
|
|
|
结论:需要代码验证(Round 2 执行)。
|
2026-04-15 11:14:39 +00:00
|
|
|
|
|
2026-04-15 11:16:49 +00:00
|
|
|
|
**Q4:方案 A vs B 最终推荐**
|
2026-04-15 11:14:39 +00:00
|
|
|
|
|
|
|
|
|
|
**初步倾向**:方案 A(每个座位一个 SKU)
|
|
|
|
|
|
|
2026-04-15 11:16:49 +00:00
|
|
|
|
理由:
|
2026-04-15 11:14:39 +00:00
|
|
|
|
1. 安全性:ShopXO 原生原子扣库存,无需自建锁,超卖风险最低
|
|
|
|
|
|
2. 正确性:与 ShopXO SPEC 机制对齐,is_exist_many_spec=1 时原生防超卖生效
|
|
|
|
|
|
3. 可追溯性:每个 SKU 独立订单项,核销链路清晰
|
|
|
|
|
|
|
2026-04-15 06:11:43 +00:00
|
|
|
|
---
|
2026-04-15 11:14:39 +00:00
|
|
|
|
|
2026-04-15 11:20:22 +00:00
|
|
|
|
### BackendArchitect Round 2 深入分析(Q1+Q2)
|
|
|
|
|
|
|
|
|
|
|
|
详细分析见 `docs/ROUND2_ANALYSIS.md`。核心结论:
|
|
|
|
|
|
|
|
|
|
|
|
**Q1 结论:可行,但必须旁路 `GoodsSpecificationsInsert()`**
|
|
|
|
|
|
|
|
|
|
|
|
- ShopXO 的 `GoodsSpecificationsInsert()` 在每次商品保存时 `DELETE` 所有现有 spec 后重建,10K+ 座位场景不可用
|
|
|
|
|
|
- 可行路径:**直接 SQL INSERT** 到 `sxo_goods_spec_type`、`sxo_goods_spec_base`、`sxo_goods_spec_value` 三表
|
|
|
|
|
|
- 关键代码:`BuyService.php:1677-1681` 的 `dec()` 机制 = MySQL 条件原子扣减 `UPDATE SET inventory = inventory - N WHERE inventory >= N`,ShopXO 防超卖依赖此机制
|
|
|
|
|
|
- TOCTOU 窗口极小(选座模式并发低 + InnoDB 行锁),**推荐接受此风险**
|
|
|
|
|
|
- 性能:10000 座位 = ~3-4 秒(需分批 500 条/批提交)
|
|
|
|
|
|
|
|
|
|
|
|
**Q2 结论:推荐方案乙(最小修复集)**
|
|
|
|
|
|
|
|
|
|
|
|
- `is_exist_many_spec=0` → 执行 `UPDATE goods SET is_exist_many_spec=1 WHERE id=112`
|
|
|
|
|
|
- 写入 `$vr-` 规格维度到 `sxo_goods_spec_type`
|
|
|
|
|
|
- 幂等保护:票生成逻辑已有 `spec_base_id` 冗余,不依赖 DB 引用
|
|
|
|
|
|
|
|
|
|
|
|
**Q4 初步推荐:方案 A**
|
|
|
|
|
|
|
|
|
|
|
|
- 原子性已验证(BuyService dec 机制)
|
|
|
|
|
|
- 数据完整性高(每个座位 inventory=1)
|
|
|
|
|
|
- 票务链路清晰(spec_base_id → 座位直接映射)
|
|
|
|
|
|
- 方案 B 的"SKU 少"优势在演唱会 10K+ 场景不成立(插件自管,不走 ShopXO 后台)
|
|
|
|
|
|
|
2026-04-15 11:21:15 +00:00
|
|
|
|
### SecurityEngineer Round 2 分析(Q3)
|
2026-04-15 11:20:22 +00:00
|
|
|
|
|
2026-04-15 11:21:15 +00:00
|
|
|
|
> SecurityEngineer 在 Round 2 进行了 ThinkPHP View 层的 $vr- 前缀安全审计,结论:无高危风险。
|
2026-04-15 11:20:22 +00:00
|
|
|
|
|
2026-04-15 11:21:15 +00:00
|
|
|
|
### FrontendDev Round 2 深入分析
|
2026-04-15 11:20:22 +00:00
|
|
|
|
|
2026-04-15 11:21:15 +00:00
|
|
|
|
### Q4 最终推荐:方案 A(每个座位一个 SPEC/SKU)—— 明确推荐
|
|
|
|
|
|
|
|
|
|
|
|
**经过代码级验证后,确认推荐方案 A。**
|
|
|
|
|
|
|
|
|
|
|
|
#### 核心发现
|
|
|
|
|
|
|
|
|
|
|
|
**发现 1:当前 ticket_detail.html 的 submit() 是 Plan B 模式**
|
|
|
|
|
|
|
|
|
|
|
|
检查 `shopxo/app/plugins/vr_ticket/view/goods/ticket_detail.html` 第 413-418 行:
|
|
|
|
|
|
```javascript
|
|
|
|
|
|
var goodsParams = JSON.stringify([{
|
|
|
|
|
|
goods_id: this.goodsId,
|
|
|
|
|
|
spec_base_id: this.sessionSpecId, // ← session spec_id,不是 seat spec_base_id
|
|
|
|
|
|
stock: this.selectedSeats.length, // ← seat count,不是 1
|
|
|
|
|
|
extension_data: extensionData
|
|
|
|
|
|
}]);
|
|
|
|
|
|
```
|
|
|
|
|
|
→ 当前实现是 **Plan B**(按场次买多个座位),`specBaseIdMap` 已声明但**未接入** submit 逻辑。
|
|
|
|
|
|
|
|
|
|
|
|
**发现 2:BuyService 依赖 spec_base 原子扣库存**
|
|
|
|
|
|
|
|
|
|
|
|
`BuyService.php` 第 113-115 行:
|
|
|
|
|
|
```php
|
|
|
|
|
|
$goods['price'] = (float) $goods_base['data']['spec_base']['price'];
|
|
|
|
|
|
$goods['original_price'] = (float) $goods_base['data']['spec_base']['original_price'];
|
|
|
|
|
|
$goods['spec_base_id'] = $goods_base['data']['spec_base']['id'];
|
|
|
|
|
|
```
|
|
|
|
|
|
→ ShopXO 购买流程从 `spec_base` 表读取库存并原子扣减。`spec_base` 为空时购买走裸商品逻辑(inventory 字段在 goods 表)。
|
|
|
|
|
|
|
|
|
|
|
|
#### 方案 A vs B 最终对比
|
|
|
|
|
|
|
|
|
|
|
|
| 维度 | 方案 A(每座=SKU) | 方案 B(每 Zone=SKU) |
|
|
|
|
|
|
|------|-------------------|---------------------|
|
|
|
|
|
|
| **防超卖** | ShopXO 原生原子扣库存(stock=1),DB 层保证 | 自建 FOR UPDATE 锁,需自己写并发逻辑 |
|
|
|
|
|
|
| **实现复杂度** | 后端需批量生成 1 万+ SKU;前端 `submit()` 需改为逐座提交 | 后端简单;前端按 Zone 分组即可 |
|
|
|
|
|
|
| **多 Zone 混买** | 每座一行 goods_params,后端原子处理 | 前端分组但后端共享 Zone 库存,复杂度高 |
|
|
|
|
|
|
| **后台可维护性** | 10000+ SKU 行,但可 Hook 隐藏 | Zone 数量少,后台友好 |
|
|
|
|
|
|
| **调试/故障排查** | 每个 SKU 独立,可追溯 | 共享库存,出问题难以定位 |
|
|
|
|
|
|
| **与 ShopXO 生态** | 完全对齐,无缝集成 | 绕过 spec 校验,部分 ShopXO 功能失效 |
|
|
|
|
|
|
| **当前代码适配成本** | `ticket_detail.html` submit() 需重构 | 基本无需改动 |
|
|
|
|
|
|
|
|
|
|
|
|
#### Plan A 前端实现路径(ticket_detail.html)
|
|
|
|
|
|
|
|
|
|
|
|
关键修改:将 `submit()` 从"session-level 提交"改为"seat-level 逐座提交":
|
|
|
|
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
|
|
// Plan A: 每座一行 goods_params,逐座购买
|
|
|
|
|
|
this.selectedSeats.forEach(function(seat) {
|
|
|
|
|
|
var seatSpecBaseId = app.specBaseIdMap[seat.row + '_' + seat.col]?.spec_base_id;
|
|
|
|
|
|
// 如果 spec_base_id 存在,走 ShopXO 原生购买
|
|
|
|
|
|
// 否则走 Plan B 回退逻辑
|
|
|
|
|
|
});
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
`specBaseIdMap` 数据结构已就位(从后端 PHP 注入),前端只需接入即可。
|
|
|
|
|
|
|
|
|
|
|
|
### Q3 验证:$vr- 前缀安全
|
|
|
|
|
|
|
|
|
|
|
|
**结论:低风险,确认安全。**
|
|
|
|
|
|
|
|
|
|
|
|
证据:
|
|
|
|
|
|
1. `ticket_detail.html` 使用 ThinkPHP 模板 `{:$goods.title|default='...'}` —— `$goods` 是 PHP 变量,不是模板表达式
|
|
|
|
|
|
2. `$vr_seat_template.seat_map` 是 PHP 对象访问,`|json_encode|raw` 是模板过滤器链,`|raw` 仅用于跳过 HTML 转义,不触发变量插值
|
|
|
|
|
|
3. ThinkPHP `{$var}` 默认转义输出;`{:$expr}` 执行表达式但需要 `$var` 存在
|
|
|
|
|
|
4. `$vr-` 作为 spec name 字符串存储在 DB 中,不作为 PHP 变量名
|
|
|
|
|
|
|
|
|
|
|
|
**唯一需注意**:ShopXO 后台规格管理页可能将 `$vr-` 显示不当(纯展示问题,不影响安全)。
|
|
|
|
|
|
|
|
|
|
|
|
### Q2 前端视角最小修复
|
|
|
|
|
|
|
|
|
|
|
|
当前 `ticket_detail.html` 的 `loadSoldSeats()` 是 TODO,Plan A 需要:
|
|
|
|
|
|
1. 后端生成 spec_base SKUs(BackendArchitect 负责)
|
|
|
|
|
|
2. 前端 `loadSoldSeats()` 调用 API 查询各 seat spec_base 的库存状态
|
|
|
|
|
|
|
|
|
|
|
|
最小可行路径:**先让购买流程能跑通,再迭代优化**。
|
2026-04-15 11:20:22 +00:00
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
2026-04-15 11:15:12 +00:00
|
|
|
|
## 行动项(优先级排序)
|
2026-04-15 11:14:39 +00:00
|
|
|
|
|
2026-04-15 11:16:49 +00:00
|
|
|
|
| 优先级 | 行动项 | 负责 |
|
|
|
|
|
|
|--------|--------|------|
|
|
|
|
|
|
| P0 | 紧急修复商品 112 broken state | BackendArchitect |
|
2026-04-15 11:21:15 +00:00
|
|
|
|
| P1 | 实现方案 A 批量 SKU 生成(GoodsSpecificationsInsert 直接 SQL) | BackendArchitect |
|
|
|
|
|
|
| P2 | 重构 ticket_detail.html submit() 接入 specBaseIdMap | FrontendDev |
|
|
|
|
|
|
| P3 | Hook 隐藏插件 SKU(spec_base_id_map key = seat_id) | FrontendDev |
|
2026-04-15 11:14:39 +00:00
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
2026-04-15 06:11:43 +00:00
|
|
|
|
## 共识投票
|
2026-04-15 06:00:20 +00:00
|
|
|
|
|
2026-04-15 11:21:15 +00:00
|
|
|
|
[CONSENSUS: NO] — Round 3 进行中:所有 Q1-Q4 分析完成,准备输出最终决策报告
|
2026-04-15 06:11:43 +00:00
|
|
|
|
|
|
|
|
|
|
---
|
2026-04-15 06:00:20 +00:00
|
|
|
|
|
2026-04-15 11:16:49 +00:00
|
|
|
|
## Round 3 安全审计结果(保留,仅供参考)
|
|
|
|
|
|
|
|
|
|
|
|
### Task S1 — Admin 鉴权覆盖完整性审查 ✅ 验证通过
|
|
|
|
|
|
### Task S2 — SQL 注入风险审计 ✅ 无注入风险
|
|
|
|
|
|
### Task S3 — XSS / CSRF 防护检查 ✅ 通过
|
|
|
|
|
|
### Task S5 — IDOR 水平越权检查 ✅ 通过
|
|
|
|
|
|
### Task S4 — 敏感操作审计日志设计 ✅ 设计完成
|