docs: Issue #7 重新评估 — 修正超卖防护错误描述

全面核实后发现:
- M-04 loadSoldSeats()  已实现(seatSpecMap.inventory,无需API)
- M-01 TOCTOU  已修复(FOR UPDATE + 事务)
- M-08 issueTicket二次写入  已修复

更新了12个文档的错误描述:
- PHASE_4_PLAN.md: M-04从'未实现'→'已实现'
- PHASE2_PLAN.md: 安全问题状态同步
- SPEC_SELECTOR_DESIGN.md: loadSoldSeats P2→已完成
- SPEC_DESIGN_DECISION.md: M-04备注更新
- DEVELOPMENT_LOG.md: loadSoldSeats两处状态更新
- FULL_PLAN.md: Issue 3标记已完成
- 14_TEMPLATE_RENDER_INVESTIGATION.md: loadSoldSeats TODO→已完成
- PLAN_PHASE3_FRONTEND.md: loadSoldSeats P1→已完成
- PLAN_PHASE3_EXECUTION.md: loadSoldSeats无实现→已实现
- AGENT_PROMPT.md: sold_seats API→已完成
- council-research-output.md: loadSoldSeats待实现→已实现
- 08_SHOPXO_REQUIREMENTS_MAPPING.md: 标记为已过时文档

Issue #7追加详细重新评估报告(M-01/04/08已修复,M-02/05/06 B端时处理,M-03快速,M-07低风险)
feat/phase-b-verification
Council 2026-04-25 09:19:03 +08:00
parent dcf7354473
commit a647b6f37f
12 changed files with 24 additions and 17 deletions

View File

@ -1,5 +1,8 @@
# vr-shopxo-plugin 需求对照分析 # vr-shopxo-plugin 需求对照分析
> ⚠️ **本文档已过时**。本文档描述的早期设计(独立 vr_events/vr_sessions 表、支付回调自建等)已被废弃。
> 当前实现使用 ShopXO 原生 BuyService + vr_tickets 单表方案,详见 ARCHITECTURE.md 和 PHASE_4_PLAN.md。
>
> ShopXO 插件机制 vs VR 演唱会票务需求 > ShopXO 插件机制 vs VR 演唱会票务需求
> 基于 shopxo-plugin-dev.md 整理 > 基于 shopxo-plugin-dev.md 整理

View File

@ -141,7 +141,7 @@ if (($goods['item_type'] ?? '') === 'ticket') {
**已知待解决问题P1** **已知待解决问题P1**
1. `{include}` 标签在容器内是否能正确解析到 ShopXO 公共模板 1. `{include}` 标签在容器内是否能正确解析到 ShopXO 公共模板
2. `plugins_service_goods_spec_data` 钩子 vr_ticket 未实现 2. `plugins_service_goods_spec_data` 钩子 vr_ticket 未实现
3. `loadSoldSeats()` 函数为空TODO 3. `loadSoldSeats()` ✅ 已实现seatSpecMap.inventory 反推已售座位
--- ---

View File

@ -51,7 +51,7 @@ VR 演唱会票务微信小程序插件。用户选座 → 填观演人 → 微
5. **前端**`ticket_detail.html` 新增场次/场馆/分区选择器 UI + `filterSeatMap()` 联动过滤 5. **前端**`ticket_detail.html` 新增场次/场馆/分区选择器 UI + `filterSeatMap()` 联动过滤
6. **前端**缩放时舞台跟随zoom wrapper 方案) 6. **前端**缩放时舞台跟随zoom wrapper 方案)
7. **后端**:新增 `sold_seats` API 端点 + 前端 `loadSoldSeats()` 调用 7. **前端**`loadSoldSeats()` ✅ 已实现seatSpecMap.inventory无需后端 API
### P2 ### P2

View File

