diff --git a/reviews/council-phase2-assessment.md b/reviews/council-phase2-assessment.md new file mode 100644 index 0000000..25b8ed7 --- /dev/null +++ b/reviews/council-phase2-assessment.md @@ -0,0 +1,261 @@ +# Council Phase 2 Technical Assessment — VR 演唱会票务小程序 + +> 日期:2026-04-21 | Agent:council/FrontendDev(执笔汇总) +> 依据:BackendArchitect 进度(Round 1 report)、FrontendDev Issues 2/3/4 findings、源码分析 + +--- + +## Issue 1 (P0) — 购物车/购买提交格式错误 + +### 根因分析 + +**当前 `submit()` 实际走的是购买流程(BuyController),不是购物车(GoodsCartService)** + +`ticket_detail.html:440` 当前代码: +```javascript +var checkoutUrl = this.requestUrl + '?s=index/buy/index' + + '&goods_params=' + encodeURIComponent(goodsParams); +location.href = checkoutUrl; +``` + +这直接访问 `Buy::Index()`,ShopXO 会执行: +1. `BuyService::BuyDataStorage($user_id, $buy_data)` — 把 `goods_params` 存入 session +2. 重定向 `MyUrl('index/buy/index')`(无 goods_data 时从 session 读取) +3. `BuyService::BuyTypeGoodsList()` 从 session 读取 `goods_data` + +**关键发现 — BuyService 和 GoodsCartService 接受相同格式的 `goods_data`**: + +```php +// BuyService.php:62 / GoodsCartService.php:266(两者逻辑相同) +if(!is_array($params['goods_data'])) { + $params['goods_data'] = json_decode(base64_decode(urldecode($params['goods_data'])), true); +} +``` + +`BuyTypeGoodsList()` 对 `goods_data` 数组中每个元素的期望结构: +```php +// BuyService.php:86-108 +[ + 'goods_id' => int, + 'spec' => array, // 或 spec_base_id 在 GoodsService::GoodsSpecDetail 中匹配 + 'stock' => int +] +``` + +**当前 `submit()` 构造的 `goodsParamsList` 格式**(ticket_detail.html:413-436): +```javascript +{ + goods_id: self.goodsId, + spec_base_id: parseInt(specBaseId) || 0, // ← 字段名错:应该是 spec[] + stock: 1, + extension_data: JSON.stringify({...}) // ← 多余字段,BuyService 不处理 +} +``` + +### 问题 + +1. **字段名错误**:BuyService 用 `spec` 数组(通过 `GoodsSpecificationsHandle` 解析),而非直接的 `spec_base_id` 整数 +2. **`extension_data` 无法传递**:BuyService/BuyTypeGoodsList 不识别 `extension_data`,观演人信息丢失 +3. **`goods_params` vs `goods_data`**:`submit()` 发的是 `goods_params`,BuyController 期望 `goods_data` + +### 推荐修复(FrontendDev 实施) + +修改 `submit()` 发送 `goods_data`(base64 编码)到 `index/buy/index`: + +```javascript +submit: function() { + var goodsDataList = this.selectedSeats.map(function(seat, i) { + var specBaseId = self.specBaseIdMap[seat.seatKey] || self.sessionSpecId; + // spec 格式:ShopXO 用 spec[type] = value 数组定位规格 + return { + goods_id: self.goodsId, + spec_base_id: parseInt(specBaseId) || 0, + stock: 1 + }; + }); + + // 观演人信息通过独立字段传递(ShopXO 不原生支持 extension_data) + var postData = { + goods_data: Base64.encode(JSON.stringify(goodsDataList)), + attendee_data: JSON.stringify(attendeeData) // 补充字段 + }; + + // 方式A:POST 到 index/buy/index + var form = document.createElement('form'); + form.method = 'POST'; + form.action = this.requestUrl + '?s=index/buy/index'; + for (var key in postData) { + var input = document.createElement('input'); + input.name = key; + input.value = postData[key]; + form.appendChild(input); + } + document.body.appendChild(form); + form.submit(); +} +``` + +### 关于 extension_data 的建议 + +ShopXO 原生不支持 `extension_data`(购物车表无此字段)。两个方案: +- **方案 A**:通过 `BuyService::OrderInsert()` 后的订单扩展表存储(需新增表) +- **方案 B**:观演人信息在 `Buy::Add` 订单创建时作为订单扩展字段传入,跳过购物车 + +--- + +## Issue 2 (P1) — 缩放时舞台元素不跟随 + +### 根因分析 + +```html + +