# Plan — 调试 "Undefined array key 'id'" PHP 错误

> 版本：v1.2 | 日期：2026-04-20 | Agent：council/BackendArchitect + council/DebugAgent（并行协作）
> 关联提交：bbea35d83（feat: 保存时自动填充 template_snapshot）

---

## 任务概述

调试 ShopXO 后台编辑票务商品（goods_id=118）保存时报错：
```
Undefined array key "id"
```

根因代码位于 `bbea35d83` 新增的 `AdminGoodsSaveHandle.php` save_thing_end 时机。

---

## 任务清单

- [x] [Done: council/BackendArchitect] **Task 1**: 根因定位 — 逐行分析所有 "id" 访问位置
- [x] [Done: council/BackendArchitect] **Task 2**: Db::name() 表前缀问题 — ShopXO 插件表前缀行为确认
- [x] [Done: council/BackendArchitect] **Task 3**: 根因 1 — `$r['id']` 空安全（AdminGoodsSaveHandle 第 77 行）
- [x] [Done: council/BackendArchitect] **Task 4**: 根因 2 — `find()` 返回 null 的空安全（AdminGoodsSaveHandle 第 71 行）
- [x] [Done: council/BackendArchitect] **Task 5**: 根因 3 — `$config['template_id']` / `selected_rooms` 数据类型问题
- [x] [Done: council/BackendArchitect] **Task 6**: SeatSkuService::BatchGenerate 类似问题审计
- [x] [Done: council/BackendArchitect] **Task 7**: 修复方案汇总 + 建议修复优先级
- [x] [Done: council/BackendArchitect] **Task 8**: 将修复方案写入 `reviews/BackendArchitect-on-Issue-13-debug.md`

- [x] [Done: council/DebugAgent] **Task 9**: Round 1 静态分析 → `reviews/DebugAgent-PRELIMINARY.md`
- [ ] [Claimed: council/DebugAgent] **Task 10**: Round 2 — 验证 database.php 前缀配置 + 读取 Admin.php 第 66 行
- [ ] [Claimed: council/DebugAgent] **Task 11**: Round 2 — 编写 DebugAgent 最终根因报告 → `reports/DebugAgent-ROOT_CAUSE.md`

---

## 阶段划分

| 阶段 | 内容 |
|------|------|
| **Draft** | ✅ Task 1-6（BackendArchitect）+ Task 9（DebugAgent）|
| **Review** | ✅ Task 7（BackendArchitect）+ Task 11（DebugAgent）|
| **Finalize** | ✅ Task 8：输出评审报告到 reviews/ |

---

## 根因结论（已验证）

1. **Primary（99%）**: `AdminGoodsSaveHandle.php:77` — `$r['id']` 无空安全，rooms 中缺少 id key 时崩溃
2. **Secondary（5%）**: `AdminGoodsSaveHandle.php:71` — `find()` 返回 null 后直接访问 `$template['seat_map']`
3. **Tertiary（静默）**: `AdminGoodsSaveHandle.php:77` — `selected_rooms` 类型不匹配，`in_array` 永远 false
4. **已排除**: 表前缀问题 — `Db::name()` 和 `BaseService::table()` 均查询 `vrt_vr_seat_templates`，等价
5. **已排除**: SeatSkuService::BatchGenerate — 第 100 行已有 `!empty()` 空安全 fallback

## DebugAgent 补充结论（Round 1）

6. **PHP 8+ `??` 行为**：`$template['seat_map'] ?? '{}'` 对空数组 `[]` 的键访问**无效**，需用 `isset()`
7. **vr_goods_config JSON 解码**：有 `is_array()` 防御，访问 `$config['template_id']` 安全

---

## 执行顺序（DebugAgent Round 2）

```
Task 10: 读 shopxo/config/database.php → 确认 prefix 值；读 Admin.php 第 66 行
Task 11: 综合输出 reports/DebugAgent-ROOT_CAUSE.md
```

---

## 关键文件（只读）

| 文件 | 关注点 |
|------|--------|
| `shopxo/app/plugins/vr_ticket/hook/AdminGoodsSaveHandle.php` | save_thing_end 逻辑，template_snapshot 填充代码 |
| `shopxo/app/plugins/vr_ticket/service/SeatSkuService.php` | BatchGenerate、ensureAndFillVrSpecTypes |
| `shopxo/app/plugins/vr_ticket/service/BaseService.php` | table() 前缀方法 |
| `shopxo/config/database.php` | ShopXO 数据库表前缀配置（Task 10 需读） |
| `docs/VR_GOODS_CONFIG_SPEC.md` | vr_goods_config v3.0 JSON 格式 |
