6.9 KiB
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() 之前,新增以下判断:
// 票务商品:加载自定义模板并注入座位数据
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
实际数据流(修正版):
- 从
goods.vr_goods_configJSON 字段读取商品配置(不是独立的 vrt_vr_goods_config 表) - 解析 JSON,取出第一个配置块的
template_id和sessions场次列表 - 从
vr_seat_templates表查询座位模板(含 seat_map / spec_base_id_map JSON) - 从 ShopXO 原生表
goods_spec_value+goods_spec_base联查,生成场次列表 - 返回
['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_dataJSON 中读取
状态: ✅ 已提交(7bd896764)
三、调查过程中的关键发现
3.1 ThinkTemplate include 标签解析机制
流程追溯:
View::fetch($absPath)→ Think 驱动的fetch()- Think 驱动调用
$this->template->fetch($template, $data)(ThinkTemplate 实例) - ThinkTemplate
fetch()→ 读取文件内容 →parse($content)→parseInclude($content) parseInclude()→parseTemplateName($file)→parseTemplateFile($template)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 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):
{include}标签在容器内是否能正确解析到 ShopXO 公共模板plugins_service_goods_spec_data钩子 vr_ticket 未实现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。
六、关联提交
7bd896764feat(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 |