vr-shopxo-plugin/docs/PLAN_5DIM_REFACTOR.md

12 KiB
Raw Blame History

Phase 3 P0 — 5维 Spec 重构:演播室层补全

版本v1.0 | 日期2026-04-22 | 状态:P0 阻塞


一、问题定义

1.1 现象

当前商品详情页ticket_detail.html可以选择

  • 场次
  • 场馆
  • 分区A区/B区/C区
  • 座位

但设计文档要求的层级是:

场次 → 场馆 → 演播室 → 分区 → 座位号

演播室第3层完全消失。用户在整个购买流程中感知不到这一层的存在。

1.2 影响

  • seat_map JSON 没有 rooms 字段,只有 flat sections[]
  • GoodsSpecType 里没有 $vr-演播室 记录
  • SPEC_DIMS 常量只有 4 维(缺演播室)
  • buildSeatSpecMap() 无法输出演播室维度
  • 前后端代码虽有 rooms fallback 预留,但从未真正启用

二、数据模型现状

2.1 当前 seat_map JSONgoods_id=112座位模板 ID=1

{
  "venue": {
    "name": "国家体育馆",
    "address": "北京市朝阳区奥体中心",
    "image": ""
  },
  "sections": [
    {"char": "A", "name": "VIP区", "color": "#e74c3c"},
    {"char": "B", "name": "看台",  "color": "#3498db"},
    {"char": "C", "name": "普通",  "color": "#2ecc71"}
  ],
  "map": ["AAAAAA", "BBBBBB", "CCCCCC"],
  "seats": {
    "A": {"price": 899, "color": "#e74c3c", "label": "VIP"},
    "B": {"price": 599, "color": "#3498db", "label": "看台"},
    "C": {"price": 299, "color": "#2ecc71", "label": "普通"}
  },
  "row_labels": ["A", "B", "C"]
}

问题sectionsmapseats 都是 flat 结构,没有 rooms 嵌套层。

2.2 目标 seat_map JSON

{
  "venue": {
    "name": "国家体育馆",
    "address": "北京市朝阳区奥体中心",
    "image": ""
  },
  "rooms": [
    {
      "id": "room_001",
      "name": "主厅",
      "sections": [
        {"char": "A", "name": "VIP区", "color": "#e74c3c"},
        {"char": "B", "name": "看台",  "color": "#3498db"},
        {"char": "C", "name": "普通",  "color": "#2ecc71"}
      ],
      "map": ["AAAAAA", "BBBBBB", "CCCCCC"],
      "seats": {
        "A": {"price": 899, "color": "#e74c3c", "label": "VIP"},
        "B": {"price": 599, "color": "#3498db", "label": "看台"},
        "C": {"price": 299, "color": "#2ecc71", "label": "普通"}
      }
    }
  ]
}

变更说明

  • sectionsmapseats 从 flat 移入 rooms[0]
  • rooms[].id = 演播室标识(room_001
  • rooms[].name = 演播室名称(主厅
  • 保留 flat sections/map/seats 作为 fallbackAdmin.php:646 和 ticket_detail.html:262 已有兼容逻辑)
  • 未来可扩展多个 room多厅模式

2.3 当前 GoodsSpecTypegoods_id=112

ID name 含义 value 示例
1942 $vr-场馆 场馆 [{"name":"国家体育馆","images":""}]
1943 $vr-分区 分区 [{"name":"A区","images":""},{"name":"B区","images":""},{"name":"C区","images":""}]
1944 $vr-时段 场次时间 [{"name":"2026-05-01 19:00","images":""}]
1945 $vr-座位号 座位号 [{"name":"待选座位","images":""}]

注意:数据库里是 $vr-时段SPEC_DIMS 里是 $vr-场次,需统一命名。

2.4 GoodsSpecValuegoods_id=112

当前0 条

BatchGenerate() 虽然写了 GoodsSpecBaseSKU 存在,有价格/库存),但没有写 GoodsSpecValue维度连接表导致 buildSeatSpecMap() 只能从 seat_map JSON 反推维度,演播室完全丢失。


三、目标 5 维 Spec 结构

3.1 SPEC_DIMS 常量(目标值)

const SPEC_DIMS = [
    '$vr-场次',    // 第1维场次时间来自 GoodsSpecType
    '$vr-场馆',    // 第2维场馆名来自 GoodsSpecType
    '$vr-演播室',  // 第3维演播室新增
    '$vr-分区',    // 第4维分区/区号(来自 seat_map.rooms[].sections
    '$vr-座位号',  // 第5维座位号来自 seat_key 的 row_col 部分)
];

