2026-04-19 23:24:57 +00:00
|
|
|
|
# vr_goods_config JSON 规格说明
|
|
|
|
|
|
|
2026-04-20 00:30:00 +00:00
|
|
|
|
> 版本:v3.0 | 日期:2026-04-20 | 状态:**已确认,待实现**
|
2026-04-20 01:04:23 +00:00
|
|
|
|
> 关联 Issue:#13
|
2026-04-19 23:24:57 +00:00
|
|
|
|
|
2026-04-20 01:25:01 +00:00
|
|
|
|
## 目录
|
|
|
|
|
|
- [一、vr_goods_config 完整结构](#一vr_goods_config-完整结构)
|
|
|
|
|
|
- [二、设计意图](#二设计意图)
|
|
|
|
|
|
- [三、spec_base_id_map 生成与存储](#三spec_base_id_map-生成与存储)
|
|
|
|
|
|
- [四、AdminGoodsSaveHandle 改动方案](#四admingoodsshandler-改动方案)
|
|
|
|
|
|
- [五、前端数据结构](#五前端数据结构)
|
|
|
|
|
|
- [六、需要修改的文件](#六需要修改的文件)
|
|
|
|
|
|
- [七、降级兼容](#七降级兼容)
|
|
|
|
|
|
- [八、已确认的设计决策](#八已确认的设计决策)
|
|
|
|
|
|
|
2026-04-19 23:24:57 +00:00
|
|
|
|
---
|
|
|
|
|
|
|
2026-04-20 01:04:23 +00:00
|
|
|
|
## ⚠️ v3.0 vs 旧版本的区别
|
2026-04-19 23:24:57 +00:00
|
|
|
|
|
2026-04-20 01:04:23 +00:00
|
|
|
|
**旧版**:rooms/sections/seats 存放在 `vr_seat_templates.seat_map` JSON 里,前端需要跨表查询。
|
2026-04-20 00:30:00 +00:00
|
|
|
|
|
2026-04-20 01:04:23 +00:00
|
|
|
|
**v3.0(最终)**:发布时将 `vr_seat_templates.seat_map` 快照存入 `template_snapshot`,和用户选择一起存储,前端完全不跨表。
|
2026-04-19 23:24:57 +00:00
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
2026-04-20 00:30:00 +00:00
|
|
|
|
## 一、vr_goods_config 完整结构
|
2026-04-19 23:24:57 +00:00
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
|
[
|
|
|
|
|
|
{
|
2026-04-20 01:04:23 +00:00
|
|
|
|
"version": 3.0,
|
2026-04-19 23:24:57 +00:00
|
|
|
|
"template_id": 4,
|
2026-04-20 00:30:00 +00:00
|
|
|
|
"selected_rooms": ["room_id_1776341371905", "room_id_1776341444657"],
|
2026-04-20 01:04:23 +00:00
|
|
|
|
"selected_sections": {
|
|
|
|
|
|
"room_id_1776341371905": ["A", "B"],
|
|
|
|
|
|
"room_id_1776341444657": ["A"]
|
|
|
|
|
|
},
|
2026-04-20 00:30:00 +00:00
|
|
|
|
"sessions": [
|
|
|
|
|
|
{ "start": "15:00", "end": "16:59" },
|
|
|
|
|
|
{ "start": "18:00", "end": "21:59" }
|
|
|
|
|
|
],
|
2026-04-20 01:04:23 +00:00
|
|
|
|
"template_snapshot": {
|
|
|
|
|
|
"venue": {
|
|
|
|
|
|
"name": "测试 2",
|
|
|
|
|
|
"address": "测试地址",
|
|
|
|
|
|
"location": { "lng": "", "lat": "" },
|
|
|
|
|
|
"images": []
|
|
|
|
|
|
},
|
|
|
|
|
|
"rooms": [
|
|
|
|
|
|
{
|
|
|
|
|
|
"id": "room_id_1776341371905",
|
|
|
|
|
|
"name": "1号放映室VV",
|
|
|
|
|
|
"map": ["AAAAB__BBB_BAAAA", "AAAAB__BBB_BAAAA"],
|
|
|
|
|
|
"sections": [
|
|
|
|
|
|
{ "char": "A", "name": "VIP区", "price": 100, "color": "#f06292" },
|
|
|
|
|
|
{ "char": "B", "name": "看台区", "price": 50, "color": "#4fc3f7" }
|
|
|
|
|
|
],
|
|
|
|
|
|
"seats": {
|
|
|
|
|
|
"A": { "char": "A", "name": "VIP区", "price": 100, "color": "#f06292" },
|
|
|
|
|
|
"B": { "char": "B", "name": "看台区", "price": 50, "color": "#4fc3f7" }
|
|
|
|
|
|
}
|
2026-04-19 23:24:57 +00:00
|
|
|
|
}
|
2026-04-20 01:04:23 +00:00
|
|
|
|
]
|
|
|
|
|
|
}
|
2026-04-19 23:24:57 +00:00
|
|
|
|
}
|
|
|
|
|
|
]
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 字段说明
|
|
|
|
|
|
|
|
|
|
|
|
| 字段 | 类型 | 必填 | 说明 |
|
|
|
|
|
|
|------|------|------|------|
|
2026-04-20 01:04:23 +00:00
|
|
|
|
| `version` | float | ✅ | 协议版本(当前 3.0),用于前向兼容判断 |
|
|
|
|
|
|
| `template_id` | int | ✅ | 发布/编辑时读取最新 vr_seat_templates 的依据 |
|
|
|
|
|
|
| `selected_rooms` | string[] | ✅ | 用户选择:启用了哪些演播(房间 ID 列表) |
|
|
|
|
|
|
| `selected_sections` | object | ✅ | 用户选择:key=房间ID,value=该房间选中的分区字符列表 |
|
|
|
|
|
|
| `sessions` | object[] | ✅ | 用户管理:场次列表 |
|
|
|
|
|
|
| `template_snapshot` | object | ✅ | 发布时从 vr_seat_templates.seat_map 读取的快照(含 venue + rooms) |
|
|
|
|
|
|
|
|
|
|
|
|
### selected_sections 格式说明
|
|
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
|
"selected_sections": {
|
|
|
|
|
|
"room_id_1776341371905": ["A", "B"],
|
|
|
|
|
|
"room_id_1776341444657": ["A"]
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
key = 房间 ID,value = 该房间选中的分区字符数组。为什么用对象格式?因为同一个 section char(如 "A")可能在不同房间里代表不同的区(VIP区 vs 普通区),所以必须按 room_id 区分。
|
2026-04-19 23:24:57 +00:00
|
|
|
|
|
2026-04-20 00:30:00 +00:00
|
|
|
|
---
|
2026-04-19 23:24:57 +00:00
|
|
|
|
|
2026-04-20 01:04:23 +00:00
|
|
|
|
## 二、设计意图
|
2026-04-19 23:24:57 +00:00
|
|
|
|
|
2026-04-20 01:04:23 +00:00
|
|
|
|
### 流程说明
|
2026-04-19 23:24:57 +00:00
|
|
|
|
|
2026-04-20 01:04:23 +00:00
|
|
|
|
```
|
|
|
|
|
|
商品发布/编辑时:
|
|
|
|
|
|
前端提交 → selected_rooms / selected_sections / sessions
|
|
|
|
|
|
后端 AdminGoodsSaveHandle →
|
|
|
|
|
|
1. 用 template_id 读取 vr_seat_templates.seat_map(最新数据)
|
|
|
|
|
|
2. 按 selected_rooms 过滤,填充 template_snapshot
|
|
|
|
|
|
3. 和 selected_* 一起写入 goods.vr_goods_config
|
|
|
|
|
|
4. BatchGenerate 生成 SKU
|
|
|
|
|
|
```
|
2026-04-20 00:30:00 +00:00
|
|
|
|
|
2026-04-20 01:04:23 +00:00
|
|
|
|
### 现有前端兼容性
|
2026-04-20 00:30:00 +00:00
|
|
|
|
|
2026-04-20 01:04:23 +00:00
|
|
|
|
- 前端只提交 `selected_rooms` / `selected_sections` / `sessions`,**不提交 `template_snapshot`**
|
|
|
|
|
|
- `template_snapshot` 由后端在保存时自动填充
|
|
|
|
|
|
- 现有商品编辑体验**完全不受影响**
|
2026-04-20 00:30:00 +00:00
|
|
|
|
|
2026-04-20 01:04:23 +00:00
|
|
|
|
### template_snapshot 的作用
|
2026-04-20 00:30:00 +00:00
|
|
|
|
|
2026-04-20 01:04:23 +00:00
|
|
|
|
- 前端渲染所需的所有座位图/sections/seats 数据都来自 `template_snapshot`
|
|
|
|
|
|
- `selected_rooms` / `selected_sections` 用于高亮、过滤等交互逻辑
|
|
|
|
|
|
- 若 `template_snapshot` 为空(兼容旧商品),降级读 `vr_seat_templates` 表
|
2026-04-19 23:24:57 +00:00
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
2026-04-20 00:30:00 +00:00
|
|
|
|
## 三、spec_base_id_map 生成与存储
|
2026-04-19 23:24:57 +00:00
|
|
|
|
|
2026-04-20 00:30:00 +00:00
|
|
|
|
### 3.1 当前断路问题
|
2026-04-19 23:24:57 +00:00
|
|
|
|
|
|
|
|
|
|
```
|
2026-04-20 00:30:00 +00:00
|
|
|
|
BatchGenerate() → 生成 GoodsSpecBase.id
|
|
|
|
|
|
→ 从未写入 spec_base_id_map ← 断路
|
2026-04-20 01:04:23 +00:00
|
|
|
|
前端 JS → 用 "roomId_row_col" 格式查 → 永远查不到
|
2026-04-19 23:24:57 +00:00
|
|
|
|
```
|
|
|
|
|
|
|
2026-04-20 01:04:23 +00:00
|
|
|
|
### 3.2 解决方案:使用 goods_spec_base.extends
|
2026-04-20 00:30:00 +00:00
|
|
|
|
|
2026-04-20 01:04:23 +00:00
|
|
|
|
ShopXO 原生 `goods_spec_base` 表有 `extends` 字段(JSON 扩展数据)。BatchGenerate 每次都删除+重建全量 spec,放心写入 `extends`。
|
2026-04-20 00:30:00 +00:00
|
|
|
|
|
|
|
|
|
|
**存储时**(BatchGenerate 写入 GoodsSpecBase):
|
|
|
|
|
|
```php
|
|
|
|
|
|
$extends = json_encode([
|
2026-04-20 01:04:23 +00:00
|
|
|
|
'seat_key' => $roomId . '_' . $rowLabel . '_' . $col
|
2026-04-20 00:30:00 +00:00
|
|
|
|
], JSON_UNESCAPED_UNICODE);
|
|
|
|
|
|
|
|
|
|
|
|
Db::name('GoodsSpecBase')->insertGetId([
|
|
|
|
|
|
'goods_id' => $goodsId,
|
|
|
|
|
|
'price' => $seatPrice,
|
|
|
|
|
|
'inventory' => 1,
|
|
|
|
|
|
// ... 其他字段
|
2026-04-20 01:04:23 +00:00
|
|
|
|
'extends' => $extends,
|
2026-04-20 00:30:00 +00:00
|
|
|
|
]);
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-04-20 01:04:23 +00:00
|
|
|
|
**读取时**(GetGoodsViewData 动态构建):
|
2026-04-20 00:30:00 +00:00
|
|
|
|
```php
|
|
|
|
|
|
$specs = Db::name('GoodsSpecBase')
|
|
|
|
|
|
->where('goods_id', $goodsId)
|
|
|
|
|
|
->where('inventory', '>', 0)
|
|
|
|
|
|
->select();
|
|
|
|
|
|
|
|
|
|
|
|
$specBaseIdMap = [];
|
|
|
|
|
|
foreach ($specs as $spec) {
|
|
|
|
|
|
$ext = json_decode($spec['extends'] ?? '{}', true);
|
|
|
|
|
|
if (!empty($ext['seat_key'])) {
|
|
|
|
|
|
$specBaseIdMap[$ext['seat_key']] = intval($spec['id']);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**spec_base_id_map 最终格式**:
|
2026-04-19 23:24:57 +00:00
|
|
|
|
```json
|
|
|
|
|
|
{
|
2026-04-20 00:30:00 +00:00
|
|
|
|
"room_id_1776341371905_A_3": 2001,
|
2026-04-20 01:04:23 +00:00
|
|
|
|
"room_id_1776341371905_B_5": 2002
|
2026-04-19 23:24:57 +00:00
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-04-20 01:04:23 +00:00
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 四、AdminGoodsSaveHandle 改动方案
|
|
|
|
|
|
|
|
|
|
|
|
### 保存时填充 template_snapshot
|
2026-04-20 00:30:00 +00:00
|
|
|
|
|
2026-04-20 01:04:23 +00:00
|
|
|
|
在 `save_thing_end` 时机,BatchGenerate 之前:
|
|
|
|
|
|
|
|
|
|
|
|
```php
|
|
|
|
|
|
// foreach ($configs as $config) 循环内:
|
|
|
|
|
|
$templateId = intval($config['template_id'] ?? 0);
|
|
|
|
|
|
$selectedRooms = $config['selected_rooms'] ?? [];
|
|
|
|
|
|
|
|
|
|
|
|
// 1. 读取最新 vr_seat_templates.seat_map
|
|
|
|
|
|
$template = Db::name(self::table('seat_templates'))->find($templateId);
|
|
|
|
|
|
$seatMap = json_decode($template['seat_map'] ?? '{}', true);
|
|
|
|
|
|
$allRooms = $seatMap['rooms'] ?? [];
|
|
|
|
|
|
|
|
|
|
|
|
// 2. 按 selected_rooms 过滤(只存用户选中的房间)
|
|
|
|
|
|
$filteredRooms = [];
|
|
|
|
|
|
foreach ($allRooms as $room) {
|
|
|
|
|
|
if (in_array($room['id'], $selectedRooms)) {
|
|
|
|
|
|
$filteredRooms[] = $room;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 3. 填充 template_snapshot
|
|
|
|
|
|
$config['template_snapshot'] = [
|
|
|
|
|
|
'venue' => $seatMap['venue'] ?? [],
|
|
|
|
|
|
'rooms' => $filteredRooms,
|
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
|
|
// 4. 用更新后的 config 覆盖($configs[$i] = $config)
|
|
|
|
|
|
// 5. 后续 BatchGenerate 继续使用更新后的 $config
|
|
|
|
|
|
```
|
2026-04-19 23:24:57 +00:00
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
2026-04-20 01:04:23 +00:00
|
|
|
|
## 五、前端数据结构(GetGoodsViewData 输出)
|
2026-04-19 23:24:57 +00:00
|
|
|
|
|
2026-04-20 00:30:00 +00:00
|
|
|
|
```php
|
|
|
|
|
|
[
|
|
|
|
|
|
'vr_seat_template' => [
|
2026-04-20 01:04:23 +00:00
|
|
|
|
'venue' => $config['template_snapshot']['venue'] ?? [],
|
|
|
|
|
|
'rooms' => $config['template_snapshot']['rooms'] ?? [], // 直接透传快照
|
|
|
|
|
|
'sessions' => $config['sessions'] ?? [],
|
|
|
|
|
|
'selected_rooms' => $config['selected_rooms'] ?? [],
|
|
|
|
|
|
'selected_sections'=> $config['selected_sections'] ?? {}, // {room_id: [chars]}
|
|
|
|
|
|
'spec_base_id_map' => $specBaseIdMap, // 动态构建
|
2026-04-20 00:30:00 +00:00
|
|
|
|
],
|
2026-04-20 01:04:23 +00:00
|
|
|
|
'goods_spec_data' => $goodsSpecData,
|
|
|
|
|
|
'goods_config' => $config
|
2026-04-20 00:30:00 +00:00
|
|
|
|
]
|
2026-04-19 23:24:57 +00:00
|
|
|
|
```
|
2026-04-20 00:30:00 +00:00
|
|
|
|
|
|
|
|
|
|
### goods_spec_data 生成逻辑
|
|
|
|
|
|
|
|
|
|
|
|
```php
|
2026-04-20 01:04:23 +00:00
|
|
|
|
// 从 goods_spec_base 按场次维度聚合(每个座位 = 一条 GoodsSpecBase)
|
|
|
|
|
|
$specs = Db::name('GoodsSpecBase')->where('goods_id', $goodsId)
|
|
|
|
|
|
->where('inventory', '>', 0)->select();
|
2026-04-20 00:30:00 +00:00
|
|
|
|
|
2026-04-20 01:04:23 +00:00
|
|
|
|
$sessionPrices = [];
|
|
|
|
|
|
$sessionSpecs = [];
|
2026-04-20 00:30:00 +00:00
|
|
|
|
|
|
|
|
|
|
foreach ($specs as $spec) {
|
2026-04-20 01:04:23 +00:00
|
|
|
|
// 从 goods_spec_value 找到场次维度值(格式:"HH:mm-HH:mm")
|
2026-04-20 00:30:00 +00:00
|
|
|
|
$sessionValue = Db::name('GoodsSpecValue')
|
|
|
|
|
|
->where('goods_spec_base_id', $spec['id'])
|
2026-04-20 01:04:23 +00:00
|
|
|
|
->where('value', 'REGEXP', '^[0-9]{2}:[0-9]{2}-[0-9]{2}:[0-9]{2}$')
|
2026-04-20 00:30:00 +00:00
|
|
|
|
->find();
|
|
|
|
|
|
|
|
|
|
|
|
if ($sessionValue) {
|
|
|
|
|
|
$sessionStr = $sessionValue['value'];
|
|
|
|
|
|
if (!isset($sessionPrices[$sessionStr]) || $spec['price'] < $sessionPrices[$sessionStr]) {
|
|
|
|
|
|
$sessionPrices[$sessionStr] = floatval($spec['price']);
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!isset($sessionSpecs[$sessionStr])) {
|
|
|
|
|
|
$sessionSpecs[$sessionStr] = intval($spec['id']);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
$goodsSpecData = [];
|
|
|
|
|
|
foreach ($sessions as $s) {
|
|
|
|
|
|
$start = $s['start'] ?? '';
|
|
|
|
|
|
$end = $s['end'] ?? '';
|
|
|
|
|
|
$sessionStr = $start && $end ? "{$start}-{$end}" : ($start ?: $end);
|
|
|
|
|
|
$goodsSpecData[] = [
|
|
|
|
|
|
'spec_id' => $sessionSpecs[$sessionStr] ?? 0,
|
|
|
|
|
|
'spec_name' => $sessionStr,
|
|
|
|
|
|
'price' => $sessionPrices[$sessionStr] ?? floatval($goods['price'] ?? 0),
|
|
|
|
|
|
];
|
|
|
|
|
|
}
|
2026-04-19 23:24:57 +00:00
|
|
|
|
```
|
|
|
|
|
|
|
2026-04-20 00:30:00 +00:00
|
|
|
|
### 前端 JS 使用方式
|
2026-04-19 23:24:57 +00:00
|
|
|
|
|
|
|
|
|
|
```javascript
|
2026-04-20 00:30:00 +00:00
|
|
|
|
// ticket_detail.html JS
|
|
|
|
|
|
var specBaseIdMap = <?php echo json_encode($vr_seat_template['spec_base_id_map'] ?? []); ?>;
|
|
|
|
|
|
|
|
|
|
|
|
// submit() 时:根据选中座位查 spec_base_id
|
|
|
|
|
|
var seatKey = seat.roomId + '_' + seat.rowLabel + '_' + seat.colNum;
|
|
|
|
|
|
// 例:"room_id_1776341371905_A_3"
|
|
|
|
|
|
var specBaseId = specBaseIdMap[seatKey] || 0;
|
|
|
|
|
|
|
|
|
|
|
|
// goods_params 格式(每座一行)
|
2026-04-19 23:24:57 +00:00
|
|
|
|
{
|
2026-04-20 00:30:00 +00:00
|
|
|
|
goods_id: goodsId,
|
2026-04-20 01:04:23 +00:00
|
|
|
|
spec_base_id: specBaseId,
|
2026-04-20 00:30:00 +00:00
|
|
|
|
stock: 1,
|
|
|
|
|
|
extension_data: JSON.stringify({ attendee, seat: {...} })
|
2026-04-19 23:24:57 +00:00
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-04-20 00:30:00 +00:00
|
|
|
|
---
|
2026-04-19 23:24:57 +00:00
|
|
|
|
|
2026-04-20 01:04:23 +00:00
|
|
|
|
## 六、需要修改的文件
|
2026-04-20 00:30:00 +00:00
|
|
|
|
|
|
|
|
|
|
| 文件 | 改动 |
|
|
|
|
|
|
|------|------|
|
2026-04-20 01:04:23 +00:00
|
|
|
|
| `AdminGoodsSaveHandle` | save_thing_end 中 BatchGenerate 之前:从 vr_seat_templates 读取并填充 `config.template_snapshot` |
|
|
|
|
|
|
| `SeatSkuService::BatchGenerate()` | insertGetId 中写入 `extends.seat_key` |
|
|
|
|
|
|
| `SeatSkuService::GetGoodsViewData()` | 重写:读 `template_snapshot`;从 `extends` 动态构建 `spec_base_id_map`;适配 `selected_sections` 对象格式 |
|
2026-04-20 00:30:00 +00:00
|
|
|
|
| `ticket_detail.html` JS | `seatKey` 格式改为 `roomId + '_' + rowLabel + '_' + colNum` |
|
2026-04-19 23:24:57 +00:00
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
2026-04-20 01:04:23 +00:00
|
|
|
|
## 七、降级兼容
|
2026-04-20 00:30:00 +00:00
|
|
|
|
|
|
|
|
|
|
```php
|
|
|
|
|
|
$config = json_decode($goods['vr_goods_config'] ?? '', true);
|
|
|
|
|
|
if (empty($config)) {
|
2026-04-20 01:04:23 +00:00
|
|
|
|
return /* 错误 */;
|
2026-04-20 00:30:00 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-20 01:04:23 +00:00
|
|
|
|
$config = $config[0] ?? $config;
|
2026-04-20 00:30:00 +00:00
|
|
|
|
|
2026-04-20 01:04:23 +00:00
|
|
|
|
if (version_compare($config['version'] ?? 0, 3.0, '<')) {
|
|
|
|
|
|
// 旧版格式(无 template_snapshot):降级读 vr_seat_templates 表
|
2026-04-20 00:30:00 +00:00
|
|
|
|
return self::GetGoodsViewDataLegacy($goodsId, $config);
|
|
|
|
|
|
}
|
2026-04-19 23:24:57 +00:00
|
|
|
|
|
2026-04-20 00:30:00 +00:00
|
|
|
|
// v3.0 新格式
|
|
|
|
|
|
// ...
|
|
|
|
|
|
```
|
2026-04-19 23:24:57 +00:00
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
2026-04-20 00:30:00 +00:00
|
|
|
|
## 八、已确认的设计决策
|
2026-04-19 23:24:57 +00:00
|
|
|
|
|
2026-04-20 00:30:00 +00:00
|
|
|
|
| 决策 | 结论 |
|
|
|
|
|
|
|------|------|
|
2026-04-20 01:04:23 +00:00
|
|
|
|
| `selected_sections` 格式 | 对象 `{ room_id: ["A","B"] }`(每个房间独立选择) |
|
|
|
|
|
|
| `template_snapshot` | 发布时从 vr_seat_templates.seat_map 读取并存储,不实时查表 |
|
|
|
|
|
|
| `spec_base_id_map` | 不入库,GetGoodsViewData 动态从 `extends.seat_key` 构建 |
|
|
|
|
|
|
| `seat_key` 格式 | `{roomId}_{rowLabel}_{colNum}`(无 MD5) |
|
|
|
|
|
|
| `goods_spec_data.price` | 取该场次所有座位中的最低价(用于卡片显示) |
|
|
|
|
|
|
| 现有前端兼容性 | ✅ 前端只提交选择项,template_snapshot 由后端保存时填充 |
|