vr-shopxo-plugin/docs/06_SEAT_MAP_INTEGRATION.md

8.0 KiB
Raw Blame History

选座系统 + ShopXO 后台集成架构

调研日期2026-04-14 关联文档ARCHITECTURE.md, 01_SHOPXO_TECHNICAL_RESEARCH.md


一、选座地图:行业标准做法

1.1 核心原理

"字符地图"是业界通用方案,不是我们发明的:

'aaa___aaa'   ← a=可用座, _=过道/柱子/墙壁
'bbb__bbbbb'  ← b=另一种座位(不同价格区)
'____________' ← 纯过道/无座位

加载时前端把每个字符翻译成可交互的 DOM/SVG 元素:

  • 可选座位 → 可点击
  • 过道 _ → 渲染为空白间隔或装饰元素
  • 不同字符类型 → 不同颜色/价格/状态

1.2 主流实现对比

方案 技术 优点 缺点
字符地图 + DOM/SVG 字符串地图 + div/SVG 轻量、易编辑、易生成 复杂形状需精确计算
SVG 手绘 设计师导出 SVG 座位形状自然 需要设计工具,导入复杂
Canvas Konva.js / Fabric.js 性能好,适合超大型场馆 无 DOM 元素,交互复杂
seats.io 商业 SaaS 功能完整 付费,不可定制

推荐:字符地图 + Vue 3 SVG 渲染自研AI 可完全生成)

1.3 座位地图 JSON 结构

{
  "venue_id": "venue_001",
  "map": [
    "aaaaaaaaaaaa",
    "aaaaaaaaaaaa",
    "bbbbbbbb__bb",
    "bbbbbbbbbbbb",
    "__cccccccccc__"
  ],
  "row_labels": ["A", "B", "C", "D", "E"],
  "seats": {
    "a": { "price": 299, "label": "VIP区", "classes": "seat-vip" },
    "b": { "price": 199, "label": "普通区", "classes": "seat-normal" },
    "c": { "price": 99,  "label": "后排区", "classes": "seat-back" },
    "_": null
  },
  "sections": [
    { "name": "VIP区", "color": "#FF6B6B", "rows": [0, 1] },
    { "name": "普通区", "color": "#4ECDC4", "rows": [2, 3] }
  ],
  "screen": { "label": "舞台/银幕", "position": "top" }
}

1.4 座位实时状态(动态层)

{
  "seats": {
    "1_1": { "status": "available" },
    "1_2": { "status": "sold" },
    "1_3": { "status": "selected" },
    "2_5": { "status": "locked" }
  }
}

座位状态含义:

  • available — 可选
  • sold — 已售
  • selected — 当前用户选中
  • locked — 被其他用户临时锁定(可选,支持超时释放)

1.5 spec_base_id_map与 ShopXO SKU 绑定)

{
  "spec_base_id_map": {
    "1_1":  { "spec_base_id": 10001, "venue": "A区", "row": "A", "col": 1, "price": 299 },
    "1_2":  { "spec_base_id": 10002, "venue": "A区", "row": "A", "col": 2, "price": 299 },
    "3_5":  { "spec_base_id": 10003, "venue": "B区", "row": "C", "col": 5, "price": 199 }
  }
}

绑定流程

用户在前端选座 seat_id="3_5"
    → 查 spec_base_id_map 拿到 spec_base_id=10003
    → 调 ShopXO Buy API: goods_id + spec_base_id
    → ShopXO 原子扣 spec_base.inventory = 1FOR UPDATE
    → 订单完成

二、ShopXO 后台集成方案

2.1 核心设计:复用 ShopXO 分类作为 Venue Type

每个 venue type = 一个 ShopXO 分类

ShopXO 已有完整的商品多级分类系统(sxo_goods_category),我们直接复用:

sxo_goods_category
├── 演唱会
│   ├── 杭州大剧院-演唱会场
│   ├── 北京鸟巢-演唱会场     ← 绑定 seat_map_json
│   └── 上海梅赛德斯-演唱会场
├── 话剧
│   ├── 人艺剧院
│   └── 国家大剧院
└── 电影(可选)

优点

  • 不需要改 sxo_goods 表结构
  • 直接用 ShopXO 原生"分类选择器"(后台商品编辑已有,无需开发)
  • 商家在 ShopXO 后台创建商品时,分类 = venue type 绑定一步到位

2.2 场馆表vr_venues

