vr-shopxo-plugin/ARCHITECTURE.md

17 KiB
Raw Permalink Blame History

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 精简为:

{
  "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 库:

$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_eventsvr_sessionsvr_venues 表,座位配置由 vr_seat_templates 通过分类绑定实现。

vr_seat_templates 表结构

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 表结构

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 精简版)

{
  "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 商品级私有?

技术栈

  • PHP 8+ / ThinkPHP 8ShopXO 生态)
  • MySQL 5.7+ShopXO 同一实例)
  • uni-appshopxo-uniapp已支持微信小程序
  • phpqrcodeextend/qrcode/phpqrcode.php,内置)
  • uni.scanCode(微信/支付宝等小程序扫码)

官方文档(开发前必查)

开发文档

文档 内容
docs/00_OVERVIEW.md 项目总览
docs/01_SHOPXO_TECHNICAL_RESEARCH.md ShopXO 技术能力完整调研
docs/02_FRONTEND_CUSTOMIZATION.md shopxo-uniapp 编译与自定义
docs/03_VERIFICATION_SYSTEM.md 核销系统设计
docs/04_IMPLEMENTATION_ROADMAP.md 实施路线图与 Agent 分工
docs/06_SEAT_MAP_INTEGRATION.md 选座系统 + 座位地图v2.0 已精简)
docs/ALIGNMENT.md 架构对齐文档(用户需求 vs 文档现状)