council(finalize): FirstPrinciples - consolidated Phase 2 assessment report
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>council/FirstPrinciples
parent
a6cb084ca2
commit
875b7c1848
13
plan.md
13
plan.md
|
|
@ -61,7 +61,7 @@
|
|||
|
||||
- BackendArchitect: `[APPROVE]` — merged report produced
|
||||
- FrontendDev: `[APPROVE]` — merged report produced, Issue 2 zoom fix proposed
|
||||
- FirstPrinciples: `[APPROVE]` — review complete, see `reviews/FirstPrinciples-on-council-assessment.md`
|
||||
- FirstPrinciples: `[APPROVE]` — final consolidated report at `reviews/council-phase2-assessment.md`
|
||||
|
||||
## 新增发现(P2 潜在)
|
||||
|
||||
|
|
@ -71,10 +71,13 @@
|
|||
|
||||
## 输出文件
|
||||
|
||||
| 文件 | 内容 | 负责人 |
|
||||
|------|------|--------|
|
||||
| `reviews/FirstPrinciples-on-phase2-assessment.md` | 第一性原则分析报告 | FirstPrinciples |
|
||||
| `reviews/council-phase2-assessment.md` | 合并评估报告(最终输出) | FirstPrinciples |
|
||||
| 文件 | 内容 | 状态 |
|
||||
|------|------|------|
|
||||
| `reviews/council-phase2-assessment.md` | 合并评估报告(最终输出) | ✅ Done |
|
||||
| `reviews/FirstPrinciples-on-council-assessment.md` | FirstPrinciples review | ✅ Done |
|
||||
| `reviews/FirstPrinciples-on-phase2-assessment.md` | 第一性原则分析报告 | ✅ Done |
|
||||
|
||||
**Phase 2 评估完成,所有文件已合并至 `reviews/council-phase2-assessment.md`**
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,259 @@
|
|||
# VR 演唱会票务小程序 Phase 2 技术评估报告
|
||||
|
||||
> 日期:2026-04-21
|
||||
> 协作产出:BackendArchitect、FrontendDev、FirstPrinciples
|
||||
> 源码依据:BuyService.php、GoodsCartService.php、SeatSkuService.php、ticket_detail.html
|
||||
|
||||
---
|
||||
|
||||
## 执行摘要
|
||||
|
||||
Phase 2 完成了对 4 个已知问题的根因分析,识别了 1 个未被提及的潜在 Bug(多场次),并给出了分优先级的修复方案。
|
||||
|
||||
**结论**:所有 4 个问题均为可修复的技术问题,无架构级障碍。
|
||||
|
||||
---
|
||||
|
||||
## 问题总览
|
||||
|
||||
| # | 问题 | 优先级 | 根因分类 |
|
||||
|---|------|--------|---------|
|
||||
| 1 | 购买提交流程失效 | **P0** | GET→POST 机制错误 + 参数契约不匹配 |
|
||||
| 2 | 缩放时舞台不跟随 | **P1** | DOM 结构导致 transform 不共享 |
|
||||
| 3 | spec 加载机制回滚 | **P1** | ShopXO spec 匹配机制不兼容 + API 端点缺失 |
|
||||
| 4 | 商品详情/图片加载 | **P2** | 模板未引入 ShopXO 内容渲染组件 |
|
||||
|
||||
**新增发现(潜在 Bug)**:
|
||||
| # | 问题 | 优先级 |
|
||||
|---|------|--------|
|
||||
| 5 | GetGoodsViewData() 只返回第一个场次 | **P2 潜在** |
|
||||
|
||||
---
|
||||
|
||||
## Issue 1(P0):购买提交流程失效
|
||||
|
||||
### 根因分析(三层叠加)
|
||||
|
||||
**第一层(致命)**:`location.href` 产生 GET 请求,但 `Buy::Index()` 只在 `data_post` 为真时调用 `BuyDataStorage()`。
|
||||
|
||||
```php
|
||||
// Buy.php:56-62
|
||||
public function Index()
|
||||
{
|
||||
if($this->data_post) {
|
||||
BuyService::BuyDataStorage($this->user['id'], $this->data_post);
|
||||
return MyRedirect(MyUrl('index/buy/index'));
|
||||
} else {
|
||||
// GET 分支:从 session 读取,URL 参数从未被读取
|
||||
$buy_data = BuyService::BuyDataRead($this->user['id']);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`goods_params` URL 参数从未被读取 → `BuyDataStorage` 从未被调用 → `BuyDataRead` 返回空 → "商品数据为空"。
|
||||
|
||||
**第二层(严重)**:字段名不匹配。
|
||||
- 前端发送:`goods_params`(JSON 数组)
|
||||
- ShopXO 期望:`goods_data`(JSON 数组)
|
||||
|
||||
**第三层(中等)**:规格匹配机制不兼容。
|
||||
- 当前:`spec_base_id: parseInt(specBaseId)` — 直接传 ID
|
||||
- ShopXO:`spec: [{type, value}]` — 通过 type:value 字符串匹配 GoodsSpecValue 表
|
||||
|
||||
### 推荐修复
|
||||
|
||||
**前端(FrontendDev)**:
|
||||
|
||||
```javascript
|
||||
// 隐藏表单 POST(最小化变更,复用 Buy 链路)
|
||||
submit: function() {
|
||||
var goodsDataList = this.selectedSeats.map(function(seat, i) {
|
||||
var specBaseId = self.specBaseIdMap[seat.seatKey] || self.sessionSpecId;
|
||||
return {
|
||||
goods_id: self.goodsId,
|
||||
spec: [{type: '座位', value: seat.seatKey}], // ShopXO 规格格式
|
||||
stock: 1
|
||||
};
|
||||
});
|
||||
|
||||
var form = document.createElement('form');
|
||||
form.method = 'POST';
|
||||
form.action = MyUrl('index/buy/index');
|
||||
var input = document.createElement('input');
|
||||
input.name = 'goods_data';
|
||||
input.value = Base64.encode(JSON.stringify(goodsDataList));
|
||||
form.appendChild(input);
|
||||
document.body.appendChild(form);
|
||||
form.submit();
|
||||
}
|
||||
```
|
||||
|
||||
**关于 extension_data(观演人信息)**:ShopXO 原生不支持 `extension_data`(OrderDetail 表无此字段)。推荐方案:新增 `vr_order_attendee` 表,在 `BuyService::OrderInsert()` 后存储观演人信息。
|
||||
|
||||
### 关键提醒(FirstPrinciples)
|
||||
|
||||
> Issue 1 的准确描述是「Buy 传输机制损坏」,而非「购物车格式错误」。
|
||||
>
|
||||
> 当前代码实际上绕过了购物车,直接进入 Buy 链路。这说明直觉上的「绕过购物车」需求并不存在——只是 `submit()` 的传递方式错了。
|
||||
|
||||
---
|
||||
|
||||
## Issue 2(P1):缩放时舞台不跟随
|
||||
|
||||
### 根因分析
|
||||
|
||||
```html
|
||||
<div class="vr-seat-map-wrapper">
|
||||
<div class="vr-stage">舞 台</div> <!-- 平级 sibling -->
|
||||
<div class="vr-seat-rows" id="seatRows"></div>
|
||||
</div>
|
||||
```
|
||||
|
||||
CSS `transform: scale()` 只作用于应用元素的子树。舞台和座位行是平级元素,缩放座位时舞台不动。
|
||||
|
||||
### 推荐修复
|
||||
|
||||
**方案:将舞台和座位包裹在同一 zoom 容器内**(FrontendDev 实施)
|
||||
|
||||
```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-zoom-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
transform-origin: center top;
|
||||
transition: transform 0.2s ease;
|
||||
}
|
||||
```
|
||||
|
||||
**注意**:舞台的 `border-radius` 在缩放后可能变形,需在 zoom 场景下单独调整。
|
||||
|
||||
---
|
||||
|
||||
## Issue 3(P1):spec 加载机制回滚
|
||||
|
||||
### 根因分析
|
||||
|
||||
**问题 A**:`loadSoldSeats()` 是空 stub,无任何网络请求。
|
||||
|
||||
```javascript
|
||||
loadSoldSeats: function() {
|
||||
// TODO: 从后端加载已售座位
|
||||
// $.get(...); // 空,无任何调用
|
||||
}
|
||||
```
|
||||
|
||||
**问题 B**:ShopXO 的 `GoodsSpecDetail` 通过 `spec.value` 字符串匹配规格,而非直接接受 `spec_base_id`。
|
||||
|
||||
```php
|
||||
// GoodsService.php:2749-2757
|
||||
$spec = array_column($params['spec'], 'value');
|
||||
$where['value'] = $spec;
|
||||
$ids = Db::name('GoodsSpecValue')->where($where)->column('goods_spec_base_id');
|
||||
```
|
||||
|
||||
VR 票务场景下,每个座位对应独立的 `GoodsSpecBase` 记录(inventory=1)。ShopXO 标准流程需要生成 `GoodsSpecValue` 记录(type='座位', value='seatKey')。
|
||||
|
||||
### 推荐修复
|
||||
|
||||
**后端(BackendArchitect)**:新增插件 API 端点
|
||||
|
||||
```
|
||||
GET /?s=api/vr-ticket/sold-seats&goods_id=X&spec_base_id=Y
|
||||
Response: {code: 0, data: {sold_seats: ["A_1", "A_2", "B_5"]}}
|
||||
```
|
||||
|
||||
前端在选中场次后调用此接口,标记 `.sold` class。
|
||||
|
||||
**关于 spec 加载**:ShopXO 的 spec 数据通过 `GetGoodsViewData()` 在模板渲染时注入前端。如果已生成了 `GoodsSpecBase` 记录,最直接的方式是维护座位→规格映射,并提供独立的已售座位查询 API,而不是依赖 ShopXO 的规格匹配流程。
|
||||
|
||||
---
|
||||
|
||||
## Issue 4(P2):商品详情/图片加载
|
||||
|
||||
### 现状
|
||||
|
||||
- **商品内容**(`$goods['content']`):✅ 正常渲染
|
||||
- **商品相册**(`$goods['images']`):⚠️ 数据存在但未使用
|
||||
- `renderSessions()` 依赖 `goods_spec_data`,不含 `images`
|
||||
- `.vr-goods-photos` 已定义样式但从未被调用
|
||||
- **`.goods-detail-content` CSS**:⚠️ 缺失
|
||||
|
||||
### 建议
|
||||
|
||||
如需展示商品图片,在模板中添加图片渲染逻辑。如票务详情页不需要 ShopXO 商品内容区,可降级为「确认不需要」。
|
||||
|
||||
---
|
||||
|
||||
## Issue 5(P2 潜在):GetGoodsViewData 只返回第一个场次
|
||||
|
||||
### 根因分析
|
||||
|
||||
```php
|
||||
// SeatSkuService.php:BatchGenerate()
|
||||
return array_merge($data, [
|
||||
'goods_spec_data' => $validConfigs[0], // ← 只取第一个配置
|
||||
'vr_seat_template' => $template,
|
||||
]);
|
||||
```
|
||||
|
||||
`validConfigs[0]` 意味着多场次商品只返回第一个场次的座位模板和规格数据。当一个商品配置了多场演唱会时,其余场次对用户不可见。
|
||||
|
||||
### 修复方向
|
||||
|
||||
修改 `BatchGenerate()` 返回格式,将 `goods_spec_data` 改为数组(`$validConfigs`),前端根据选中场次索引读取对应数据。
|
||||
|
||||
---
|
||||
|
||||
## 第一性原则视角的关键提醒
|
||||
|
||||
1. **P0 的真正来源**:`submit()` 用 GET 做 POST 的事。Buy 链路本身可用,不需要重构 spec 系统。
|
||||
|
||||
2. **spec_base_id_map 是性能缓存**:不是业务必需。如果 `onOrderPaid` 能通过 seatKey 查询到 spec_base_id,map 可以去掉。保留它是合理的优化,但需要确保同步机制。
|
||||
|
||||
3. **购物车对票务无价值**:当前实现已绕过购物车进入 Buy 链路。选座的实时性决定了购物车会增加超卖风险,快速购买是正确方向。
|
||||
|
||||
4. **已售座位展示是 P1,不是 P0**:真正的 P0 是 `onOrderPaid` 防双售。前端是否实时显示已售状态,是体验优化,不是业务正确性的根本。
|
||||
|
||||
5. **`onOrderPaid` 是座位唯一性权威**(未审计):在 P0 修复部署前,必须验证此 Hook 是否正确实现了座位锁定逻辑。若未实现,任何前端修复都无法防止重复销售。
|
||||
|
||||
6. **最小修复范围**:只需修复 `submit()` 的传递方式(隐藏表单 POST)。不需要重构 spec 系统,不需要引入实时已售座位更新(除非 spec 加载方案已实施)。
|
||||
|
||||
---
|
||||
|
||||
## 修复优先级与分工
|
||||
|
||||
| 优先级 | 问题 | 负责 | 修复说明 |
|
||||
|--------|------|------|---------|
|
||||
| P0 | Issue 1 submit() | FrontendDev | 改用隐藏表单 POST,复用 Buy 链路 |
|
||||
| P1 | Issue 2 舞台缩放 | FrontendDev | 新增 zoom wrapper 容器 |
|
||||
| P1 | Issue 3 spec 加载 | BackendArchitect | 新增插件 sold_seats API 端点 |
|
||||
| P2 | Issue 4 商品详情 | FrontendDev | 确认是否需要,补充 CSS |
|
||||
| P2 | Issue 5 多场次 | BackendArchitect | BatchGenerate 返回数组格式 |
|
||||
| FP | extension_data | BackendArchitect | 新增 vr_order_attendee 表 |
|
||||
|
||||
---
|
||||
|
||||
## 附录:ShopXO Buy 链路关键代码索引
|
||||
|
||||
| 文件 | 行号 | 说明 |
|
||||
|------|------|------|
|
||||
| `Buy.php` | 56-62 | Index() — POST/GET 分支,BuyDataStorage/BuyDataRead |
|
||||
| `BuyService.php` | ~51 | BuyGoods — goods_data 参数校验 + base64 解码 |
|
||||
| `BuyService.php` | ~173 | GoodsSpecificationsHandle — 规格解析 |
|
||||
| `BuyService.php` | ~104-109 | GoodsSpecDetail 调用 — 通过 spec.value 匹配 |
|
||||
| `GoodsService.php` | 2720-2795 | GoodsSpecDetail — type:value 查询 GoodsSpecValue |
|
||||
| `BuyService.php` | 1932-1936 | BuyDataStorage — session 缓存(21600s TTL) |
|
||||
| `SeatSkuService.php` | BatchGenerate | 返回 validConfigs[0] — 多场次 Bug |
|
||||
|
||||
---
|
||||
|
||||
*VR 演唱会票务小程序 Phase 2 技术评估 — Council 协作完成*
|
||||
Loading…
Reference in New Issue