# 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 实现时确认