docs: 修正 docs/14 + 新增 PHASE2_PLAN.md
- docs/14: 修正数据流/表名/Think驱动等3处错误,删除过时状态 - PHASE2_PLAN.md: Phase 2 当前状态 + 模板渲染待验证项 + 下一步计划 - DEVELOPMENT_LOG.md: 追加 Phase 2 完成记录council/SecurityEngineer
parent
7bd8967648
commit
914e2a0fc3
|
|
@ -0,0 +1,182 @@
|
|||
# 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` |
|
||||
|
|
@ -397,3 +397,51 @@ vr-shopxo-plugin 仓库:
|
|||
| spec_base JSON 结构最终版 | P2 | 已确认 Q4 方案,待落地 |
|
||||
| 支付回调联调 | P2 | 等待 Phase 2 后台完成后测试 |
|
||||
| 核销 API RLS | P2 | 待实现 |
|
||||
|
||||
---
|
||||
|
||||
## 十一、Phase 2 前台展示层完成(2026-04-20)
|
||||
|
||||
### 11.1 完成内容
|
||||
|
||||
**Commit 7bd896764:**
|
||||
- `Goods.php`:item_type=ticket → 绝对路径 View::fetch(ticket_detail.html) + 数据注入
|
||||
- `SeatSkuService.php`:新增 GetGoodsViewData(),从 goods.vr_goods_config JSON + ShopXO 原生平表读取座位图+场次
|
||||
- `TicketService.php`:onOrderPaid 改用 sxo_order_detail + JSON spec 解析座位号,幂等改为 seat_info
|
||||
|
||||
**docs/14 修正:**
|
||||
- 修正了数据流描述(vrt_vr_goods_config → goods.vr_goods_config)
|
||||
- 修正了表名(vrt_order_detail → sxo_order_detail)
|
||||
- 删除了错误的 Think 驱动修改说明
|
||||
|
||||
**新文档:**
|
||||
- `docs/PHASE2_PLAN.md`(本文件):Phase 2 当前状态 + 下一步计划
|
||||
|
||||
### 11.2 模板渲染问题说明
|
||||
|
||||
Goods.php 绝对路径方案已实现,但 `{include file="public/head"}` 标签是否能在容器内正确解析**待实测**。
|
||||
|
||||
详见 `docs/PHASE2_PLAN.md` 第二章。
|
||||
|
||||
### 11.3 当前 Git 状态
|
||||
|
||||
```
|
||||
7bd896764 feat(Phase 2): 完成票务商品前端展示层 ← HEAD
|
||||
dc63cff77 chore: clean up my_test_plugin residual hooks
|
||||
```
|
||||
|
||||
### 11.4 Phase 2 剩余工作
|
||||
|
||||
| 任务 | 状态 |
|
||||
|------|------|
|
||||
| 模板渲染实测(容器内) | ⚠️ 待大头操作 |
|
||||
| loadSoldSeats() 实现 | ❌ 未开始 |
|
||||
| vr_ticket Hook.php 补充 | ❌ 未开始 |
|
||||
| 4 个后台控制器联调 | ❌ 未开始 |
|
||||
| 核销 API | ❌ 未开始 |
|
||||
|
||||
### 11.5 清理记录(2026-04-20)
|
||||
|
||||
- `shopxo/test_ticket.php` → 移至 `_backup_20260420/test_ticket.php`(临时测试脚本,不入仓库)
|
||||
- `docs/14_TEMPLATE_RENDER_INVESTIGATION.md` → 重写修正版(删除错误信息,保留调查价值)
|
||||
- 核心代码(Goods.php / SeatSkuService.php / TicketService.php)→ 全部提交推送
|
||||
|
|
|
|||
|
|
@ -0,0 +1,146 @@
|
|||
# Phase 2 — 计划与当前状态
|
||||
|
||||
> 版本:v1.0 | 日期:2026-04-20 | 状态:执行中
|
||||
> 关联提交:7bd896764
|
||||
|
||||
---
|
||||
|
||||
## 一、Phase 2 完成情况
|
||||
|
||||
### ✅ 已完成
|
||||
|
||||
| 任务 | 文件 | 说明 |
|
||||
|------|------|------|
|
||||
| Goods.php 改法 | `app/index/controller/Goods.php` | item_type=ticket → ticket_detail.html + 数据注入 |
|
||||
| GetGoodsViewData() | `SeatSkuService.php` | 为前端模板提供座位图+场次数据 |
|
||||
| onOrderPaid() 修复 | `TicketService.php` | sxo_order_detail + JSON spec 解析 |
|
||||
| 模板渲染调研 | `docs/14_TEMPLATE_RENDER_INVESTIGATION.md` | 已修正,记录完整调查过程 |
|
||||
|
||||
### ⚠️ 待验证(容器内)
|
||||
|
||||
| 任务 | 优先级 | 说明 |
|
||||
|------|--------|------|
|
||||
| `{include}` 标签实测 | P0 | ticket_detail.html 中 `{include file="public/head"}` 是否能正确解析 |
|
||||
| ModuleInclude 备选方案 | P1 | 若 `{include}` 失败,切换 `ModuleInclude()` |
|
||||
| loadSoldSeats() | P1 | 查询已售座位,前端座位图需显示已售状态 |
|
||||
|
||||
### ❌ 未开始
|
||||
|
||||
| 任务 | 说明 |
|
||||
|------|------|
|
||||
| vr_ticket Hook.php 钩子补充 | 缺失 `plugins_service_goods_spec_data` 处理 |
|
||||
| 后台座位模板管理 | admin/controller/SeatTemplate.php(已生成,未调试) |
|
||||
| 后台电子票列表 | admin/controller/Ticket.php(已生成,未调试) |
|
||||
| 后台核销员管理 | admin/controller/Verifier.php(已生成,未调试) |
|
||||
| 后台核销记录 | admin/controller/Verification.php(已生成,未调试) |
|
||||
| 核销 API | B 端扫码核销 REST 接口 |
|
||||
|
||||
---
|
||||
|
||||
## 二、模板渲染问题现状
|
||||
|
||||
### 问题
|
||||
|
||||
票务商品详情页 ThinkTemplate 标签未解析(`{$...}` / `{include}` / `{if}` 以原文输出)。
|
||||
|
||||
### 根因
|
||||
|
||||
Goods.php 原来用 `MyView()` 加载主题模板,票务商品需要加载插件独立模板 `ticket_detail.html`。
|
||||
|
||||
### 解决路径(Goods.php 绝对路径方案)
|
||||
|
||||
```
|
||||
Goods::Index()
|
||||
→ $goods['item_type'] === 'ticket'
|
||||
→ SeatSkuService::GetGoodsViewData($goods_id)
|
||||
→ MyViewAssign([vr_seat_template, goods_spec_data])
|
||||
→ View::fetch($tplFile) [$tplFile = 绝对路径]
|
||||
→ ThinkTemplate 渲染 ticket_detail.html(含 {include} 标签)
|
||||
```
|
||||
|
||||
### 待实测项(容器内操作)
|
||||
|
||||
```bash
|
||||
# 在 shopxo-php 容器内
|
||||
docker exec -it shopxo-php bash
|
||||
cd /var/www/html
|
||||
curl "http://localhost:10000/?s=goods/index/id/118.html"
|
||||
|
||||
# 检查:
|
||||
# 1. {include file="public/head"} 是否被解析为 HTML 内容
|
||||
# 2. {$goods.title} 是否显示商品标题
|
||||
# 3. 座位图是否正常渲染
|
||||
```
|
||||
|
||||
### 失败备选
|
||||
|
||||
若 `{include}` 标签失败,修改 `ticket_detail.html`:
|
||||
```php
|
||||
<!-- 替换 -->
|
||||
{include file="public/head" /}
|
||||
<!-- 改为 -->
|
||||
<?php echo ModuleInclude('public/head'); ?>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 三、Phase 2 接下来的工作
|
||||
|
||||
### Step 1:模板渲染实测(容器内)
|
||||
|
||||
**操作人:** 大头(容器在本机)
|
||||
|
||||
```bash
|
||||
docker ps | grep shopxo-php # 确认容器运行中
|
||||
curl -s "http://localhost:10000/?s=goods/index/id/118.html" | head -50
|
||||
```
|
||||
|
||||
**成功标准:** HTML 源码中不再有 ThinkTemplate 标签(`{include}` / `{$` / `{if}`),座位图 div 正常显示。
|
||||
|
||||
### Step 2:座位图已售状态
|
||||
|
||||
SeatSkuService 需要补充 `loadSoldSeats()` 方法,查询 `vr_tickets` 表中该商品+场次已生成的票,返回已售座位 ID 列表,前端据此灰化已售座位。
|
||||
|
||||
### Step 3:后台管理页面联调
|
||||
|
||||
4 个后台控制器(SeatTemplate / Ticket / Verifier / Verification)均已生成,需要:
|
||||
1. 确认路由可访问(后台 URL 格式)
|
||||
2. 验证 CRUD 操作正常
|
||||
3. 确认 RLS 策略
|
||||
|
||||
### Step 4:核销 API
|
||||
|
||||
`POST /api/vr_ticket/verify` — B 端小程序扫码调用。
|
||||
|
||||
---
|
||||
|
||||
## 四、数据库表结构(当前)
|
||||
|
||||
| 表名 | 用途 | 状态 |
|
||||
|------|------|------|
|
||||
| `vrt_vr_seat_templates` | 座位模板 | ✅ |
|
||||
| `vrt_vr_tickets` | 电子票 | ✅ |
|
||||
| `vrt_vr_verifiers` | 核销员 | ✅ |
|
||||
| `vrt_vr_verifications` | 核销记录 | ✅ |
|
||||
| `vrt_vr_audit_log` | 审计日志 | ✅ |
|
||||
| `goods.vr_goods_config` | 商品配置(JSON) | ✅ |
|
||||
| `sxo_order_detail` | 订单明细(ShopXO) | ✅ |
|
||||
|
||||
---
|
||||
|
||||
## 五、已知风险
|
||||
|
||||
| 风险 | 影响 | 缓解 |
|
||||
|------|------|------|
|
||||
| `{include}` 标签容器内解析失败 | P0,页面无样式 | 切换 ModuleInclude 方案 |
|
||||
| shopxo-php 容器未启动 | 无法验证 | 每次操作前 `docker ps` 确认 |
|
||||
| Admin 控制器鉴权链不完整 | 后台无法访问 | 确认继承 Common 并调用 IsLogin/IsPower |
|
||||
| 座位模板与商品分类绑定逻辑 | 需确认 1:N 还是 1:1 | 实测验证 |
|
||||
|
||||
---
|
||||
|
||||
## 六、决策点(待大头确认)
|
||||
|
||||
1. **模板 include 方案**:先试 `{include}`,失败后换 `ModuleInclude()`,还是直接内联 CSS/JS?
|
||||
2. **loadSoldSeats()**:是否需要实时查库,还是前端纯靠 JS 状态管理?
|
||||
3. **后台前端框架**:Layui 是否继续使用,还是改用其他方案?
|
||||
Loading…
Reference in New Issue