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

6.9 KiB
Raw Permalink Blame History

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_detailShopXO 原生平表)
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

实际数据流(修正版):

  1. goods.vr_goods_config JSON 字段读取商品配置(不是独立的 vrt_vr_goods_config 表)
  2. 解析 JSON取出第一个配置块的 template_idsessions 场次列表
  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() 表名:OrderGoodsorder_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 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。

方向 BModuleInclude() 替换

{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