council(draft): BackendReviewer - add API review report for vr-ticket-uniapp-supplement.md
Identified 5 issues: - QR payload missing 'code' field - Auth mechanism not clearly documented - extension_data storage chain unclear for multi-seat orders - seatSpecMap acquisition method unresolved - Ticket list response fields incomplete Top 3 gaps: seatSpecMap API, extension_data chain, QR payload structure Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>master
parent
abab933b9b
commit
b12dc7959e
|
|
@ -0,0 +1,297 @@
|
||||||
|
# BackendReviewer — VR票务补充文档审查报告
|
||||||
|
|
||||||
|
> 审查时间:2026-05-14
|
||||||
|
> 审查人:BackendReviewer(后端架构师视角)
|
||||||
|
> 文档:`vr-ticket-uniapp-supplement.md`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 文档评分:7/10
|
||||||
|
|
||||||
|
文档结构完整,覆盖了核心数据结构、API 接口、交互规范。但存在若干后端实现与文档描述不一致的问题,以及缺少关键实现细节。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 一、发现的问题
|
||||||
|
|
||||||
|
### 问题 1:QR payload 结构错误(P0 高优先级)
|
||||||
|
|
||||||
|
**文档描述**(第 6.1 节):
|
||||||
|
```json
|
||||||
|
{ "id": 482815, "g": 118, "iat": 1745286000, "exp": 1745287800 }
|
||||||
|
```
|
||||||
|
|
||||||
|
**实际后端**(`TicketService.php:191-197`):
|
||||||
|
```php
|
||||||
|
$qr_payload = BaseService::signQrPayload([
|
||||||
|
'id' => $ticket_id,
|
||||||
|
'g' => $og['goods_id'],
|
||||||
|
'code' => $ticket_code, // ← 文档漏掉了这个字段!
|
||||||
|
'iat' => $now,
|
||||||
|
'exp' => $now + 1800,
|
||||||
|
]);
|
||||||
|
```
|
||||||
|
|
||||||
|
**影响**:前端按文档解析 QR payload 会缺少 `code` 字段,导致:
|
||||||
|
- 无法通过 QR 拿到 ticket_code(核销时可能需要)
|
||||||
|
- QR 验签逻辑可能失败(后端用 `code` 生成签名)
|
||||||
|
|
||||||
|
**修复建议**:文档第 6.1 节 QR payload 应补充 `code` 字段:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"id": 482815,
|
||||||
|
"g": 118,
|
||||||
|
"code": "uuid格式-xxx-xxx",
|
||||||
|
"iat": 1745286000,
|
||||||
|
"exp": 1745287800
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 问题 2:Auth 鉴权机制描述不准确(P1)
|
||||||
|
|
||||||
|
**文档描述**(第 2.3 节):
|
||||||
|
> 无参数(依赖 C 端 session)
|
||||||
|
|
||||||
|
**实际后端**(`api/Ticket.php:24-57`):
|
||||||
|
```php
|
||||||
|
// 支持三种方式:
|
||||||
|
// 1. X-Token header
|
||||||
|
// 2. Authorization: Bearer xxx header
|
||||||
|
// 3. cookie user_info
|
||||||
|
// 4. ShopXO 标准 session
|
||||||
|
```
|
||||||
|
|
||||||
|
**影响**:前端需要明确使用哪个 Auth 方式。UniApp 中推荐使用 `X-Token` header。
|
||||||
|
|
||||||
|
**修复建议**:文档第 2.3 节补充鉴权说明:
|
||||||
|
```
|
||||||
|
请求 Header: X-Token: {user_token}
|
||||||
|
响应 401: 未登录,跳转登录页
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 问题 3:CartSave extension_data 存储链路不清晰(P1)
|
||||||
|
|
||||||
|
**文档描述**(第 2.2 节):
|
||||||
|
```json
|
||||||
|
"extension_data": "{\"attendee\":{...},\"seat\":{...}}"
|
||||||
|
```
|
||||||
|
|
||||||
|
**实际后端**:
|
||||||
|
1. `CartSave` 将 `extension_data` 存入订单主表 `order.extension_data`
|
||||||
|
2. `onOrderPaid` Hook 从 `order.extension_data` 读取 `attendee` 信息
|
||||||
|
3. **但**观演人信息实际上是多座位时每座一个,而不是整个订单共用一个
|
||||||
|
|
||||||
|
**问题**:当用户选多个座位时,每个座位的 `extension_data` 独立存储在各自的 `order_detail` 中,而不是订单主表。后端需要知道每个座位对应哪个观演人。
|
||||||
|
|
||||||
|
**实际实现**(待确认):ShopXO BuyService 是否支持将 `extension_data` 写入 `order_detail` 表?
|
||||||
|
|
||||||
|
**修复建议**:文档需要明确:
|
||||||
|
- `extension_data` 写入位置(order 主表 vs order_detail 行)
|
||||||
|
- 多座位场景下,每个 `goods_data` 条目携带各自的 `extension_data`
|
||||||
|
- 后端 TicketService 如何从 `order_detail` 获取每座的 attendee 信息
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 问题 4:seatSpecMap 获取方式未确定(P1)
|
||||||
|
|
||||||
|
**文档**(第 4.5 节):
|
||||||
|
> 待确认:seatSpecMap 获取方式
|
||||||
|
> - 方案 A(推荐):后端在商品详情 API 中直接返回
|
||||||
|
> - 方案 B:前端 JS 重建(不推荐)
|
||||||
|
> - 方案 C:新增独立接口
|
||||||
|
|
||||||
|
**实际情况**:
|
||||||
|
- `SeatSkuService::GetGoodsViewData()` 是 PHP 方法,UniApp 无法直接调用
|
||||||
|
- 商品详情 API `/api/goods/detail` 默认不返回 `seatSpecMap`
|
||||||
|
- 后端需要改造:在商品详情响应中嵌入 `seatSpecMap`
|
||||||
|
|
||||||
|
**修复建议**:文档应明确推荐方案 A,并列出后端改造任务:
|
||||||
|
```
|
||||||
|
后端任务:在 GoodsService 或插件 hook 中,
|
||||||
|
当 goods.is_vr_ticket=1 时,在商品详情响应中注入 seatSpecMap
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 问题 5:票夹列表响应字段不完整(P2)
|
||||||
|
|
||||||
|
**文档描述**(第 2.3 节):
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"id": 482815,
|
||||||
|
"goods_id": 118,
|
||||||
|
"goods_title": "VR演唱会",
|
||||||
|
"seat_info": "主要展厅 A区 1排1座",
|
||||||
|
"session_time": "15:00-16:59",
|
||||||
|
"venue_name": "测试场馆",
|
||||||
|
"real_name": "张三",
|
||||||
|
"verify_status": 0,
|
||||||
|
"issued_at": "2026-05-01 12:00:00",
|
||||||
|
"short_code": "003a2hgmgety"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**实际后端**(`WalletService::getUserTickets`):
|
||||||
|
- `seat_info` 格式:`场次|场馆|演播室|分区|座位号`(5段,用竖线分隔)
|
||||||
|
- 需要确认返回字段是否与文档一致
|
||||||
|
|
||||||
|
**修复建议**:补充 `qr_data` 字段说明(可选,用于直接展示 QR 图)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 二、关键 Gap(前端开发前必须确认)
|
||||||
|
|
||||||
|
### Gap 1:seatSpecMap 返回方式(P0)
|
||||||
|
|
||||||
|
**问题**:前端无法获取座位规格映射表,导致选座功能无法实现。
|
||||||
|
|
||||||
|
**必须解决**:后端需要在商品详情 API 响应中返回 `seatSpecMap`,或在插件中新增专用接口。
|
||||||
|
|
||||||
|
**截止时间**:Phase 2 开发前必须完成
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Gap 2:CartSave extension_data 传递链路(P0)
|
||||||
|
|
||||||
|
**问题**:多座位下单时,每个座位的观演人信息如何传递到后端?
|
||||||
|
|
||||||
|
**必须解决**:确认 ShopXO BuyService 如何处理 `extension_data`:
|
||||||
|
- 写入 `order.extension_data` 还是 `order_detail`?
|
||||||
|
- 多座位场景下每座独立的 extension_data 如何存储?
|
||||||
|
|
||||||
|
**截止时间**:Phase 3 开发前必须完成
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Gap 3:QR payload 验签逻辑(P1)
|
||||||
|
|
||||||
|
**问题**:前端需要验证 QR payload 的有效性(含 `code` 字段的签名验证)
|
||||||
|
|
||||||
|
**必须解决**:
|
||||||
|
1. 文档更新 QR payload 结构(补充 `code` 字段)
|
||||||
|
2. 后端提供签名字段 `sig` 的生成方式(HMAC-SHA256)
|
||||||
|
3. 前端需要知道 secret 从哪里获取(后端提供接口?)
|
||||||
|
|
||||||
|
**截止时间**:票详情页开发前必须完成
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 三、改进建议(具体的、可操作的)
|
||||||
|
|
||||||
|
### 建议 1:更新 QR payload 文档(第 6.1 节)
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
### 6.1 QR 票数据结构
|
||||||
|
|
||||||
|
```json
|
||||||
|
// QR payload(Base64 编码)
|
||||||
|
{
|
||||||
|
"id": 482815,
|
||||||
|
"g": 118,
|
||||||
|
"code": "uuid格式-xxx-xxx",
|
||||||
|
"iat": 1745286000,
|
||||||
|
"exp": 1745287800
|
||||||
|
}
|
||||||
|
|
||||||
|
// 签名:sig = HMAC-SHA256("code.id.g.iat.exp", secret) 取前8位
|
||||||
|
```
|
||||||
|
```
|
||||||
|
|
||||||
|
### 建议 2:补充 Auth 鉴权说明(第 2.3 节)
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
### 票夹 API
|
||||||
|
|
||||||
|
**请求 Header**:
|
||||||
|
```
|
||||||
|
X-Token: {user_token}
|
||||||
|
```
|
||||||
|
(也支持 Authorization: Bearer xxx,ShopXO session cookie)
|
||||||
|
|
||||||
|
**响应 401**:未登录,跳转登录页
|
||||||
|
```
|
||||||
|
|
||||||
|
### 建议 3:明确后端待配合事项(第 9 节)
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
## 九、后端待配合事项
|
||||||
|
|
||||||
|
1. **seatSpecMap 返回**:在商品详情 API 响应中嵌入 `seatSpecMap`
|
||||||
|
- 触发条件:`goods.is_vr_ticket = 1`
|
||||||
|
- 数据来源:`SeatSkuService::buildSeatSpecMap()`
|
||||||
|
- 预计工作量:2h
|
||||||
|
|
||||||
|
2. **CartSave 链路确认**:
|
||||||
|
- 确认 extension_data 是否写入 order_detail
|
||||||
|
- 多座位场景下每座独立的 attendee 信息如何存储
|
||||||
|
|
||||||
|
3. **QR 签名字段说明**:
|
||||||
|
- 提供前端验签所需的 secret 获取方式
|
||||||
|
- 或提供后端验签接口
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 四、对前端开发者的建议(3条)
|
||||||
|
|
||||||
|
### 1. 优先确认 seatSpecMap 接口
|
||||||
|
|
||||||
|
在开始选座功能开发前,必须与后端确认:
|
||||||
|
- seatSpecMap 通过哪个 API 获取?
|
||||||
|
- 返回的数据结构是否与文档一致?
|
||||||
|
|
||||||
|
**建议**:先让后端在商品详情 API 中返回 seatSpecMap,再开始前端开发。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 2. Auth 统一使用 X-Token
|
||||||
|
|
||||||
|
UniApp 中统一使用 `X-Token` header 进行用户鉴权:
|
||||||
|
```javascript
|
||||||
|
uni.request({
|
||||||
|
url: '...',
|
||||||
|
header: {
|
||||||
|
'X-Token': uni.getStorageSync('token')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
避免混用 Authorization 或 cookie。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 3. QR 验签先做本地过期检查
|
||||||
|
|
||||||
|
前端先做本地过期检查,再考虑签名验证:
|
||||||
|
```javascript
|
||||||
|
const payload = JSON.parse(atob(qr_data.split('|')[1]));
|
||||||
|
if (payload.exp < Date.now() / 1000) {
|
||||||
|
// 已过期,调用 refreshQr 接口获取新 QR
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
签名验证(sig 字段)可延后实现,先确保核心功能可用。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 五、审查总结
|
||||||
|
|
||||||
|
| 维度 | 评分 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| 数据结构 | 9/10 | 描述清晰,与后端一致 |
|
||||||
|
| API 接口 | 6/10 | 存在不一致和缺失(QR payload、Auth) |
|
||||||
|
| 交互规范 | 8/10 | 详细,可直接参考 |
|
||||||
|
| 实现细节 | 5/10 | 缺少关键链路说明(extension_data) |
|
||||||
|
|
||||||
|
**综合评分:7/10**
|
||||||
|
|
||||||
|
文档整体质量良好,但 API 部分需要后端确认和修正,特别是:
|
||||||
|
1. QR payload 结构(缺少 code 字段)
|
||||||
|
2. Auth 鉴权机制(需明确 X-Token 方式)
|
||||||
|
3. extension_data 存储链路(多座位场景)
|
||||||
|
|
||||||
|
这三个问题必须在 Phase 2 开发前解决,否则前端无法正确实现购票和票夹功能。
|
||||||
|
|
@ -0,0 +1,63 @@
|
||||||
|
# Council Plan — BackendReviewer
|
||||||
|
|
||||||
|
## Task
|
||||||
|
Review `vr-ticket-uniapp-supplement.md` from backend perspective: check API specs consistency with actual vr-shopxo-plugin implementation, identify missing details, and propose fixes.
|
||||||
|
|
||||||
|
## Files to Review
|
||||||
|
- `/Users/bigemon/WorkSpace/vr-shopxo-uniapp/docs/vr-ticket-uniapp-supplement.md`
|
||||||
|
- Backend code: `/Users/bigemon/WorkSpace/vr-shopxo-plugin/shopxo/app/plugins/vr_ticket/`
|
||||||
|
|
||||||
|
## Backend Review Checklist
|
||||||
|
|
||||||
|
- [ ] 1. Compare API spec (Section 2) with actual backend implementations
|
||||||
|
- [ ] 2.1 商品详情 API — seatSpecMap 返回方式
|
||||||
|
- [ ] 2.2 购物车提交 API — extension_data 传递链路
|
||||||
|
- [ ] 2.3 票夹 API — 响应字段对照
|
||||||
|
- [ ] 2. Check QR payload structure (Section 6.1)
|
||||||
|
- [ ] 3. Verify short_code format documentation
|
||||||
|
- [ ] 4. Document missing backend details:
|
||||||
|
- [ ] extension_data 在 order_detail 中的存储格式
|
||||||
|
- [ ] Auth 机制(X-Token vs cookie)
|
||||||
|
- [ ] 核销 API 完整响应码
|
||||||
|
- [ ] 5. Identify top 3 API gaps (must resolve before frontend dev)
|
||||||
|
|
||||||
|
## Phase Breakdown
|
||||||
|
|
||||||
|
### Phase 1: Analysis (current)
|
||||||
|
Read supplement doc + backend code, compare, identify gaps
|
||||||
|
|
||||||
|
### Phase 2: Report
|
||||||
|
Output detailed review report with:
|
||||||
|
- Document score (1-10)
|
||||||
|
- Found issues (specific)
|
||||||
|
- Key gaps (must-fix before dev)
|
||||||
|
- Improvement suggestions (actionable)
|
||||||
|
- 3 recommendations for frontend devs
|
||||||
|
|
||||||
|
### Phase 3: Finalize
|
||||||
|
Merge findings into main, coordinate with FrontendReviewer
|
||||||
|
|
||||||
|
## Status
|
||||||
|
|
||||||
|
| Task | Status |
|
||||||
|
|------|--------|
|
||||||
|
| Read supplement doc | Done |
|
||||||
|
| Analyze backend code | Done |
|
||||||
|
| Identify API gaps | Done |
|
||||||
|
| Write review report | Done |
|
||||||
|
| Merge to main | In Progress |
|
||||||
|
|
||||||
|
## Coordination
|
||||||
|
- FrontendReviewer: reviewing frontend implementation (goods-vr-ticket.vue, components)
|
||||||
|
- Need to sync on: seatSpecMap acquisition, extension_data format
|
||||||
|
|
||||||
|
## Key Findings (Top 3 API Gaps)
|
||||||
|
|
||||||
|
1. **Gap 1: seatSpecMap 返回方式(P0)** — 前端无法获取座位规格映射表
|
||||||
|
2. **Gap 2: CartSave extension_data 传递链路(P0)** — 多座位场景下 attendee 信息存储
|
||||||
|
3. **Gap 3: QR payload 结构错误(P1)** — 缺少 `code` 字段
|
||||||
|
|
||||||
|
## Review Report
|
||||||
|
- Document score: 7/10
|
||||||
|
- Critical issues: 5
|
||||||
|
- Must-fix before Phase 2: 2 (seatSpecMap, extension_data)
|
||||||
Loading…
Reference in New Issue