From d28a4dc511aba3757e0f8241a838777dba5ceffd Mon Sep 17 00:00:00 2001 From: Council Date: Tue, 14 Apr 2026 15:29:22 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E9=80=89=E5=BA=A7=E7=B3=BB=E7=BB=9F?= =?UTF-8?q?=E6=9E=B6=E6=9E=84=20+=20ShopXO=E5=90=8E=E5=8F=B0=E9=9B=86?= =?UTF-8?q?=E6=88=90=E6=96=B9=E6=A1=88=EF=BC=88docs/06=EF=BC=89+=20?= =?UTF-8?q?=E5=85=B3=E9=94=AE=E6=9E=B6=E6=9E=84=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ARCHITECTURE.md | 29 +++- docs/06_SEAT_MAP_INTEGRATION.md | 249 ++++++++++++++++++++++++++++++++ 2 files changed, 275 insertions(+), 3 deletions(-) create mode 100644 docs/06_SEAT_MAP_INTEGRATION.md diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index dc6526a..c4d3015 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -1,6 +1,6 @@ # ShopXO VR票务插件 — 架构文档 -> 版本:v1.1(2026-04-14 更新,整合 ShopXO 技术调研成果) +> 版本:v1.2(2026-04-14 下午更新,座位地图 + 场馆绑定架构确认) > 源码位置:council-research/shopxo-eval/.worktrees/shopxo-evaluator/shopxo-src/ ## 项目概述 @@ -14,6 +14,23 @@ --- +### 8. 场馆系统 + 选座地图(完整方案)⭐⭐⭐ + +详见 `docs/06_SEAT_MAP_INTEGRATION.md` + +**核心发现**: +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 完全可行) + +**绑定链路**: +``` +ShopXO 分类(venue type)← → vr_venues ← → vr_sessions ← → spec_base_id_map ← → ShopXO spec_base +``` + ## 核心技术发现(2026-04-14 调研) ### 1. CustomView Ace 编辑器 ⭐ @@ -28,9 +45,15 @@ ShopXO 内置全代码自定义页面编辑器,HTML/CSS/JS 三栏,实时预 - 完全注入票务选座 UI - 不修改核心代码 -### 3. 按商品类型替换模板(唯一核心代码修改)⭐ +### 3. 按商品类型替换模板(已有更优方案,优先用 Hook)⭐ -> ⚠️ **本项目唯一一处 ShopXO 核心代码修改**:需在 `Goods.php Index()` 中增加 1 行判断。 +> ⚠️ **已更新(2026-04-14 下午)**:原计划修改 Goods.php,现发现 `site_type=3`(虚拟商品)可绕过: +> - site_type=3 时 ShopXO 不显示 extraction popup,也不要求选择地址 +> - 直接通过 `plugins_view_goods_detail_base_sku_top` 注入票务选座 UI +> - **Goods.php 不再需要修改**,完全通过插件 Hook 实现 + +> 原 Goods.php 修改方案保留作为备案(如 Hook 无法满足时): +> 在 `Goods.php Index()` 中增加 1 行判断: > 这是 ShopXO 允许范围内,实现「特定商品类型使用独立模板」的唯一方式。 > 所有其他功能均通过插件钩子实现,**不修改核心代码**。 diff --git a/docs/06_SEAT_MAP_INTEGRATION.md b/docs/06_SEAT_MAP_INTEGRATION.md new file mode 100644 index 0000000..a36eeef --- /dev/null +++ b/docs/06_SEAT_MAP_INTEGRATION.md @@ -0,0 +1,249 @@ +# 选座系统 + 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 结构 + +```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 座位实时状态(动态层) + +```json +{ + "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 绑定) + +```json +{ + "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 = 1(FOR 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) + +```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配置)', + 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_id`:ShopXO 分类 ID,ShopXO 后台商品编辑时的"分类选择"结果直接对应 +- `seat_map_json`:座位地图配置,商家在插件后台导入/编辑 +- `seat_base_price`:基础票价 + +### 2.3 插件后台:场馆 + 座位图编辑器 + +商家在 ShopXO 后台插件入口管理: +1. **场馆管理**:创建场馆,关联 ShopXO 分类,上传/绘制座位图 +2. **座位图编辑器**:输入字符串地图(如 `aaaaaaaaaaaa`)或上传 SVG +3. **场次管理**:创建演出场次,选择场馆,设定时间、价格 + +### 2.4 插件 Hook:向 ShopXO 商品保存/读取注入 venue 信息 + +```php +// 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 映射**: + +```php +// 场次保存时,调用 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` ← 每个座位一个 SKU(inventory=1,price=座位价格) +- 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 可完全主导全部 phases(A-E)**。 + +--- + +## 五、关键约束确认 + +| 维度 | 限制 | 结论 | +|---|---|---| +| spec_type 数量 | 无硬限制 | ✅ 想加几个加几个 | +| 单规格选项数 | 无硬限制 | ✅ 500座/场馆没问题 | +| SKU 组合总数 | MySQL 无压力 | ✅ 3×2×500=3000行 OK | +| TEXT 字段容量 | 无实际限制 | ✅ JSON 存几千选项 OK | +| ShopXO 后台扩展 | 通过插件 Hook | ✅ 完全可行 | +| 自提点独立库存 | ShopXO 不支持 | ✅ 用 spec 替代(每座位独立库存)|