7.6 KiB
7.6 KiB
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() 方法
// 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
// Hook.php plugins_service_goods_data 分支
case 'plugins_service_goods_data':
$ret = TicketService::InjectGoodsDetailData($params);
break;
// 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 拦截保存
// Hook.php 新增
case 'plugins_service_goods_cart_save_handle':
TicketService::CartSaveWithSeats($params);
break;
路径 2(绕过购物车):票务商品不走购物车,直接从选座→订单(参考大麦/猫眼模式)
建议:票务场景走路径 2(绕过购物车),因为:
- 每个座位唯一库存(inventory=1),购物车没有意义
- 用户选座→立即下单→支付,更符合票务直觉
- 避免购物车超卖复杂性
方案 B:实现完整 /seatmap API
在 index/Index.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)
- 实现
SeatSkuService::getSoldSeats()— 修复 soldSeats API - 注册
plugins_service_goods_dataHook — 解锁 seatSpecMap 注入 - 决策 CartSave 路径:票务商品走绕过购物车的直购模式
短期执行(P1)
- 实现完整
/seatmapAPI — 统一座位数据接口 - 完善 Phase 4 Tree API 设计文档 — 补充 API 契约和路由设计
可延后(P2)
- Phase 4 Tree API 实现 — 依赖 Phase 2/3 稳定后启动
五、投票
议题:下一步主攻方向
投票:C — 双线并行
理由:
- 后端 P0 Gap(
getSoldSeats缺失、Hook 未注册)属于"修完就能用"的阻断性问题,工作量小但价值高,完全修复预计 2-3 小时 - 前端当前有 H5 票务页(
ticket_detail.html)作为保底,可以独立推进 uniapp 选座组件开发,无需等待全部 API - Tree API(Phase 4)设计尚未完成,过早投入实现是浪费,应在 P0/P1 修复后作为第二阶段启动
- "双线并行"中的分工建议:
- 后端:修复 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