# BackendArchitect Phase 2 技术评估 Findings > Agent: council/BackendArchitect | Date: 2026-04-21 | Round 2 --- ## B1: GoodsCartService::Save API 契约分析 ### 结论:`Save()` 方法未找到,但找到了真正的下单入口 **关键发现**:ShopXO 的票务下单流程 **不经过购物车**,而是直接 POST 到 `index/buy/index`。 ```php // Buy.php Index() — 真正的入口 public function Index() { if($this->data_post) { // 将数据存储到缓存,以 user_id 为 key BuyService::BuyDataStorage($this->user['id'], $this->data_post); return MyRedirect(MyUrl('index/buy/index')); } // 读取缓存,展示订单确认页 $buy_data = BuyService::BuyDataRead($this->user['id']); } ``` **真正接收的数据结构**(来自 `BuyService::BuyInitialize`): ```php // BuyService.php ~line 51 — 参数契约 $p = [ [ 'checked_type' => 'empty', 'key_name' => 'goods_data', // ← 核心字段 'error_msg' => MyLang('common_service.buy.buy_goods_data_error_tips'), ], ]; // goods_data 格式: // [{goods_id, spec, stock, ...}] // 或从 base64 解码:json_decode(base64_decode(urldecode($params['goods_data'])), true) ``` ### BuyService::BuyInitialize 处理流程 ```php foreach($params['goods_data'] as $v) { // 1. 规格解析 — GoodsSpecificationsHandle() // 期望: {goods_id, spec: [{type, value}], stock, extension_data?, ...} $goods['spec'] = self::GoodsSpecificationsHandle($v); // 2. 调用 GoodsService::GoodsSpecDetail(spec: [{type, value}]) // ← 关键:这里通过 spec.value 匹配 GoodsSpecValue 表,而不是 spec_base_id // 如果 spec 为空但商品有多规格,必须报错 // 如果 spec 不为空但商品无规格,也必须报错 // 3. 从返回的 spec_base 获取 inventory, price, spec_base_id $goods['inventory'] = $goods_base['data']['spec_base']['inventory']; $goods['price'] = $goods_base['data']['spec_base']['price']; $goods['spec_base_id'] = $goods_base['data']['spec_base']['id']; } ``` ### 结论(B1) **ShopXO 的 spec 匹配机制是 `type:value` 匹配,不是 `spec_base_id` 直接传递。** `GoodsSpecDetail` 内部逻辑: 1. 从 `params['spec']` 提取 `value` 数组 → `spec = array_column($params['spec'], 'value')` 2. `WHERE goods_id=X AND value IN (...)` 查询 `GoodsSpecValue` 表 → 得到 `goods_spec_base_id` 3. 从 `GoodsSpecBase` 读取最终规格记录 --- ## B2: ticket_detail.html submit() 参数校验 ### 当前代码(submit 函数) ```javascript var goodsParamsList = this.selectedSeats.map(function(seat, i) { var specBaseId = self.specBaseIdMap[seat.seatKey] || self.sessionSpecId; return { goods_id: self.goodsId, spec_base_id: parseInt(specBaseId) || 0, // ← 直接传 ID stock: 1, extension_data: JSON.stringify({...}) }; }); // 重定向到 checkoutUrl: var checkoutUrl = this.requestUrl + '?s=index/buy/index' + '&goods_params=' + encodeURIComponent(goodsParams); // ← 拼到 URL ``` **问题 1(严重)**:`goods_params` 是 URL 参数,而不是 POST body。 - ShopXO `Buy::Index()` 通过 `$this->data_post` 判断是否 POST - URL 参数在 `$_GET`,不在 `$_POST`,所以 `$this->data_post` 可能是 `false` - 应该用 `