# ShopXO 酷炫前端模板实现方案调研报告

> 调研日期：2026-04-20
> 状态：**Round 3 收敛版本**
> 参与：FrontendDev (Q1/Q4)、BackendArchitect (Q2)、ProductManager (Q3)、FirstPrinciples (Q4 集成)

---

## Q2 结论：单订单多 SKU 支持

### 核心答案

**ShopXO 订单模型技术上支持同一商品多规格（多 SKU）出现在同一订单中**，但现有 vr_ticket 模板的 `submit()` 只传单行，完整多座位下单需要做两件事：① 让前端传多行 `goods_data`，② 阻止 `OrderSplitService` 按 warehouse 拆单。

### 证据来源

| 文件 | 关键代码 | 说明 |
|------|---------|------|
| `BuyService.php:86` | `foreach($params['goods_data'] as $v)` | 循环处理每个商品项，每项独立 spec_base_id |
| `BuyService.php:423-435` | `extension_data` JSON 序列化 | 每行 item 支持挂载座位/观演人扩展数据 |
| `BuyService.php:101` | `md5(goods_id + spec implode)` | 内部用 goods_id+spec 组合生成唯一行 ID |
| `OrderSplitService.php:52-53` | `GoodsWarehouseAggregate()` | **拆单触发点**：按仓库分组，多 SKU 同一仓库则合并 |
| `OrderSplitService.php:289-310` | 按 spec MD5 找 spec_base_id | spec_base_id 已可不同（座位级 SKU） |
| `ticket_detail.html:413-436` | `goodsParamsList.map()` | Plan A 代码已写好，但 URL 只传第一行（bug） |

### 最小可行方案（Multi-Seat Now）

**改动点仅 3 处，全在 ticket_detail.html，不碰 ShopXO 核心：**

```
ticket_detail.html submit() 函数

BEFORE:  location.href = checkoutUrl + '&goods_params=' + encodeURIComponent(goodsParams)
AFTER:   goodsParamsList 整体 base64 编码，拆分成多条 goods_data 逐条 POST 到 CartSave，
         然后跳转到合并支付流程（ShopXO 购物车天然支持多商品同单）
```

**为什么走购物车路线更稳：**
- `BuyCart` → `BuyTypeGoodsList` → 直接调用 `BuyGoods`，完美支持多 `goods_data` 行
- 不需要hook `OrderSplitService`，购物车结算路径不触发按仓库拆单（只按商品拆）
- 核销逻辑不受影响：支付成功后 `plugins_service_order_pay_success_handle_end` 钩子正常触发

### 理想方案（Multi-Seat Proper）

在插件中挂载 `plugins_service_buy_group_goods_handle` 钩子，拦截 `OrderSplitService`，将同一 goods_id + 不同 spec_base_id 的多行合并进同一个 order_base：

```
plugins_service_buy_group_goods_handle:
  - 按 goods_id 聚合，而非按 warehouse_id
  - 每个 goods_id 只生成一条 order_base，goods_items 内嵌多个 spec_base_id 不同的行
  - extension_data 按座位索引扁平化存储
```

**ShopXO 官方立场**：这是非标准用法，建议走购物车路线。

### 最大风险点

1. **OrderSplitService 按仓库拆单** — 如果场次商品和周边商品挂在不同仓库，多座位票务订单会被拆成多个子单。用户会收到多笔支付通知，体验割裂。→ **最小方案走购物车绕过此风险**。
2. **座位级 SKU 未在 ShopXO 后台创建** — `specBaseIdMap` 依赖数据库中已存在的 `sxo_goods_spec_base` 记录。如果模板生成的 seatKey（如 "A_1"）没有对应的 spec_base_id，`submit()` 会降级到 Zone 级别 SKU（同一 zone 全部座位共享一个 spec），失去座位粒度。→ **需要后台管理员先为每个座位创建规格**。

### 优先级与依赖

- **Q2 是 Q4 的前提** — Q4 的"多座位选座流程"依赖 Q2 的多 SKU 订单能力。
- Q2 本身不依赖 Q1，可以独立推进。
- Q3 和 Q4 无依赖，但 Q3 生成的代码需适配 Q4 选型（H5 vs uni-app）。