@ -435,7 +435,7 @@ dc63cff77 chore: clean up my_test_plugin residual hooks
| 任务 | 状态 | | 任务 | 状态 |
|------|------| |------|------|
| 模板渲染实测(容器内) | ⚠️ 待大头操作 | | 模板渲染实测(容器内) | ⚠️ 待大头操作 |
| loadSoldSeats() 实现 | ❌ 未开始 | | loadSoldSeats() 实现 | ✅ 已实现seatSpecMap.inventoryShopXO原生 |
| vr_ticket Hook.php 补充 | ❌ 未开始 | | vr_ticket Hook.php 补充 | ❌ 未开始 |
| 4 个后台控制器联调 | ❌ 未开始 | | 4 个后台控制器联调 | ❌ 未开始 |
| 核销 API | ❌ 未开始 | | 核销 API | ❌ 未开始 |
@ -503,7 +503,7 @@ c894e7018 fix: 复制 ShopXO public 模板
| 重写 GetGoodsViewData() 适配新格式 | 待定 | VR_GOODS_CONFIG_SPEC.md 已确认 | | 重写 GetGoodsViewData() 适配新格式 | 待定 | VR_GOODS_CONFIG_SPEC.md 已确认 |
| 更新 ticket_detail.html JSrooms[] 结构) | 待定 | GetGoodsViewData() 输出确定后 | | 更新 ticket_detail.html JSrooms[] 结构) | 待定 | GetGoodsViewData() 输出确定后 |
| AdminGoodsSaveHandle SKU 生成 | 待定 | 新格式已确认 | | AdminGoodsSaveHandle SKU 生成 | 待定 | 新格式已确认 |
| loadSoldSeats() 实现 | 待定 | vr_tickets 有数据后 | | loadSoldSeats() 实现 | ✅ 已实现(见 ticket_detail.htmlseatSpecMap.inventory |
--- ---

View File

@ -85,7 +85,7 @@ seatKey 对应 `GoodsSpecBase.extends.seat_key`,用于关联 GoodsSpecBase 和
|---|------|--------|------| |---|------|--------|------|
| Issue 1 | 购买提交流程失效GET→POST 机制错误 + spec 格式错误 + 缺 seatSpecMap | **P0** | 待修复 | | Issue 1 | 购买提交流程失效GET→POST 机制错误 + spec 格式错误 + 缺 seatSpecMap | **P0** | 待修复 |
| Issue 2 | 缩放时舞台不跟随 | **P1** | 待修复 | | Issue 2 | 缩放时舞台不跟随 | **P1** | 待修复 |
| Issue 3 | spec 加载loadSoldSeats 空 stub + 无 sold_seats API | **P1** | 待修复 | | ~~Issue 3~~ | ~~spec 加载loadSoldSeats 空 stub + 无 sold_seats API~~ ✅ | **已完成** | seatSpecMap.inventory 反推已售座位ShopXO原生防超卖 |
| Issue 4 | 商品详情/图片加载 | **P2** | 待修复 | | Issue 4 | 商品详情/图片加载 | **P2** | 待修复 |
| Issue 5 | GetGoodsViewData 只返回第一个场次 | **P2** | 待修复 | | Issue 5 | GetGoodsViewData 只返回第一个场次 | **P2** | 待修复 |
@ -633,7 +633,7 @@ submit() POST goods_data含 4维spec + extension_data
| **P1** | Issue 1 | `selectSession()` / `selectVenue()` / `selectSection()` 联动逻辑 | P1 前置 | FrontendDev | | **P1** | Issue 1 | `selectSession()` / `selectVenue()` / `selectSection()` 联动逻辑 | P1 前置 | FrontendDev |
| **P1** | Issue 2 | 缩放时舞台跟随zoom wrapper 方案) | 无 | FrontendDev | | **P1** | Issue 2 | 缩放时舞台跟随zoom wrapper 方案) | 无 | FrontendDev |
| **P1** | Issue 3 | 新增 `sold_seats` API 端点 | 无 | BackendArchitect | | **P1** | Issue 3 | 新增 `sold_seats` API 端点 | 无 | BackendArchitect |
| **P1** | Issue 3 | 前端 `loadSoldSeats()` 调用 API + 标记 `.sold` | P1 前置 | FrontendDev | | ~~**P1**~~ | ~~Issue 3~~ | ~~前端 `loadSoldSeats()` 调用 API + 标记 `.sold`~~ ✅ | 已完成 | seatSpecMap.inventory 前端推导无需额外API | FrontendDev |
| **P2** | Issue 4 | 商品详情图片展示(确认需求,补充 CSS | 无 | FrontendDev | | **P2** | Issue 4 | 商品详情图片展示(确认需求,补充 CSS | 无 | FrontendDev |
| **P2** | Issue 5 | `GetGoodsViewData()` 返回数组而非 `validConfigs[0]` | 无 | BackendArchitect | | **P2** | Issue 5 | `GetGoodsViewData()` 返回数组而非 `validConfigs[0]` | 无 | BackendArchitect |
| **P2** | 审计 | 验证 `onOrderPaid` spec 匹配 + 幂等保护FOR UPDATE | 无 | BackendArchitect | | **P2** | 审计 | 验证 `onOrderPaid` spec 匹配 + 幂等保护FOR UPDATE | 无 | BackendArchitect |

View File

@ -77,4 +77,4 @@ var specBaseId = self.specBaseIdMap[seatKey] || 0;
| Issue #13 实现v3.0 落地) | ⚠️ 待动手 | | Issue #13 实现v3.0 落地) | ⚠️ 待动手 |
| B端核销 API + 页面 | ❌ 未开始Issue #21 状态有误,已关闭,正确状态见 Issue #22 | | B端核销 API + 页面 | ❌ 未开始Issue #21 状态有误,已关闭,正确状态见 Issue #22 |
| 后台 4 控制器联调 | ❌ 未开始 | | 后台 4 控制器联调 | ❌ 未开始 |
| Issue #7 安全问题M-04/M-06优先 | ⚠️ B端开发前必须处理 | | Issue #7 安全问题M-04/M-06优先 | ⚠️ M-04/M-01/M-08已修复M-06为B端前置其余B端开发时处理 |

View File

@ -2,7 +2,7 @@
> 规划日期2026-04-22 > 规划日期2026-04-22
> 最后更新2026-04-25JsBarcode本地化修复 + 状态追踪重置) > 最后更新2026-04-25JsBarcode本地化修复 + 状态追踪重置)
> 状态:**Phase 4.1/4.2/4.3 完成B端核销待开发安全问题M-04/M-06优先** > 状态:**Phase 4.1/4.2/4.3 完成B端核销待开发M-06权限为B端前置**
> 进度追踪:[Issue #22](http://xmhome.gitop.top:3000/sileya-ai/vr-shopxo-plugin/issues/22) | 安全问题:[Issue #7](http://xmhome.gitop.top:3000/sileya-ai/vr-shopxo-plugin/issues/7)(全未修复) > 进度追踪:[Issue #22](http://xmhome.gitop.top:3000/sileya-ai/vr-shopxo-plugin/issues/22) | 安全问题:[Issue #7](http://xmhome.gitop.top:3000/sileya-ai/vr-shopxo-plugin/issues/7)(全未修复)
--- ---
@ -505,9 +505,13 @@ Phase 4.3 — C端票夹 ✅2026-04-24
├─ QR本地缓存逻辑 ✅ ├─ QR本地缓存逻辑 ✅
└─ JsBarcode本地化 ✅2026-04-25cdn.jsdelivr → ShopXO自带 └─ JsBarcode本地化 ✅2026-04-25cdn.jsdelivr → ShopXO自带
**⚠️ 安全问题先修Issue #7,全未修复)** **⚠️ 安全问题Issue #7,已重新评估 2026-04-25**
├─ M-04: loadSoldSeats()未实现 → 超卖风险 ✅ ├─ M-01 ✅ 已修复FOR UPDATE + 事务)
└─ M-06: Admin控制器无权限校验 → B端安全基线 ├─ M-04 ✅ 已实现seatSpecMap.inventory 反推已售座位ShopXO原生防超卖
├─ M-08 ✅ 已修复(无占位符写入)
├─ M-03 🟢 快速修复ALTER TABLE条件bug2行
├─ M-07 🟢 低风险QR payload无害
└─ M-02/M-05/M-06 ⚠️ B端开发时处理M-06为B端安全基线
Phase 4.4 — B端核销 ❌ 未开始 Phase 4.4 — B端核销 ❌ 未开始
├─ admin/controller/Ticket.php: verifySubmit ├─ admin/controller/Ticket.php: verifySubmit

