# Plan — DebugAgent: "Undefined array key 'id'" 调试计划 > 版本:v1.0 | 日期:2026-04-20 | Agent:council/DebugAgent --- ## 任务概述 调查 ShopXO 后台编辑票务商品(goods_id=118)保存时报错: ``` Undefined array key "id" ``` --- ## 根因分析摘要(Round 1 快速结论) ### 1. 最可能触发点 **AdminGoodsSaveHandle.php 第 71 行**: ```php $template = Db::name('vr_seat_templates')->find($templateId); $seatMap = json_decode($template['seat_map'] ?? '{}', true); // ← 危险 ``` - `find()` 查不到记录时返回 `null` - `??` 操作符只防御 `$template` 为 `null`,**不防御** `$template` 存在但键 `'seat_map'` 缺失 - PHP 8+ 会报 `Undefined array key "seat_map"`,而非 "id" **真正报 "id" 的位置**——第 77 行 `array_filter` 回调内: ```php return in_array($r['id'], $config['selected_rooms'] ?? []); // ^^^^^^ 如果 $r(room 对象)缺少 'id' 键则触发 ``` 但如果 room 数据正常(有 id),则第 71 行是实际断路点。 ### 2. 表前缀问题(核心根因) | 代码 | 表名方法 | 实际 SQL 表 | |------|---------|------------| | `BaseService::table('seat_templates')` | `'vr_' + 'seat_templates'` | `vr_seat_templates` ✅ | | `Db::name('vr_seat_templates')` | ThinkPHP `Db::name()` | 取决于 `database.php` 配置前缀 | | `Db::name('Goods')` | ThinkPHP `Db::name()` | `sxo_goods` ✅(ShopXO 系统表) | **ShopXO 数据库配置**(需确认 `shopxo/config/database.php`): - 若 `prefix` = `'sxo_'`:则 `Db::name('vr_seat_templates')` → 查表 `sxo_vr_seat_templates`(不存在) - 若 `prefix` = `'vrt_'`:则 `Db::name('vr_seat_templates')` → 查表 `vrt_vr_seat_templates`(不存在) - 正确表名应来自 ThinkPHP 原始前缀(ShopXO 插件表一般不带前缀或用独立前缀) **SeatSkuService 使用 `BaseService::table()` → `vr_seat_templates`(正确)** **AdminGoodsSaveHandle 使用 `Db::name('vr_seat_templates')` → 可能查错表(错误)** ### 3. 如果 `find($templateId)` 返回 null 第 71 行:`$template['seat_map']` → `Undefined array key 'seat_map'`(不是 'id') 第 72 行:`$allRooms = $seatMap['rooms'] ?? [];` → 此行安全(`??` 防御) ### 4. vr_goods_config JSON 解码 ```php $configs = json_decode($rawConfig, true); // → array|null if (is_array($configs) && !empty($configs)) { ... } // 防御正确 ``` `$configs` 是数组时 `$config['template_id']` 访问安全(不会触发 "id" 错误)。 ### 5. selected_rooms 数据类型 - `selected_rooms`: `string[]`(room id 数组),e.g. `["room_id_xxx"]` - `$r['id']`: 来自 `seat_map.rooms[].id`,通常是字符串 - 类型匹配:无类型强制问题,但若 `room.id` 为 `null` 或缺失则触发 "id" ### 6. $data['item_type'] 访问安全 第 59 行:`($data['item_type'] ?? '') === 'ticket'` — 有 `??` 防御,安全。 --- ## 任务清单(Round 2 执行) - [ ] **Task 1**: 读取 `shopxo/config/database.php`,确认 `prefix` 配置值 - [ ] **Task 2**: 读取 `AdminGoodsSaveHandle.php` 第 70-72 行,确认 `$template` 为 null 时实际报错信息 - [ ] **Task 3**: 确认 ShopXO `Db::name()` 表前缀行为(查 ShopXO 源码或文档) - [ ] **Task 4**: 编写根因报告 `reports/DebugAgent-ROOT_CAUSE.md` - [ ] **Task 5**: 给出修复建议(用 `BaseService::table()` 替代 `Db::name()`) --- ## 阶段划分 | 阶段 | 内容 | 状态 | |------|------|------| | **Draft** | Round 1:代码静态分析,定位可疑行 | ✅ 完成 | | **Review** | Round 2:读取配置文件确认表前缀,输出根因报告 | 待做 | | **Finalize** | Round 3:合并报告到 main,提交调试结论 | 待做 | --- ## 依赖 - Task 1-3 必须按顺序执行(需读取配置文件) - 不需要 BackendArchitect / SecurityEngineer 配合,可独立完成 --- ## 执行顺序 Task 1 → Task 2 → Task 3(串行,每步确认后立即 commit)→ Task 4 → Task 5 --- ## 声称 - [Claimed: council/DebugAgent] — Task 1-5 全部