vr-shopxo-plugin/reviews/council-phase2-assessment.md

261 lines
8.8 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 Phase 2 Technical Assessment — VR 演唱会票务小程序
> 日期2026-04-21 | Agentcouncil/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) // 补充字段
};
// 方式APOST 到 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
<!-- ticket_detail.html:141-144 -->
<div class="vr-seat-map-wrapper">
<div class="vr-stage">舞 台</div> <!-- 平级 sibling -->
<div class="vr-seat-rows" id="seatRows"></div> <!-- 平级 sibling -->
</div>
```
`.vr-stage``.vr-seat-rows` 是平级元素。对 `.vr-seat-rows` 应用 CSS `transform: scale()` 时,座位缩放,舞台不动。
### 修复方案FrontendDev 实施)
引入 `.vr-zoom-container` 包裹舞台和座位:
```html
<div class="vr-seat-map-wrapper">
<div class="vr-zoom-container" id="zoomContainer">
<div class="vr-stage">舞 台</div>
<div class="vr-seat-rows" id="seatRows"></div>
</div>
</div>
```
```css
.vr-seat-map-wrapper { overflow: hidden; }
.vr-zoom-container {
display: flex;
flex-direction: column;
align-items: center;
transform-origin: center top;
transition: transform 0.2s ease;
}
```
缩放 JS 只需操作 `#zoomContainer``transform: scale()`
**风险**:舞台 `border-radius: 50% 50% 0 0 / 20px 20px 0 0` 在缩放后会变形,需要调整。
---
## Issue 3 (P1) — spec 加载问题(已回滚)
### 根因分析
`ticket_detail.html:375-383`
```javascript
loadSoldSeats: function() {
// TODO: 从后端加载已售座位
// $.get(...); // 空 stub无任何网络请求
}
```
- `loadSoldSeats()` 是空 TODO stub无任何 AJAX 调用
- 前端无 `sold_seats` 后端接口
- 商品规格/库存的 `goods_spec_data` 来自 PHP 模板GetGoodsViewData 返回),非前端动态加载
### 修复方案
1. 后端新增 `plugins/vr_ticket/index/sold_seats` 接口,返回已售座位 ID 列表
2. 前端 `loadSoldSeats()` 调用该接口,标记 `.sold` class
关于 spec 加载ShopXO 的 spec 数据通过 `GetGoodsViewData()` 在模板渲染时注入前端,前端无需额外 API 调用即可获取场次/价格数据。
---
## Issue 4 (P2) — 商品详情/图片加载评估
### 现状
- **商品内容**`$goods['content']`):✅ 正常渲染PHP 直接输出 HTML
- **商品相册**`$goods['images']`):⚠️ 数据存在但**未使用**
- `renderSessions()` 依赖 `goods_spec_data`spec 数组),不含 `images`
- `.vr-goods-photos` 已定义样式但从未被调用
- **`.goods-detail-content` CSS**:⚠️ 缺失,导致内容可能样式混乱
### 建议
如需展示商品图片,在模板中添加:
```php
<?php if (!empty($goods['images'])): ?>
<div class="vr-goods-info">
<div class="vr-goods-photos">
<?php foreach(array_slice(explode(',', $goods['images']), 0, 5) as $img): ?>
<img src="<?php echo ResourcesService::AttachmentPathHandle($img); ?>">
<?php endforeach; ?>
</div>
</div>
<?php endif; ?>
```
---
## 第一性原则综合分析
### 多座位提交是否需要走购物车?
**核心问题**:票务选座后,是否必须经过购物车?
当前设计:选座 → 直接进入购买确认页(`index/buy/index`**实际上跳过了购物车**(通过 URL 参数 `goods_params` 直传)。这是合理的,因为:
1. 选座是实时操作,座位状态随时变化,购物车会给用户错误预期
2. 多座位同时下单,购物车逐条处理会导致超卖风险
3. 用户目标是"下单"而非"加购物车"
**建议**:正式命名为"快速购买",而非"购物车"API 契约改为 `index/buy/add`(订单添加)而非 `index/cart/save`
### spec_base_id_map 是否过于复杂?
当前设计:每个座位一个 `spec_base_id`,通过 `rowLabel_colNum` 查找。
**更简单的方案**:座位作为 `extension_data` 存储在订单级别,单个 Zone 级别 SKU 即可。
但座次级 SKU 的价值在于:
1. 库存隔离(每个座位只能被一人购买)
2. 订单详情展示具体座位信息
**建议保持现状**,但需确保 `spec_base_id` 正确映射。
### extension_data 的业务价值
观演人信息(姓名、手机、身份证)必须传递到订单,但 ShopXO 原生不支持。
**推荐方案**:扩展订单商品表 `OrderDetail` 或新增 `vr_order_attendee` 表:
```sql
CREATE TABLE vr_order_attendee (
id INT AUTO_INCREMENT PRIMARY KEY,
order_id INT NOT NULL,
seat_label VARCHAR(50) NOT NULL,
real_name VARCHAR(100) NOT NULL,
phone VARCHAR(20) NOT NULL,
id_card VARCHAR(20),
add_time INT
);
```
---
## 修复优先级汇总
| 优先级 | Issue | 修复方向 | 负责 |
|--------|-------|----------|------|
| P0 | Issue 1 submit() 格式 | 改为 `goods_data` + POST修复字段名 | FrontendDev |
| P1 | Issue 2 舞台缩放 | 引入 `.vr-zoom-container` 包裹舞台+座位 | FrontendDev |
| P1 | Issue 3 spec 加载 | 新增 `sold_seats` 接口 + 前端调用 stub | BackendArchitect |
| P2 | Issue 4 商品图片 | 补充图片渲染代码和 CSS | FrontendDev |
| FP | extension_data | 新增 `vr_order_attendee` 表存储观演人 | BackendArchitect |
---
## 参考文献
- FrontendDev findings: `reviews/FrontendDev-Issue2-StageZoom.md`, `FrontendDev-Issue3-SpecLoading.md`, `FrontendDev-Issue4-GoodsDetail.md`
- 源码:`ticket_detail.html`, `BuyService.php`, `GoodsCartService.php`, `SeatSkuService.php`