docs: new vr_goods_config spec + Phase 2 v2.0 plan
parent
1b0ac3276d
commit
977cc57aef
|
|
@ -445,3 +445,62 @@ dc63cff77 chore: clean up my_test_plugin residual hooks
|
||||||
- `shopxo/test_ticket.php` → 移至 `_backup_20260420/test_ticket.php`(临时测试脚本,不入仓库)
|
- `shopxo/test_ticket.php` → 移至 `_backup_20260420/test_ticket.php`(临时测试脚本,不入仓库)
|
||||||
- `docs/14_TEMPLATE_RENDER_INVESTIGATION.md` → 重写修正版(删除错误信息,保留调查价值)
|
- `docs/14_TEMPLATE_RENDER_INVESTIGATION.md` → 重写修正版(删除错误信息,保留调查价值)
|
||||||
- 核心代码(Goods.php / SeatSkuService.php / TicketService.php)→ 全部提交推送
|
- 核心代码(Goods.php / SeatSkuService.php / TicketService.php)→ 全部提交推送
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 十二、模板渲染修复 + JSON 格式升级(2026-04-20 白天)
|
||||||
|
|
||||||
|
### 12.1 模板渲染修复(v2.0 路线 B)
|
||||||
|
|
||||||
|
**问题**:ThinkTemplate 的 `{include file="public/head"}` 标签在 Linux 下因 `view_depr=/` 导致路径拼接错误,页面以纯文本输出。
|
||||||
|
|
||||||
|
**解决方案(路线 B)**:
|
||||||
|
1. `{include}` / `{:}` ThinkTemplate 标签 → `<?php echo ModuleInclude(...) ?>` 原生 PHP
|
||||||
|
2. `{$var|default='...'}` → `<?php echo $var ?? '...' ?>`
|
||||||
|
3. `{json_decode(...)|raw}` → `<?php echo json_encode(...) ?>`
|
||||||
|
4. 复制 ShopXO `app/index/view/default/public/` → `plugins/vr_ticket/view/goods/public/`
|
||||||
|
|
||||||
|
**提交记录**:
|
||||||
|
```
|
||||||
|
349ec063c fix: 替换 ThinkTemplate 标签为 PHP ModuleInclude
|
||||||
|
c894e7018 fix: 复制 ShopXO public 模板 + 修复 footer_page 不存在问题
|
||||||
|
1b0ac3276 fix: 替换为票务专用精简 footer(449行→53行)
|
||||||
|
```
|
||||||
|
|
||||||
|
**渲染结果**:✅ 商品详情页正常渲染,但场次为空(待适配新 JSON 格式)。
|
||||||
|
|
||||||
|
### 12.2 vr_goods_config JSON 格式重新设计(重大变更)
|
||||||
|
|
||||||
|
**背景**:大头 + Gemini 重新设计了 vr_goods_config 规格,从依赖 `vr_seat_templates` 表实时查询,改为商品发布时快照模式。
|
||||||
|
|
||||||
|
**新格式核心**:
|
||||||
|
- `goods.vr_goods_config` 包含完整的 `rooms[]` 快照(座位图+sections+seats)
|
||||||
|
- 不再需要实时查 `vr_seat_templates` 表
|
||||||
|
- `spec_base_id_map` 格式:`{room_id}_{row}_{colNum}` → `spec_base_id`
|
||||||
|
|
||||||
|
**设计原则**:
|
||||||
|
- 商品发布时快照 → 已发布商品与 `vr_seat_templates` 解耦
|
||||||
|
- 修改模板不影响已发布商品 → 绝对一致性
|
||||||
|
- SKU 和 config 一起过时、一起更新
|
||||||
|
|
||||||
|
**新文档**:
|
||||||
|
- `docs/VR_GOODS_CONFIG_SPEC.md` — 新 JSON 格式完整规格说明(已确认)
|
||||||
|
- `docs/PHASE2_PLAN.md` v2.0 — 同步更新,下一步工作计划
|
||||||
|
|
||||||
|
### 12.3 当前 Git 状态
|
||||||
|
|
||||||
|
```
|
||||||
|
1b0ac3276 fix: 替换为票务专用精简 footer ← HEAD
|
||||||
|
c894e7018 fix: 复制 ShopXO public 模板
|
||||||
|
349ec063c fix: 替换 ThinkTemplate 标签为 PHP ModuleInclude
|
||||||
|
7bd896764 feat(Phase 2): 完成票务商品前端展示层
|
||||||
|
```
|
||||||
|
|
||||||
|
### 12.4 接下来需要实现
|
||||||
|
|
||||||
|
| 任务 | 负责人 | 依赖 |
|
||||||
|
|------|--------|------|
|
||||||
|
| 重写 GetGoodsViewData() 适配新格式 | 待定 | VR_GOODS_CONFIG_SPEC.md 已确认 |
|
||||||
|
| 更新 ticket_detail.html JS(rooms[] 结构) | 待定 | GetGoodsViewData() 输出确定后 |
|
||||||
|
| AdminGoodsSaveHandle SKU 生成 | 待定 | 新格式已确认 |
|
||||||
|
| loadSoldSeats() 实现 | 待定 | vr_tickets 有数据后 |
|
||||||
|
|
|
||||||
|
|
@ -1,146 +1,158 @@
|
||||||
# Phase 2 — 计划与当前状态
|
# Phase 2 — 计划与当前状态
|
||||||
|
|
||||||
> 版本:v1.0 | 日期:2026-04-20 | 状态:执行中
|
> 版本:v2.0 | 日期:2026-04-20 | 状态:执行中
|
||||||
> 关联提交:7bd896764
|
> 关联提交:c894e7018(模板渲染已通)、1b0ac3276(精简 footer)
|
||||||
|
> 关联文档:`docs/VR_GOODS_CONFIG_SPEC.md`(新 JSON 格式规格)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 一、Phase 2 完成情况
|
## ⚠️ 重大更新(v2.0)
|
||||||
|
|
||||||
|
**vr_goods_config JSON 格式已重新设计。**
|
||||||
|
|
||||||
|
旧格式依赖 `vr_seat_templates` 表实时查询,新格式在商品发布时将 `rooms` 快照存入商品表,前端不再跨表查询。
|
||||||
|
|
||||||
|
详见 `docs/VR_GOODS_CONFIG_SPEC.md`。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 一、Phase 2 当前状态
|
||||||
|
|
||||||
### ✅ 已完成
|
### ✅ 已完成
|
||||||
|
|
||||||
| 任务 | 文件 | 说明 |
|
| 任务 | 提交 | 说明 |
|
||||||
|------|------|------|
|
|------|------|------|
|
||||||
| Goods.php 改法 | `app/index/controller/Goods.php` | item_type=ticket → ticket_detail.html + 数据注入 |
|
| 模板渲染 | c894e7018 | ThinkTemplate → PHP ModuleInclude,渲染正常 |
|
||||||
| GetGoodsViewData() | `SeatSkuService.php` | 为前端模板提供座位图+场次数据 |
|
| 票务专用 footer | 1b0ac3276 | 精简 footer,移除 ShopXO 默认导航 |
|
||||||
| onOrderPaid() 修复 | `TicketService.php` | sxo_order_detail + JSON spec 解析 |
|
| Goods.php 改法 | 7bd896764 | item_type=ticket → ticket_detail.html |
|
||||||
| 模板渲染调研 | `docs/14_TEMPLATE_RENDER_INVESTIGATION.md` | 已修正,记录完整调查过程 |
|
| onOrderPaid() 修复 | 7bd896764 | sxo_order_detail + JSON spec 解析 |
|
||||||
|
|
||||||
### ⚠️ 待验证(容器内)
|
### ⚠️ 进行中
|
||||||
|
|
||||||
| 任务 | 优先级 | 说明 |
|
| 任务 | 说明 | 依赖 |
|
||||||
|------|--------|------|
|
|------|------|------|
|
||||||
| `{include}` 标签实测 | P0 | ticket_detail.html 中 `{include file="public/head"}` 是否能正确解析 |
|
| GetGoodsViewData() 重写 | 适配新 vr_goods_config JSON 格式 | VR_GOODS_CONFIG_SPEC.md 已确认 |
|
||||||
| ModuleInclude 备选方案 | P1 | 若 `{include}` 失败,切换 `ModuleInclude()` |
|
| 前端 JS 更新 | 适配 rooms[] 结构渲染 | GetGoodsViewData() 输出格式确定后 |
|
||||||
| loadSoldSeats() | P1 | 查询已售座位,前端座位图需显示已售状态 |
|
| loadSoldSeats() 实现 | 查询 vr_tickets,标记已售座位 | vr_tickets 表有数据后 |
|
||||||
|
|
||||||
### ❌ 未开始
|
### ❌ 未开始
|
||||||
|
|
||||||
| 任务 | 说明 |
|
| 任务 | 说明 |
|
||||||
|------|------|
|
|------|------|
|
||||||
| vr_ticket Hook.php 钩子补充 | 缺失 `plugins_service_goods_spec_data` 处理 |
|
| AdminGoodsSaveHandle SKU 生成 | 根据 selected_rooms 生成 goods_spec_base 条目 |
|
||||||
| 后台座位模板管理 | admin/controller/SeatTemplate.php(已生成,未调试) |
|
| 后台 4 控制器联调 | SeatTemplate/Ticket/Verifier/Verification |
|
||||||
| 后台电子票列表 | admin/controller/Ticket.php(已生成,未调试) |
|
| 核销 API | POST /api/vr_ticket/verify |
|
||||||
| 后台核销员管理 | admin/controller/Verifier.php(已生成,未调试) |
|
|
||||||
| 后台核销记录 | admin/controller/Verification.php(已生成,未调试) |
|
|
||||||
| 核销 API | B 端扫码核销 REST 接口 |
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 二、模板渲染问题现状
|
## 二、vr_goods_config 新格式(已确认)
|
||||||
|
|
||||||
### 问题
|
```json
|
||||||
|
[
|
||||||
票务商品详情页 ThinkTemplate 标签未解析(`{$...}` / `{include}` / `{if}` 以原文输出)。
|
{
|
||||||
|
"template_id": 4,
|
||||||
### 根因
|
"selected_rooms": ["room_id_xxx"],
|
||||||
|
"selected_sections": { "room_id_xxx": ["A", "B"] },
|
||||||
Goods.php 原来用 `MyView()` 加载主题模板,票务商品需要加载插件独立模板 `ticket_detail.html`。
|
"rooms": [
|
||||||
|
{
|
||||||
### 解决路径(Goods.php 绝对路径方案)
|
"id": "room_id_xxx",
|
||||||
|
"name": "1号放映室VV",
|
||||||
```
|
"map": ["AAAAB__BBB_BAAAA", "AAAAB__BBB_BAAAA"],
|
||||||
Goods::Index()
|
"sections": [
|
||||||
→ $goods['item_type'] === 'ticket'
|
{ "char": "A", "name": "VIP区", "price": 100, "color": "#f06292" },
|
||||||
→ SeatSkuService::GetGoodsViewData($goods_id)
|
{ "char": "B", "name": "看台区", "price": 50, "color": "#4fc3f7" }
|
||||||
→ MyViewAssign([vr_seat_template, goods_spec_data])
|
],
|
||||||
→ View::fetch($tplFile) [$tplFile = 绝对路径]
|
"seats": {
|
||||||
→ ThinkTemplate 渲染 ticket_detail.html(含 {include} 标签)
|
"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" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
### 待实测项(容器内操作)
|
**核心变化**:
|
||||||
|
- `rooms[]` 包含完整座位图+sections+seats,不再查 `vr_seat_templates` 表
|
||||||
|
- `selected_sections` 控制每个房间内渲染哪些分区
|
||||||
|
- `spec_base_id_map` 格式:`{room_id}_{row}_{colNum}` → `spec_base_id`
|
||||||
|
|
||||||
```bash
|
完整规格见 `docs/VR_GOODS_CONFIG_SPEC.md`。
|
||||||
# 在 shopxo-php 容器内
|
|
||||||
docker exec -it shopxo-php bash
|
|
||||||
cd /var/www/html
|
|
||||||
curl "http://localhost:10000/?s=goods/index/id/118.html"
|
|
||||||
|
|
||||||
# 检查:
|
---
|
||||||
# 1. {include file="public/head"} 是否被解析为 HTML 内容
|
|
||||||
# 2. {$goods.title} 是否显示商品标题
|
|
||||||
# 3. 座位图是否正常渲染
|
|
||||||
```
|
|
||||||
|
|
||||||
### 失败备选
|
## 三、下一步工作
|
||||||
|
|
||||||
若 `{include}` 标签失败,修改 `ticket_detail.html`:
|
### Step 1(立即):重写 GetGoodsViewData()
|
||||||
|
|
||||||
|
**目标**:适配新 JSON 格式,从 `goods.vr_goods_config` 直接读取 rooms[],注入前端模板。
|
||||||
|
|
||||||
|
**新输出格式**:
|
||||||
```php
|
```php
|
||||||
<!-- 替换 -->
|
[
|
||||||
{include file="public/head" /}
|
'vr_seat_template' => [
|
||||||
<!-- 改为 -->
|
'rooms' => [...], // 直接透传 vr_goods_config[0].rooms
|
||||||
<?php echo ModuleInclude('public/head'); ?>
|
'sessions' => [...], // 直接透传 vr_goods_config[0].sessions
|
||||||
|
'selected_sections' => {...}, // 直接透传 vr_goods_config[0].selected_sections
|
||||||
|
],
|
||||||
|
'goods_spec_data' => [...], // 场次+价格(用于前端卡片)
|
||||||
|
'goods_config' => {...} // 原始 vr_goods_config[0]
|
||||||
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**降级兼容**:若 `vr_goods_config` 中无 `rooms` 字段,按旧逻辑查 `vr_seat_templates` 表。
|
||||||
|
|
||||||
|
### Step 2(立即):更新 ticket_detail.html JS
|
||||||
|
|
||||||
|
**目标**:适配 `rooms[]` 结构,多房间支持。
|
||||||
|
|
||||||
|
**改动点**:
|
||||||
|
1. `seatMap` → `rooms[]`(数组,每个房间一个座位图)
|
||||||
|
2. `renderSeatMap()` → `renderRoom(roomIndex)`(按房间渲染)
|
||||||
|
3. `specBaseIdMap` 格式变为 `{room_id}_{row}_{colNum}` → `spec_base_id`
|
||||||
|
4. `loadSoldSeats()` 实现:查 `vr_tickets.seat_info` 格式为 `room_id/rowLabel/colNum`
|
||||||
|
|
||||||
|
### Step 3:AdminGoodsSaveHandle SKU 生成
|
||||||
|
|
||||||
|
**目标**:用户选择房间后,在商品保存时自动生成 `goods_spec_base` SKU 条目。
|
||||||
|
|
||||||
|
**逻辑**:展开 rooms[].map,生成每个座位的 SKU,存入 `spec_base_id_map`。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 三、Phase 2 接下来的工作
|
## 四、模板渲染当前状态
|
||||||
|
|
||||||
### Step 1:模板渲染实测(容器内)
|
| 项目 | 状态 |
|
||||||
|
|------|------|
|
||||||
**操作人:** 大头(容器在本机)
|
| 模板渲染 | ✅ 正常(PHP ModuleInclude 方案) |
|
||||||
|
| 票务 footer | ✅ 已精简 |
|
||||||
```bash
|
| 场次显示 | ❌ 待适配新 JSON 格式 |
|
||||||
docker ps | grep shopxo-php # 确认容器运行中
|
| 座位图渲染 | ❌ 待适配 rooms[] 结构 |
|
||||||
curl -s "http://localhost:10000/?s=goods/index/id/118.html" | head -50
|
| 已售座位标记 | ❌ 待实现 loadSoldSeats() |
|
||||||
```
|
|
||||||
|
|
||||||
**成功标准:** HTML 源码中不再有 ThinkTemplate 标签(`{include}` / `{$` / `{if}`),座位图 div 正常显示。
|
|
||||||
|
|
||||||
### Step 2:座位图已售状态
|
|
||||||
|
|
||||||
SeatSkuService 需要补充 `loadSoldSeats()` 方法,查询 `vr_tickets` 表中该商品+场次已生成的票,返回已售座位 ID 列表,前端据此灰化已售座位。
|
|
||||||
|
|
||||||
### Step 3:后台管理页面联调
|
|
||||||
|
|
||||||
4 个后台控制器(SeatTemplate / Ticket / Verifier / Verification)均已生成,需要:
|
|
||||||
1. 确认路由可访问(后台 URL 格式)
|
|
||||||
2. 验证 CRUD 操作正常
|
|
||||||
3. 确认 RLS 策略
|
|
||||||
|
|
||||||
### Step 4:核销 API
|
|
||||||
|
|
||||||
`POST /api/vr_ticket/verify` — B 端小程序扫码调用。
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 四、数据库表结构(当前)
|
## 五、数据库表结构(当前)
|
||||||
|
|
||||||
| 表名 | 用途 | 状态 |
|
| 表名 | 用途 | 状态 |
|
||||||
|------|------|------|
|
|------|------|------|
|
||||||
| `vrt_vr_seat_templates` | 座位模板 | ✅ |
|
| `vrt_vr_seat_templates` | 场馆模板(rooms 母版) | ✅ |
|
||||||
| `vrt_vr_tickets` | 电子票 | ✅ |
|
| `vrt_vr_tickets` | 电子票 | ✅ |
|
||||||
| `vrt_vr_verifiers` | 核销员 | ✅ |
|
| `vrt_vr_verifiers` | 核销员 | ✅ |
|
||||||
| `vrt_vr_verifications` | 核销记录 | ✅ |
|
| `vrt_vr_verifications` | 核销记录 | ✅ |
|
||||||
| `vrt_vr_audit_log` | 审计日志 | ✅ |
|
| `goods.vr_goods_config` | 商品配置快照(新 JSON 格式) | ⚠️ 待适配 |
|
||||||
| `goods.vr_goods_config` | 商品配置(JSON) | ✅ |
|
| `goods_spec_base` | SKU 库存(ShopXO 原生平表) | ⚠️ 待自动生成 |
|
||||||
| `sxo_order_detail` | 订单明细(ShopXO) | ✅ |
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 五、已知风险
|
## 六、已知风险
|
||||||
|
|
||||||
| 风险 | 影响 | 缓解 |
|
| 风险 | 影响 | 缓解 |
|
||||||
|------|------|------|
|
|------|------|------|
|
||||||
| `{include}` 标签容器内解析失败 | P0,页面无样式 | 切换 ModuleInclude 方案 |
|
| vr_goods_config 仍是旧格式 | 场次/座位图不显示 | AdminGoodsSaveHandle 生成新格式后可解决 |
|
||||||
| shopxo-php 容器未启动 | 无法验证 | 每次操作前 `docker ps` 确认 |
|
| 旧版 GetGoodsViewData 未适配新格式 | 前端无数据 | Step 1 完成后解决 |
|
||||||
| Admin 控制器鉴权链不完整 | 后台无法访问 | 确认继承 Common 并调用 IsLogin/IsPower |
|
| spec_base_id_map 格式变化 | 已选座位提交逻辑需同步更新 | Step 2 中同步更新 JS |
|
||||||
| 座位模板与商品分类绑定逻辑 | 需确认 1:N 还是 1:1 | 实测验证 |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 六、决策点(待大头确认)
|
|
||||||
|
|
||||||
1. **模板 include 方案**:先试 `{include}`,失败后换 `ModuleInclude()`,还是直接内联 CSS/JS?
|
|
||||||
2. **loadSoldSeats()**:是否需要实时查库,还是前端纯靠 JS 状态管理?
|
|
||||||
3. **后台前端框架**:Layui 是否继续使用,还是改用其他方案?
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,190 @@
|
||||||
|
# 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 实现时确认
|
||||||
Loading…
Reference in New Issue