命名统一$vr-场次 替代 $vr-时段

3.2 seat_key 格式(不变)

{room_id}_{row_label}_{col_num}
例room_001_A_1  →  主厅 A排1号

3.3 GoodsSpecType 目标记录goods_id=112

顺序 name 来源 value 说明
1 $vr-场次 商品规格维度 场次时间列表
2 $vr-场馆 商品规格维度 场馆名
3 $vr-演播室 seat_map.rooms[].name 演播室列表
4 $vr-分区 seat_map.rooms[].sections[].name 分区列表
5 $vr-座位号 seat_key row_col 座位号(自动生成)

3.4 buildSeatSpecMap() 目标输出

$seatSpecMap['room_001_A_1'] = [
    'spec_base_id' => 123,
    'price'        => 899.00,
    'inventory'   => 1,
    'spec'         => [
        ['type' => '$vr-场次',   'value' => '2026-05-01 19:00'],
        ['type' => '$vr-场馆',   'value' => '国家体育馆'],
        ['type' => '$vr-演播室', 'value' => '主厅'],        // ← 新增
        ['type' => '$vr-分区',   'value' => 'VIP区'],
        ['type' => '$vr-座位号', 'value' => 'A1'],
    ],
    'venueName'   => '国家体育馆',
    'roomId'      => 'room_001',
    'roomName'    => '主厅',     // ← 新增
    'section'     => ['char' => 'A', 'name' => 'VIP区', 'color' => '#e74c3c'],
    'rowLabel'    => 'A',
    'colNum'      => 1,
];

四、受影响文件清单

4.1 数据库(需 Migration

操作 说明
vrt_goods_spec_type 清空 goods_id=112 的旧记录重新插入5条 $vr-演播室,统一 $vr-场次
vrt_goods_spec_base 保留(已有 sku + seat_key 不改结构
vrt_goods_spec_value 清空重建按5维生成 连接 spec_base_id 和维度
vrt_vr_seat_templates 更新 seat_map JSON 加 rooms 层
vrt_vr_goods_config 检查 config JSON 是否受影响 通常只存 template_id 和快照

当前数据量极小1个模板0条 GoodsSpecValue可直接 truncate 后重生成。

4.2 PHP 文件

文件 行号 改动
SeatSkuService.php 29 SPEC_DIMS 改为5维$vr-演播室$vr-场次 替代 $vr-时段
SeatSkuService.php ~171-178 batchGenerate() 按5维提取维度需加演播室提取
SeatSkuService.php ~270 whereIn('name', SPEC_DIMS) 过滤5个维度
SeatSkuService.php ~306 插入缺失维度逻辑按5维顺序
SeatSkuService.php ~522-700 buildSeatSpecMap() 完全重写:从 rooms[] 结构读取,加 roomName 提取
SeatSkuService.php ~648-665 switch case 加 $vr-演播室
BaseService.php 187-190 维度默认值数组加 $vr-演播室
TicketService.php 65,68 $vr-座位号 / $vr-分区 不变
Admin.php ~176-191 VenueSave 保存 seat_map 时自动包 rooms 层fallback已有

4.3 前端文件

文件 改动
ticket_detail.html specTypeList 增加 $vr-演播室 选择器;renderAllSelectors() 渲染演播室选择器
ticket_detail.html filterSeats() 增加 currentRoom 过滤条件
ticket_detail.html submit() 的 spec 数组加 $vr-演播室 维度

4.4 不需要改的文件fallback 已存在)

  • Admin.php:646-653 — 已有 $seatMap['rooms'] fallback会自动适配新 JSON
  • ticket_detail.html:262 — 已有 seatMapData.rooms[0].map fallback
  • ticket_detail.html:269-272 — 已有 rooms fallback 逻辑

五、Migration 执行步骤

假设 goods_id=112座位模板 ID=1。

Step 1更新 seat_map JSON座位模板

-- 查看当前 seat_map
SELECT id, name, seat_map FROM vrt_vr_seat_templates WHERE id=1;

-- 更新 JSON 结构:加 rooms 层
-- 旧结构 flat sections/map/seats → 移入 rooms[0]

JSON 转换伪代码:

$old = json_decode($old_seat_map, true);
$new = [
    'venue' => $old['venue'],
    'rooms' => [[
        'id'       => 'room_001',
        'name'     => '主厅',
        'sections' => $old['sections'] ?? [],
        'map'      => $old['map'] ?? [],
        'seats'    => $old['seats'] ?? [],
    ]],
    // 保留 flat fallback兼容旧代码
    'sections' => $old['sections'] ?? [],
    'map'      => $old['map'] ?? [],
    'seats'    => $old['seats'] ?? [],
];

Step 2重建 GoodsSpecType5维

DELETE FROM vrt_goods_spec_type WHERE goods_id=112;

INSERT INTO vrt_goods_spec_type (goods_id, name, value, add_time) VALUES
(112, '$vr-场次',   '[{"name":"2026-05-01 19:00","images":""}]', UNIX_TIMESTAMP()),
(112, '$vr-场馆',   '[{"name":"国家体育馆","images":""}]', UNIX_TIMESTAMP()),
(112, '$vr-演播室', '[{"name":"主厅","images":""}]', UNIX_TIMESTAMP()),
(112, '$vr-分区',   '[{"name":"A区","images":""},{"name":"B区","images":""},{"name":"C区","images":""}]', UNIX_TIMESTAMP()),
(112, '$vr-座位号', '[{"name":"待选座位","images":""}]', UNIX_TIMESTAMP());

Step 3重建 GoodsSpecValue连接 goods_spec_base 和维度)