---

## Q1 结论：ShopXO 自定义模板最佳实践

### 核心答案

**票务详情页不走 DIY 设计器，直接修改 `ticket_detail.html` 的 PHP+原生 JS**；uni-app 端 fork `shopxo-uniapp` 改写 `goods-detail.vue`，无需经过 ShopXO 模板中间层。

### 证据来源

| 文件/文档 | 结论 |
|----------|------|
| `docs/02_FRONTEND_CUSTOMIZATION.md` | DIY 设计器只支持静态 HTML 区块嵌入，无法参数化；uni-app 完全独立于 ShopXO 模板 |
| `docs/12_UNIAPP_FRONTEND_RESEARCH.md` | shopxo-uniapp 是独立 Vue 项目，通过 API 对接 ShopXO；CSS 在 H5/小程序完全一致（WebView 同源） |
| `docs/14_TEMPLATE_RENDER_INVESTIGATION.md` | ShopXO view/goods/ 模板使用原生 PHP + 原生 JS，session/buy 等控制器直接 render |
| `ticket_detail.html` | 当前已实现：场次选择 + 座位图渲染 + 观演人表单 + 购买栏 |

### 最小可行方案

**H5 端**：在现有 `ticket_detail.html` 基础上增强，引入：
- 座位类型图例（已完成）
- 已售座位 ✅ 已实现（`loadSoldSeats()` 使用 seatSpecMap.inventory，无需AJAX）
- 座位缩放/拖拽交互（原生 JS，<200 行）
- 动态场次切换时重置已选座位（已写但未调用）

**技术栈**：`原生 HTML + 内联 CSS + 内联 JS`，无框架依赖，ShopXO 模板系统直接渲染，无需构建。

### 理想方案

**uni-app 端**：
1. Fork `shopxo-uniapp` → `vr-shopxo-uniapp`
2. 重写 `pages/goods-detail/goods-detail.vue`，接入 vr_ticket API
3. 新建 `pages/ticket-seat/ticket-seat.vue`（选座主流程）
4. 新建 `pages/ticket-wallet/ticket-wallet.vue`（票夹）
5. H5 本地预览 = 小程序编译效果，CSS 完全一致

**关键约束（uni-app 开发规范）**：
- 用 `rpx` 不用 `vw/vh`
- 用 `<view>` 不用 `<div>`
- 避免 `calc()` 混用单位
- `position: fixed` 吸顶在 H5 正常，小程序需 shopxo-uniapp 已有方案

### 最大风险点

1. **shopxo-uniapp fork 同步成本** — 官方 shopxo-uniapp 更新后需要手动同步，未来维护成本高。→ 建议在 fork 分支上做票务专属页面，官方页面保持独立升级路径。
2. **ShopXO 版本 vs shopxo-diy 版本匹配** — shopxo-diy v1.4.2 ↔ ShopXO v6.8.0，如果使用 DIY 设计器管理非票务页面，版本必须严格匹配。

### 优先级与依赖

- Q1 是 Q4 的技术基础，Q1 的结论直接支撑 Q4 的技术选型决策。
- Q3 依赖 Q1 的约束条件输出。

---

## Q3 结论：第三方无代码构建服务提示词策略

### 核心答案

**用"模板 + 示例 + 约束"三层结构撰写 Prompt**，ShopXO 模板的特殊性（模块化 PHP 标签、ShopXO 资源路径 API）在 Prompt 中明确声明，生成代码后只需做两件事后处理：① 替换静态资源路径为 `ModuleInclude()` 调用，② 注入座位图数据结构（从 PHP 模板变量传入）。

### Prompt 三层结构

```
【第一层：角色定义】
你是一个 ShopXO v6.8.0 模板开发者，擅长编写票务商品详情页。

【第二层：约束清单】
- HTML 结构：使用 <?php echo ModuleInclude('public/header'); ?> 包裹页面头
- 样式：全部内联 <style>，CSS 类名前缀 vr- 避免冲突
- JS 数据注入：const app = <?php echo json_encode($php_var); ?>
- 资源路径：静态资源用 <?php echo ModuleInclude('images/foo.png'); ?>
- 不使用：Vue/React CDN、外部 CDN（ShopXO 必须离线可用）

【第三层：具体需求】
[座位图 UI 规格：rpx 规范、颜色、尺寸 + 交互事件定义]
[ShopXO 数据契约：goods_spec_data、vr_seat_template、extension_data]
```

