191 lines
6.2 KiB
Markdown
191 lines
6.2 KiB
Markdown
|
|
# vr_goods_config JSON 规格说明
|
|||
|
|
|
|||
|
|
> 版本:v2.0 | 日期:2026-04-20 | 状态:已确认,待实现
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 一、设计原则
|
|||
|
|
|
|||
|
|
1. **商品发布时快照**:用户在后端选择场馆房间后,将完整的房间数据**复制一份**存入 `goods.vr_goods_config`。不从 `vr_seat_templates` 实时读取。
|
|||
|
|
2. **绝对一致性**:修改 `vr_seat_templates` 不影响已发布的商品。SKU(spec_base)和 `vr_goods_config` 一起过时、一起更新。
|
|||
|
|
3. **向下兼容**:保留 `template_id` 字段(用于标识来源),但不再用它去查 `vr_seat_templates` 表。
|
|||
|
|
4. **单一真相源**:前端渲染所需的所有数据(座位图、场次、价格)全部来自 `vr_goods_config` 的快照,不跨表查询。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 二、vr_goods_config JSON 结构
|
|||
|
|
|
|||
|
|
```json
|
|||
|
|
[
|
|||
|
|
{
|
|||
|
|
"template_id": 4,
|
|||
|
|
"selected_rooms": ["room_id_1776341371905"],
|
|||
|
|
"selected_sections": {
|
|||
|
|
"room_id_1776341371905": ["A", "B"]
|
|||
|
|
},
|
|||
|
|
"rooms": [
|
|||
|
|
{
|
|||
|
|
"id": "room_id_1776341371905",
|
|||
|
|
"name": "1号放映室VV",
|
|||
|
|
"map": [
|
|||
|
|
"AAAAB__BBB_BAAAA",
|
|||
|
|
"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" }
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
],
|
|||
|
|
"sessions": [
|
|||
|
|
{ "start": "15:00", "end": "16:59" },
|
|||
|
|
{ "start": "18:00", "end": "21:59" }
|
|||
|
|
]
|
|||
|
|
}
|
|||
|
|
]
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 字段说明
|
|||
|
|
|
|||
|
|
| 字段 | 类型 | 必填 | 说明 |
|
|||
|
|
|------|------|------|------|
|
|||
|
|
| `template_id` | int | ✅ | 来源场馆模板 ID(用于溯源,不用于查询) |
|
|||
|
|
| `selected_rooms` | string[] | ✅ | 本商品启用的房间 ID 列表 |
|
|||
|
|
| `selected_sections` | object | ✅ | key=房间ID,value=启用的分区字符列表(如 `["A","B"]`) |
|
|||
|
|
| `rooms` | object[] | ✅ | 房间完整数据快照(直接复制自 `vr_seat_templates.rooms`) |
|
|||
|
|
| `sessions` | object[] | ✅ | 本商品的场次列表 |
|
|||
|
|
|
|||
|
|
### rooms.seats 字段说明
|
|||
|
|
|
|||
|
|
`seats` 是 `sections` 的快捷索引,key = `char`(座位字符),格式与 `sections` 条目相同:
|
|||
|
|
```json
|
|||
|
|
"seats": {
|
|||
|
|
"A": { "char": "A", "name": "VIP区", "price": 100, "color": "#f06292" },
|
|||
|
|
"B": { "char": "B", "name": "看台区", "price": 50, "color": "#4fc3f7" }
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 向下兼容(旧格式迁移)
|
|||
|
|
|
|||
|
|
旧格式(有 `vr_seat_templates` 表关联逻辑):
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"template_id": 4,
|
|||
|
|
"sessions": [{"start": "...", "end": "..."}]
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
识别方式:`rooms` 字段不存在 → 降级读取 `vr_seat_templates` 表。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 三、SKU 生成逻辑(AdminGoodsSaveHandle Hook)
|
|||
|
|
|
|||
|
|
商品保存时,根据 `selected_rooms` 数组,从 `vr_seat_templates.rooms` 取出对应房间,展开每个房间的 `map` 座位,生成 SKU 条目到 `goods_spec_base` + `goods_spec_value`。
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
rooms[room_id].map
|
|||
|
|
└─ 每行字符串(如 "AAAAB__BBB_BAAAA")
|
|||
|
|
└─ 每个非 _ / - 的字符 → 一个 SKU
|
|||
|
|
├─ goods_spec_base.id → 库存主键
|
|||
|
|
├─ goods_spec_base.spec_name → "排:row, 座:colNum"
|
|||
|
|
├─ goods_spec_base.price → seats[char].price
|
|||
|
|
└─ goods_spec_base.spec_type → "vrseat:{room_id}:{char}"
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**spec_base_id_map(存到 vr_goods_config 的 rooms[] 中)格式:**
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"{room_id}_{row}_{colNum}": goods_spec_base.id
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
> 注:具体 SKU 生成字段名/存储位置待 AdminGoodsSaveHandle 实现时确认。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 四、前端渲染数据流
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
goods.vr_goods_config(快照)
|
|||
|
|
└─ [0].rooms[] → 前端 JS rooms[]
|
|||
|
|
└─ [0].sessions[] → 场次卡片
|
|||
|
|
└─ [0].selected_sections{} → 控制哪些分区渲染
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 前端数据结构
|
|||
|
|
|
|||
|
|
```javascript
|
|||
|
|
// GetGoodsViewData() 注入给模板
|
|||
|
|
{
|
|||
|
|
vr_seat_template: {
|
|||
|
|
rooms: [...], // rooms 快照数组
|
|||
|
|
sessions: [...], // 场次列表
|
|||
|
|
selected_sections: {} // 分区过滤
|
|||
|
|
},
|
|||
|
|
goods_spec_data: [...], // 场次规格(price 来自 goods_spec_base)
|
|||
|
|
goods_config: { ... } // 原始 vr_goods_config[0]
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### loadSoldSeats(已选座位)
|
|||
|
|
|
|||
|
|
从 `vr_tickets` 表查询该商品+当前场次已生成的票:
|
|||
|
|
```sql
|
|||
|
|
SELECT seat_info FROM vrt_vr_tickets
|
|||
|
|
WHERE goods_id = :goods_id AND verify_status != 1
|
|||
|
|
```
|
|||
|
|
`seat_info` 格式:`"room_id/rowLabel/colNum"`(例:`room_id_xxx/A/3`)
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 五、GetGoodsViewData() 重写要点
|
|||
|
|
|
|||
|
|
**输入**:`goods_id`
|
|||
|
|
|
|||
|
|
**输出**:
|
|||
|
|
```php
|
|||
|
|
[
|
|||
|
|
'vr_seat_template' => [
|
|||
|
|
'rooms' => [...], // 来自 vr_goods_config[0].rooms
|
|||
|
|
'sessions' => [...], // 来自 vr_goods_config[0].sessions
|
|||
|
|
'selected_sections' => {...}, // 来自 vr_goods_config[0].selected_sections
|
|||
|
|
],
|
|||
|
|
'goods_spec_data' => [...], // 场次+价格(用于前端场次卡片)
|
|||
|
|
'goods_config' => {...} // 原始 vr_goods_config[0]
|
|||
|
|
]
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**逻辑**:
|
|||
|
|
1. 读取 `goods.vr_goods_config` JSON
|
|||
|
|
2. 若 `rooms` 字段存在 → 直接使用(新格式)
|
|||
|
|
3. 若 `rooms` 不存在 → 降级:按旧逻辑查 `vr_seat_templates` 表(旧格式兼容)
|
|||
|
|
4. 场次价格从 `goods_spec_base` 表读取
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 六、需要更新的文件
|
|||
|
|
|
|||
|
|
| 文件 | 操作 | 说明 |
|
|||
|
|
|------|------|------|
|
|||
|
|
| `SeatSkuService.php` | 重写 GetGoodsViewData() | 新 JSON 格式解析 |
|
|||
|
|
| `ticket_detail.html` | 更新 JS | rooms[] 结构渲染 + loadSoldSeats |
|
|||
|
|
| `docs/VR_GOODS_CONFIG_SPEC.md` | 新建 | 本文档,记录 JSON 规格 |
|
|||
|
|
| `docs/PHASE2_PLAN.md` | 更新 | 补充新格式 + 待办 |
|
|||
|
|
| `docs/DEVELOPMENT_LOG.md` | 追加 | 记录本次 JSON 格式升级 |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 七、已确认的设计决策
|
|||
|
|
|
|||
|
|
1. ✅ 商品发布时快照 `vr_seat_templates.rooms` 到 `goods.vr_goods_config.rooms`
|
|||
|
|
2. ✅ `vr_goods_config` 包含完整的座位图+sections+seats 数据
|
|||
|
|
3. ✅ 前端不跨表查询,全部数据来自 `vr_goods_config` 快照
|
|||
|
|
4. ✅ `spec_base_id_map` 格式:`{room_id}_{row}_{colNum}` → `spec_base_id`
|
|||
|
|
5. ✅ 座位已售状态:查 `vr_tickets.seat_info`(格式:`room_id/rowLabel/colNum`)
|
|||
|
|
6. ⚠️ SKU 生成字段名/存储位置:待 AdminGoodsSaveHandle 实现时确认
|