docs: v2.1 - 通用扩展方法论 + Q4 spec_value复用粒度
parent
401f7b500d
commit
bde23d3195
247
ARCHITECTURE.md
247
ARCHITECTURE.md
|
|
@ -1,6 +1,6 @@
|
|||
# ShopXO VR票务插件 — 架构文档
|
||||
|
||||
> 版本:v1.3(2026-04-14 更新,venue_data 直接写入 sxo_goods,vr_sessions 职责明确)
|
||||
> 版本:v2.1(2026-04-14 更新,新增通用扩展方法论 + Q4 spec_value 复用粒度)
|
||||
> 源码位置:council-research/shopxo-eval/.worktrees/shopxo-evaluator/shopxo-src/
|
||||
|
||||
## 项目概述
|
||||
|
|
@ -9,31 +9,52 @@
|
|||
|
||||
当 vr-ticket-mp 主线项目因维护成本或架构限制无法继续时,此插件作为 Plan B:
|
||||
- **完全复用** ShopXO 已有能力(会员体系/积分/优惠券/微信支付)
|
||||
- **仅扩展** 票务专属逻辑(场次/座位/观演人/QR核销)
|
||||
- **优先通过插件机制扩展**,如插件机制不够用(如商品详情页按类型返回不同模板),允许直接修改 ShopXO 源码的最小范围(MIT 协议允许)。原则:改源码比绕弯快时,直接改;以进度为先,不为「不修改」而引入额外复杂度。
|
||||
- **仅扩展** 票务专属逻辑(座位/观演人/QR核销)
|
||||
- **优先通过插件机制扩展**,如插件机制不够用,允许直接修改 ShopXO 源码的最小范围(MIT 协议允许)。原则:改源码比绕弯快时,直接改;以进度为先,不为「不修改」而引入额外复杂度。
|
||||
|
||||
---
|
||||
|
||||
### 8. 场馆系统 + 选座地图(完整方案)⭐⭐⭐
|
||||
## 核心设计决策(v2.0 重大更新)
|
||||
|
||||
详见 `docs/06_SEAT_MAP_INTEGRATION.md`
|
||||
### 1. spec = 场次 ✅ 已确认
|
||||
|
||||
**核心架构(2026-04-14 更新)**:
|
||||
1. **字符地图是行业标准**:场馆平面图 → 字符串地图(如 `aaa___aaa`)→ 前端渲染为 SVG/DOM
|
||||
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 的每个 spec 选项 = 一个演出场次**
|
||||
|
||||
**数据流**:
|
||||
```
|
||||
sxo_goods.venue_data JSON
|
||||
├── venue.seat_map → 前端渲染座位图
|
||||
├── sessions[] → 场次选择器
|
||||
└── spec_base_id_map → 选座 → Buy API → 原子扣 spec_base.inventory
|
||||
- 不需要独立的 `vr_sessions` 表
|
||||
- 商品的多个 spec 值(如"2026-06-01 19:30"、"2026-06-02 14:00")= 多个场次
|
||||
- 用户在前端选择场次(spec)→ 再选择座位 → 提交购买
|
||||
|
||||
### 2. 座位模板绑定分类 ✅ 已确认
|
||||
|
||||
座位模板 `vr_seat_templates` 通过分类 ID 绑定到商品:
|
||||
- 后台创建座位模板 → 选择绑定分类(如"VR演唱会-A区")
|
||||
- 创建商品 → 选择该分类
|
||||
- 插件自动将模板 seat_map 写入商品 `venue_data`
|
||||
|
||||
### 3. venue_data 仅存座位配置 ✅ 已确认
|
||||
|
||||
`sxo_goods.venue_data` 精简为:
|
||||
```json
|
||||
{
|
||||
"seat_map": { /* 座位图配置(来自 vr_seat_templates)*/ },
|
||||
"spec_base_id_map": { /* seat_id → spec_base_id */ }
|
||||
}
|
||||
```
|
||||
sessions 信息由 ShopXO spec 系统提供,不再存储在 venue_data 中。
|
||||
|
||||
### 4. 票务商品识别:venue_data 非空 ✅ 已确认
|
||||
|
||||
- 不新增 `item_type` 字段
|
||||
- 判断逻辑:`!empty($goods['venue_data'])` → 票务商品
|
||||
- 前端 Vue:判断 `goods.venue_data` 是否有值
|
||||
|
||||
### 5. 完全通过 Hook 实现,不改 Goods.php ✅ 已确认
|
||||
|
||||
- `plugins_view_goods_detail_base_sku_top` 注入票务选座 UI
|
||||
- 完全覆盖原有规格选择区
|
||||
- Goods.php 修改方案降级为备案(如 Hook 注入范围不够时启用)
|
||||
|
||||
---
|
||||
|
||||
## 核心技术发现(2026-04-14 调研)
|
||||
|
||||
|
|
@ -49,29 +70,12 @@ ShopXO 内置全代码自定义页面编辑器,HTML/CSS/JS 三栏,实时预
|
|||
- 完全注入票务选座 UI
|
||||
- 不修改核心代码
|
||||
|
||||
### 3. 按商品类型替换模板(已有更优方案,优先用 Hook)⭐
|
||||
### 3. 票务商品识别方案(v2.0 更新)⭐
|
||||
|
||||
> ⚠️ **已更新(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 允许范围内,实现「特定商品类型使用独立模板」的唯一方式。
|
||||
> 所有其他功能均通过插件钩子实现,**不修改核心代码**。
|
||||
|
||||
```php
|
||||
// app/index/controller/Goods.php Index() 方法,约第 440 行
|
||||
// 在 return MyView(); 之前插入:
|
||||
|
||||
if(!empty($goods['item_type']) && $goods['item_type'] == 'ticket') {
|
||||
return MyView('/goods/ticket_detail'); // 自定义票务模板
|
||||
}
|
||||
return MyView(); // 默认模板
|
||||
```
|
||||
|
||||
对应模板文件:`app/index/view/default/goods/ticket_detail.html`(ShopXO 主题目录下)
|
||||
> **已废弃 Goods.php 修改方案**:完全通过 Hook + 前端判断实现
|
||||
> - `plugins_view_goods_detail_base_sku_top` 注入票务选座 UI
|
||||
> - 前端 Vue 判断 `goods.venue_data` 是否有值 → 有值则加载票务专用前端
|
||||
> - **Goods.php 不再需要修改**
|
||||
|
||||
### 4. shopxo-uniapp 支持微信小程序 ⭐
|
||||
|
||||
|
|
@ -111,7 +115,7 @@ $qr_url = MyUrl('index/qrcode/index', ['content' => base64_encode($data)]);
|
|||
│ ┌─────────────────────────────────────────────┐ │
|
||||
│ │ vr_ticket 插件 │ │
|
||||
│ │ ┌────────────────────────────────────────┐ │ │
|
||||
│ │ │ EventService — 场次管理 │ │ │
|
||||
│ │ │ SeatTemplateService — 座位模板管理 │ │ │
|
||||
│ │ │ TicketService — QR票生成/发放 │ │ │
|
||||
│ │ │ VerifyService — 核销验证 │ │ │
|
||||
│ │ └────────────────────────────────────────┘ │ │
|
||||
|
|
@ -155,19 +159,90 @@ $qr_url = MyUrl('index/qrcode/index', ['content' => base64_encode($data)]);
|
|||
| `sxo_order` | 订单 |
|
||||
| `sxo_order_address` | 地址(含身份证) |
|
||||
| `sxo_payment` | 支付配置 |
|
||||
| `sxo_goods` | 商品(含票务商品) |
|
||||
| `sxo_goods_spec_base` | SKU(座位=库存1) |
|
||||
| `sxo_goods` | 商品(含票务商品,venue_data 存座位配置) |
|
||||
| `sxo_goods_spec_base` | SKU(每个座位 = inventory=1 的 SKU) |
|
||||
|
||||
### 插件独立表
|
||||
### 插件独立表(v2.0 精简版)
|
||||
|
||||
| 表 | 用途 |
|
||||
|---|---|
|
||||
| `vr_events` | 活动 |
|
||||
| `vr_sessions` | 场次时间+库存 |
|
||||
| `vr_tickets` | 电子票(含QR数据) |
|
||||
| `vr_seat_templates` | 座位模板(seat_map JSON + 绑定分类ID) |
|
||||
| `vr_tickets` | 电子票(含QR数据 + 观演人) |
|
||||
| `vr_verifiers` | 核销员 |
|
||||
| `vr_verifications` | 核销记录 |
|
||||
|
||||
> **v2.0 变更**:删除 `vr_events`、`vr_sessions`、`vr_venues` 表,座位配置由 `vr_seat_templates` 通过分类绑定实现。
|
||||
|
||||
### vr_seat_templates 表结构
|
||||
|
||||
```sql
|
||||
CREATE TABLE vr_seat_templates (
|
||||
id BIGINT PRIMARY KEY AUTO_INCREMENT,
|
||||
name VARCHAR(180) NOT NULL COMMENT '模板名称(如:鸟巢-A区)',
|
||||
category_id BIGINT UNSIGNED NOT NULL COMMENT '绑定的 ShopXO 分类ID',
|
||||
seat_map LONGTEXT NOT NULL COMMENT '座位地图JSON',
|
||||
status TINYINT UNSIGNED DEFAULT 1,
|
||||
add_time INT UNSIGNED DEFAULT 0,
|
||||
upd_time INT UNSIGNED DEFAULT 0,
|
||||
UNIQUE KEY category_id (category_id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='VR演唱会座位模板';
|
||||
```
|
||||
|
||||
### vr_tickets 表结构
|
||||
|
||||
```sql
|
||||
CREATE TABLE vr_tickets (
|
||||
id BIGINT PRIMARY KEY AUTO_INCREMENT,
|
||||
order_id BIGINT UNSIGNED NOT NULL COMMENT '订单ID',
|
||||
order_no CHAR(60) NOT NULL,
|
||||
goods_id BIGINT UNSIGNED NOT NULL,
|
||||
user_id BIGINT UNSIGNED NOT NULL,
|
||||
ticket_code CHAR(36) NOT NULL COMMENT 'UUID票码',
|
||||
qr_data TEXT COMMENT '加密QR内容',
|
||||
seat_info VARCHAR(255) COMMENT '座位信息(如 A区-3排-5座)',
|
||||
real_name VARCHAR(60) COMMENT '观演人姓名',
|
||||
phone CHAR(15) COMMENT '手机号',
|
||||
id_card CHAR(18) COMMENT '身份证号',
|
||||
verify_status TINYINT DEFAULT 0 COMMENT '0未核销, 1已核销',
|
||||
verify_time INT UNSIGNED DEFAULT 0,
|
||||
verifier_id BIGINT UNSIGNED DEFAULT 0,
|
||||
issued_at INT UNSIGNED DEFAULT 0,
|
||||
created_at INT UNSIGNED DEFAULT 0,
|
||||
updated_at INT UNSIGNED DEFAULT 0,
|
||||
UNIQUE KEY ticket_code (ticket_code),
|
||||
KEY order_id (order_id),
|
||||
KEY user_id (user_id),
|
||||
KEY verify_status (verify_status)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='VR演唱会电子票';
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## venue_data JSON 结构(v2.0 精简版)
|
||||
|
||||
```json
|
||||
{
|
||||
"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] }
|
||||
]
|
||||
},
|
||||
"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 }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 插件目录结构
|
||||
|
|
@ -177,29 +252,24 @@ app/plugins/vr_ticket/
|
|||
├── plugin.json # 插件声明
|
||||
├── service/
|
||||
│ ├── BaseService.php # 基础配置
|
||||
│ ├── EventService.php # 活动/场次服务
|
||||
│ ├── SeatTemplateService.php # 座位模板管理(替代 vr_sessions/vr_venues)
|
||||
│ ├── TicketService.php # 票生成/发放
|
||||
│ └── VerifyService.php # 核销逻辑
|
||||
├── view/
|
||||
│ ├── Goods.php # 商品详情页钩子
|
||||
│ └── User.php # 用户中心钩子
|
||||
│ └── Goods.php # 商品详情页钩子(备用,不修改核心)
|
||||
├── Admin/
|
||||
│ ├── Controller/
|
||||
│ │ ├── EventController.php
|
||||
│ │ ├── SessionController.php
|
||||
│ │ └── TicketController.php
|
||||
│ │ ├── SeatTemplateController.php # 座位模板管理
|
||||
│ │ └── TicketController.php # 票务管理
|
||||
│ └── View/
|
||||
│ ├── event_list.html
|
||||
│ ├── event_save.html
|
||||
│ ├── session_list.html
|
||||
│ ├── session_save.html
|
||||
│ ├── seat_template_list.html
|
||||
│ ├── seat_template_save.html
|
||||
│ ├── ticket_list.html
|
||||
│ └── verification_list.html
|
||||
├── Api/
|
||||
│ └── Controller/
|
||||
│ ├── EventController.php # 活动API
|
||||
│ ├── SessionController.php # 场次API
|
||||
│ └── TicketController.php # 票/核销API
|
||||
│ ├── SeatTemplateController.php # 座位模板API
|
||||
│ └── TicketController.php # 票/核销API
|
||||
└── EventListener.php # ShopXO事件监听
|
||||
|
||||
database/migrations/ # 数据库迁移
|
||||
|
|
@ -212,14 +282,15 @@ static/vr_ticket/ # 静态资源
|
|||
|
||||
```
|
||||
1. 商家后台
|
||||
→ VR票务插件 → 新建活动(关联ShopXO商品)
|
||||
→ 添加场次(时间 + 座位图 + 票价 + 库存)
|
||||
→ VR票务插件 → 座位模板管理(创建 seat_map → 绑定分类)
|
||||
→ 商品管理 → 创建商品 → 选择绑定分类(自动继承 venue_data)
|
||||
→ 商品规格管理 → 添加规格(每个规格 = 一个场次,如"2026-06-01 晚场")
|
||||
|
||||
2. 用户前端
|
||||
→ 首页浏览 → 进入商品详情页
|
||||
→ 判断 item_type == 'ticket'
|
||||
→ 判断 goods.venue_data 是否有值
|
||||
→ 跳转到票务选座页(pages/ticket-buy)
|
||||
→ 选择座位 → 填写观演人信息
|
||||
→ 选择场次(spec)→ 选择座位 → 填写观演人信息
|
||||
→ 提交订单 → 微信支付
|
||||
|
||||
3. 支付成功
|
||||
|
|
@ -228,7 +299,7 @@ static/vr_ticket/ # 静态资源
|
|||
→ 生成 QR 票 → 存入 vr_tickets 表
|
||||
→ 用户可在票夹页查看
|
||||
|
||||
4. 核销
|
||||
4. 核销(独立页面)
|
||||
→ 工作人员打开核销页(pages/plugins/vr-ticket-verify)
|
||||
→ 扫描用户 QR 码
|
||||
→ POST /api/ticket/verify
|
||||
|
|
@ -249,12 +320,54 @@ static/vr_ticket/ # 静态资源
|
|||
| **积分/优惠券** | 自建 | ShopXO 内置 |
|
||||
| **微信支付** | 自建 | ShopXO 内置 |
|
||||
| **QR 核销** | 自建 | 基于 ShopXO 已有机制 |
|
||||
| **场次管理** | vr_sessions 独立表 | ShopXO spec(已确认) |
|
||||
| **座位配置** | venue_data 完整配置 | vr_seat_templates 绑定分类(已确认) |
|
||||
| **部署** | Docker | 虚拟主机即可 |
|
||||
| **AI 参与度** | 80% | 85-90% |
|
||||
| **维护成本** | 高 | 低 |
|
||||
|
||||
---
|
||||
|
||||
## 通用扩展方法论(v2.1 新增)
|
||||
|
||||
> 已确认的通用插件扩展模式,可套用到票务/实物/套票等所有自定义类型商品。
|
||||
|
||||
```
|
||||
Plugin Table (vr_xxx)
|
||||
↑
|
||||
│ 绑定 spec_value (全局可复用)
|
||||
│
|
||||
sxo_goods.extension_data (JSON)
|
||||
↑
|
||||
│ 插件在商品加载时注入
|
||||
│
|
||||
Frontend — 读 extension_data
|
||||
→ 判断 type(票务/实物/套票)
|
||||
→ 渲染对应自定义组件
|
||||
```
|
||||
|
||||
### 核心链路
|
||||
|
||||
1. **插件数据表**(`vr_xxx`)存储自定义业务数据
|
||||
2. **绑定 spec_value**:插件 spec_value 关联插件数据(spec_value 全局可复用)
|
||||
3. **写入 `goods.extension_data`**:商品保存时,插件将 spec_value 映射写入 ShopXO 扩展字段
|
||||
4. **前端按 type 渲染**:前端读取 `extension_data` → 判断 `type` 字段 → 加载对应自定义组件
|
||||
|
||||
---
|
||||
|
||||
## 待确认问题(pending-council)
|
||||
|
||||
详见 `docs/ALIGNMENT.md` Section 二。
|
||||
|
||||
| 问题 | 说明 |
|
||||
|---|---|
|
||||
| Q1 | 座位模板与分类的绑定粒度(一个分类 = 一个座位区,还是一个商品可绑定多模板?) |
|
||||
| Q2 | spec_base_id_map 生成时机(每个 spec 独立座位,还是所有 spec 共用座位配置?) |
|
||||
| Q3 | 观演人信息存储位置(extension_data / vr_tickets / 作为 spec 选项) |
|
||||
| Q4 | spec_value 复用粒度 — 全局可复用 vs 商品级私有?(见上方通用扩展方法论) |
|
||||
|
||||
---
|
||||
|
||||
## 技术栈
|
||||
|
||||
- **PHP 8+** / ThinkPHP 8(ShopXO 生态)
|
||||
|
|
@ -282,3 +395,5 @@ static/vr_ticket/ # 静态资源
|
|||
| [docs/02_FRONTEND_CUSTOMIZATION.md](docs/02_FRONTEND_CUSTOMIZATION.md) | shopxo-uniapp 编译与自定义 |
|
||||
| [docs/03_VERIFICATION_SYSTEM.md](docs/03_VERIFICATION_SYSTEM.md) | 核销系统设计 |
|
||||
| [docs/04_IMPLEMENTATION_ROADMAP.md](docs/04_IMPLEMENTATION_ROADMAP.md) | 实施路线图与 Agent 分工 |
|
||||
| [docs/06_SEAT_MAP_INTEGRATION.md](docs/06_SEAT_MAP_INTEGRATION.md) | 选座系统 + 座位地图(v2.0 已精简) |
|
||||
| [docs/ALIGNMENT.md](docs/ALIGNMENT.md) | 架构对齐文档(用户需求 vs 文档现状) |
|
||||
|
|
|
|||
|
|
@ -0,0 +1,260 @@
|
|||
# 架构对齐文档 — 用户需求 vs 文档现状
|
||||
|
||||
> PM Agent 输出 | 2026-04-14
|
||||
> 状态:草稿,待确认
|
||||
|
||||
---
|
||||
|
||||
## 一、用户需求摘要
|
||||
|
||||
以下需求从对话记录中提取,已确认:
|
||||
|
||||
| # | 需求 | 来源 |
|
||||
|---|---|---|
|
||||
| U1 | **spec = 场次**:规格(spec)直接作为场次,不需要独立的 vr_sessions 表 | "规格(spec)就是场次" |
|
||||
| U2 | **每个商品 = 一个场馆的演唱会**:用户在一个商品内完成选座购买全流程 | "每一个商品就是一个物理意义上的电影院或者演出地址" |
|
||||
| U3 | **场次作为分类值**:场次加载为 ShopXO 的分类(category)值,后台通过分类名关联 | "场次这个东西我们直接加载成一个分类值就好了" |
|
||||
| U4 | **座位模板 → 绑定分类 → 商品关联**:后台创建座位模板 → 绑定特定分类 → 创建商品时关联 | "后端管理页:创建座位模板 → 绑定特定的分类信息 → 创建商品时关联" |
|
||||
| U5 | **前端判断 item_type 替换模板**:商品详情页加载时判断传入值,如果是 VR 票则替换为专用前端 | "前端商品详情页加载时,判断传入的值,得出这是不是VR演唱会票,如果是就替换成专用前端" |
|
||||
| U6 | **购买走 ShopXO 原生流程**:最终提交购买按钮走 ShopXO 原生订单流程,不另建独立下单 | "最终提交购买按钮时走 ShopXO 原生订单流程" |
|
||||
| U7 | **核销端独立页面**:核销是单独的页面,不在购票流程内 | "核销端是另外的独立页面" |
|
||||
| U8 | **扩展字段注入模式**:插件新增数据表 → 绑定 spec_value(全局可复用)→ 写入 goods.extension_data → 前端按数据渲染自定义 UI。这是通用模式,可套用到票务/实物/套票等所有自定义类型商品 | 通用扩展方法论确认 |
|
||||
|
||||
### Q4(新增待确认问题)
|
||||
> ShopXO spec_value 是「全局可复用」还是「商品级私有」?
|
||||
> - 全局复用:插件初始化时自动检测/创建 spec,商家直接选
|
||||
> - 商品级私有:每个商品的 spec_value 独立,插件需要引导用户正确填写
|
||||
> 影响:插件初始化策略 + 绑定方案
|
||||
|
||||
**Q4 的两个选项:**
|
||||
- Option A:全局可复用(推荐,插件可以一键初始化)
|
||||
- Option B:商品级私有(需要引导流程)
|
||||
|
||||
---
|
||||
|
||||
## 二、冲突点列表
|
||||
|
||||
### 冲突 1:vr_sessions 独立表 vs spec = 场次
|
||||
|
||||
| 维度 | 文档现状 | 用户想法 |
|
||||
|---|---|---|
|
||||
| 场次存储 | `vr_sessions` 独立表,存日期/时间/价格覆盖/库存 | ShopXO spec 系统即为场次载体,每个 spec 选项 = 一个场次 |
|
||||
| 座位布局 | `sxo_goods.venue_data` 存完整 venue + sessions[] + spec_base_id_map | 座位模板(seat_map)绑定分类,通过分类关联到商品 |
|
||||
| 多场次复用 | vr_sessions 共用 venue.seat_map,多场次同一座位图 | 每个商品 = 单一商品内嵌多个 spec(场次选项) |
|
||||
|
||||
**分析**:
|
||||
- 用户明确说"规格(spec)就是场次",意味着不需要独立的 vr_sessions 表
|
||||
- 多个场次 = 商品的多个 spec 选项(如"2026-06-01 19:30"、"2026-06-02 14:00")
|
||||
- 座位图/座位布局是商品属性,通过分类绑定关联
|
||||
|
||||
**建议方案**:
|
||||
> 废弃 `vr_sessions` 独立表,改为:
|
||||
> - 商品的每个 spec 选项 = 一个场次(spec_value = 日期时间)
|
||||
> - `sxo_goods.venue_data` 仅存座位图配置(seat_map),不存 sessions[]
|
||||
> - 座位图与分类绑定,商品通过分类继承座位图配置
|
||||
|
||||
---
|
||||
|
||||
### 冲突 2:venue_data 存完整配置 vs 座位模板绑定分类
|
||||
|
||||
| 维度 | 文档现状 | 用户想法 |
|
||||
|---|---|---|
|
||||
| 座位配置存储 | `sxo_goods.venue_data` 存完整 venue + seat_map + sessions[] + spec_base_id_map | 座位模板单独管理,绑定到分类,商品通过分类关联 |
|
||||
| 绑定机制 | 每个商品直接存完整配置 | 商品关联分类 → 分类关联座位模板 → 座位模板提供 seat_map |
|
||||
|
||||
**分析**:
|
||||
- 用户强调"创建座位模板 → 绑定特定的分类信息 → 创建商品时关联"
|
||||
- 这是解耦思路:座位模板可复用,多个商品(同一场馆不同演出)共用同一模板
|
||||
- 分类在这里是"座位模板 ID"的载体
|
||||
|
||||
**建议方案**:
|
||||
> 保留座位模板表 `vr_seat_templates`:
|
||||
> - 存储 seat_map JSON(座位图布局)
|
||||
> - 绑定到一个特定分类(如 category_id = 场馆分类 ID)
|
||||
> - 商品创建时选择该分类 → 自动继承 seat_map → 商品的 spec 展示座位选择
|
||||
|
||||
---
|
||||
|
||||
### 冲突 3:item_type 字段写入机制
|
||||
|
||||
| 维度 | 文档现状 | 用户想法 |
|
||||
|---|---|---|
|
||||
| item_type 来源 | 文档提到 Goods.php 修改来识别 ticket 类型 | 前端商品详情页加载时判断"传入的值",识别 VR 票类型 |
|
||||
| 谁来写 item_type | 未明确(后台手动设置?插件自动?) | 用户认为商品数据中有某个值可以用来判断类型 |
|
||||
|
||||
**分析**:
|
||||
- 用户说"判断传入的值" → 说明商品数据里有某个字段可区分票务商品
|
||||
- 这个字段可能是:`item_type`、`goods_type`、或者自定义的 `venue_data` 是否存在
|
||||
- 最简单方案:商品有 `venue_data` 就认为是票务商品,不需要额外字段
|
||||
|
||||
**建议方案**:
|
||||
> `item_type` 不作为独立字段,用 `sxo_goods.venue_data IS NOT NULL` 作为票务商品标识:
|
||||
> - 创建商品时勾选"票务商品"或选择座位模板 → 插件写入 `venue_data`
|
||||
> - 前端判断 `$goods.venue_data` 是否有值 → 有值则加载票务专用前端
|
||||
> - 无需修改 ShopXO 核心或新增字段
|
||||
|
||||
---
|
||||
|
||||
### 冲突 4:Goods.php 修改 vs 纯 Hook 方案
|
||||
|
||||
| 维度 | 文档现状 | 用户想法 |
|
||||
|---|---|---|
|
||||
| 模板替换方式 | 文档记录了 Goods.php Index() 加判断的方案(MIT 允许范围内最小改动) | 用户说"前端商品详情页加载时,判断传入的值,得出这是不是VR演唱会票,如果是就替换成专用前端" |
|
||||
| 是否改核心文件 | 文档认为改 1 行可接受,保留为备案 | 用户想法中没有提到需要改核心 PHP 文件 |
|
||||
|
||||
**分析**:
|
||||
- 用户的前端判断逻辑可以完全在 Hook + Vue 前端实现
|
||||
- 不需要修改 Goods.php
|
||||
|
||||
**建议方案**:
|
||||
> 废弃 Goods.php 修改方案,完全用 Hook:
|
||||
> - `plugins_view_goods_detail_base_sku_top` 注入票务选座 UI(覆盖原有规格选择区)
|
||||
> - 前端 Vue 判断 `goods.venue_data` 是否有值 → 决定是否跳转到票务页面
|
||||
> - 保留 Goods.php 方案作为备案(如 Hook 注入不够时)
|
||||
|
||||
---
|
||||
|
||||
### 冲突 5:vr_venues 场馆表是否还需要?
|
||||
|
||||
| 维度 | 文档现状 | 用户想法 |
|
||||
|---|---|---|
|
||||
| 场馆概念 | `vr_venues` 表存场馆名称/地址/座位图,多 session 共用一个 venue | 用户说"每一个商品就是一个物理意义上的电影院或者演出地址" |
|
||||
| 粒度 | venue 是复用层,session 是时间层 | 商品 = 场馆 + 演出,座位模板是绑定到分类的 |
|
||||
|
||||
**分析**:
|
||||
- 用户思路里"商品 = 场馆的一个演出",不需要跨商品的 venue 复用
|
||||
- 座位模板(seat_map)是绑定分类的,不是绑定 venue 的
|
||||
- `vr_venues` 表在用户思路里没有明确位置
|
||||
|
||||
**建议方案**:
|
||||
> 废弃 `vr_venues` 表,改用 `vr_seat_templates`(座位模板)表:
|
||||
> - 模板存储 seat_map JSON + 分类绑定
|
||||
> - 商品通过分类关联模板
|
||||
> - 不需要独立的场馆表(场馆名称可存商品信息里)
|
||||
|
||||
---
|
||||
|
||||
## 三、确认方案(无争议部分)
|
||||
|
||||
以下结论可以直接落地:
|
||||
|
||||
### 3.1 spec = 场次(已确认)
|
||||
|
||||
每个 ShopXO spec 选项 = 一个演出场次(如"2026-06-01 晚场"、"2026-06-02 下午场")。
|
||||
|
||||
- 不需要 `vr_sessions` 表
|
||||
- 商品的多个 spec 值 = 多个场次
|
||||
- spec_base_id_map 绑定 seat_id → spec_base_id → 购买流程
|
||||
|
||||
### 3.2 座位模板绑定分类(已确认)
|
||||
|
||||
座位模板 `vr_seat_templates` 表:
|
||||
```sql
|
||||
CREATE TABLE vr_seat_templates (
|
||||
id BIGINT PRIMARY KEY AUTO_INCREMENT,
|
||||
name VARCHAR(180) NOT NULL COMMENT '模板名称(如:鸟巢-A区)',
|
||||
category_id BIGINT UNSIGNED NOT NULL COMMENT '绑定的分类ID',
|
||||
seat_map LONGTEXT NOT NULL COMMENT '座位地图JSON',
|
||||
status TINYINT UNSIGNED DEFAULT 1,
|
||||
add_time INT UNSIGNED DEFAULT 0,
|
||||
upd_time INT UNSIGNED DEFAULT 0,
|
||||
UNIQUE KEY category_id (category_id)
|
||||
);
|
||||
```
|
||||
|
||||
绑定流程:
|
||||
1. 后台创建座位模板 → 选择绑定分类(如"VR演唱会-A区")
|
||||
2. 创建商品 → 选择该分类
|
||||
3. 插件自动将模板 seat_map 写入商品 `venue_data`
|
||||
|
||||
### 3.3 venue_data 精简为仅座位配置
|
||||
|
||||
`sxo_goods.venue_data` JSON 结构简化:
|
||||
```json
|
||||
{
|
||||
"seat_map": { /* 座位图配置 */ },
|
||||
"spec_base_id_map": { /* seat_id → spec_base_id */ }
|
||||
}
|
||||
```
|
||||
|
||||
sessions 信息不再存在 venue_data 里,由 ShopXO spec 系统提供。
|
||||
|
||||
### 3.4 item_type 判断用 venue_data 非空
|
||||
|
||||
- 不新增 `item_type` 字段
|
||||
- 判断逻辑:`!empty($goods['venue_data'])` → 票务商品
|
||||
- 前端 Vue 同理:`goods.venue_data` 是否有值
|
||||
|
||||
### 3.5 完全通过 Hook 实现,不改 Goods.php
|
||||
|
||||
- `plugins_view_goods_detail_base_sku_top` 注入票务选座 UI
|
||||
- 保留 Goods.php 修改方案为备案(如 Hook 注入范围不够时启用)
|
||||
|
||||
### 3.6 核销端独立页面
|
||||
|
||||
- B 端核销页:`pages/plugins/vr-ticket-verify/check/check.vue`(Fork 自 realstore/check.vue)
|
||||
- 不在购票主流程内
|
||||
|
||||
---
|
||||
|
||||
## 四、待确认问题(需要 Council 判断)
|
||||
|
||||
以下问题无法从对话记录中确认解决方案,打上 `pending-council` 标签:
|
||||
|
||||
### Q1: 座位模板与分类的绑定粒度
|
||||
|
||||
**背景**:用户说"绑定特定的分类信息",但一个演出可能有多个座位区(A区/B区/C区),每个区是不同的座位图。
|
||||
|
||||
**选项**:
|
||||
- A:每个分类 = 一个座位区(多个分类绑定同一个演出)
|
||||
- B:一个分类 = 一个完整场馆(座位图内分区)
|
||||
- C:一个商品可以绑定多个座位模板
|
||||
|
||||
**待验证**:用户的"分类"是指 ShopXO 商品分类,还是自定义的"场馆座位分类"?需要明确。
|
||||
|
||||
### Q2: spec_base_id_map 的生成时机
|
||||
|
||||
**背景**:spec = 场次时,座位选择是跨 spec(场次)的,还是每个 spec(场次)有独立的座位?
|
||||
|
||||
**选项**:
|
||||
- A:每个 spec(场次)有独立的座位配置(不同日期同一座位区)
|
||||
- B:所有 spec 共用同一座位配置(日期不同,座位相同)
|
||||
|
||||
**待验证**:用户说"只要用户想要买某一个场馆的演唱会,他就在这一个商品里面操作就行" → 倾向于 B(同一座位图,日期不同)。
|
||||
|
||||
### Q4: spec_value 复用粒度 — 全局还是商品级?
|
||||
|
||||
**背景**:用户在确认通用扩展方法论时提出,spec_value 的复用粒度直接影响插件初始化策略。
|
||||
|
||||
**选项**:
|
||||
- Option A:全局可复用(推荐,插件可以一键初始化)— 插件初始化时自动检测/创建 spec_value,商家创建商品时直接选
|
||||
- Option B:商品级私有(需要引导流程)— 每个商品的 spec_value 独立,插件需要引导用户正确填写
|
||||
|
||||
**影响**:插件初始化策略 + 绑定方案。
|
||||
|
||||
---
|
||||
|
||||
### Q3: 观演人信息的存储位置
|
||||
|
||||
**背景**:用户说"走 ShopXO 原生订单流程",但票务需要观演人信息。
|
||||
|
||||
**选项**:
|
||||
- A:观演人信息存 `sxo_order.extension_data`(ShopXO 扩展字段)
|
||||
- B:观演人信息存 `vr_tickets` 表
|
||||
- C:观演人信息作为 spec 选项的一部分
|
||||
|
||||
**待验证**:ShopXO 原生订单的 extension_data 是否足够存观演人信息?是否有更优雅的方式?
|
||||
|
||||
---
|
||||
|
||||
## 五、ARCHITECTURE.md 更新计划
|
||||
|
||||
根据以上对齐结论,ARCHITECTURE.md 需要做以下更新:
|
||||
|
||||
1. **删除 vr_sessions 表**,改为 spec = 场次
|
||||
2. **删除 vr_venues 表**,改为 vr_seat_templates 表(座位模板绑定分类)
|
||||
3. **简化 venue_data JSON 结构**,只保留 seat_map + spec_base_id_map
|
||||
4. **明确 item_type 判断机制**:venue_data 非空
|
||||
5. **删除 Goods.php 修改方案**,改为纯 Hook 方案
|
||||
6. **更新购票流程**,反映 spec = 场次的设计
|
||||
7. **更新目录结构**,反映 vr_seat_templates 替代 vr_venues/vr_sessions
|
||||
Loading…
Reference in New Issue