### 生成代码后处理步骤

1. **路径替换**：全局搜索 `src="/static/` → `<?php echo ModuleInclude('static/') ?>`
2. **变量注入点**：在 `<script>` 顶部注入 `var seatMap = <?php echo json_encode($vr_seat_template['seat_map']); ?>`
3. **事件绑定**：`onclick` 属性需改为 `onclick="vrTicketApp.toggleSeat(this)"` 格式（原生 JS）
4. **样式隔离**：检查是否覆盖 `.goods-detail-*` 等 ShopXO 全局类名，如有则加 `.vr-ticket-page` 限定符

### 最大风险点

1. **无代码服务生成的 UI 过于复杂** — 座位图等高交互组件无法用无代码工具精确生成，强行生成会导致大量调试工作。→ **无代码服务适合静态展示区块（票务商品介绍、艺人信息图），座位图选座交互必须手写**。
2. **ShopXO 离线可用约束** — ShopXO 运行在企业内部/私有化部署场景，所有资源必须本地化，无代码服务默认 CDN 引用必须全部替换。

---

## Q4 结论：uni-app 兼容性技术栈选型

### 核心答案

**推荐方案：一套 shopxo-uniapp fork + 条件编译**，票务页面走独立路由（H5/小程序双端），商城标准页面复用 shopxo-uniapp 原生实现。

### 技术选型对比

| 维度 | 原生 HTML 模板 | uni-app fork shopxo-uniapp | Flutter / React Native |
|------|---------------|---------------------------|----------------------|
| H5 本地预览 | ✅ 直接浏览器打开 | ✅ HBuilderX H5 运行 | ❌ 需真机调试 |
| 微信小程序 | ❌ 不支持 | ✅ 一键编译 | ✅ 需分别开发 |
| ShopXO API 对接 | 需手动 HTTP | shopxo-uniapp 已封装 | 需手动 HTTP |
| 学习成本 | 低 | 中（需熟悉 Vue） | 高 |
| 座位图等复杂交互 | 原生 JS 手写 | Vue 组件手写 | 手写 |
| 开发速度 | 快（单文件） | 中 | 慢 |

**最终推荐**：`fork shopxo-uniapp`，用 Vue 3 + SCSS，票务页面自研，其他页面复用。

### 票务页面与商城标准页面共存方案

```
vr-shopxo-uniapp/
├── pages/index/index.vue         ← 改写：底部 Tab 新增「票务」Tab
├── pages/goods-detail/            ← 改写：票务商品跳 ticket-seat 页面
├── pages/ticket-seat/             ← 新建：选座 + 购票主流程（Vue 组件）
├── pages/ticket-wallet/           ← 新建：票夹（我的票）
├── pages/ticket-verify/           ← 新建：B 端核销
├── App.vue                        ← request_url 指向目标商城
└── pages.json                     ← 路由配置
```

**H5/小程序一致性**：uni-app H5 和小程序都基于 WebView，CSS 渲染一致。关键：用 `rpx`，用 `<view>`，避免浏览器私有前缀。

### 最大风险点

1. **shopxo-uniapp 官方更新同步** — 100+ forks，官方更新需手动 cherry-pick 到 vr fork。建议将票务专属页面与商城原生页面放在不同目录，改动隔离，升级时只同步商城页面。
2. **ShopXO 版本与 shopxo-uniapp 版本匹配** — shopxo-uniapp 的 API 契约随 ShopXO 后端版本变化，vr_ticket 插件如使用 shopxo-uniapp，请确认 ShopXO 版本（当前 v6.8.0），使用对应的 shopxo-uniapp 版本。

### 优先级与依赖

- **Q4 依赖 Q2（多座位选座）和 Q1（H5 模板基础）**
- Q4 本身是最终落地执行层，前三个 Q 的结论在 Q4 中整合实现

