# 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
```

### 任务清单

- [x] **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' ✓
- [x] **P1-T2**: 对比 Admin.php 根目录模式 vs 当前 admin/controller/ 子目录模式
  - 根目录 Admin.php (`app/plugins/vr_ticket/admin/Admin.php`) 可以被正确加载 ✓
  - 旧子目录控制器无法被 PluginsService 找到（类路径不匹配）✗
- [x] **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_power`
  - `SHOW FULL COLUMNS FROM vrt_power`
  - 确认 name 字段 charset 和 collate
- [ ] **P2-T2**: 如果是数据库 latin1 问题 — 修复方案
  - 方案A：ALTER TABLE 转换 latin1 → utf8mb4
  - 方案B：MySQL CONVERT/CAST 函数读取时转换
  - 方案C：PHP 层以 latin1 读出再转 utf8

---

## 视图路径问题

### 修复方案
- BackendArchitect 已将视图复制到 `app/admin/view/default/plugins/view/vr_ticket/admin/view/`
- `admin/Admin.php` 中使用 `return view('seat_template/list', $data)`（相对路径）
- ShopXO 会自动从 `app/admin/view/default/plugins/view/vr_ticket/admin/view/` 解析
- ✓ 路径问题已通过 BackendArchitect 的 Vrticket.php 方式部分解决
- admin/Admin.php 使用 ThinkPHP 的 view() 助手函数，相对路径正确解析

---

## 阶段划分

| 阶段 | 内容 | 负责 |
|------|------|------|
| **Round 1（规划）** | 分析根因，制定修复方案 | FrontendDev |
| **Round 2（执行）** | 实施 admin/Admin.php + plugin.json 修复 | FrontendDev |
| **Round 3（综合）** | 合并到 main，完整验证 | 所有成员 |

---

## 依赖关系

- P1-T3 和 P1-T4 需要实际访问 URL 验证（无法在 CLI 环境截图）
- P2-T1 需要连接数据库检查编码

---

## 交付物

1. 修复后的 `shopxo/app/plugins/vr_ticket/admin/Admin.php`（路由正确）
2. 修复后的 `shopxo/app/plugins/vr_ticket/plugin.json`（sidebar URL 使用 camelCase）
3. 乱码问题修复（需数据库层修复）

## 状态

| 任务 | 状态 | 备注 |
|------|------|------|
| P1-T1 | [Done] | PluginsService 路由机制已分析 |
| P1-T2 | [Done] | admin/Admin.php 模式正确 |
| P1-T3 | [Done] | admin/Admin.php 已创建 + plugin.json 已修复 |
| P1-T4 | [Pending] | 需实际访问 URL 截图验证 |
| P2-T1 | [Pending] | 数据库编码检查（需 DB 访问）|
| P2-T2 | [Pending] | 数据库修复（如需要）|

---

## 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

```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`