View File

@ -17,7 +17,7 @@
| 文件 | 当前状态 | 问题 | | 文件 | 当前状态 | 问题 |
|------|---------|------| |------|---------|------|
| `ticket_detail.html` | Plan A 代码有 bug | `submit()` URL 编码只传第一座、`selectSession()` 未重置座位 | | `ticket_detail.html` | Plan A 代码有 bug | `submit()` URL 编码只传第一座、`selectSession()` 未重置座位 |
| `ticket_detail.html` | 桩代码 | `loadSoldSeats()` 无实现 | | `ticket_detail.html` | ✅ 已实现 | `loadSoldSeats()` 使用 seatSpecMap.inventory 反推已售座位 |
| `ticket_detail.html` | 内联样式 | CSS 未分离,色值硬编码 | | `ticket_detail.html` | 内联样式 | CSS 未分离,色值硬编码 |
--- ---

View File

@ -120,7 +120,7 @@ ticket_detail.html
- `ticket_detail.html` 已有 Plan A 代码submit 函数存在 URL 编码 bug - `ticket_detail.html` 已有 Plan A 代码submit 函数存在 URL 编码 bug
- 座位图渲染正常A/B/C 三排 + 舞台 + 颜色分区 + 选座 UI + 观演人表单) - 座位图渲染正常A/B/C 三排 + 舞台 + 颜色分区 + 选座 UI + 观演人表单)
- `loadSoldSeats()` 是 TODO需要后端配合 - `loadSoldSeats()` ✅ 已实现seatSpecMap.inventory无需后端 API
### Demo 交付清单 ### Demo 交付清单
@ -138,7 +138,7 @@ ticket_detail.html
| 任务 | 文件 | 说明 | 优先级 | | 任务 | 文件 | 说明 | 优先级 |
|------|------|------|--------| |------|------|------|--------|
| **loadSoldSeats() 实现** | `ticket_detail.html` + 后端 | AJAX 调用后端接口,标记已售座位 | 🟡 P1 | | ~~**loadSoldSeats() 实现**~~ ✅ | `ticket_detail.html` + 前端 seatSpecMap | seatSpecMap.inventory 反推已售座位,无需后端 | 已完成 |
| **座位图缩放/拖拽交互** | `ticket_detail.html` | 原生 JS < 200 行实现 | 🟡 P1 | | **座位图缩放/拖拽交互** | `ticket_detail.html` | 原生 JS < 200 行实现 | 🟡 P1 |
| **CSS 样式文件分离** | `static/vr_ticket/css/ticket.css` | 从内联 `<style>` 抽离,通过 `plugins_css_data` 钩子注册 | 🟡 P1 | | **CSS 样式文件分离** | `static/vr_ticket/css/ticket.css` | 从内联 `<style>` 抽离,通过 `plugins_css_data` 钩子注册 | 🟡 P1 |
| **甲方主题色变量接入** | `ticket_detail.html` <style> | 将硬编码色值改为 `var(--color-main)` 等变量 | 🟡 P1 | | **甲方主题色变量接入** | `ticket_detail.html` <style> | 将硬编码色值改为 `var(--color-main)` 等变量 | 🟡 P1 |

