vr-shopxo-uniapp/backend-review-report.md

8.2 KiB
Raw Blame History

BackendReviewer — VR票务补充文档审查报告

审查时间2026-05-14 审查人BackendReviewer后端架构师视角 文档:vr-ticket-uniapp-supplement.md


文档评分7/10

文档结构完整覆盖了核心数据结构、API 接口、交互规范。但存在若干后端实现与文档描述不一致的问题,以及缺少关键实现细节。


一、发现的问题

问题 1QR payload 结构错误P0 高优先级)

文档描述(第 6.1 节):

{ "id": 482815, "g": 118, "iat": 1745286000, "exp": 1745287800 }

实际后端TicketService.php:191-197

$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 字段:

{
  "id": 482815,
  "g": 118,
  "code": "uuid格式-xxx-xxx",
  "iat": 1745286000,
  "exp": 1745287800
}

问题 2Auth 鉴权机制描述不准确P1

文档描述(第 2.3 节):

无参数(依赖 C 端 session

实际后端api/Ticket.php:24-57

// 支持三种方式:
// 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: 未登录,跳转登录页

问题 3CartSave extension_data 存储链路不清晰P1

文档描述(第 2.2 节):

"extension_data": "{\"attendee\":{...},\"seat\":{...}}"

实际后端

  1. CartSaveextension_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 信息

问题 4seatSpecMap 获取方式未确定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 节):

{
  "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 1seatSpecMap 返回方式P0

问题:前端无法获取座位规格映射表,导致选座功能无法实现。

必须解决:后端需要在商品详情 API 响应中返回 seatSpecMap,或在插件中新增专用接口。

截止时间Phase 2 开发前必须完成


Gap 2CartSave extension_data 传递链路P0

问题:多座位下单时,每个座位的观演人信息如何传递到后端?

必须解决:确认 ShopXO BuyService 如何处理 extension_data

  • 写入 order.extension_data 还是 order_detail
  • 多座位场景下每座独立的 extension_data 如何存储?

截止时间Phase 3 开发前必须完成


Gap 3QR payload 验签逻辑P1

问题:前端需要验证 QR payload 的有效性(含 code 字段的签名验证)

必须解决

  1. 文档更新 QR payload 结构(补充 code 字段)
  2. 后端提供签名字段 sig 的生成方式HMAC-SHA256
  3. 前端需要知道 secret 从哪里获取(后端提供接口?)

截止时间:票详情页开发前必须完成


三、改进建议(具体的、可操作的)

建议 1更新 QR payload 文档(第 6.1 节)

### 6.1 QR 票数据结构

```json
// QR payloadBase64 编码)
{
  "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 xxxShopXO session cookie

**响应 401**:未登录,跳转登录页

建议 3明确后端待配合事项第 9 节)

## 九、后端待配合事项

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 进行用户鉴权:

uni.request({
  url: '...',
  header: {
    'X-Token': uni.getStorageSync('token')
  }
})

避免混用 Authorization 或 cookie。


3. QR 验签先做本地过期检查

前端先做本地过期检查,再考虑签名验证:

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 开发前解决,否则前端无法正确实现购票和票夹功能。