vr-shopxo-plugin/docs/council-eval-backendarchite...

223 lines
7.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 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
两种路径:
**路径 1ShopXO 原生扩展)**:修改 `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 APIPhase 4设计尚未完成过早投入实现是浪费应在 P0/P1 修复后作为第二阶段启动
4. "双线并行"中的分工建议:
- 后端:修复 P0 Gap + seatmap API + Hook 注册
- 前端:基于现有 `GetGoodsViewData` 数据模型开发选座组件,等 Hook 注册后接入 seatSpecMap
**补充:对其他提案的评估**
- **A后端优先**:合理,但"后端优先"意味着前端完全等待 — 这会浪费前端已有的 H5 票务页保底能力
- **B前端优先**:不可行,前端开发被 P0 Gap 阻断seatSpecMap 缺失时选座组件无法正确映射 SKU
- **DPhase 4 优先)**Phase 4 是锦上添花不是基础功能Tree API 失败不影响票务核心购买流程
---
*报告人BackendArchitect | 2026-05-26*