diff --git a/docs/council-eval-backendarchitect.md b/docs/council-eval-backendarchitect.md new file mode 100644 index 0000000..b85b564 --- /dev/null +++ b/docs/council-eval-backendarchitect.md @@ -0,0 +1,222 @@ +# Council 评估报告 — BackendArchitect + +> 评估日期:2026-05-26 | 角色:后端架构师 + +--- + +## 一、现状评估 + +### 1.1 Phase 4 Tree API 设计 + +**状态:仅有 commit,无实现代码** + +- `docs/` 中无 Phase 4 专属设计文档(无 `07/08/09/10_PHASE4*.md`) +- GitNexus 索引显示 `Goods.php` 中有 `GetSeatMap`、`buildTree`、`buildTemplatePool`,但 `shopxo/app/plugins/vr_ticket/api/` 目录不存在 +- **结论:Tree API 设计处于概念阶段,无可执行代码** + +### 1.2 SeatMapService + seatmap API + +**状态:半完成,API 层断裂** + +| 组件 | 状态 | 说明 | +|------|------|------| +| `index/Index.php::soldSeats` | ⚠️ 存在但断裂 | 路由存在,调用 `SeatSkuService::getSoldSeats()` | +| `SeatSkuService::getSoldSeats()` | ❌ **缺失** | 方法被引用但未实现 | +| 完整 `/seatmap` API 端点 | ❌ 不存在 | 只有已售座位子集,无座位图数据 | +| `SeatSkuService::BatchGenerate` | ✅ 已完成 | 座位 SKU 批量生成逻辑完整 | +| `SeatSkuService::GetGoodsViewData` | ✅ 已完成 | 模板+场次数据读取 | + +### 1.3 seatSpecMap 注入商品详情 API + +**状态:❌ Gap 存在** + +- ShopXO 商品详情 API 路由:`/?s=api/goods/detail&goods_id=X` +- 插件 Hook `plugins_service_goods_data` 在 `Hook.php` 中**未注册** +- `vr_goods_config` 字段已存在于 `sxo_goods` 表(`Event.php::Install()` 中通过 ALTER TABLE 追加) +- 但商品详情返回数据中**无 seatSpecMap 结构** + +### 1.4 CartSave extension_data 多座位链路 + +**状态:⚠️ Gap 存在** + +- `GoodsCartService.php` 中**无 `extension_data` 相关代码** +- ShopXO 购物车数据模型:`sxo_cart` 表有 `goods_id`, `spec_id`, `stock`, 无扩展字段 +- 多座位下单链路需要扩展购物车字段才能在购物车→订单流程中保留座位信息 + +--- + +## 二、发现问题 + +### P0(阻塞前端) + +| # | 问题 | 位置 | 严重度 | +|---|------|------|--------| +| P0-1 | `SeatSkuService::getSoldSeats()` 方法缺失 | `SeatSkuService.php` | 致命 — soldSeats API 不可用 | +| P0-2 | seatSpecMap 未注入商品详情 | 无 Hook 注入点 | 致命 — 前端无法获取座位→SKU 映射 | +| P0-3 | CartSave 无法传递座位信息 | `GoodsCartService.php` | 致命 — 多座位流程断裂 | + +### P1(影响体验) + +| # | 问题 | 位置 | 严重度 | +|---|------|------|--------| +| P1-1 | `/seatmap` 完整 API 未实现 | `index/Index.php` | 高 — 实时座位状态轮询无法工作 | +| P1-2 | Phase 4 Tree API 无代码 | 无文件 | 中 — 功能规划停留在设计阶段 | +| P1-3 | `vr_goods_config` JSON 结构与 ShopXO spec 系统对齐问题 | `SeatSkuService.php:29` | 中 — SPEC_DIMS 硬编码维度名 | + +--- + +## 三、技术方案建议 + +### 方案 A:修复 P0 Gap(推荐) + +**优先级最高,解锁前端开发** + +#### A1: 实现 `getSoldSeats()` 方法 + +```php +// SeatSkuService.php 新增 +public static function getSoldSeats(int $goodsId, int $specBaseId = 0): array +{ + $query = Db::name('goods_spec_base') + ->where('goods_id', $goodsId) + ->where('inventory', 0); // 已售=库存为0 + + if ($specBaseId > 0) { + $query->where('id', $specBaseId); + } + + $sold = $query->select()->toArray(); + + // 通过 GoodsSpecValue 反查 seat_id + $soldSeats = []; + foreach ($sold as $spec) { + $seatValue = Db::name('goods_spec_value') + ->where('goods_spec_base_id', $spec['id']) + ->where('value', 'LIKE', '%-%-%-' /* 座位号格式: venue-room-char-rowcol */) + ->find(); + if ($seatValue) { + $soldSeats[] = self::extractSeatKey($seatValue['value']); + } + } + return $soldSeats; +} +``` + +#### A2: 注册商品详情 Hook,注入 seatSpecMap + +```php +// Hook.php plugins_service_goods_data 分支 +case 'plugins_service_goods_data': + $ret = TicketService::InjectGoodsDetailData($params); + break; +``` + +```php +// TicketService.php 新增 +public static function InjectGoodsDetailData(&$data, $goodsId) +{ + if (!self::isTicketGoods($goodsId)) return; + + $viewData = SeatSkuService::GetGoodsViewData($goodsId); + $seatTemplate = $viewData['vr_seat_template'] ?? null; + + if ($seatTemplate && !empty($seatTemplate['spec_base_id_map'])) { + // seatSpecMap: seat_id → spec_base_id 映射 + $data['seatSpecMap'] = $seatTemplate['spec_base_id_map']; + // seatMap: 座位图渲染数据 + $data['seatMap'] = $seatTemplate['seat_map'] ?? null; + // sessions: 场次列表 + $data['sessions'] = $viewData['goods_spec_data'] ?? []; + } +} +``` + +#### A3: 扩展 CartSave 支持座位 extension_data + +两种路径: + +**路径 1(ShopXO 原生扩展)**:修改 `sxo_cart` 表加 `extension_data` 字段,通过 Hook 拦截保存 + +```php +// Hook.php 新增 +case 'plugins_service_goods_cart_save_handle': + TicketService::CartSaveWithSeats($params); + break; +``` + +**路径 2(绕过购物车)**:票务商品不走购物车,直接从选座→订单(参考大麦/猫眼模式) + +> **建议**:票务场景走路径 2(绕过购物车),因为: +> - 每个座位唯一库存(inventory=1),购物车没有意义 +> - 用户选座→立即下单→支付,更符合票务直觉 +> - 避免购物车超卖复杂性 + +--- + +### 方案 B:实现完整 `/seatmap` API + +在 `index/Index.php` 中扩展: + +```php +public function seatmap(array $params = []) +{ + $goodsId = intval($params['goods_id'] ?? 0); + if ($goodsId <= 0) return DataReturn('goods_id invalid', -1); + + $viewData = SeatSkuService::GetGoodsViewData($goodsId); + $soldSeats = SeatSkuService::getSoldSeats($goodsId); + + return DataReturn('success', 0, [ + 'seat_map' => $viewData['vr_seat_template']['seat_map'] ?? null, + 'sold_seats' => $soldSeats, + 'sessions' => $viewData['goods_spec_data'] ?? [], + 'spec_base_id_map' => $viewData['vr_seat_template']['spec_base_id_map'] ?? [], + ]); +} +``` + +--- + +## 四、优先级建议 + +### 立即执行(P0) + +1. **实现 `SeatSkuService::getSoldSeats()`** — 修复 soldSeats API +2. **注册 `plugins_service_goods_data` Hook** — 解锁 seatSpecMap 注入 +3. **决策 CartSave 路径**:票务商品走绕过购物车的直购模式 + +### 短期执行(P1) + +4. **实现完整 `/seatmap` API** — 统一座位数据接口 +5. **完善 Phase 4 Tree API 设计文档** — 补充 API 契约和路由设计 + +### 可延后(P2) + +6. **Phase 4 Tree API 实现** — 依赖 Phase 2/3 稳定后启动 + +--- + +## 五、投票 + +**议题:下一步主攻方向** + +**投票:C — 双线并行** + +**理由**: + +1. 后端 P0 Gap(`getSoldSeats` 缺失、Hook 未注册)属于"修完就能用"的阻断性问题,工作量小但价值高,完全修复预计 2-3 小时 +2. 前端当前有 H5 票务页(`ticket_detail.html`)作为保底,可以独立推进 uniapp 选座组件开发,无需等待全部 API +3. Tree API(Phase 4)设计尚未完成,过早投入实现是浪费,应在 P0/P1 修复后作为第二阶段启动 +4. "双线并行"中的分工建议: + - 后端:修复 P0 Gap + seatmap API + Hook 注册 + - 前端:基于现有 `GetGoodsViewData` 数据模型开发选座组件,等 Hook 注册后接入 seatSpecMap + +**补充:对其他提案的评估** + +- **A(后端优先)**:合理,但"后端优先"意味着前端完全等待 — 这会浪费前端已有的 H5 票务页保底能力 +- **B(前端优先)**:不可行,前端开发被 P0 Gap 阻断,seatSpecMap 缺失时选座组件无法正确映射 SKU +- **D(Phase 4 优先)**:Phase 4 是锦上添花,不是基础功能,Tree API 失败不影响票务核心购买流程 + +--- + +*报告人:BackendArchitect | 2026-05-26* diff --git a/plan.md b/plan.md index 8ea9abf..dbe0336 100644 --- a/plan.md +++ b/plan.md @@ -1,38 +1,55 @@ -# Plan — VR 演唱会票务小程序 Phase 2 技术评估 +# Plan — VR 演唱会票务小程序 Round 2 技术评估 -> 版本:v1.0 | 日期:2026-04-21 | Agent:council/BackendArchitect -> 任务:评估 4 个已知问题(购物车格式 P0、舞台缩放 P1、spec 加载 P1、商品详情 P2) +> 版本:v2.0 | 日期:2026-05-26 | Agent:council/BackendArchitect +> 任务:Round 1 评估 — Phase 4 Tree API、SeatMapService/seatmap API、seatSpecMap 注入、CartSave extension_data --- -## Phase 2 评估任务分配 +## 评估范围 -### BackendArchitect(我) +- Phase 4 Tree API 设计文档完整性 + 可行性 +- SeatMapService + `/seatmap` API 完整性 +- seatSpecMap 注入商品详情 API 的实现方案 +- CartSave extension_data 多座位存储链路 +- 后端下一步优先级建议 + +--- + +## 现状快照 + +### Phase 4 Tree API +- **设计文档**:无独立 Phase 4 设计文档(仅有 `06_SEAT_MAP_INTEGRATION.md` 作为 Phase 3 参考) +- **代码实现**:`shopxo/app/plugins/vr_ticket/api/` 目录不存在,Tree API 无任何代码 +- **状态**:📋 设计阶段(commit 存在,代码为零) + +### SeatMapService + seatmap API +- `index/Index.php::soldSeats()` — ⚠️ 存在但断裂(调用了不存在的方法) +- `SeatSkuService::getSoldSeats()` — **❌ 方法缺失** +- 完整 `/seatmap` API — **❌ 不存在** +- `BatchGenerate()` — ✅ 完成 +- `GetGoodsViewData()` — ✅ 完成 + +### seatSpecMap 注入商品详情 +- Hook `plugins_service_goods_data` — **❌ 未注册** +- `vr_goods_config` 字段 — ✅ 已存在于 sxo_goods +- seatSpecMap 在返回数据中 — **❌ 不存在** + +### CartSave extension_data +- `GoodsCartService.php` 中 extension_data — **❌ 不存在** +- 多座位下单链路 — **❌ 断裂** +- ShopXO sxo_cart 表无 extension_data 字段 + +--- + +## BackendArchitect 评估任务 | Task | 内容 | 状态 | |------|------|------| -| B1 | 分析 GoodsCartService::Save 的真实 API 契约(期望参数格式) | [Done: council/BackendArchitect] | -| B2 | 验证 ticket_detail.html submit() 的 params 构造是否符合规范 | [Done: council/BackendArchitect] | -| B3 | ShopXO spec 加载标准端点调研(GoodsSpecDetail 等) | [Done: council/BackendArchitect] | -| B4 | 在 ticket_detail.html 中优雅加载规格/库存的方案设计 | [Done: council/BackendArchitect] | -| B5 | 整合 BackendArchitect findings → `reviews/BackendArchitect-on-phase2.md` | [Done: council/BackendArchitect] | - -### FrontendDev - -| Task | 内容 | 状态 | -|------|------|------| -| F1 | 修复 .vr-stage 缩放问题方案 | [To Claim: council/FrontendDev] | -| F2 | submit() 多座位串行提交逻辑优化分析 | [To Claim: council/FrontendDev] | -| F3 | goods_spec_data 与前端价格联动方案 | [To Claim: council/FrontendDev] | -| F4 | 商品图片/详情内容加载现状评估 | [To Claim: council/FrontendDev] | - -### FirstPrinciples - -| Task | 内容 | 状态 | -|------|------|------| -| P1 | 多座位提交 API 设计正交性分析 | [To Claim: council/FirstPrinciples] | -| P2 | spec_base_id_map 设计复杂度评审 | [To Claim: council/FirstPrinciples] | -| P3 | 票务场景是否需要走购物车流程?关键问题识别 | [To Claim: council/FirstPrinciples] | +| B1 | Phase 4 Tree API 设计文档评估 | [Done: council/BackendArchitect] | +| B2 | SeatMapService + seatmap API 完整性检查 | [Done: council/BackendArchitect] | +| B3 | seatSpecMap 注入方案设计 | [Done: council/BackendArchitect] | +| B4 | CartSave extension_data 多座位链路分析 | [Done: council/BackendArchitect] | +| B5 | 输出评估报告 + 投票 | [Done: council/BackendArchitect] | --- @@ -40,34 +57,42 @@ | Round | 内容 | 产出 | |-------|------|------| -| Round 1(当前) | 各自调研,BackendArchitect 读关键代码 | plan.md 更新 + 各自 findings 文件 | -| Round 2 | 合并 findings,交叉 Review | `reviews/council-phase2-assessment.md` 草稿 | -| Round 3 | 收敛共识,形成修复方案建议 | 完整 assessment 文档 + 可执行 todo | -| Round 4 | 最终 review + 投票 | `[CONSENSUS: YES]` | +| Round 1(当前) | 独立评估,输出报告 | `docs/council-eval-backendarchitect.md` + plan.md | +| Round 2 | 合并各成员评估,协调者汇总 | 等待 FrontendDeveloper/SecurityEngineer/PerformanceBenchmarker | +| Round 3 | 执行 P0 修复(getSoldSeats + Hook + CartSave 决策) | 代码变更 | +| Round 4 | 最终 Review + Consensus | `[CONSENSUS: YES]` | --- -## BackendArchitect 调研计划 +## 待执行任务(P0 → P1 排序) -**关键文件列表**(按优先级): +### P0 — 立即修复 -1. `shopxo/app/service/GoodsCartService.php` — Save() 方法签名 + 参数契约 -2. `shopxo/app/service/GoodsService.php` — GoodsSpecDetail() 规格加载 -3. `shopxo/app/plugins/vr_ticket/view/goods/ticket_detail.html` — submit() 函数 -4. `shopxo/app/plugins/vr_ticket/service/SeatSkuService.php` — GetGoodsViewData() -5. `docs/VR_GOODS_CONFIG_SPEC.md` — v3.0 配置格式 -6. `docs/PHASE2_PLAN.md` — Phase 2 计划文档 +| Task | 内容 | 依赖 | 状态 | +|------|------|------|------| +| P0-1 | 实现 `SeatSkuService::getSoldSeats()` | 无 | [To Claim] | +| P0-2 | 注册 `plugins_service_goods_data` Hook | 无 | [To Claim] | +| P0-3 | 实现 `TicketService::InjectGoodsDetailData()` | P0-2 | [To Claim] | +| P0-4 | 决策 CartSave 路径(建议:绕过购物车直购) | 无 | [To Claim] | -**调研目标**: -- B1: 找到 GoodsCartService::Save 的参数期望(goods_id, spec_base_id, stock 等字段) -- B2: 对比 ticket_detail.html submit() 当前构造的 params -- B3: 找到 ShopXO spec 数据加载的入口(是否有 API endpoint 可被前端直接调用?) -- B4: 设计 ticket_detail.html 中加载真实库存的最小方案 +### P1 — 短期完善 + +| Task | 内容 | 依赖 | 状态 | +|------|------|------|------| +| P1-1 | 实现完整 `/seatmap` API | P0-1 | [To Claim] | +| P1-2 | 完善 Phase 4 Tree API 设计文档 | 无 | [To Claim] | +| P1-3 | 扩展 Admin.php 支持 seatSpecMap 管理 | P0-3 | [To Claim] | + +### P2 — 可延后 + +| Task | 内容 | 依赖 | 状态 | +|------|------|------|------| +| P2-1 | Phase 4 Tree API 实现 | P1-2 | [To Claim] | +| P2-2 | 实时座位锁定机制(SKIP LOCKED 已部分实现) | P1-1 | [To Claim] | --- -## 输出目标 +## 输出 -- BackendArchitect findings: `reviews/BackendArchitect-on-phase2.md` -- 最终合并文档: `reviews/council-phase2-assessment.md` -- 每个问题的根因分析 + 前后端修复方案 + API 设计建议 + 第一性原则关键提醒 +- 评估报告:`docs/council-eval-backendarchitect.md` +- 投票:`docs/council-eval-backendarchitect.md#五投票`