---

## 优先级矩阵

| 优先级 | 任务 | 负责 Agent | 前置条件 |
|--------|------|-----------|---------|
| P0 | Q2 多 SKU — 走购物车路线打通多座位下单 | BackendArchitect | 无 |
| P1 | Q4 uni-app fork — 建立项目骨架 | FrontendDev | Q1 结论 |
| P2 | Q4 ticket-seat.vue — 选座核心组件 | FrontendDev | P0 完成 |
| P3 | Q1 ticket_detail.html 增强 — 已售座位实时标记 | FrontendDev | 无 |
| P4 | Q3 提示词策略落地 — 无代码服务辅助静态区块 | ProductManager | Q1 结论 |
| P5 | Q2 理想方案 — 插件 hook 拦截 OrderSplitService | BackendArchitect | P0 验证 |

---

## 最小可行方案 vs 理想方案对比

| 维度 | 最小可行方案 | 理想方案 |
|------|------------|---------|
| 多座位下单 | 购物车路线（不碰 OrderSplitService） | 插件 hook 拦截，实现原生多 SKU 单订单 |
| 前端 H5 | 增强 ticket_detail.html（< 3 处改动） | 迁移到 uni-app H5 |
| 前端小程序 | shopxo-uniapp fork，票务页面 Vue 自研 | 完整迁移，小程序体验与 H5 一致 |
| 座位图 | 原生 JS，< 200 行 | Vue 组件，含缩放/拖拽/动画 |
| 观演人表单 | HTML + JS，支持动态增减 | Vue 组件化，数据校验 |
| 核销 B 端 | 复用现有后台核销页面 | 新建小程序核销页面（扫码 + API） |
| 交付周期 | 1 天（可上线 demo） | 2-3 周（完整票务流程） |

---

## 最大技术风险点汇总

| 风险 | 严重程度 | 缓解措施 |
|------|---------|---------|
| OrderSplitService 拆单导致多座位订单被拆 | 高 | 最小方案走购物车绕过；理想方案用插件 hook 拦截 |
| 座位级 SKU 未在后台创建 | 中 | 后台管理界面增加「批量生成座位规格」功能 |
| shopxo-uniapp fork 同步成本 | 中 | 票务页面与商城页面目录隔离，改动隔离升级 |
| 无代码服务无法生成高交互组件 | 低（已有认知） | 座位图等核心交互手写，静态区块用无代码辅助 |
| ShopXO 版本不匹配 shopxo-diy | 低（不走 DIY） | 不使用 DIY 设计器 |

---

## 关键文件清单

| 文件 | 用途 |
|------|------|
| `shopxo/app/service/BuyService.php` | 订单创建入口，多 SKU 关键代码 |
| `shopxo/app/service/OrderSplitService.php` | 拆单逻辑，多座位订单被拆的风险点 |
| `shopxo/app/plugins/vr_ticket/view/goods/ticket_detail.html` | 当前票务详情页模板，Q2 多 SKU Plan A 代码已在此 |
| `docs/12_UNIAPP_FRONTEND_RESEARCH.md` | uni-app 调研存档，Q1/Q4 依赖此文档 |
| `docs/02_FRONTEND_CUSTOMIZATION.md` | ShopXO DIY 设计器局限性证明 |
| `docs/14_TEMPLATE_RENDER_INVESTIGATION.md` | 模板渲染机制调查 |
| `docs/09_SHOPXO_HOOKS_REFERENCE.md` | 插件钩子清单，Q2 理想方案所需 hook 在此 |

---

## 结论

1. **多座位下单可行**：走购物车路线，1 天内可上线多座位下单 Demo。
2. **uni-app 是最终目标**：fork shopxo-uniapp 票务页面自研，商城页面复用，H5 预览 = 小程序编译效果。
3. **无代码服务辅助有限**：适合静态展示区块，座位图等核心交互必须手写。
4. **Immediate Action**：BackendArchitect 提交 Q2 Plan A（购物车路线），FrontendDev 启动 shopxo-uniapp fork 项目骨架。