当前 GoodsSpecBase 有 sku + extends.seat_key。需要生成 5 条 GoodsSpecValue 记录,每条对应一个维度。

GoodsSpecValue 表结构:

-- goods_spec_base_id → 哪个 SKU
-- name → 维度名(如 $vr-演播室)
-- value → 维度值(如 主厅)
-- md5_key → 唯一键

生成逻辑(参考 buildSeatSpecMap

  1. 遍历所有 GoodsSpecBasegoods_id=112, inventory>0
  2. 从 extends.seat_key 解析 room_id, row_label, col_num
  3. 从 seat_map JSON 反查 roomName通过 room_id
  4. 生成 5 条 GoodsSpecValue

Step 4验证

  1. 访问商品详情页,检查 specTypeList 是否包含 5 个维度
  2. 检查前端演播室选择器是否正确渲染
  3. 选择座位后 submit检查 goods_data 中的 spec 数组是否有 5 个维度
  4. 检查 BuyService::BuyGoods 能正确解析 5 维 goods_data

六、前端交互变更

6.1 新的选择器层级

[场次选择器]  ← goods_spec_data已有
[场馆选择器]  ← specTypeList['$vr-场馆'].options已有
[演播室选择器] ← specTypeList['$vr-演播室'].options新增
[分区选择器]  ← specTypeList['$vr-分区'].options已有
[座位图]     ← 按 currentRoom/currentSection 过滤(已有 filterSeats需加 room 过滤)

6.2 filterSeats() 变更

// 现有
if (self.currentVenue) { matchVenue = ... }
if (self.currentSection) { matchSection = ... }

// 新增
if (self.currentRoom) {
    matchRoom = false;
    for (var i = 0; i < seatInfo.spec.length; i++) {
        if (seatInfo.spec[i].type === '$vr-演播室' && seatInfo.spec[i].value === self.currentRoom) {
            matchRoom = true;
            break;
        }
    }
}

6.3 submit() spec 数组格式(不变)

spec: seatInfo.spec  // 5维完整数组

七、已知约束

  1. 当前是单 room 模式rooms[0]),演播室选择器默认选主厅,用户不可切换。未来可扩展多 room。
  2. GoodsSpecValue 为 0 的根因BatchGenerate() 没有写 GoodsSpecValue只有 GoodsSpecBase。这是之前就存在的问题不是本次引入的。本次修复 BatchGenerate 的同时也要补全 GoodsSpecValue。
  3. 命名统一$vr-时段$vr-场次,涉及 DB 数据和 SPEC_DIMS 常量。

八、验收标准

  • seat_map JSON 有 rooms[] 结构sections/map/seats 移入 rooms[0]
  • GoodsSpecType 有 5 条记录,包含 $vr-演播室
  • SPEC_DIMS 是 5 维数组
  • buildSeatSpecMap() 输出 seatSpecMap 包含 roomName 和 $vr-演播室 维度
  • 前端有演播室选择器
  • filterSeats() 按 currentRoom 过滤
  • submit() 提交的 spec 数组有 5 个维度
  • BuyService::BuyGoods 能正确处理 5 维 goods_data

九、相关文档

  • docs/SESSION_REPORT_20260421_PHASE2_FIX.md — Phase 2 会话记录
  • docs/FULL_PLAN.md — 完整开发计划
  • docs/PLAN_GHOST_SPEC_FIX.md — 幽灵 spec 修复(相关)
  • shopxo/app/plugins/vr_ticket/service/SeatSkuService.php — 核心服务
  • shopxo/app/plugins/vr_ticket/admin/Admin.php — 座位模板管理