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

10 KiB
Raw Blame History

Council 评估报告 — BackendArchitectRound 3 更新)

评估日期2026-05-26 | 角色:后端架构师 | Git: 0d6d20062


一、现状评估2026-05-26 现场核查)

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.phpWorktree 为空
api/Goods.php::seatmap() ⚠️ 父仓库有,工作树无 调用不存在的 SeatMapService::GetSeatMap()

Round 3 修正api/Goods.php 在父仓库存在,有 seatmap() 方法,但父仓库 Worktree 隔离造成版本混淆。Tree API 设计完整,代码仅存在 SeatSkuService::GetGoodsViewData() 部分复用逻辑。

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() 完整

回 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.phpseatmap() 方法(父仓库)调用 SeatMapService::GetSeatMap(),但该类不存在(实际类名为 SeatSkuService,方法为 GetGoodsViewData)。这是 命名混淆 Bug

1.3 seatSpecMap 注入商品详情 API

状态: Gap 仍成立

组件 状态 说明
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 注入

确认Gap 1 完全成立。H5 工作是因为 ticket_detail.html 是模板文件,在服务器端直接调用 SeatSkuService::GetGoodsViewData()。UniApp 走 API 层,plugins_service_goods_data Hook 不注册,则 ShopXO 的商品详情 API 不会触发 VR 座位数据注入。

1.4 CartSave extension_data 多座位链路

状态: H5 已验证UniApp 只需复刻

组件 状态 说明
ticket_detail.html:762 订单提交 已实现 extension_data 嵌套在 order_base
TicketService::onOrderPaid() 已实现 逐行生成票(多座位支持)
UniApp CartSave 与 H5 相同 JSON 格式 Gap 2 已消除,只要后端提供端点即可

Round 2 修正确认Gap 2 已验证可工作,核心问题变为"UniApp 需要相同的 API 端点格式"。


二、发现问题Round 3 修正)

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 一致

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 一致

三、技术方案建议

P0-1 FixgetSoldSeats() 实现

文件SeatSkuService.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 Fixplugins_service_goods_data Hook 注册

文件Hook.php:13 追加 case

case 'plugins_service_goods_data':
    $goodsId = $params['goods_id'] ?? 0;
    if ($goodsId > 0) {
        TicketService::InjectGoodsDetailData($params['data'], $goodsId);
    }
    break;

新增方法TicketService.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'];

    // seatSpecMapseat_key → 完整规格(含 row/col/room/section/price
    $data['seatSpecMap'] = $viewData['seatSpecMap'] ?? [];
    // goods_spec_data场次列表含价格
    $data['goods_spec_data'] = $viewData['goods_spec_data'] ?? [];
    // specTypeList5维规格维度定义
    $data['specTypeList'] = $viewData['specTypeList'] ?? [];
    // seatMap座位图渲染数据
    $data['seatMap'] = $seatTemplate['seat_map'] ?? null;
    // goods_config当前生效的配置块
    $data['goods_config'] = $viewData['goods_config'] ?? null;
}

P1 Fix命名统一SeatSkuService vs SeatMapService

当前引用 应改为 位置
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

注意:此修复应在 api/ 目录重新纳入 Git 追踪后执行。


四、优先级建议

优先级 任务 预计工时 收益
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-22h 内可完成)
  2. 再推进 api/ 目录规范化
  3. Phase 4 在 P0/P1 稳定后作为独立任务启动

五、投票Round 3

议题:下一步主攻方向

投票A — 后端优先

理由

  1. 最小改动最大收益getSoldSeats() 实现30 行)+ Hook 注册10 行)= 约 40 行代码,修复后 UniApp 票务链路全部解锁。这是最低成本的最高收益修复。

  2. 运行时崩溃是根本阻塞Round 2 的"H5 已绕过"分析正确,但忽略了 Index.php:soldSeats 对所有直接调用 API 的客户端UniApp / 第三方)都会触发 PHP 致命错误。必须修复。

  3. Hook 注册是 UniApp 的唯一入口Gap 1 对 H5 无影响H5 走模板层直接调用),但对 UniApp 来说是唯一入口。没有 Hook 注册UniApp 永远无法通过标准 API 获取 seatSpecMap。

  4. Gap 2CartSave已消除Round 2 确认 H5 已验证 extension_data 链路正确UniApp 只需复刻同样的 JSON 结构,不存在后端缺口。

  5. Phase 4 不应前置Tree API 是体验增强不是购买流程的基础设施在核心票务链路P0未稳定前启动 Phase 4 是资源浪费。

补充:对其他提案的评估

  • B前端优先不可行。UniApp 选座组件需要 seatSpecMap 数据,但 Gap 1 不修则数据不可得。前端等 Hook 注册后再开发效率更高。
  • C双线并行:在 P0 明确可修复的前提下,"双线并行"是浪费:前端等待期间无所事事,不如后端一次性修完再解锁前端。
  • DPhase 4 优先)Phase 4 是锦上添花不是基础设施。Tree API 失败不影响用户购票核心流程。

报告人BackendArchitect | 2026-05-26 | Round 3