fix: venue_data方案替换分类方案,vr_sessions职责明确化
parent
d28a4dc511
commit
401f7b500d
|
|
@ -1,6 +1,6 @@
|
|||
# ShopXO VR票务插件 — 架构文档
|
||||
|
||||
> 版本:v1.2(2026-04-14 下午更新,座位地图 + 场馆绑定架构确认)
|
||||
> 版本:v1.3(2026-04-14 更新,venue_data 直接写入 sxo_goods,vr_sessions 职责明确)
|
||||
> 源码位置:council-research/shopxo-eval/.worktrees/shopxo-evaluator/shopxo-src/
|
||||
|
||||
## 项目概述
|
||||
|
|
@ -18,17 +18,21 @@
|
|||
|
||||
详见 `docs/06_SEAT_MAP_INTEGRATION.md`
|
||||
|
||||
**核心发现**:
|
||||
**核心架构(2026-04-14 更新)**:
|
||||
1. **字符地图是行业标准**:场馆平面图 → 字符串地图(如 `aaa___aaa`)→ 前端渲染为 SVG/DOM
|
||||
2. **ShopXO 分类 = Venue Type 绑定**:每个"场馆类型"对应一个 ShopXO 分类,商品挂分类 = 绑定 venue type
|
||||
3. **vr_venues 表**:商家在插件后台管理场馆,上传/编辑座位图 JSON
|
||||
4. **vr_sessions.seat_map_json**:每个场次存一份座位图配置
|
||||
5. **spec_base_id_map**:seat_id(如 `"3_5"`)→ spec_base_id(如 10003)映射,绑定 ShopXO 购买流程
|
||||
6. **ShopXO spec 系统无硬限制**:规格种类数、单规格选项数、SKU 组合数均无限制(3场馆×2票种×500座位=3000 SKU 完全可行)
|
||||
2. **venue_data 直接存在 sxo_goods**:每个票务商品 = 1 场演出,`sxo_goods.venue_data`(LONGTEXT)存完整配置
|
||||
3. **venue_data JSON 内容**:`venue`(场馆+座位图)+ `sessions[]`(场次列表)+ `spec_base_id_map`(座位→SKU映射)
|
||||
4. **vr_venues**:场馆主数据(名称/地址/座位图),多个商品/场次可复用同一个场馆
|
||||
5. **vr_sessions**:每个演出场次(日期+时间),共用 venue 的座位图,独立库存
|
||||
6. **spec_base_id_map**:seat_id(如 `"3_5"`)→ spec_base_id → ShopXO 购买流程
|
||||
7. **ShopXO spec 系统无硬限制**:3场馆×2票种×500座位=3000 SKU 完全可行
|
||||
|
||||
**绑定链路**:
|
||||
**数据流**:
|
||||
```
|
||||
ShopXO 分类(venue type)← → vr_venues ← → vr_sessions ← → spec_base_id_map ← → ShopXO spec_base
|
||||
sxo_goods.venue_data JSON
|
||||
├── venue.seat_map → 前端渲染座位图
|
||||
├── sessions[] → 场次选择器
|
||||
└── spec_base_id_map → 选座 → Buy API → 原子扣 spec_base.inventory
|
||||
```
|
||||
|
||||
## 核心技术发现(2026-04-14 调研)
|
||||
|
|
|
|||
|
|
@ -102,93 +102,164 @@
|
|||
|
||||
---
|
||||
|
||||
## 二、ShopXO 后台集成方案
|
||||
## 二、核心架构:venue_data 直接写入 sxo_goods
|
||||
|
||||
### 2.1 核心设计:复用 ShopXO 分类作为 Venue Type
|
||||
### 2.1 为什么不用 ShopXO 分类?
|
||||
|
||||
**每个 venue type = 一个 ShopXO 分类**
|
||||
ShopXO 分类是**商品类型**(演唱会/话剧/周边),不是**具体场馆**。
|
||||
多场馆 × 每个场馆不同座位配置 → 分类不够用。
|
||||
|
||||
ShopXO 已有完整的商品多级分类系统(`sxo_goods_category`),我们直接复用:
|
||||
**最优解:直接在 sxo_goods 表加字段,完整配置存在商品里。**
|
||||
|
||||
```
|
||||
sxo_goods_category
|
||||
├── 演唱会
|
||||
│ ├── 杭州大剧院-演唱会场
|
||||
│ ├── 北京鸟巢-演唱会场 ← 绑定 seat_map_json
|
||||
│ └── 上海梅赛德斯-演唱会场
|
||||
├── 话剧
|
||||
│ ├── 人艺剧院
|
||||
│ └── 国家大剧院
|
||||
└── 电影(可选)
|
||||
### 2.2 数据库改动:sxo_goods 新增 venue_data 字段
|
||||
|
||||
```sql
|
||||
ALTER TABLE sxo_goods
|
||||
ADD COLUMN venue_data LONGTEXT COMMENT '票务插件:场馆+场次+座位配置JSON';
|
||||
```
|
||||
|
||||
**优点**:
|
||||
- 不需要改 `sxo_goods` 表结构
|
||||
- 直接用 ShopXO 原生"分类选择器"(后台商品编辑已有,无需开发)
|
||||
- 商家在 ShopXO 后台创建商品时,分类 = venue type 绑定一步到位
|
||||
`LONGTEXT` ≈ 4GB,存完整座位图配置绑绑有余。
|
||||
ShopXO 已有先例:`sxo_order.extension_data` 和 `sxo_goods_spec_base.extends` 都是 `LONGTEXT`。
|
||||
|
||||
### 2.2 场馆表(vr_venues)
|
||||
### 2.3 venue_data JSON 结构
|
||||
|
||||
```json
|
||||
{
|
||||
"venue": {
|
||||
"id": 1,
|
||||
"name": "北京鸟巢",
|
||||
"address": "北京市朝阳区国家体育场南路1号",
|
||||
"seat_map": {
|
||||
"map": ["aaaaaaaaaaaa", "aaaaaaaaaaaa", "bbbbbb__bb", "bbbbbbbbbbbb"],
|
||||
"row_labels": ["A", "B", "C", "D"],
|
||||
"seats": {
|
||||
"a": { "price": 599, "label": "VIP区", "classes": "seat-vip" },
|
||||
"b": { "price": 399, "label": "普通区", "classes": "seat-normal" },
|
||||
"_": null
|
||||
},
|
||||
"sections": [
|
||||
{ "name": "VIP区", "color": "#FF6B6B", "rows": [0, 1] },
|
||||
{ "name": "普通区", "color": "#4ECDC4", "rows": [2, 3] }
|
||||
]
|
||||
}
|
||||
},
|
||||
"sessions": [
|
||||
{
|
||||
"id": 1,
|
||||
"datetime": "2026-06-01 19:30",
|
||||
"price_overrides": { "a": 699, "b": 399 }
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"datetime": "2026-06-02 19:30",
|
||||
"price_overrides": { "a": 599, "b": 299 }
|
||||
}
|
||||
],
|
||||
"spec_base_id_map": {
|
||||
"1_1": { "spec_base_id": 10001, "row": "A", "col": 1, "seat_type": "a", "price": 599 },
|
||||
"1_2": { "spec_base_id": 10002, "row": "A", "col": 2, "seat_type": "a", "price": 599 },
|
||||
"3_5": { "spec_base_id": 10003, "row": "C", "col": 5, "seat_type": "b", "price": 399 }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**每个商品 = 1 个演出场次 = 完整的票务配置**
|
||||
- 商家创建"周杰伦北京鸟巢演唱会2026-06-01场次"商品时,venue_data 包含:venue 信息 + 座位图 + spec_base_id_map
|
||||
- 用户打开商品详情 → `sxo_goods.venue_data` 已经在商品数据里 → 直接渲染选座 UI
|
||||
|
||||
### 2.4 vr_sessions 是什么?
|
||||
|
||||
`vr_sessions`(场次表)用于管理**同一场演出在不同时间的多场次**:
|
||||
|
||||
```
|
||||
商品:周杰伦北京鸟巢2026演唱会
|
||||
├── vr_sessions[1]:2026-06-01 19:30 第一场
|
||||
├── vr_sessions[2]:2026-06-02 19:30 第二场
|
||||
└── vr_sessions[3]:2026-06-03 14:00 第三场(下午场,价格不同)
|
||||
|
||||
每个 session:
|
||||
- id / datetime(具体时间)
|
||||
- price_overrides(该场次的价格覆盖,优先级高于 venue.seat_map.seats)
|
||||
- 复用 venue.seat_map 和 spec_base_id_map(座位布局不变)
|
||||
- 独立库存(每个场次的 spec_base.inventory 独立追踪)
|
||||
```
|
||||
|
||||
**如果一个商品只有单场演出**,vr_sessions 可以简化为 venue_data 里的 sessions 数组。
|
||||
|
||||
**vr_sessions 独立表的价值**:
|
||||
- 多场次共用同一个 venue(座位图不变)
|
||||
- 每个 session 有独立的 spec_base.inventory(第一场售罄≠第三场售罄)
|
||||
- 每个 session 可以有不同的 price_overrides(早鸟票/周末票等)
|
||||
|
||||
### 2.5 vr_venues(场馆表)
|
||||
|
||||
```sql
|
||||
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 '座位地图JSON(map[] + seats配置)',
|
||||
id BIGINT PRIMARY KEY AUTO_INCREMENT,
|
||||
name VARCHAR(180) NOT NULL COMMENT '场馆名称',
|
||||
address VARCHAR(255) NOT NULL DEFAULT '' COMMENT '场馆地址',
|
||||
seat_map_json LONGTEXT COMMENT '座位地图JSON',
|
||||
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)
|
||||
status TINYINT UNSIGNED NOT NULL DEFAULT 1,
|
||||
add_time INT UNSIGNED NOT NULL DEFAULT 0,
|
||||
upd_time INT UNSIGNED NOT NULL DEFAULT 0
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='VR演唱会场馆';
|
||||
```
|
||||
|
||||
**关键字段**:
|
||||
- `category_id`:ShopXO 分类 ID,ShopXO 后台商品编辑时的"分类选择"结果直接对应
|
||||
- `seat_map_json`:座位地图配置,商家在插件后台导入/编辑
|
||||
- `seat_base_price`:基础票价
|
||||
**vr_venues 的作用**:
|
||||
- 场馆基础信息统一管理(名称/地址)
|
||||
- seat_map_json 是场馆的"硬件配置"(场地座位布局是固定的)
|
||||
- 多个 session 共用一个 venue,venue_data 在每个 session/goods 里存一份副本
|
||||
|
||||
### 2.3 插件后台:场馆 + 座位图编辑器
|
||||
### 2.6 插件后台:场馆 + 商品票务配置编辑器
|
||||
|
||||
商家在 ShopXO 后台插件入口管理:
|
||||
1. **场馆管理**:创建场馆,关联 ShopXO 分类,上传/绘制座位图
|
||||
2. **座位图编辑器**:输入字符串地图(如 `aaaaaaaaaaaa`)或上传 SVG
|
||||
3. **场次管理**:创建演出场次,选择场馆,设定时间、价格
|
||||
|
||||
### 2.4 插件 Hook:向 ShopXO 商品保存/读取注入 venue 信息
|
||||
1. **场馆管理**:`vr_venues` CRUD,上传/编辑座位图(字符串地图编辑器)
|
||||
2. **商品票务配置**:选择 venue → 选择/创建 session → 系统将 venue.seat_map_json + session 信息整合写入 `sxo_goods.venue_data`
|
||||
3. **SKU 批量生成**:根据 seat_map 生成/更新 `sxo_goods_spec_base` 中的座位 SKU(spec_base_id_map)
|
||||
|
||||
### 2.7 插件 Hook
|
||||
|
||||
```php
|
||||
// plugins_service_goods_handle_begin — 保存商品时
|
||||
// plugins_service_goods_handle_begin — 保存商品时,拦截 venue_data
|
||||
public static function GoodsSaveHandle(&$params, &$goods, $goods_id)
|
||||
{
|
||||
// venue_id 由插件后台单独保存,不依赖 ShopXO 商品表
|
||||
// 关联:vr_venues ← (goods_id) → ShopXO sxo_goods
|
||||
if (!empty($params['venue_data'])) {
|
||||
// ShopXO 会自动将 venue_data 写入 sxo_goods.venue_data 字段
|
||||
}
|
||||
}
|
||||
|
||||
// plugins_service_goods_data — 读取商品时,注入 venue 信息
|
||||
public static function GoodsDataHandle(&$data, $goods_id)
|
||||
// plugins_service_goods_data — 读取商品时,注入票务配置
|
||||
public static function GoodsDataHandle(&$data, &$goods_id)
|
||||
{
|
||||
// 读取该商品关联的 venue + 场次列表
|
||||
$data['vr_venues'] = VrVenueService::GetVenueByGoodsId($goods_id);
|
||||
$data['vr_sessions'] = VrSessionService::GetSessionsByGoodsId($goods_id);
|
||||
// venue_data 已存在 sxo_goods.venue_data,直接可用
|
||||
// 前端模板通过 $goods.venue_data 直接访问
|
||||
}
|
||||
```
|
||||
|
||||
### 2.5 商品详情页加载流程
|
||||
### 2.8 商品详情页加载流程
|
||||
|
||||
```
|
||||
用户打开票务商品详情页
|
||||
│
|
||||
→ 触发 Goods.php Hook 判断 item_type=ticket
|
||||
→ site_type=3(虚拟),触发票务 Hook 注入选座区
|
||||
│
|
||||
→ 插件读取 goods_id 对应的 vr_venues + vr_sessions
|
||||
→ 前端读取 $goods.venue_data
|
||||
│ ├── venue.seat_map → 渲染座位图 SVG
|
||||
│ ├── venue.sessions → 显示场次选择器
|
||||
│ └── spec_base_id_map → 选座后查 SKU
|
||||
│
|
||||
→ 前端展示:
|
||||
│ 步骤1:选择场次(日期+时间)
|
||||
│ 步骤2:加载该场次座位图(从 seat_map_json 渲染 SVG)
|
||||
│ 步骤3:用户点击座位 → 获取 spec_base_id
|
||||
│ 步骤4:调 ShopXO Buy API 购买该 SKU
|
||||
→ 步骤1:用户选场次(datetime)
|
||||
│ └── 从 sessions[] 取 price_overrides 渲染座位图价格
|
||||
│
|
||||
→ 步骤2:用户点击座位 → 获取 seat_id(如 "3_5")
|
||||
│ └── 查 spec_base_id_map → 拿到 spec_base_id
|
||||
│
|
||||
→ 步骤3:调 ShopXO Buy API → spec_base_id + goods_id
|
||||
│ └── ShopXO BuyService::OrderInsertHandle() 原子扣库存
|
||||
│
|
||||
→ 步骤4:支付成功 → 插件生成 ticket_code + QR
|
||||
```
|
||||
|
||||
---
|
||||
|
|
@ -211,15 +282,16 @@ public static function BindSessionToSpecBase($session_id)
|
|||
```
|
||||
|
||||
**绑定关系**:
|
||||
- `vr_sessions.spec_base_id_map` ← JSON 映射(seat_id → spec_base_id)
|
||||
- `sxo_goods.venue_data.spec_base_id_map` ← JSON 映射(seat_id → spec_base_id),完整配置存在商品表
|
||||
- `sxo_goods_spec_base` ← 每个座位一个 SKU(inventory=1,price=座位价格)
|
||||
- ShopXO `BuyService::OrderInsertHandle` ← 原子扣 inventory,天然防超卖
|
||||
|
||||
### 3.2 场次变更时的 SKU 联动
|
||||
### 3.2 场次/座位变更时的 SKU 联动
|
||||
|
||||
- **场次新增座位**:调用 `GoodsSpecificationsInsert` 新增 spec_base
|
||||
- **场次删除座位**:将对应 spec_base.inventory 置为 0(软删除)
|
||||
- **新增座位**:调用 ShopXO `GoodsSpecificationsInsert()` 新增 spec_base
|
||||
- **删除座位**:将对应 spec_base.inventory 置为 0(软删除)
|
||||
- **价格变更**:更新 `sxo_goods_spec_base.price`
|
||||
- **配置更新后**:重新生成 `sxo_goods.venue_data.spec_base_id_map` 并保存
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -227,7 +299,7 @@ public static function BindSessionToSpecBase($session_id)
|
|||
|
||||
| 阶段 | 内容 | 工作量 |
|
||||
|---|---|---|
|
||||
| **Phase A** | vr_venues / vr_sessions 表 + CRUD | 小 |
|
||||
| **Phase A** | sxo_goods 加 venue_data LONGTEXT 字段 + vr_venues/vr_sessions 表 + CRUD | 小 |
|
||||
| **Phase B** | 场馆座位图编辑器(字符串地图) | 中 |
|
||||
| **Phase C** | Vue 3 选座组件(渲染 + 交互) | 中 |
|
||||
| **Phase D** | spec_base_id_map 绑定逻辑 | 中 |
|
||||
|
|
|
|||
Loading…
Reference in New Issue