CREATE TABLE vr_venues (
  id           BIGINT PRIMARY KEY AUTO_INCREMENT,
  category_id  INT UNSIGNED NOT NULL COMMENT 'ShopXO分类ID绑定venue type',
  name         VARCHAR(180) NOT NULL COMMENT '场馆名称',
  address      VARCHAR(255) NOT NULL DEFAULT '' COMMENT '场馆地址',
  seat_map_json LONGTEXT COMMENT '座位地图JSONmap[] + seats配置',
  seat_base_price INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '基础票价',
  status      TINYINT UNSIGNED NOT NULL DEFAULT 1 COMMENT '0下架 1上架',
  add_time    INT UNSIGNED NOT NULL DEFAULT 0,
  upd_time    INT UNSIGNED NOT NULL DEFAULT 0,
  INDEX idx_category_id (category_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='VR演唱会场馆';

关键字段

  • category_idShopXO 分类 IDShopXO 后台商品编辑时的"分类选择"结果直接对应
  • seat_map_json:座位地图配置,商家在插件后台导入/编辑
  • seat_base_price:基础票价

2.3 插件后台:场馆 + 座位图编辑器

商家在 ShopXO 后台插件入口管理:

  1. 场馆管理:创建场馆,关联 ShopXO 分类,上传/绘制座位图
  2. 座位图编辑器:输入字符串地图(如 aaaaaaaaaaaa)或上传 SVG
  3. 场次管理:创建演出场次,选择场馆,设定时间、价格

2.4 插件 Hook向 ShopXO 商品保存/读取注入 venue 信息

// plugins_service_goods_handle_begin — 保存商品时
public static function GoodsSaveHandle(&$params, &$goods, $goods_id)
{
    // venue_id 由插件后台单独保存,不依赖 ShopXO 商品表
    // 关联vr_venues ← (goods_id) → ShopXO sxo_goods
}

// plugins_service_goods_data — 读取商品时,注入 venue 信息
public static function GoodsDataHandle(&$data, $goods_id)
{
    // 读取该商品关联的 venue + 场次列表
    $data['vr_venues'] = VrVenueService::GetVenueByGoodsId($goods_id);
    $data['vr_sessions'] = VrSessionService::GetSessionsByGoodsId($goods_id);
}

2.5 商品详情页加载流程

用户打开票务商品详情页
    │
    → 触发 Goods.php Hook 判断 item_type=ticket
    │
    → 插件读取 goods_id 对应的 vr_venues + vr_sessions
    │
    → 前端展示:
    │   步骤1选择场次日期+时间)
    │   步骤2加载该场次座位图从 seat_map_json 渲染 SVG
    │   步骤3用户点击座位 → 获取 spec_base_id
    │   步骤4调 ShopXO Buy API 购买该 SKU

三、与 ShopXO spec 系统的衔接

3.1 座位图与 ShopXO SKU 的绑定时机

场次创建时自动生成 SKU 映射

// 场次保存时,调用 SKU 绑定函数
public static function BindSessionToSpecBase($session_id)
{
    // 1. 读取 vr_sessions.seat_map_json
    // 2. 遍历 map[],为每个"非_"字符生成/查找 spec_base_id
    // 3. 生成 spec_base_id_map 存入 vr_sessions
    // 4. 调用 ShopXO GoodsSpecificationsInsert() 写入 spec_base 表
}

绑定关系

  • vr_sessions.spec_base_id_map ← JSON 映射seat_id → spec_base_id
  • sxo_goods_spec_base ← 每个座位一个 SKUinventory=1price=座位价格)
  • ShopXO BuyService::OrderInsertHandle ← 原子扣 inventory天然防超卖

3.2 场次变更时的 SKU 联动

  • 场次新增座位:调用 GoodsSpecificationsInsert 新增 spec_base
  • 场次删除座位:将对应 spec_base.inventory 置为 0软删除
  • 价格变更:更新 sxo_goods_spec_base.price

四、实现优先级

阶段 内容 工作量
Phase A vr_venues / vr_sessions 表 + CRUD
Phase B 场馆座位图编辑器(字符串地图)
Phase C Vue 3 选座组件(渲染 + 交互)
Phase D spec_base_id_map 绑定逻辑
Phase E 实时座位状态轮询/推送

AI 可完全主导全部 phasesA-E


五、关键约束确认

维度 限制 结论
spec_type 数量 无硬限制 想加几个加几个
单规格选项数 无硬限制 500座/场馆没问题
SKU 组合总数 MySQL 无压力 3×2×500=3000行 OK
TEXT 字段容量 无实际限制 JSON 存几千选项 OK
ShopXO 后台扩展 通过插件 Hook 完全可行
自提点独立库存 ShopXO 不支持 用 spec 替代(每座位独立库存)