10 KiB
vr-shopxo-plugin Phase 2 Bugfix — plan.md
版本:v1.0 | 日期:2026-04-16 | Agent:council/FrontendDev 背景:Phase 2 后台管理两个致命问题 — 侧栏乱码 + 路由无法渲染
问题总览
| # | 问题 | 症状 | 优先级 |
|---|---|---|---|
| P1 | 插件控制器路由无法渲染 | 内容区空白,"template not exists" | 高 |
| P2 | 侧边栏插件名乱码 | VR票务(应为 VR票务) |
中 |
P1 — 路由无法渲染问题
已知现象
- 访问
adminwatekc.php?s=VrTicket/SeatTemplateList→ 侧栏正常,主内容区空白 - ShopXO
Plugins/Index控制器调用插件时有strtolower+ucfirst类名匹配问题 - 当前
SeatTemplate.php在admin/controller/子目录
已知正确模式(freightfee/answers)
app/plugins/{plugin}/
├── Admin.php ← 直接在插件根目录,继承 think\Controller
├── Hook.php
├── config.json
└── admin/view/... ← 视图在 admin/view/ 子目录
当前 vr_ticket 结构(有问题)
app/plugins/vr_ticket/
├── admin/controller/SeatTemplate.php ← ❌ 在子目录
└── admin/view/seat_template/list.html
任务清单
- P1-T1: 验证
strtolower+ucfirst路由匹配机制- PluginsService::PluginsControlCall:
class = \app\plugins\{plugin}\{group}\{ucfirst(control)} - sidebar URL
/plugins/vr_ticket/admin/seatTemplateList - → pluginsname=vr_ticket, pluginscontrol=admin, pluginsaction=seatTemplateList
- → class = \app\plugins\vr_ticket\admin\Admin ✓
- → method = ucfirst('seatTemplateList') = 'SeatTemplateList' ✓
- PluginsService::PluginsControlCall:
- P1-T2: 对比 Admin.php 根目录模式 vs 当前 admin/controller/ 子目录模式
- 根目录 Admin.php (
app/plugins/vr_ticket/admin/Admin.php) 可以被正确加载 ✓ - 旧子目录控制器无法被 PluginsService 找到(类路径不匹配)✗
- 根目录 Admin.php (
- P1-T3: 实施修复 — 创建
admin/Admin.php(注意:不是根目录,是 admin/ 子目录)admin/Admin.php路径 → 类名\app\plugins\vr_ticket\admin\Admin✓- 方法使用 camelCase:
SeatTemplateList(),TicketList()等 - sidebar URL 必须用 camelCase:
pluginsaction=seatTemplateList - 修复 plugin.json sidebar URL:改为
/plugins/vr_ticket/admin/seatTemplateList格式
- P1-T4: 验证修复后路由能否正常渲染(需实际访问 URL 截图)
P2 — 侧栏插件名乱码问题
已知现象
- 侧栏显示:
VR票务(应为VR票务) - 这是 UTF-8 字符串被当作 Latin1/ISO-8859-1 解码的结果
- 乱码规律:
票(E7 A5 8A) → Latin1 解码为票务
乱码根因假设
| 假设 | 可能性 | 验证方式 |
|---|---|---|
数据库 vrt_power 表 name 字段 latin1 编码存储 |
高 | 检查 MySQL SHOW CREATE TABLE vrt_power |
| 数据库连接 charset 不匹配 | 中 | 检查 ShopXO 数据库配置 charset |
| plugin.json 编码问题 | 低 | plugin.json 已是正确 UTF-8 |
任务清单
- P2-T1: 确认乱码根因 — 检查 vrt_power 表结构
SHOW CREATE TABLE vrt_powerSHOW FULL COLUMNS FROM vrt_power- 确认 name 字段 charset 和 collate
- P2-T2: 如果是数据库 latin1 问题 — 修复方案
- 方案A:ALTER TABLE 转换 latin1 → utf8mb4
- 方案B:MySQL CONVERT/CAST 函数读取时转换
- 方案C:PHP 层以 latin1 读出再转 utf8
视图路径问题(Round 5 根因确认 + 修复)
根因(BackendArchitect 分析)
ThinkPHP 5 视图路径解析规则:
- 相对路径(如
'seat_template/list'):相对于控制器 namespace 对应的默认视图目录 - namespace
app\plugins\vr_ticket\admin→ 默认视图目录app/plugins/vr_ticket/admin/view/ - 实际文件在
app/admin/view/default/plugins/view/vr_ticket/admin/view/← 路径不匹配!
实际文件位置
app/admin/view/default/plugins/view/vr_ticket/admin/view/
├── seat_template/
│ ├── list.html
│ └── save.html
├── ticket/
│ ├── list.html
│ └── detail.html
├── venue/
│ ├── list.html
│ └── save.html
├── verifier/
│ ├── list.html
│ └── save.html
└── verification/
└── list.html
修复方案
ThinkPHP 5 以 / 开头的视图路径为绝对路径,相对于配置的视图根目录(app/admin/view/default/)解析。
修复前(错误):
return view('seat_template/list', $data); // 解析到 app/plugins/vr_ticket/admin/view/ ← 不存在
修复后(正确):
return view('/plugins/view/vr_ticket/admin/view/seat_template/list', $data);
// → app/admin/view/default/plugins/view/vr_ticket/admin/view/seat_template/list.html ✓
所有 9 个 view() 调用已全部修复为绝对路径格式。
Vrticket.php 的参考价值
shopxo/app/admin/controller/Vrticket.php 使用 MyView('../../../plugins/vr_ticket/admin/' . $template) 手动处理路径。
Admin.php 使用 ThinkPHP view() 函数,以 / 开头则由 ThinkPHP 自动解析到 app/admin/view/default/。
阶段划分
| 阶段 | 内容 | 负责 |
|---|---|---|
| Round 1(规划) | 分析根因,制定修复方案 | FrontendDev |
| Round 2(执行) | 实施 admin/Admin.php + plugin.json 修复 | FrontendDev |
| Round 3(综合) | 合并到 main,完整验证 | 所有成员 |
依赖关系
- P1-T3 和 P1-T4 需要实际访问 URL 验证(无法在 CLI 环境截图)
- P2-T1 需要连接数据库检查编码
交付物
- 修复后的
shopxo/app/plugins/vr_ticket/admin/Admin.php(路由正确) - 修复后的
shopxo/app/plugins/vr_ticket/plugin.json(sidebar URL 使用 camelCase) - 乱码问题修复(需数据库层修复)
状态
| 任务 | 状态 | 备注 |
|---|---|---|
| P1-T1 | [Done] | PluginsService 路由机制已分析 |
| P1-T2 | [Done] | admin/Admin.php 模式正确 |
| P1-T3 | [Done] | admin/Admin.php 已创建 + plugin.json 已修复 |
| P1-T4 | [Pending] | 需实际访问 URL 截图验证 |
| P2-T1 | [Done] | 根因:plugins.name 字段 Latin1 存储 |
| P2-T2 | [Done] | SQL 修复脚本见 docs/SQL_FIX_garbled_plugin_name.md |
| P1-视图路径 | [Done] | 所有 9 个 view() 改为绝对路径 /plugins/view/vr_ticket/admin/view/... |
BackendArchitect Round 5 实现
交付物
- ✅
shopxo/app/plugins/vr_ticket/admin/Admin.php— 9 个 view() 调用全部改为/plugins/view/vr_ticket/admin/view/...绝对路径 - ✅
docs/SQL_FIX_garbled_plugin_name.md— 乱码修复 SQL 脚本 - ✅
plan.md— 更新根因分析
P1 乱码 DB 根因(最终确认)
plugins.name字段 =VR票务(Latin1 解码的 UTF-8 字节)- 安装时
plugin.json的title: "VR票务"被以 Latin1 编码存入 MySQL - 读取时 MySQL 连接 charset 是 utf8mb4,所以 Latin1 字节被错误解码为乱码
- 修复:执行
UPDATE sx_plugins SET name = 'VR票务' WHERE plugins = 'vr_ticket'
乱码字节分析
票 UTF-8: E7 A5 8A → Latin1 解读为: 票务
务 UTF-8: E5 8A B1 → (in VR票务 combined string)
BackendArchitect — Round 1 文档评估任务
任务:对三份文档进行评审,不动代码,不碰远程,输出到
reviews/目录 关联提交:914e2a0fc(docs: 修正 docs/14 + 新增 PHASE2_PLAN.md)
评审任务清单
- [Claimed: council/BackendArchitect] 评审 docs/14 →
reviews/BackendArchitect-on-14_TEMPLATE_RENDER_INVESTIGATION.md - [Claimed: council/BackendArchitect] 评审 docs/PHASE2_PLAN.md →
reviews/BackendArchitect-on-PHASE2_PLAN.md - [Claimed: council/BackendArchitect] 评审 docs/DEVELOPMENT_LOG.md(第十一、十二章)→
reviews/BackendArchitect-on-DEVELOPMENT_LOG.md - [Claimed: council/BackendArchitect] 综合建议 →
reviews/BackendArchitect-DOCUMENTATION_REVIEW_SUMMARY.md
三份文档清单
| # | 文档 | 重点 |
|---|---|---|
| 1 | docs/14_TEMPLATE_RENDER_INVESTIGATION.md | 数据流描述、表名、解决方案合理性 |
| 2 | docs/PHASE2_PLAN.md | 任务优先级、风险评估、决策点清晰度 |
| 3 | docs/DEVELOPMENT_LOG.md(第11-12章) | 事实准确性、时间线一致性、遗漏信息 |
评审维度
- 准确性:技术描述、数据流、表结构是否正确
- 完整性:边界条件、安全考量、依赖项是否遗漏
- 可操作性:下一步行动是否清晰可执行
- 一致性:各文档之间、表名/文件路径/状态描述是否一致
- 误导风险:是否有表述容易让后续接手者误解
规则
- 只读文档,不读 .php / .html / .sql 文件
- 不修改任何文件
- 不 push 到远程
- 评审在本地 worktree 完成
依赖关系
无依赖,三份文档可并行评审。
SecurityEngineer Round 5 补充
关键发现:VenueList() 方法缺失(Critical Bug)
plugin.json sidebar URL /plugins/vr_ticket/admin/venueList 链接到 VenueList() 方法,但 admin/Admin.php 中该方法不存在 → 点击"场馆配置"菜单会导致 500 错误。
已修复:在 admin/Admin.php 中添加:
VenueList()— 场馆列表(含 v3.0 seat_map 解析)VenueSave()— 场馆创建/编辑(含 v3.0 JSON 构建和验证)VenueDelete()— 场馆软删除(含审计日志)countSeatsV2()— v2 格式(数组)座位计数辅助方法
安全审计结论
| 安全项 | 风险等级 | 结论 |
|---|---|---|
| SQL 注入 | LOW | 所有查询使用 ThinkPHP query builder + 参数绑定 |
| XSS | LOW | ThinkPHP 模板引擎自动转义,无 |raw 输出 |
| 路径遍历 | LOW | 所有视图路径为硬编码方法名,无用户输入 |
| CSRF | MEDIUM | ShopXO 框架级缺失,插件层面无法单独修复 |
| 数据编码(P1乱码) | LOW | DB latin1 存储导致乱码,非安全漏洞 |
P1 乱码 DB 修复 SQL
-- 1. 诊断
SELECT id, name, title, LENGTH(name), HEX(name) FROM shx_plugins WHERE name LIKE '%vr%';
-- 2. 修复 plugins 表
UPDATE shx_plugins SET name = 'vr_ticket', title = 'VR票务' WHERE name = 'vr_ticket';
-- 3. 修复 vrt_power 表(如果存在乱码)
SELECT id, name, LENGTH(name), HEX(name) FROM vrt_power WHERE name LIKE '%票%';
UPDATE vrt_power SET name = 'VR票务' WHERE HEX(name) LIKE '%E7A58A%';
详细安全分析见:reviews/SecurityEngineer-round5-review.md