2026-04-15 13:15:45 +00:00
|
|
|
|
# 后台编辑器 + 商品发布注入方案设计
|
|
|
|
|
|
|
2026-04-15 13:39:56 +00:00
|
|
|
|
> 版本:v2.0 | 日期:2026-04-15 | 状态:**待大头确认后执行**
|
|
|
|
|
|
>
|
|
|
|
|
|
> v2.0 更新:经 PM Auditor 代码级核查,修正 10 处与实际代码不符的描述(见文末勘误表)
|
2026-04-15 13:15:45 +00:00
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 一、整体架构一句话
|
|
|
|
|
|
|
|
|
|
|
|
**插件在 ShopXO 后台建一套"场馆配置"管理界面,用户发布票务商品时,选场馆 → 插件自动生成海量 Spec 并注入商品,用户全程不碰 ShopXO 原生 Spec 管理。**
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 二、三大核心区域
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
┌─────────────────────────────────────────────────────────────────┐
|
|
|
|
|
|
│ ShopXO 原生后台 │
|
|
|
|
|
|
│ │
|
|
|
|
|
|
│ ┌─────────────────┐ ┌──────────────────────┐ │
|
|
|
|
|
|
│ │ 商品管理 │ │ 插件专属后台(新增) │ │
|
|
|
|
|
|
│ │ 发布/编辑商品 │ ←→ │ 场馆配置管理 │ │
|
|
|
|
|
|
│ │ (注入点) │ │ 座位分区模板编辑器 │ │
|
2026-04-15 13:39:56 +00:00
|
|
|
|
│ └─────────────────┘ └──────────────────────┘ │
|
2026-04-15 13:15:45 +00:00
|
|
|
|
└─────────────────────────────────────────────────────────────────┘
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 区域 A:插件专属后台(新增)
|
|
|
|
|
|
|
|
|
|
|
|
商户在 ShopXO 后台左侧菜单进入「VR票务」:
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
VR票务
|
2026-04-15 13:39:56 +00:00
|
|
|
|
├── 场馆配置 ← Phase 3-1 新增(当前不存在)
|
2026-04-15 13:15:45 +00:00
|
|
|
|
├── 座位模板 ← 已存在(Phase 2)
|
|
|
|
|
|
├── 电子票管理 ← 已存在
|
|
|
|
|
|
├── 核销员管理 ← 已存在
|
|
|
|
|
|
└── 核销记录 ← 已存在
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-04-15 13:39:56 +00:00
|
|
|
|
> ⚠️ **当前状态**:`Venue.php` 控制器不存在。`vr_seat_templates` 表只有 `name` / `category_id` / `seat_map` / `spec_base_id_map` 字段,`venue` 信息目前不存在于数据库中。
|
2026-04-15 13:15:45 +00:00
|
|
|
|
|
|
|
|
|
|
### 区域 B:ShopXO 商品发布页(注入点)
|
|
|
|
|
|
|
2026-04-15 13:39:56 +00:00
|
|
|
|
> ⚠️ **待核实**:商品发布页 Tab 名称需对照 `saveinfo.html` 模板确认。当前描述"规格型号"Tab 可能不准确。
|
|
|
|
|
|
|
2026-04-15 13:15:45 +00:00
|
|
|
|
商户在 ShopXO 后台「商品管理 → 添加商品」:
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
添加商品
|
|
|
|
|
|
[商品名称] [商品分类]
|
|
|
|
|
|
|
2026-04-15 13:39:56 +00:00
|
|
|
|
▼ 票务配置 ← ShopXO 原生区域,我们注入票务选择器
|
|
|
|
|
|
[请选择场馆 ▼] ← 场馆下拉(来自 vr_seat_templates 表)
|
2026-04-15 13:15:45 +00:00
|
|
|
|
[请选择分区 ▼] ← 分区多选(根据场馆联动)
|
|
|
|
|
|
|
|
|
|
|
|
[商品详情 富文本编辑器]
|
|
|
|
|
|
▼ 其他Tab(参数/图片等)← ShopXO 原生
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 区域 C:插件商品详情页(已存在)
|
|
|
|
|
|
|
|
|
|
|
|
用户在前台看到票务商品详情页,选座下单,这个已实现。
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 三、场馆配置管理(区域 A)
|
|
|
|
|
|
|
2026-04-15 13:39:56 +00:00
|
|
|
|
### 3.1 数据结构(当前 vs 目标)
|
|
|
|
|
|
|
|
|
|
|
|
> ⚠️ **当前 seat_map 实际结构**(从 `SeatSkuService::BatchGenerate` 代码反推):
|
|
|
|
|
|
> - **无** `venue` 顶层字段
|
|
|
|
|
|
> - **无** `zones` 顶层字段
|
|
|
|
|
|
> - 顶层字段为:`map`、`seats`、`sections`、`row_labels`
|
|
|
|
|
|
> - `$vr-场馆` 的值目前**硬编码**为"国家体育馆"
|
2026-04-15 13:15:45 +00:00
|
|
|
|
|
2026-04-15 13:39:56 +00:00
|
|
|
|
**目标结构(Phase 3-1 需调整)**:
|
2026-04-15 13:15:45 +00:00
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
|
{
|
2026-04-15 13:39:56 +00:00
|
|
|
|
"venue": { // ← Phase 3-1 新增:从 venue 表读取或直接存这里
|
2026-04-15 13:15:45 +00:00
|
|
|
|
"name": "国家体育馆",
|
|
|
|
|
|
"address": "北京市朝阳区",
|
|
|
|
|
|
"image": "/uploads/vr/venue/1.jpg"
|
|
|
|
|
|
},
|
|
|
|
|
|
"map": ["AAAAAA", "BBBBBB", "CCCCCC"],
|
2026-04-15 13:39:56 +00:00
|
|
|
|
"seats": { // ← 实际字段名,不是 zones
|
2026-04-15 13:15:45 +00:00
|
|
|
|
"A": { "price": 899, "color": "#e74c3c", "label": "VIP区" },
|
|
|
|
|
|
"B": { "price": 599, "color": "#3498db", "label": "看台区" },
|
|
|
|
|
|
"C": { "price": 299, "color": "#2ecc71", "label": "普通区" }
|
|
|
|
|
|
},
|
2026-04-15 13:39:56 +00:00
|
|
|
|
"sections": [ // ← 实际字段名,存储分区元信息
|
2026-04-15 13:15:45 +00:00
|
|
|
|
{ "char": "A", "name": "VIP区", "color": "#e74c3c" },
|
|
|
|
|
|
{ "char": "B", "name": "看台区", "color": "#3498db" },
|
|
|
|
|
|
{ "char": "C", "name": "普通区", "color": "#2ecc71" }
|
2026-04-15 13:39:56 +00:00
|
|
|
|
],
|
|
|
|
|
|
"row_labels": ["A", "B", "C"]
|
2026-04-15 13:15:45 +00:00
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-04-15 13:39:56 +00:00
|
|
|
|
**关键理解**:
|
|
|
|
|
|
- `seats` = 每行(row char)的默认价格/颜色/标签(整行统一)
|
|
|
|
|
|
- `sections` = 每个分区的元信息(char → name/color 映射)
|
|
|
|
|
|
- `venue` 信息目前**不存在**于 seat_map,Phase 3-1 需要决策:是加到 seat_map 里,还是新建 `vr_venues` 表
|
2026-04-15 13:15:45 +00:00
|
|
|
|
|
|
|
|
|
|
### 3.2 场馆配置管理页面(表单可视化编辑器)
|
|
|
|
|
|
|
2026-04-15 13:39:56 +00:00
|
|
|
|
> ⚠️ **待实现**:当前 `Venue.php` 控制器不存在,Phase 3-1 需要新建。
|
|
|
|
|
|
|
2026-04-15 13:15:45 +00:00
|
|
|
|
商户在插件后台「场馆配置 → 添加」,看到以下表单:
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
【场馆基本信息】
|
|
|
|
|
|
场馆名称:[________________________]
|
|
|
|
|
|
场馆地址:[________________________]
|
|
|
|
|
|
场馆图片:[上传按钮]
|
|
|
|
|
|
|
|
|
|
|
|
【分区配置】(可以加/减分区)
|
2026-04-15 13:39:56 +00:00
|
|
|
|
┌──────────────────────────────────────────────────┐
|
|
|
|
|
|
│ Char │ 标签:VIP区 │ 单价:899元 │ 颜色:[红] │
|
|
|
|
|
|
├──────────────────────────────────────────────────┤
|
|
|
|
|
|
│ Char │ 标签:看台区 │ 单价:599元 │ 颜色:[蓝] │
|
|
|
|
|
|
├──────────────────────────────────────────────────┤
|
|
|
|
|
|
│ Char │ 标签:普通区 │ 单价:299元 │ 颜色:[绿] │
|
|
|
|
|
|
└──────────────────────────────────────────────────┘
|
2026-04-15 13:15:45 +00:00
|
|
|
|
[+ 添加分区] [- 删除分区]
|
|
|
|
|
|
|
|
|
|
|
|
【座位排布预览】
|
|
|
|
|
|
A A A A A A
|
|
|
|
|
|
B B B B B B
|
|
|
|
|
|
C C C C C C
|
|
|
|
|
|
|
|
|
|
|
|
每排座位数:[6___] 排数自动生成
|
|
|
|
|
|
|
|
|
|
|
|
【保存】 【取消】
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**技术实现**:
|
|
|
|
|
|
- layui 表单 + Vue3 CDN(轻量,不破坏 ShopXO 后台已有的 jQuery/layui 结构)
|
|
|
|
|
|
- 约 500 行前端代码,1-1.5 人天
|
2026-04-15 13:39:56 +00:00
|
|
|
|
- 保存时:表单数据 → 编码成 JSON → 写入 `vr_seat_templates.seat_map`
|
2026-04-15 13:15:45 +00:00
|
|
|
|
|
2026-04-15 13:39:56 +00:00
|
|
|
|
### 3.3 venue 信息的设计决策(待讨论)
|
2026-04-15 13:15:45 +00:00
|
|
|
|
|
2026-04-15 13:39:56 +00:00
|
|
|
|
**选项 A**:venue 信息存入 `seat_map.venue`(JSON 顶层)
|
|
|
|
|
|
- 优点:简单,不改表结构
|
|
|
|
|
|
- 缺点:一个模板只能关联一个 venue
|
2026-04-15 13:15:45 +00:00
|
|
|
|
|
2026-04-15 13:39:56 +00:00
|
|
|
|
**选项 B**:新建 `vr_venues` 表,`vr_seat_templates.venue_id` 外键关联
|
|
|
|
|
|
- 优点:一个 venue 可对应多个模板(如鸟巢主场地 + 鸟巢室外场)
|
|
|
|
|
|
- 缺点:多一张表,多一套 CRUD
|
|
|
|
|
|
|
|
|
|
|
|
> 当前 `vr_seat_templates` 表只有 `name` 字段同时承载"模板名"和"场馆名",暂未分离。
|
2026-04-15 13:15:45 +00:00
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 四、商品发布页注入(区域 B)
|
|
|
|
|
|
|
2026-04-15 13:39:56 +00:00
|
|
|
|
### 4.1 注入原理
|
2026-04-15 13:15:45 +00:00
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
ShopXO admin Goods::SaveInfo()
|
2026-04-15 13:39:56 +00:00
|
|
|
|
→ 调用 hook plugins_view_admin_goods_save(Goods.php:159)
|
|
|
|
|
|
→ 触发 vr_ticket/hook/AdminGoodsSave.php(Phase 3-2 新建)
|
2026-04-15 13:15:45 +00:00
|
|
|
|
→ 返回票务配置面板 HTML
|
2026-04-15 13:39:56 +00:00
|
|
|
|
→ 插入 saveinfo.html 的 base tab 内(<div class="am-form-group"> 容器)
|
2026-04-15 13:15:45 +00:00
|
|
|
|
```
|
|
|
|
|
|
|
2026-04-15 13:39:56 +00:00
|
|
|
|
### 4.2 钩子注册方式
|
2026-04-15 13:15:45 +00:00
|
|
|
|
|
2026-04-15 13:39:56 +00:00
|
|
|
|
> ⚠️ **重要修正**:ShopXO 插件系统**不支持** `backend_hook` 字段。
|
|
|
|
|
|
> `plugin.json` 实际使用 `hooks` 数组。
|
2026-04-15 13:15:45 +00:00
|
|
|
|
|
2026-04-15 13:39:56 +00:00
|
|
|
|
**plugin.json 正确格式**:
|
2026-04-15 13:15:45 +00:00
|
|
|
|
```json
|
|
|
|
|
|
{
|
2026-04-15 13:39:56 +00:00
|
|
|
|
"hooks": [
|
|
|
|
|
|
"plugins_service_order_pay_success_handle_end",
|
|
|
|
|
|
"plugins_service_order_delete_success",
|
|
|
|
|
|
"plugins_view_admin_goods_save",
|
|
|
|
|
|
"plugins_service_goods_save_handle"
|
|
|
|
|
|
]
|
2026-04-15 13:15:45 +00:00
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-04-15 13:39:56 +00:00
|
|
|
|
> 当前 `plugin.json` 只有前两个钩子,后两个是 **Phase 3-2 需要追加的**。
|
|
|
|
|
|
|
|
|
|
|
|
### 4.3 AdminGoodsSave.php(待新建)
|
|
|
|
|
|
|
|
|
|
|
|
> ⚠️ **当前不存在**:`vr_ticket/` 目录下无 `AdminGoodsSave.php` 文件。
|
|
|
|
|
|
|
|
|
|
|
|
Phase 3-2 需要新建:
|
|
|
|
|
|
- 文件路径:`plugins/vr_ticket/hook/AdminGoodsSave.php`
|
|
|
|
|
|
- `plugins_view_admin_goods_save` 钩子返回 HTML 注入票务表单
|
|
|
|
|
|
- `plugins_service_goods_save_handle` 钩子处理票务数据保存
|
|
|
|
|
|
|
2026-04-15 13:15:45 +00:00
|
|
|
|
### 4.4 场馆下拉数据来源
|
|
|
|
|
|
|
2026-04-15 13:39:56 +00:00
|
|
|
|
`AdminGoodsSave.php` 查询 `vr_seat_templates` 表:
|
2026-04-15 13:15:45 +00:00
|
|
|
|
|
|
|
|
|
|
```php
|
|
|
|
|
|
$templates = Db::name('vr_seat_templates')
|
|
|
|
|
|
->field('id, name, seat_map')
|
|
|
|
|
|
->where(['status' => 1])
|
|
|
|
|
|
->select();
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-04-15 13:39:56 +00:00
|
|
|
|
当前 `seat_map` 中无 `venue.name`,所以下拉显示的是 `vr_seat_templates.name` 字段(如 "Bird Nest - Zone A")。
|
|
|
|
|
|
|
|
|
|
|
|
> ⚠️ Phase 3-1 需要决策:venue 独立后,下拉应显示 venue.name 而非模板 name。
|
|
|
|
|
|
|
2026-04-15 13:15:45 +00:00
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 五、商品发布完整流程
|
|
|
|
|
|
|
|
|
|
|
|
### 场景:商户发布一张票务商品
|
|
|
|
|
|
|
|
|
|
|
|
**Step 1**:商户进入 ShopXO 后台 → 商品管理 → 添加商品
|
|
|
|
|
|
|
|
|
|
|
|
**Step 2**:填写基础信息
|
|
|
|
|
|
```
|
|
|
|
|
|
商品名称:周杰伦 VR 虚拟演唱会
|
2026-04-15 13:39:56 +00:00
|
|
|
|
商品分类:VR演出
|
2026-04-15 13:15:45 +00:00
|
|
|
|
```
|
|
|
|
|
|
|
2026-04-15 13:39:56 +00:00
|
|
|
|
**Step 3**:在注入的票务配置面板选场馆和分区
|
2026-04-15 13:15:45 +00:00
|
|
|
|
```
|
2026-04-15 13:39:56 +00:00
|
|
|
|
票务配置
|
|
|
|
|
|
场馆:[Bird Nest - Zone A ▼] ← AdminGoodsSave 注入的下拉
|
|
|
|
|
|
分区:[✓VIP区 ✓看台区] ← 多选,根据场馆联动
|
2026-04-15 13:15:45 +00:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**Step 4**:点击发布
|
|
|
|
|
|
|
|
|
|
|
|
```
|
2026-04-15 13:39:56 +00:00
|
|
|
|
商户点击"发布商品"
|
|
|
|
|
|
↓
|
|
|
|
|
|
Goods::Save() 调用 GoodsService::GoodsSave()(Goods.php:187)
|
|
|
|
|
|
↓
|
2026-04-15 13:15:45 +00:00
|
|
|
|
GoodsService::GoodsSave() 执行标准商品保存逻辑
|
2026-04-15 13:39:56 +00:00
|
|
|
|
↓
|
|
|
|
|
|
触发钩子 plugins_service_goods_save_handle(GoodsService.php:1550)
|
|
|
|
|
|
↓
|
2026-04-15 13:15:45 +00:00
|
|
|
|
AdminGoodsSaveHandle() 收到 POST 数据
|
2026-04-15 13:39:56 +00:00
|
|
|
|
├── 提取 template_id 和选中的分区 chars
|
|
|
|
|
|
├── 调用 SeatSkuService::BatchGenerate($goodsId, $templateId)
|
|
|
|
|
|
│ └── 注意:BatchGenerate() 签名是 (int $goodsId, int $seatTemplateId)
|
|
|
|
|
|
│ └── 当前版本:按 template 全量生成(不支持按 zones 过滤)
|
|
|
|
|
|
│ └── 为每个座位生成一行 sxo_goods_spec_base(inventory=1)
|
|
|
|
|
|
│ └── 同时写入 sxo_goods_spec_value($vr-场馆/$vr-分区/$vr-时段/$vr-座位号)
|
|
|
|
|
|
├── 更新 vr_seat_templates.spec_base_id_map(持久化,非内存)
|
|
|
|
|
|
└── 返回(让标准保存流程继续)
|
|
|
|
|
|
↓
|
2026-04-15 13:15:45 +00:00
|
|
|
|
商品保存完成
|
2026-04-15 13:39:56 +00:00
|
|
|
|
↓
|
2026-04-15 13:15:45 +00:00
|
|
|
|
订单后续流程不变(已有实现)
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 六、Spec 生成后的内部结构(技术细节)
|
|
|
|
|
|
|
2026-04-15 13:39:56 +00:00
|
|
|
|
> ⚠️ **表前缀**:ShopXO 原生表使用 `sxo_` 前缀,插件自定义表使用 `{$prefix}vrt_`。
|
|
|
|
|
|
|
2026-04-15 13:15:45 +00:00
|
|
|
|
以"国家体育馆 + VIP区(A) + 看台区(B)"为例:
|
|
|
|
|
|
|
2026-04-15 13:39:56 +00:00
|
|
|
|
### sxo_goods_spec_base(每行 = 一个座位 SKU)
|
2026-04-15 13:15:45 +00:00
|
|
|
|
|
|
|
|
|
|
| spec_base_id | goods_id | inventory | price |
|
|
|
|
|
|
|---|---|---|---|
|
|
|
|
|
|
| 2001 | 123 | 1 | 899 | ← A_1 座位
|
|
|
|
|
|
| 2002 | 123 | 1 | 899 | ← A_2 座位
|
|
|
|
|
|
| ... | 123 | 1 | 899 | ← A_6 座位
|
|
|
|
|
|
| 3001 | 123 | 1 | 599 | ← B_1 座位
|
|
|
|
|
|
| ... | 123 | 1 | 599 | ← B_6 座位
|
|
|
|
|
|
|
2026-04-15 13:39:56 +00:00
|
|
|
|
### sxo_goods_spec_type(每行 = 一个规格维度)
|
2026-04-15 13:15:45 +00:00
|
|
|
|
|
|
|
|
|
|
| id | goods_id | name | value |
|
|
|
|
|
|
|---|---|---|---|
|
|
|
|
|
|
| 1 | 123 | $vr-场馆 | `[{"name":"国家体育馆"}]` |
|
|
|
|
|
|
| 2 | 123 | $vr-分区 | `[{"name":"VIP区"},{"name":"看台区"}]` |
|
2026-04-15 13:39:56 +00:00
|
|
|
|
| 3 | 123 | $vr-时段 | `[{"name":"待选场次"}]` |
|
|
|
|
|
|
| 4 | 123 | $vr-座位号 | `[{"name":"A_1"},{"name":"A_2"},...,{"name":"B_6"}]` |
|
|
|
|
|
|
|
|
|
|
|
|
### vr_seat_templates.spec_base_id_map(持久化字段)
|
2026-04-15 13:15:45 +00:00
|
|
|
|
|
2026-04-15 13:39:56 +00:00
|
|
|
|
> ⚠️ **修正**:`spec_base_id_map` 不是内存/缓存,是持久化到 `vr_seat_templates` 表的字段。
|
2026-04-15 13:15:45 +00:00
|
|
|
|
|
|
|
|
|
|
商户在前台选座时,前端根据 seatKey(如 `"A_1"`)查表得到对应的 spec_base_id:
|
|
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
|
{
|
|
|
|
|
|
"A_1": 2001, "A_2": 2002, ..., "A_6": 2006,
|
|
|
|
|
|
"B_1": 3001, "B_2": 3002, ..., "B_6": 3012
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
2026-04-15 13:39:56 +00:00
|
|
|
|
## 七、BatchGenerate 实际接口(已实现代码)
|
|
|
|
|
|
|
|
|
|
|
|
> ⚠️ **关键修正**:文档之前描述的参数签名与实际代码不符。
|
|
|
|
|
|
|
|
|
|
|
|
**实际签名**(`SeatSkuService.php:34`):
|
|
|
|
|
|
```php
|
|
|
|
|
|
public static function BatchGenerate(int $goodsId, int $seatTemplateId): array
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**返回格式**:
|
|
|
|
|
|
```php
|
|
|
|
|
|
[
|
|
|
|
|
|
'code' => 0,
|
|
|
|
|
|
'data' => [
|
|
|
|
|
|
'total' => 18,
|
|
|
|
|
|
'generated' => 12,
|
|
|
|
|
|
'spec_base_id_map' => ['A_1' => 2001, 'A_2' => 2002, ...]
|
|
|
|
|
|
]
|
|
|
|
|
|
]
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**与文档描述的差异**:
|
|
|
|
|
|
- 文档说:`(goods_id, venue_id, zones[])` — ❌ 错误
|
|
|
|
|
|
- 实际是:`(int $goodsId, int $seatTemplateId)` — 全量按模板生成,不支持按 zones 过滤
|
|
|
|
|
|
|
|
|
|
|
|
> ⚠️ 如果 Phase 3-2 需要支持"按分区选择生成",需要修改 `BatchGenerate()` 增加 zones 过滤逻辑。
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 八、商品编辑时的处理(优先级低)
|
|
|
|
|
|
|
|
|
|
|
|
**问题**:商户编辑已发布票务商品时,ShopXO 后台会显示琳琅满目的 Spec 列表(几千个座位 SKU)。
|
|
|
|
|
|
|
|
|
|
|
|
**解决方案**(暂不实现,先让创建流程跑通):
|
2026-04-15 13:15:45 +00:00
|
|
|
|
|
2026-04-15 13:39:56 +00:00
|
|
|
|
**方案 A(推荐)**:商品表新增 `vr_ticket_config` JSON 字段
|
|
|
|
|
|
- 保存时:只存 `{ template_id, zones[], created_at }`
|
|
|
|
|
|
- 编辑时:从此字段还原venue + zone 选择状态,不从 spec_base 反推
|
|
|
|
|
|
- **需要迁移**:新增 `sxo_goods.vr_ticket_config` 字段
|
2026-04-15 13:15:45 +00:00
|
|
|
|
|
2026-04-15 13:39:56 +00:00
|
|
|
|
**方案 B**:直接用 `sxo_goods.extension_data`(如果 ShopXO 支持)
|
2026-04-15 13:15:45 +00:00
|
|
|
|
|
2026-04-15 13:39:56 +00:00
|
|
|
|
> ⚠️ 当前商品表**无** `vr_ticket_config` 字段。方案 A 需要新建数据库迁移。
|
2026-04-15 13:15:45 +00:00
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
2026-04-15 13:39:56 +00:00
|
|
|
|
## 九、与 ShopXO 原生 Spec 管理的关系
|
2026-04-15 13:15:45 +00:00
|
|
|
|
|
|
|
|
|
|
| 维度 | ShopXO 原生 Spec | 我们的票务 Spec |
|
|
|
|
|
|
|------|----------------|----------------|
|
|
|
|
|
|
| 谁创建 | 商户在商品编辑页手动添加 | 插件在发布时自动生成 |
|
2026-04-15 13:39:56 +00:00
|
|
|
|
| 表前缀 | `sxo_` | `sxo_goods_spec_base` / `sxo_goods_spec_value` |
|
|
|
|
|
|
| 商户感知 | 在后台规格管理看到 | 无感(我们注入的表单已覆盖场景) |
|
2026-04-15 13:15:45 +00:00
|
|
|
|
| 用户在前台看到 | 购物车/下单流程 | 票务选座 UI(ticket_detail.html)|
|
|
|
|
|
|
| 核销 | 不涉及 | 每座位一个 QR(vr_tickets 表)|
|
|
|
|
|
|
|
2026-04-15 13:39:56 +00:00
|
|
|
|
**商户永远不需要进入 ShopXO 原生的"规格管理"界面来管理票务座位。**
|
2026-04-15 13:15:45 +00:00
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
2026-04-15 13:39:56 +00:00
|
|
|
|
## 十、实施步骤
|
2026-04-15 13:15:45 +00:00
|
|
|
|
|
2026-04-15 13:39:56 +00:00
|
|
|
|
### Phase 3-1:后台场馆配置管理(新建 admin 页面)
|
2026-04-15 13:15:45 +00:00
|
|
|
|
|
2026-04-15 13:39:56 +00:00
|
|
|
|
- [ ] 决策:venue 信息存在 seat_map 内还是独立表?
|
2026-04-15 13:15:45 +00:00
|
|
|
|
- [ ] 新建 `admin/controller/Venue.php`
|
|
|
|
|
|
- [ ] 新建 `admin/view/venue/list.html`(场馆列表)
|
2026-04-15 13:39:56 +00:00
|
|
|
|
- [ ] 新建 `admin/view/venue/save.html`(表单编辑器:venue + zone + 座位排布)
|
2026-04-15 13:15:45 +00:00
|
|
|
|
- [ ] 升级 `vr_seat_templates.seat_map` JSON 结构(加入 venue 顶层)
|
2026-04-15 13:39:56 +00:00
|
|
|
|
- [ ] 将现有测试数据迁移为带 venue 的格式
|
2026-04-15 13:15:45 +00:00
|
|
|
|
|
|
|
|
|
|
### Phase 3-2:商品发布页注入
|
|
|
|
|
|
|
2026-04-15 13:39:56 +00:00
|
|
|
|
- [ ] 在 `plugin.json` 的 `hooks` 数组中追加钩子(**不用** `backend_hook`)
|
2026-04-15 13:15:45 +00:00
|
|
|
|
- [ ] 新建 `hook/AdminGoodsSave.php`(注入票务配置面板 HTML)
|
2026-04-15 13:39:56 +00:00
|
|
|
|
- [ ] 新建 `hook/AdminGoodsSaveHandle.php`(处理保存数据)
|
2026-04-15 13:15:45 +00:00
|
|
|
|
- [ ] 场馆下拉联动分区多选(Vue3,轻量)
|
2026-04-15 13:39:56 +00:00
|
|
|
|
- [ ] 确认商品发布页实际 Tab 名称(待对照 `saveinfo.html`)
|
2026-04-15 13:15:45 +00:00
|
|
|
|
|
|
|
|
|
|
### Phase 3-3:Spec 自动生成接入
|
|
|
|
|
|
|
2026-04-15 13:39:56 +00:00
|
|
|
|
- [ ] `BatchGenerate()` 增加按 zones 过滤参数(如需要)
|
2026-04-15 13:15:45 +00:00
|
|
|
|
- [ ] `extension_data` 写入 order_goods(选座信息追溯)
|
|
|
|
|
|
|
|
|
|
|
|
### Phase 3-4(优先级低):商品编辑回显
|
|
|
|
|
|
|
2026-04-15 13:39:56 +00:00
|
|
|
|
- [ ] 新建 `sxo_goods.vr_ticket_config` 字段迁移
|
|
|
|
|
|
- [ ] 编辑页加载时解析 `vr_ticket_config`,还原 venue + zone
|
2026-04-15 13:15:45 +00:00
|
|
|
|
- [ ] 编辑页票务配置面板回显
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
2026-04-15 13:39:56 +00:00
|
|
|
|
## 十一、勘误表(v1.0 → v2.0)
|
|
|
|
|
|
|
|
|
|
|
|
| # | v1.0 描述 | 实际代码 | v2.0 修正 |
|
|
|
|
|
|
|---|---------|---------|---------|
|
|
|
|
|
|
| 1 | `plugin.json` 用 `backend_hook` 注册 | ShopXO 用 `hooks` 数组 | 改为追加到 `hooks` 数组 |
|
|
|
|
|
|
| 2 | `AdminGoodsSave.php` 已存在或待实现 | 文件不存在 | 明确为 Phase 3-2 新建任务 |
|
|
|
|
|
|
| 3 | seat_map 顶层有 `venue` 字段 | 不存在,`$vr-场馆` 硬编码 | 新增设计决策:venue 存在哪 |
|
|
|
|
|
|
| 4 | seat_map 顶层有 `zones` 字段 | 实际是 `seats` 和 `sections` | 全文修正字段名 |
|
|
|
|
|
|
| 5 | `BatchGenerate(goods_id, venue_id, zones[])` | `(int $goodsId, int $seatTemplateId)` 全量生成 | 修正签名,注明全量/过滤差异 |
|
|
|
|
|
|
| 6 | `goods_spec_base` 前缀是 `vrt_` | ShopXO 原生表是 `sxo_` | 修正前缀,说明 `sxo_` vs `vrt_` |
|
|
|
|
|
|
| 7 | `vr_ticket_config` 字段存在 | 不存在,需新建迁移 | 明确为 Phase 3-4 新建任务 |
|
|
|
|
|
|
| 8 | 场馆配置是 Phase 2 已完成 | `Venue.php` 不存在,Phase 3-1 待实现 | 修正当前状态标注 |
|
|
|
|
|
|
| 9 | `spec_base_id_map` 是内存/缓存 | 持久化到 `vr_seat_templates.spec_base_id_map` | 修正存储位置描述 |
|
|
|
|
|
|
| 10 | 商品分类需绑定票务配置 | 代码中未强制校验 | 删除此约束描述 |
|