From 58b0d0f8fd0cd182f3e1c48101bbe4493cdf0312 Mon Sep 17 00:00:00 2001 From: Council Date: Tue, 26 May 2026 17:29:42 +0800 Subject: [PATCH] =?UTF-8?q?council(draft):=20BackendArchitect=20-=20Round?= =?UTF-8?q?=204=20=E7=8E=B0=E5=9C=BA=E6=A0=B8=E6=9F=A5=E4=BF=AE=E6=AD=A3?= =?UTF-8?q?=20+=20=E6=8A=95=E7=A5=A8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 确认 SeatMapService::GetSeatMap() 完整存在(333行),无崩溃 - 确认 Index.php 无 soldSeats action,Round 3 P0 误判已修正 - 确认 Hook plugins_service_goods_data 未注册(Gap 1 仍成立) - 确认 Gap 2(CartSave)已消除 - 投票 A:后端优先,Hook 注册解锁 UniApp 链路 Co-Authored-By: Claude Sonnet 4.6 --- docs/council-eval-backendarchitect.md | 224 ++++++++++---------------- plan.md | 107 ++++++------ 2 files changed, 132 insertions(+), 199 deletions(-) diff --git a/docs/council-eval-backendarchitect.md b/docs/council-eval-backendarchitect.md index b3f4e18..cbc3472 100644 --- a/docs/council-eval-backendarchitect.md +++ b/docs/council-eval-backendarchitect.md @@ -1,143 +1,100 @@ -# Council 评估报告 — BackendArchitect(Round 3 更新) +# Council 评估报告 — BackendArchitect(Round 4 现场核查) -> 评估日期:2026-05-26 | 角色:后端架构师 | Git: `0d6d20062` +> 评估日期:2026-05-26 | 角色:后端架构师 | Git: `0d6d20062` → 提交中 --- -## 一、现状评估(2026-05-26 现场核查) +## 一、现状评估(Round 4 现场核查) ### 1.1 Phase 4 Tree API 设计 -**状态:📋 设计文档已提交,代码为零** +**状态:📋 设计文档存在,代码为零** | 组件 | 状态 | 说明 | |------|------|------| -| `docs/PHASE_4_API.md` | ✅ 存在(父仓库 commit `40a9b0ad1`) | Tree API 设计文档 | -| `docs/PLAN_TREE_API_IMPLEMENTATION.md` | ✅ 存在(父仓库 commit `40a9b0ad1`) | 实现计划 | -| `SeatMapService.php`(设计名) | ❌ **文件不存在** | 实际文件名为 `SeatSkuService.php` | -| `SeatSkuService.php`(实际) | ✅ 存在 | 但没有 `buildTree()` 方法 | -| `api/` 目录(Worktree) | ❌ **未纳入 Git 追踪** | 父仓库有 `api/Goods.php`,Worktree 为空 | -| `api/Goods.php::seatmap()` | ⚠️ 父仓库有,工作树无 | 调用不存在的 `SeatMapService::GetSeatMap()` | +| `docs/PHASE_4_API.md` | ✅ 存在 | Tree API 设计文档 | +| `docs/PLAN_TREE_API_IMPLEMENTATION.md` | ✅ 存在 | 实现计划 | +| `SeatMapService.php`(服务类) | ✅ **存在且完整** | 333行,含 `GetSeatMap()` + `buildSeatSpecMap()` + `buildGoodsSpecData()` | +| `api/Goods.php::seatmap()` | ✅ **存在且正确** | 第241行调用 `SeatMapService::GetSeatMap($goodsId)` | +| `SeatSkuService.php` | ✅ 存在 | 独立服务,含 `BatchGenerate()` + `GetGoodsViewData()` + `buildSeatSpecMap()` | +| Tree API `buildTree()` | ❌ 代码为零 | Phase 4 设计中的核心方法未实现 | -**Round 3 修正**:`api/Goods.php` 在父仓库存在,有 `seatmap()` 方法,但父仓库 Worktree 隔离造成版本混淆。Tree API 设计完整,代码仅存在 `SeatSkuService::GetGoodsViewData()` 部分复用逻辑。 +**Round 4 修正**:设计文档中提到的 `SeatMapService` 类**在父仓库已存在且完整**,`api/Goods.php::seatmap()` 路由已正确调用它。Round 1-3 的"P0 崩溃"分析是误判。 ### 1.2 SeatMapService + seatmap API -**状态:⚠️ API 断裂,带运行时错误** +**状态:✅ 已完整实现** | 组件 | 状态 | 位置 | |------|------|------| -| `index/Index.php::soldSeats` | ⚠️ 存在 | `index/Index.php:43` | -| `SeatSkuService::getSoldSeats()` | ❌ **完全缺失** | 被 `Index.php:43` 调用但方法不存在 | -| `index/Index.php::soldSeats` | ❌ **运行时崩溃** | PHP Fatal Error | -| `SeatSkuService::GetGoodsViewData()` | ✅ 完整 | 返 +| `SeatMapService.php` | ✅ **完整** | 333行,`GetSeatMap()` + 缓存 + `buildSeatSpecMap()` + `buildGoodsSpecData()` | +| `api/Goods.php::seatmap()` | ✅ **正确** | 第233-246行,路由注册正常,调用 `SeatMapService::GetSeatMap()` | +| `SeatSkuService::buildSeatSpecMap()` | ✅ 存在 | 第533行(私有方法) | +| `SeatSkuService::GetGoodsViewData()` | ✅ 存在 | 第370行(H5 模板专用) | +| `SeatSkuService::getSoldSeats()` | ⚠️ 方法不存在 | `GetSeatMap()` 已含库存信息,可替代 | +| `index/Index.php::soldSeats` | ❌ **不存在** | `Index.php` 只有 `wallet()` 方法,无 `soldSeats` | -回 seatSpecMap + specTypeList | -| `SeatSkuService::buildSeatSpecMap()` | ✅ 完整 | `SeatSkuService.php:532` | -| `SeatSkuService::BatchGenerate()` | ✅ 完整 | 座位 SKU 批量生成 | - -**严重性澄清**:Round 2 称 H5 绕过了 soldSeats API(正确),但忽略了 UniApp 的 `soldSeats` 端点调用会触发 PHP 致命错误。`ticket_detail.html` 自身正常,但所有直接调用 `soldSeats` API 的客户端都会崩溃。 - -**新增发现**:`api/Goods.php` 的 `seatmap()` 方法(父仓库)调用 `SeatMapService::GetSeatMap()`,但该类不存在(实际类名为 `SeatSkuService`,方法为 `GetGoodsViewData`)。这是 **命名混淆 Bug**。 +**Round 4 修正**: +- Round 3 报告称"Index.php:43 调用 getSoldSeats()"——**这是误判**。`Index.php` 只有 `wallet()` 方法,无 `soldSeats` action。 +- `SeatMapService::GetSeatMap()` 已完整实现,含实时 `inventory` 字段(0=已售),可替代 `getSoldSeats()`。 +- **无运行时崩溃**,seatmap API 工作正常。 ### 1.3 seatSpecMap 注入商品详情 API -**状态:❌ Gap 仍成立** +**状态:⚠️ Gap 1 成立,但有变通方案** | 组件 | 状态 | 说明 | |------|------|------| -| `vr_goods_config` 表字段 | ✅ 存在 | `Event.php::Install()` 创建 | -| `SeatSkuService::GetGoodsViewData()` | ✅ 存在 | `SeatSkuService.php:369` | -| `Hook.php::plugins_service_goods_data` | ❌ **未注册** | `Hook.php:13-28` 无此 case | -| H5 `ticket_detail.html` | ✅ 工作正常 | 直接调用 `GetGoodsViewData()` | -| UniApp 商品详情 API | ❌ **Gap 1 成立** | Hook 不注册则无法触发 seatSpecMap 注入 | +| `SeatSkuService::GetGoodsViewData()` | ✅ 存在 | 第370行,H5 模板专用 | +| `Hook.php::plugins_service_goods_data` | ❌ **未注册** | Hook.php 无此 case | +| `api/Goods.php::detail()` | ⚠️ **不包含 seatSpecMap** | 第278-299行,formatGoodsDetail 不注入 VR 数据 | +| H5 `ticket_detail.html` | ✅ **工作正常** | 直接调用 `GetGoodsViewData()` | +| UniApp `api/goods/detail` | ❌ **Gap 1 成立** | 无 Hook 注入,无 VR 数据 | -**确认**:Gap 1 完全成立。H5 工作是因为 `ticket_detail.html` 是模板文件,在服务器端直接调用 `SeatSkuService::GetGoodsViewData()`。UniApp 走 API 层,`plugins_service_goods_data` Hook 不注册,则 ShopXO 的商品详情 API 不会触发 VR 座位数据注入。 +**Gap 1 分析修正**: +- **Gap 1 对 UniApp 仍然成立**(Hooks 未注册) +- 但 `api/Goods.php::seatmap()`(第233行)已完整提供 seatSpecMap + goods_spec_data +- **UniApp 可以绕过 Gap 1**:先调用 `/seatmap` API 获取座位图,再调用标准 `/detail` API 获取商品基础信息 +- **最优解仍为 Hook 注册**:减少前端调用次数(一次 `/detail` 获取所有数据) ### 1.4 CartSave extension_data 多座位链路 -**状态:✅ H5 已验证,UniApp 只需复刻** +**状态:✅ H5 已验证,后端无需改动** | 组件 | 状态 | 说明 | |------|------|------| | `ticket_detail.html:762` 订单提交 | ✅ 已实现 | `extension_data` 嵌套在 `order_base` | | `TicketService::onOrderPaid()` | ✅ 已实现 | 逐行生成票(多座位支持) | -| UniApp CartSave | ✅ 与 H5 相同 JSON 格式 | Gap 2 已消除,只要后端提供端点即可 | - -**Round 2 修正确认**:Gap 2 已验证可工作,核心问题变为"UniApp 需要相同的 API 端点格式"。 +| Gap 2 状态 | ✅ **已消除** | 后端链路完整,UniApp 复刻 JSON 格式即可 | --- -## 二、发现问题(Round 3 修正) +## 二、发现问题(Round 4 修正) -### P0(运行时崩溃,直接阻塞) +### P0(重新评估) -| # | 问题 | 位置 | 严重度 | Round 2 对比 | -|---|------|------|--------|-------------| -| P0-1 | `SeatSkuService::getSoldSeats()` **方法缺失** | `SeatSkuService.php`(末行为 `buildSeatSpecMap`,无此方法) | **致命** — `Index.php:43` 触发 PHP Fatal Error | 确认:Round 2 误判为"H5 绕过" | -| P0-2 | `plugins_service_goods_data` Hook **未注册** | `Hook.php:13-28`(无对应 case) | **致命** — UniApp 商品详情无法获取 seatSpecMap | 与 Round 2 一致 | +| # | 问题 | 严重度 | Round 3 对比 | +|---|------|--------|-------------| +| P0-1 `getSoldSeats()` 方法缺失 | ❌ **已消除** | `SeatMapService::GetSeatMap()` 已含库存,`Index.php` 无 soldSeats action | +| P0-2 `plugins_service_goods_data` Hook 未注册 | ⚠️ **降级为 P1** | Gap 1 成立,但 UniApp 可用 `/seatmap` 变通绕过 | +| P0-3 `Index.php:soldSeats` 触发 Fatal Error | ❌ **已消除** | Index.php 无 soldSeats action,无崩溃 | -### P1(影响扩展性) +**重新分类**: -| # | 问题 | 位置 | 严重度 | Round 2 对比 | -|---|------|------|--------|-------------| -| P1-1 | `api/Goods.php::seatmap()` 引用不存在的 `SeatMapService` | `shopxo/app/plugins/vr_ticket/api/Goods.php:241` | **高** — 命名混淆,实际方法在 `SeatSkuService::GetGoodsViewData` | 新发现 | -| P1-2 | `api/` 目录未纳入 Git 追踪(Worktree) | 工作树 `shopxo/app/plugins/vr_ticket/api/` | **高** — 无法部署 / 无法协作 | 新发现 | -| P1-3 | Phase 4 Tree API 代码为零 | 无 `buildTree()` 方法 | **中** — 设计完备,代码未开始 | 与 Round 2 一致 | +| # | 问题 | 严重度 | 说明 | +|---|------|--------|------| +| P1-A | `api/Goods.php::detail()` 不包含 seatSpecMap | **高** | UniApp `/detail` API 缺少 VR 数据注入 | +| P1-B | `plugins_service_goods_data` Hook 未注册 | **中** | UniApp detail API 最佳入口缺失 | +| P2-A | Phase 4 Tree API `buildTree()` 未实现 | **中** | 设计完整,代码为零 | +| P2-B | `api/Goods.php::seatmap()` 命名不一致 | **低** | seatmap vs seatMap(大小写) | --- ## 三、技术方案建议 -### P0-1 Fix:`getSoldSeats()` 实现 +### 方案 A(推荐):Hook 注册(最小改动) -**文件**:`SeatSkuService.php`(追加到文件末尾) - -```php -/** - * 获取已售座位列表(seatKey 数组) - * - * 已售座位定义:inventory = 0 的 GoodsSpecBase 记录 - * seatKey 格式:roomId_rowLabel_colNum(如 "room_0_A_3") - * - * @param int $goodsId - * @param int $specBaseId 可选,限定查询 - * @return string[] - */ -public static function getSoldSeats(int $goodsId, int $specBaseId = 0): array -{ - if ($goodsId <= 0) return []; - - $query = \think\facade\Db::name('goods_spec_base') - ->where('goods_id', $goodsId) - ->where('inventory', 0); - - if ($specBaseId > 0) { - $query->where('id', $specBaseId); - } - - $soldSpecs = $query->select()->toArray(); - if (empty($soldSpecs)) return []; - - // 从 extends.seat_key 提取 - $seatKeys = []; - foreach ($soldSpecs as $spec) { - $extends = json_decode($spec['extends'] ?? '{}', true); - $seatKey = $extends['seat_key'] ?? ''; - if (!empty($seatKey)) { - $seatKeys[] = $seatKey; - } - } - - return $seatKeys; -} -``` - -**触发点**:`index/Index.php:43` 现有调用无需修改。 - -### P0-2 Fix:`plugins_service_goods_data` Hook 注册 - -**文件**:`Hook.php:13` 追加 case: +**文件**:`Hook.php` 追加 case: ```php case 'plugins_service_goods_data': @@ -148,51 +105,37 @@ case 'plugins_service_goods_data': break; ``` -**新增方法**:`TicketService.php`(追加到文件末尾): +**新增方法**:`TicketService.php`: ```php -/** - * 注入商品详情 VR 票务数据(通过 plugins_service_goods_data Hook) - * - * @param array &$data ShopXO 商品详情数据(引用传递) - * @param int $goodsId - */ public static function InjectGoodsDetailData(array &$data, int $goodsId): void { if ($goodsId <= 0) return; - $vrConfig = \think\facade\Db::name('goods') ->where('id', $goodsId) ->value('vr_goods_config'); - if (empty($vrConfig)) return; - $viewData = SeatSkuService::GetGoodsViewData($goodsId); - if (empty($viewData['vr_seat_template'])) return; - - $seatTemplate = $viewData['vr_seat_template']; - - // seatSpecMap:seat_key → 完整规格(含 row/col/room/section/price) - $data['seatSpecMap'] = $viewData['seatSpecMap'] ?? []; - // goods_spec_data:场次列表(含价格) - $data['goods_spec_data'] = $viewData['goods_spec_data'] ?? []; - // specTypeList:5维规格维度定义 - $data['specTypeList'] = $viewData['specTypeList'] ?? []; - // seatMap:座位图渲染数据 - $data['seatMap'] = $seatTemplate['seat_map'] ?? null; - // goods_config:当前生效的配置块 - $data['goods_config'] = $viewData['goods_config'] ?? null; + if (empty($viewData['seatSpecMap'])) return; + $data['seatSpecMap'] = $viewData['seatSpecMap']; + $data['goods_spec_data'] = $viewData['goods_spec_data']; + $data['specTypeList'] = $viewData['specTypeList'] ?? []; + $data['seatMap'] = $viewData['vr_seat_template']['seat_map'] ?? null; + $data['goods_config'] = $viewData['goods_config'] ?? null; } ``` -### P1 Fix:命名统一(SeatSkuService vs SeatMapService) +**代码量**:约 30 行。效果:UniApp 调用 ShopXO 标准 `/goods/detail` API 时自动获得 VR 数据。 -| 当前引用 | 应改为 | 位置 | -|---------|-------|------| -| `use app\plugins\vr_ticket\service\SeatMapService` | `use app\plugins\vr_ticket\service\SeatSkuService` | `shopxo/app/plugins/vr_ticket/api/Goods.php:12` | -| `SeatMapService::GetSeatMap` | `SeatSkuService::GetGoodsViewData` | `shopxo/app/plugins/vr_ticket/api/Goods.php:241` | +### 方案 B(备选):UniApp 变通绕过(无需后端改动) -**注意**:此修复应在 `api/` 目录重新纳入 Git 追踪后执行。 +UniApp 端可在调用商品详情后,再调用 `/seatmap` API 补充 VR 数据。 +- **优点**:无需后端改动,立即可用 +- **缺点**:前端多一次 API 调用(可接受) + +### 方案 C:Phase 4 完整实现(独立任务) + +`buildTree()` 实现 + Tree VR 体验,作为 Phase 4 独立里程碑。 --- @@ -200,19 +143,14 @@ public static function InjectGoodsDetailData(array &$data, int $goodsId): void | 优先级 | 任务 | 预计工时 | 收益 | |--------|------|---------|------| -| **P0-1** | 实现 `getSoldSeats()` | 30min | 修复 soldSeats API 崩溃 | -| **P0-2** | Hook 注册 + `InjectGoodsDetailData()` | 1h | 解锁 UniApp 完整票务链路 | -| **P1-A** | `api/` 目录纳入 Git 追踪 + 命名修复 | 30min | 可部署、可协作 | -| **P1-B** | Phase 4 Tree API 实现(`buildTree()`) | 待定 | Phase 4 功能完成 | - -**决策路径**: -1. 先修 P0-1 + P0-2(2h 内可完成) -2. 再推进 `api/` 目录规范化 -3. Phase 4 在 P0/P1 稳定后作为独立任务启动 +| **P1-A** | Hook 注册 + `InjectGoodsDetailData()` | 30min | 解锁 UniApp 完整票务链路 | +| **P1-B** | `api/Goods.php::detail()` 命名规范化 | 10min | API 契约一致性 | +| **P2** | Phase 4 Tree API 实现 | 待定 | Tree VR 体验 | +| **P3** | Phase 4 完整 Tree 体验 | 待定 | VR 差异化功能 | --- -## 五、投票(Round 3) +## 五、投票(Round 4) **议题:下一步主攻方向** @@ -220,22 +158,22 @@ public static function InjectGoodsDetailData(array &$data, int $goodsId): void **理由**: -1. **最小改动最大收益**:`getSoldSeats()` 实现(30 行)+ Hook 注册(10 行)= 约 40 行代码,修复后 UniApp 票务链路全部解锁。这是最低成本的最高收益修复。 +1. **Hook 注册是最低成本最高收益**:约 30 行代码,一次性解决 UniApp 商品详情 API 的 VR 数据注入问题。无需前端变通,减少 API 调用次数。 -2. **运行时崩溃是根本阻塞**:Round 2 的"H5 已绕过"分析正确,但忽略了 `Index.php:soldSeats` 对所有直接调用 API 的客户端(UniApp / 第三方)都会触发 PHP 致命错误。必须修复。 +2. **Round 4 重新评估确认**:后端 seatmap API 已完整实现(P0-1/P0-3 误判已消除),核心剩余问题是 Hook 注入这一处。 -3. **Hook 注册是 UniApp 的唯一入口**:Gap 1 对 H5 无影响(H5 走模板层直接调用),但对 UniApp 来说是唯一入口。没有 Hook 注册,UniApp 永远无法通过标准 API 获取 seatSpecMap。 +3. **Gap 2 已消除**:后端票务链路(CartSave → onOrderPaid → 票生成)已完整,多座位支持已验证。 -4. **Gap 2(CartSave)已消除**:Round 2 确认 H5 已验证 extension_data 链路正确,UniApp 只需复刻同样的 JSON 结构,不存在后端缺口。 +4. **UniApp 可用方案 B 变通立即推进**:即使 Hook 暂未注册,UniApp 仍可通过"先 /seatmap 后 /detail"的方式绕过 Gap 1 立即启动开发。 -5. **Phase 4 不应前置**:Tree API 是体验增强,不是购买流程的基础设施,在核心票务链路(P0)未稳定前启动 Phase 4 是资源浪费。 +5. **Phase 4 不应前置**:Tree API 是体验增强,在核心票务链路(P1)稳定前启动 Phase 4 资源浪费。 **补充:对其他提案的评估** -- **B(前端优先)**:不可行。UniApp 选座组件需要 seatSpecMap 数据,但 Gap 1 不修则数据不可得。前端等 Hook 注册后再开发效率更高。 -- **C(双线并行)**:在 P0 明确可修复的前提下,"双线并行"是浪费:前端等待期间无所事事,不如后端一次性修完再解锁前端。 -- **D(Phase 4 优先)**:Phase 4 是锦上添花,不是基础设施。Tree API 失败不影响用户购票核心流程。 +- **B(前端优先)**:可接受——UniApp 确实可以先用方案 B 变通绕过 Gap 1 立即开发。但变通方案不如 Hook 注册简洁。 +- **C(双线并行)**:可接受,但需明确分工。后端修 Hook,前端用方案 B 变通同时推进。 +- **D(Phase 4 优先)**:不建议。Phase 4 是锦上添花,不是票务购买的基础设施。 --- -*报告人:BackendArchitect | 2026-05-26 | Round 3* +*报告人:BackendArchitect | 2026-05-26 | Round 4* \ No newline at end of file diff --git a/plan.md b/plan.md index 18aa915..f38117c 100644 --- a/plan.md +++ b/plan.md @@ -1,7 +1,7 @@ -# Plan — VR 演唱会票务小程序 Round 3 执行 +# Plan — VR 演唱会票务小程序 Round 4 执行 -> 版本:v3.0 | 日期:2026-05-26 | Agent:council/BackendArchitect -> 任务:Round 3 评估更新 — 现场代码核查,修正 P0 优先级,投票 A +> 版本:v4.0 | 日期:2026-05-26 | Agent:council/BackendArchitect +> 任务:Round 4 现场核查 — 修正误判,投票 A --- @@ -9,39 +9,38 @@ - Phase 4 Tree API 设计文档完整性 + 可行性 - SeatMapService + `/seatmap` API 完整性 -- seatSpecMap 注入商品详情 API 的实现方案(Round 3 修正) -- CartSave extension_data 多座位存储链路(Round 3 修正) +- seatSpecMap 注入商品详情 API 的实现方案 +- CartSave extension_data 多座位存储链路 - 后端下一步优先级建议 --- -## 现状快照(Round 3 现场核查) +## Round 4 现场核查结论 ### Phase 4 Tree API -- 设计文档:✅ `docs/PHASE_4_API.md` + `PLAN_TREE_API_IMPLEMENTATION.md`(父仓库 commit `40a9b0ad1`) -- `SeatSkuService.php`(实际名):✅ 存在,含 `GetGoodsViewData()` + `buildSeatSpecMap()` -- `SeatMapService.php`(设计名):❌ 不存在(命名混淆) -- `api/` 目录(Worktree):❌ 未纳入 Git 追踪(父仓库有 `Goods.php`) -- 状态:**设计完整,代码仅部分复用逻辑,命名混淆 + api 目录未追踪** +- 设计文档:✅ `docs/PHASE_4_API.md` + `PLAN_TREE_API_IMPLEMENTATION.md` +- `SeatMapService.php`(服务类):✅ **333行完整实现**,含 `GetSeatMap()` + `buildSeatSpecMap()` + `buildGoodsSpecData()` +- `api/Goods.php::seatmap()`:✅ **正确实现**,第241行调用 `SeatMapService::GetSeatMap($goodsId)` +- Tree API `buildTree()`:❌ **代码为零**(Phase 4 尚未开始) -### SeatMapService + seatmap API(Round 3 修正) -- `index/Index.php::soldSeats` — ✅ 存在,路由正常 -- `SeatSkuService::getSoldSeats()` — ❌ **方法完全缺失(致命)** -- **关键修正**:`Index.php:43` 调用不存在的方法,**所有直接调用 soldSeats API 的客户端都会触发 PHP Fatal Error** -- H5 绕过正确(不走 API),但 UniApp/第三方 调用会崩溃 -- `api/Goods.php::seatmap()`(父仓库):⚠️ 引用不存在的 `SeatMapService::GetSeatMap()` -- 命名混淆:设计文档说 `SeatMapService`,实际类名是 `SeatSkuService` +### SeatMapService + seatmap API +- `SeatMapService::GetSeatMap()`:✅ **完整**,含实时 inventory + 缓存 +- `api/Goods.php::seatmap()`:✅ **正确**,UniApp 调用无崩溃 +- `index/Index.php::soldSeats`:❌ **Index.php 无此 action**(Round 3 误判已修正) +- `SeatSkuService::getSoldSeats()`:⚠️ 方法不存在,但被替代(`GetSeatMap()` 已含库存) +- **结论:无运行时崩溃,seatmap API 工作正常** -### seatSpecMap 注入商品详情(Round 3 一致) -- Goods.php 硬编码调用 — ✅ **H5 已工作**(直接调用 `GetGoodsViewData()`) -- Hook `plugins_service_goods_data` — ❌ **未在 Hook.php 中注册** -- UniApp 调用 `api/goods/detail` API — ❌ **Gap 1 仍成立** -- 结论:Gap 1 **致命且成立** +### seatSpecMap 注入 +- Hook `plugins_service_goods_data`:❌ **未注册**(Gap 1 仍成立) +- `api/Goods.php::detail()`:❌ 不包含 VR 数据 +- H5 `ticket_detail.html`:✅ **工作正常**(直接调用 `GetGoodsViewData()`) +- UniApp detail API:❌ **Gap 1 成立**,但 `/seatmap` API 可变通绕过 +- **结论:Gap 1 成立,UniApp 可先调用 `/seatmap` 绕过** -### CartSave extension_data(Round 3 修正) -- H5 链路 `ticket_detail.html:762` — ✅ 已实现(`extension_data` 嵌套在 `order_base`) -- `TicketService::onOrderPaid()` — ✅ 逐行生成票(多座位已支持) -- 结论:**Gap 2 已消除**,UniApp 只需复刻 H5 的 JSON 格式,后端无需额外改动 +### CartSave extension_data +- `ticket_detail.html:762`:✅ 已实现,`extension_data` 嵌套在 `order_base` +- `TicketService::onOrderPaid()`:✅ 已实现,多座位支持 +- **结论:Gap 2 已消除,后端无需改动** --- @@ -49,37 +48,32 @@ | Task | 内容 | 状态 | |------|------|------| -| B1 | Phase 4 Tree API 设计文档评估 | [Done: council/BackendArchitect] | -| B2 | SeatMapService + seatmap API 完整性检查 | [Done: council/BackendArchitect] — P0 崩溃发现 | -| B3 | seatSpecMap 注入方案设计 | [Done: council/BackendArchitect] — Hook 注册即可 | +| B1 | Phase 4 Tree API 设计文档评估 | [Done: council/BackendArchitect] — 设计完整,代码为零 | +| B2 | SeatMapService + seatmap API 完整性检查 | [Done: council/BackendArchitect] — Round 3 误判已修正,API 完整 | +| B3 | seatSpecMap 注入方案设计 | [Done: council/BackendArchitect] — Hook 注册方案已明确 | | B4 | CartSave extension_data 多座位链路分析 | [Done: council/BackendArchitect] — Gap 2 已消除 | -| B5 | 输出 Round 3 评估报告 + 投票 | [Done: council/BackendArchitect] | +| B5 | 输出 Round 4 评估报告 + 投票 | [Done: council/BackendArchitect] | --- -## 待执行任务(Round 3 更新 — P0 → P1 → P2 排序) +## P0 修正(Round 4) -### P0 — 立即修复(2h 内完成) +| 原问题 | Round 3 状态 | Round 4 修正 | +|--------|-------------|-------------| +| P0-1 `getSoldSeats()` 方法缺失 | 致命 | ❌ **已消除** — `GetSeatMap()` 已含库存,无崩溃 | +| P0-2 Hook `plugins_service_goods_data` 未注册 | 致命 | ⚠️ **降级 P1** — UniApp 可用 `/seatmap` 变通绕过 | +| P0-3 `Index.php:soldSeats` 触发 Fatal Error | 致命 | ❌ **已消除** — Index.php 无 soldSeats action | -| Task | 内容 | 依赖 | 状态 | -|------|------|------|------| -| P0-1 | 实现 `SeatSkuService::getSoldSeats()` | 无 | [To Claim: council/BackendArchitect] | -| P0-2 | 注册 `plugins_service_goods_data` Hook + `InjectGoodsDetailData()` | 无 | [To Claim: council/BackendArchitect] | +--- -### P1 — 短期完善 +## 最终优先级 -| Task | 内容 | 依赖 | 状态 | -|------|------|------|------| -| P1-1 | `api/` 目录纳入 Git 追踪 + 修复 `SeatMapService` 命名混淆 | P0-2 | [To Claim] | -| P1-2 | 统一 `SeatMapService` / `SeatSkuService` 命名 | P1-1 | [To Claim] | -| P1-3 | 完善 Phase 4 Tree API 文档(API 契约) | 无 | [To Claim] | - -### P2 — 可延后 - -| Task | 内容 | 依赖 | 状态 | -|------|------|------|------| -| P2-1 | Phase 4 Tree API 实现(`buildTree()`) | P1-1 | [To Claim] | -| P2-2 | 实时座位锁定机制(SKIP LOCKED) | P0-1 | [To Claim] | +| 优先级 | 任务 | 预计工时 | 收益 | +|--------|------|---------|------| +| **P1-A** | Hook 注册 + `InjectGoodsDetailData()` | 30min | 解锁 UniApp 完整票务链路 | +| **P1-B** | `api/Goods.php::detail()` 注入 VR 数据 | 20min | 与 Hook 注册二选一 | +| **P2** | Phase 4 Tree API `buildTree()` | 待定 | Tree VR 体验 | +| **P3** | Phase 4 完整 Tree 体验 | 待定 | VR 差异化功能 | --- @@ -88,14 +82,15 @@ **投票:A — 后端优先** 理由: -1. `getSoldSeats()` 实现(30 行)修复运行时崩溃,解锁 UniApp soldSeats API -2. Hook 注册(10 行)解锁 UniApp 商品详情 API(Gap 1) -3. Gap 2(CartSave)已消除,后端无额外改动需求 -4. Phase 4 不应前置,应在 P0/P1 稳定后作为独立任务启动 +1. Hook 注册约 30 行代码,解决 Gap 1,解锁 UniApp 完整票务链路 +2. Round 4 确认:seatmap API 已完整,无运行时崩溃 +3. Gap 2 已消除,后端链路完整 +4. UniApp 可用方案 B(先 /seatmap 后 /detail)立即变通绕过 Gap 1 +5. Phase 4 是体验增强,不应作为主攻方向 --- ## 输出 -- 评估报告(更新版):`docs/council-eval-backendarchitect.md` -- 投票:`docs/council-eval-backendarchitect.md#五投票` \ No newline at end of file +- 评估报告(Round 4 更新版):`docs/council-eval-backendarchitect.md` +- 投票:`docs/council-eval-backendarchitect.md#五投票 \ No newline at end of file