vr-shopxo-plugin/docs/14_TEMPLATE_RENDER_INVESTIG...

183 lines
6.9 KiB
Markdown
Raw 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.

# VR-Ticket 模板渲染问题调查报告(已修正)
> 生成时间2026-04-19 20:39 CST
> 修正时间2026-04-20 CST
> 背景Phase 0/1 完成后,开始 Phase 2 前台模板渲染调试
---
## ⚠️ 重要修正说明2026-04-20
本文档为 2026-04-19 的调查记录,**部分内容有误**,已在此版本中修正:
| 章节 | 原错误 | 修正 |
|------|--------|------|
| 2.2 数据流 | 误以为读取 `vrt_vr_goods_config` 等独立表 | 实际:`goods.vr_goods_config` + ShopXO 原生表 `goods_spec_value` / `goods_spec_base` |
| 2.3 表名 | `vrt_order_detail` | 实际:`sxo_order_detail`ShopXO 原生平表) |
| 2.4 Think驱动 | 认为需要修改 vendor 文件 | Goods.php 绝对路径方案无需改 Think 驱动 |
| 6.决策 | "先不提交" | 代码已提交commit 7bd896764文档状态过时 |
---
## 一、问题描述
访问票务商品详情页 `http://localhost:10000/?s=goods/index/id/118.html` 时,模板**完全没有渲染**
浏览器源码中显示原始模板标签:
```
{include file="public/head" /}
{$goods.title|default='VR演唱会'}
选择场次
该商品暂无场次信息
...
```
ThinkTemplate 语法标签(`{include}`、`{$...}`、`{if}`)均以原文形式输出,页面 UI 无法正常显示。
---
## 二、已完成的代码修改(已验证)
### 2.1 Goods.php前台商品详情控制器
**文件:** `shopxo/app/index/controller/Goods.php`
**改动内容:** 在 `Goods::Index()` 方法中,`$this->PluginsHook()` 之后、`return MyView()` 之前,新增以下判断:
```php
// 票务商品:加载自定义模板并注入座位数据
if (($goods['item_type'] ?? '') === 'ticket') {
$viewData = \app\plugins\vr_ticket\service\SeatSkuService::GetGoodsViewData($goods_id);
MyViewAssign([
'vr_seat_template' => $viewData['vr_seat_template'] ?? null,
'goods_spec_data' => $viewData['goods_spec_data'] ?? [],
]);
// 使用绝对路径 + fetch() 方法
$tplFile = ROOT . 'app' . DS . 'plugins' . DS . 'vr_ticket' . DS . 'view' . DS . 'goods' . DS . 'ticket_detail.html';
return \think\facade\View::fetch($tplFile, $assign);
}
```
**状态:** ✅ 已提交7bd896764
---
### 2.2 SeatSkuService.php座位 SKU 服务层)
**文件:** `shopxo/app/plugins/vr_ticket/service/SeatSkuService.php`
**新增方法:** `GetGoodsViewData(int $goodsId): array`
**实际数据流(修正版):**
1.`goods.vr_goods_config` JSON 字段读取商品配置(不是独立的 vrt_vr_goods_config 表)
2. 解析 JSON取出第一个配置块的 `template_id``sessions` 场次列表
3.`vr_seat_templates` 表查询座位模板(含 seat_map / spec_base_id_map JSON
4. 从 ShopXO 原生表 `goods_spec_value` + `goods_spec_base` 联查,生成场次列表
5. 返回 `['vr_seat_template' => [...], 'goods_spec_data' => [...], 'goods_config' => [...]]`
**状态:** ✅ 已提交7bd896764
---
### 2.3 TicketService.php票务核心服务层
**文件:** `shopxo/app/plugins/vr_ticket/service/TicketService.php`
**修复内容(修正版):**
- `onOrderPaid()` 表名:`OrderGoods` → `order_detail`(映射到 ShopXO 原生平表 `sxo_order_detail`
- `sxo_order_detail.spec` 是 JSON 格式,需要 `json_decode()` 解析座位号
- 幂等保护:改用 `seat_info` 而非 `spec_base_id`(同一订单+同一座位名只发一张票)
- 观演人信息从 `order.extension_data` JSON 中读取
**状态:** ✅ 已提交7bd896764
---
## 三、调查过程中的关键发现
### 3.1 ThinkTemplate include 标签解析机制
**流程追溯:**
1. `View::fetch($absPath)` → Think 驱动的 `fetch()`
2. Think 驱动调用 `$this->template->fetch($template, $data)`ThinkTemplate 实例)
3. ThinkTemplate `fetch()` → 读取文件内容 → `parse($content)``parseInclude($content)`
4. `parseInclude()``parseTemplateName($file)``parseTemplateFile($template)`
5. `parseTemplateFile()``$template = $this->config['view_path'] . $template . '.' . $view_suffix`
**关键问题:** 使用绝对路径时ThinkTemplate 需要知道 `view_path` 应指向插件模板所在目录,否则 `{include file="public/head"}` 会找不到 `/var/www/html/app/index/view/default/public/head.html`
### 3.2 ShopXO 原生模板不使用 `{include}` 标签
**发现:** ShopXO 原生模板编译缓存使用 `ModuleInclude()` 而非 ThinkTemplate 原生的 `{include}` 标签:
```php
<?php echo ModuleInclude('public/header'); ?>
```
这说明 ShopXO 用自己的 `ModuleInclude()` 函数替代了部分 include 机制。
**但票务模板仍用 `{include}`** —— 因为:
- `ticket_detail.html` 是完全独立的票务 UI不继承 ShopXO 商品页基础布局
- 使用 `{include}` 可以让 ThinkTemplate 正常解析 `{$...}` 变量和 `{if}` 标签
- 最终解决方案Goods.php 绝对路径 + `View::fetch()`)使 `{include}` 能正确定位
### 3.3 Linux 路径分隔符问题
**发现:** ThinkTemplate 在 Linux 下 `view_depr``/``str_replace` 将 `/` 替换为 `\`,导致路径拼接错误。
**解决:** Goods.php 直接传绝对路径给 `View::fetch()`,绕过了 ThinkTemplate 的 `view_path` 拼接逻辑。
---
## 四、模板渲染现状
| 项目 | 状态 | 说明 |
|------|------|------|
| Goods.php 绝对路径 | ✅ 正常 | View::fetch($tplFile) 带绝对路径 |
| `{include file="public/head"}` | ⚠️ 待验证 | 理论上可以工作,需在容器内实测 |
| `{$vr_seat_template.seat_map\|raw}` | ⚠️ 待验证 | ThinkTemplate 变量解析 |
| `ModuleInclude()` | ❌ 未使用 | 票务模板不需要 |
**已知待解决问题P1**
1. `{include}` 标签在容器内是否能正确解析到 ShopXO 公共模板
2. `plugins_service_goods_spec_data` 钩子 vr_ticket 未实现
3. `loadSoldSeats()` 函数为空TODO
---
## 五、后续方向
### 方向 A推荐实测 `{include}` 标签
在容器内访问票务商品详情页,观察 `{include file="public/head"}` 是否被正确解析为 ShopXO 公共头部。
若解析失败,切换到方向 B。
### 方向 B`ModuleInclude()` 替换
`{include file="public/head" /}` 改为 `<?php echo ModuleInclude('public/head'); ?>`
### 方向 C纯内联
将所需 CSS/JS 直接写在 `ticket_detail.html` 中,完全去掉 include。
---
## 六、关联提交
- **7bd896764** `feat(Phase 2): 完成票务商品前端展示层`
- Goods.php: item_type=ticket 时加载 ticket_detail.html
- SeatSkuService.php: 新增 GetGoodsViewData()
- TicketService.php: onOrderPaid 用 sxo_order_detail + JSON spec 解析
---
## 附录:相关文件路径
| 文件 | 路径 |
|------|------|
| 票务商品模板 | `shopxo/app/plugins/vr_ticket/view/goods/ticket_detail.html` |
| Goods 控制器 | `shopxo/app/index/controller/Goods.php` |
| SeatSku 服务 | `shopxo/app/plugins/vr_ticket/service/SeatSkuService.php` |
| Ticket 服务 | `shopxo/app/plugins/vr_ticket/service/TicketService.php` |