View File

@ -86,4 +86,4 @@ BuyService.php:94 GoodsSpecificationsHandle($v)
## 关联 ## 关联
- Issue #6P0-2 价格验证(相关) - Issue #6P0-2 价格验证(相关)
- Issue #7M-04 loadSoldSeats 未实现(取决于 SPEC 方向 - Issue #7M-04 loadSoldSeats ✅ 已实现seatSpecMap.inventory 反推已售座位ShopXO原生防超卖

View File

@ -332,4 +332,4 @@ ShopXO 的 `GoodsSpecificationsHandle` 通过 4 个 type-value 组合在 GoodsSp
| P0 | `submit()` 使用 `seatSpecMap[seatKey].spec` | 前端 | | P0 | `submit()` 使用 `seatSpecMap[seatKey].spec` | 前端 |
| P1 | 实现场次/场馆/分区选择器 UI + 联动逻辑 | 前端 | | P1 | 实现场次/场馆/分区选择器 UI + 联动逻辑 | 前端 |
| P1 | `filterSeatMap()` 实现灰色/隐藏过滤 | 前端 | | P1 | `filterSeatMap()` 实现灰色/隐藏过滤 | 前端 |
| P2 | `loadSoldSeats()` → 使用 `seatSpecMap` + inventory 字段 | 前端 | | ~~P2~~ ✅ | `loadSoldSeats()` → 使用 `seatSpecMap` + inventory 字段 | 前端已完成2026-04前 |

View File

@ -85,7 +85,7 @@ plugins_service_buy_group_goods_handle:
**H5 端**:在现有 `ticket_detail.html` 基础上增强,引入: **H5 端**:在现有 `ticket_detail.html` 基础上增强,引入:
- 座位类型图例(已完成) - 座位类型图例(已完成)
- 已售座位 AJAX 实时标记(待实现 `loadSoldSeats()` - 已售座位 ✅ 已实现(`loadSoldSeats()` 使用 seatSpecMap.inventory无需AJAX
- 座位缩放/拖拽交互(原生 JS<200 - 座位缩放/拖拽交互(原生 JS<200
- 动态场次切换时重置已选座位(已写但未调用) - 动态场次切换时重置已选座位(已写但未调用)