vr-shopxo-plugin/ARCHITECTURE.md

423 lines
17 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# ShopXO VR票务插件 — 架构文档
> 版本v2.22026-04-14 更新Q4 spec 绑定方案已确认 — $vr- 前缀隔离)
> 源码位置council-research/shopxo-eval/.worktrees/shopxo-evaluator/shopxo-src/
## 项目概述
基于 ShopXO 生态的 VR 演唱会票务插件Plan B
当 vr-ticket-mp 主线项目因维护成本或架构限制无法继续时,此插件作为 Plan B
- **完全复用** ShopXO 已有能力(会员体系/积分/优惠券/微信支付)
- **仅扩展** 票务专属逻辑(座位/观演人/QR核销
- **优先通过插件机制扩展**,如插件机制不够用,允许直接修改 ShopXO 源码的最小范围MIT 协议允许)。原则:改源码比绕弯快时,直接改;以进度为先,不为「不修改」而引入额外复杂度。
---
## 核心设计决策v2.0 重大更新)
### 1. spec = 场次 ✅ 已确认
**ShopXO 的每个 spec 选项 = 一个演出场次**
- 不需要独立的 `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 调研)
### 1. CustomView Ace 编辑器 ⭐
ShopXO 内置全代码自定义页面编辑器HTML/CSS/JS 三栏,实时预览。
文件:`app/admin/view/default/customview/saveinfo.html`
访问:后台 → 营销 → 自定义页面管理
### 2. 商品详情页 30+ 钩子 ⭐
最佳注入点:`plugins_view_goods_detail_base_sku_top`(规格选择区顶部)
- 完全注入票务选座 UI
- 不修改核心代码
### 3. 票务商品识别方案v2.0 更新)⭐
> **已废弃 Goods.php 修改方案**:完全通过 Hook + 前端判断实现
> - `plugins_view_goods_detail_base_sku_top` 注入票务选座 UI
> - 前端 Vue 判断 `goods.venue_data` 是否有值 → 有值则加载票务专用前端
> - **Goods.php 不再需要修改**
### 4. shopxo-uniapp 支持微信小程序 ⭐
HBuilderX 导入 → 配置 AppID → 发行 → 微信开发者工具
条件编译指令已配置(`#ifdef MP-WEIXIN`
### 5. QR 码生成内置 ⭐
使用 `\base\Qrcode` 类 + phpqrcode 库:
```php
$qr_url = MyUrl('index/qrcode/index', ['content' => base64_encode($data)]);
```
### 6. 自提点核销页面可直接参考 ⭐
`pages/plugins/realstore/check/check.vue` — 完整 B 端核销 UI
- uni.scanCode 扫码
- 手动输入兼容
- 成功/失败状态展示
### 7. 订单 extension_data 存票务信息 ⭐
`sxo_order.extension_data` — JSON 扩展字段,可存核销状态
---
## 整体架构
```
┌─────────────────────────────────────────────────────┐
│ ShopXO PHP 后端 │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────┐ │
│ │ 会员/积分 │ │ 商品/订单 │ │ 微信支付 │ │
│ │ coupons │ │ items/orders│ │ payment │ │
│ └──────────────┘ └──────────────┘ └──────────┘ │
│ │
│ ┌─────────────────────────────────────────────┐ │
│ │ vr_ticket 插件 │ │
│ │ ┌────────────────────────────────────────┐ │ │
│ │ │ SeatTemplateService — 座位模板管理 │ │ │
│ │ │ TicketService — QR票生成/发放 │ │ │
│ │ │ VerifyService — 核销验证 │ │ │
│ │ └────────────────────────────────────────┘ │ │
│ │ ┌────────────────────────────────────────┐ │ │
│ │ │ 钩子: plugins_view_goods_detail_* │ │ │
│ │ │ 钩子: plugins_service_buy_order_* │ │ │
│ │ └────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────┐
│ shopxo-uniapp │
│ ┌──────────────┐ ┌──────────────────┐ │
│ │ 商品详情页 │ │ 票务选座页 │ ← Fork │
│ │goods-detail.vue│ │ ticket-buy.vue │ 自定义 │
│ └──────────────┘ └──────────────────┘ │
│ ┌──────────────┐ ┌──────────────────┐ │
│ │ 用户中心 │ │ 票夹页 │ ← 新建 │
│ │ user.vue │ │ ticket-wallet.vue│ │
│ └──────────────┘ └──────────────────┘ │
│ ┌──────────────┐ │
│ │ 核销页 │ ← Fork realstore/check.vue │
│ │ verify.vue │ │
│ └──────────────┘ │
└─────────────────────────────────────────────────────┘
```
---
## 数据模型
### ShopXO 复用表
| 表 | 用途 |
|---|---|
| `sxo_user` | 会员 |
| `sxo_wallet` | 余额钱包 |
| `sxo_integral` | 积分 |
| `sxo_coupon` | 优惠券 |
| `sxo_order` | 订单 |
| `sxo_order_address` | 地址(含身份证) |
| `sxo_payment` | 支付配置 |
| `sxo_goods` | 商品含票务商品venue_data 存座位配置) |
| `sxo_goods_spec_base` | SKU每个座位 = inventory=1 的 SKU |
### 插件独立表v2.0 精简版)
| 表 | 用途 |
|---|---|
| `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 }
}
}
```
---
## 插件目录结构
```
app/plugins/vr_ticket/
├── plugin.json # 插件声明
├── service/
│ ├── BaseService.php # 基础配置
│ ├── SeatTemplateService.php # 座位模板管理(替代 vr_sessions/vr_venues
│ ├── TicketService.php # 票生成/发放
│ └── VerifyService.php # 核销逻辑
├── view/
│ └── Goods.php # 商品详情页钩子(备用,不修改核心)
├── Admin/
│ ├── Controller/
│ │ ├── SeatTemplateController.php # 座位模板管理
│ │ └── TicketController.php # 票务管理
│ └── View/
│ ├── seat_template_list.html
│ ├── seat_template_save.html
│ ├── ticket_list.html
│ └── verification_list.html
├── Api/
│ └── Controller/
│ ├── SeatTemplateController.php # 座位模板API
│ └── TicketController.php # 票/核销API
└── EventListener.php # ShopXO事件监听
database/migrations/ # 数据库迁移
static/vr_ticket/ # 静态资源
```
---
## 完整购票流程
```
1. 商家后台
→ VR票务插件 → 座位模板管理(创建 seat_map → 绑定分类)
→ 商品管理 → 创建商品 → 选择绑定分类(自动继承 venue_data
→ 商品规格管理 → 添加规格(每个规格 = 一个场次,如"2026-06-01 晚场"
2. 用户前端
→ 首页浏览 → 进入商品详情页
→ 判断 goods.venue_data 是否有值
→ 跳转到票务选座页pages/ticket-buy
→ 选择场次spec→ 选择座位 → 填写观演人信息
→ 提交订单 → 微信支付
3. 支付成功
→ ShopXO 支付回调
→ TicketService::OnOrderPaid() 触发
→ 生成 QR 票 → 存入 vr_tickets 表
→ 用户可在票夹页查看
4. 核销(独立页面)
→ 工作人员打开核销页pages/plugins/vr-ticket-verify
→ 扫描用户 QR 码
→ POST /api/ticket/verify
→ VerifyService 更新核销状态
→ 返回核销结果
```
---
## 与 vr-ticket-mp 对比
| 维度 | vr-ticket-mp主线 | vr-shopxo-pluginPlan B |
|---|---|---|
| **后端** | Go + Gin自建 | PHP + ThinkPHPShopXO |
| **数据库** | Supabase Postgres | ShopXO MySQL |
| **前端** | uni-app自建 | shopxo-uniapp已有 |
| **会员体系** | Supabase Auth | ShopXO 内置 |
| **积分/优惠券** | 自建 | 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` 字段 → 加载对应自定义组件
---
### Q4 结论spec 绑定方案 — 已确认)
ShopXO spec 是模板级复制模式,用 `$vr-` 前缀做命名空间隔离:
```
插件初始化时:
→ 在 sxo_goods_spec_template 创建 "$vr-场馆" 模板
→ 商家在商品里应用此模板spec_value COPY 到商品 spec_type
插件商品加载时:
→ 遍历 sxo_goods_spec_type
→ 识别 name 以 "$vr-" 开头的类型
→ 按 spec_value.name 去 vr_venues 表查 seat_map
→ 写入 extension_data
```
**这个方案的优点:**
- 用户自己添加的普通规格永远不会冲突(没有 `$vr-` 前缀)
- 插件完全掌控自己的 spec 命名空间
- 模板可批量导入,不需要每个商品手动配置
---
## 待确认问题pending-council
详见 `docs/ALIGNMENT.md` Section 二。
| 问题 | 说明 |
|---|---|
| Q1 | 座位模板与分类的绑定粒度(一个分类 = 一个座位区,还是一个商品可绑定多模板?) |
| Q2 | spec_base_id_map 生成时机(每个 spec 独立座位,还是所有 spec 共用座位配置?) |
| Q3 | 观演人信息存储位置extension_data / vr_tickets / 作为 spec 选项) |
| Q4 | spec_value 复用粒度 — 全局可复用 vs 商品级私有? | ✅ **已解决** — `$vr-` 前缀隔离方案(见上方 Q4 结论) |
---
## 技术栈
- **PHP 8+** / ThinkPHP 8ShopXO 生态)
- **MySQL 5.7+**ShopXO 同一实例)
- **uni-app**shopxo-uniapp已支持微信小程序
- **phpqrcode**`extend/qrcode/phpqrcode.php`,内置)
- **uni.scanCode**(微信/支付宝等小程序扫码)
---
## 官方文档(开发前必查)
- 官方文档站https://doc.shopxo.net/
- 插件开发文档https://doc.shopxo.net/article/3.html
- 开发文档索引https://doc.shopxo.net/article/4.html
- uniapp 打包教程https://doc.shopxo.net/article/1/293727233598554112.html
- 完整索引:[docs/OFFICIAL_DOCS.md](docs/OFFICIAL_DOCS.md)
## 开发文档
| 文档 | 内容 |
|---|---|
| [docs/00_OVERVIEW.md](docs/00_OVERVIEW.md) | 项目总览 |
| [docs/01_SHOPXO_TECHNICAL_RESEARCH.md](docs/01_SHOPXO_TECHNICAL_RESEARCH.md) | ShopXO 技术能力完整调研 |
| [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 文档现状) |