diff --git a/plan.md b/plan.md index 0d9a11f..a8854be 100644 --- a/plan.md +++ b/plan.md @@ -1,54 +1,114 @@ -# Plan — 文档评估 (Architect) +# Plan — DebugAgent: "Undefined array key 'id'" 调试计划 -> 版本:v1.0 | 日期:2026-04-20 | Agent:council/Architect +> 版本:v1.0 | 日期:2026-04-20 | Agent:council/DebugAgent --- ## 任务概述 -对 vr-shopxo-plugin 项目三份文档进行评审: -1. `docs/14_TEMPLATE_RENDER_INVESTIGATION.md` -2. `docs/PHASE2_PLAN.md` -3. `docs/DEVELOPMENT_LOG.md`(第十一、十二章) - -评审维度:准确性、完整性、可操作性、一致性、误导风险。 -**不读代码文件,只读文档。输出到 `reviews/` 目录。** +调查 ShopXO 后台编辑票务商品(goods_id=118)保存时报错: +``` +Undefined array key "id" +``` --- -## 任务清单 +## 根因分析摘要(Round 1 快速结论) -- [x] **Task 1**: 评审 `docs/14_TEMPLATE_RENDER_INVESTIGATION.md` → `reviews/Architect-on-doc14.md` - - [Done: council/Architect] +### 1. 最可能触发点 -- [x] **Task 2**: 评审 `docs/PHASE2_PLAN.md` → `reviews/Architect-on-PHASE2_PLAN.md` - - [Done: council/Architect] +**AdminGoodsSaveHandle.php 第 71 行**: +```php +$template = Db::name('vr_seat_templates')->find($templateId); +$seatMap = json_decode($template['seat_map'] ?? '{}', true); // ← 危险 +``` -- [x] **Task 3**: 评审 `docs/DEVELOPMENT_LOG.md`(第十一、十二章)→ `reviews/Architect-on-DEV_LOG.md` - - [Done: council/Architect] +- `find()` 查不到记录时返回 `null` +- `??` 操作符只防御 `$template` 为 `null`,**不防御** `$template` 存在但键 `'seat_map'` 缺失 +- PHP 8+ 会报 `Undefined array key "seat_map"`,而非 "id" -- [x] **Task 4**: 综合三份评审,输出 Top 3 修正建议 → `reviews/Architect-DOC-SUMMARY.md` - - [Done: council/Architect] +**真正报 "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** | ✅ Task 1-3:逐份文档输出独立评审报告 | -| **Review** | ✅ Task 4:综合汇总,Top 3 修正建议 | -| **Finalize** | ✅ 提交到 main,标注完成 | +| 阶段 | 内容 | 状态 | +|------|------|------| +| **Draft** | Round 1:代码静态分析,定位可疑行 | ✅ 完成 | +| **Review** | Round 2:读取配置文件确认表前缀,输出根因报告 | 待做 | +| **Finalize** | Round 3:合并报告到 main,提交调试结论 | 待做 | --- ## 依赖 -- 三份文档已读取完毕,无需额外探索 +- Task 1-3 必须按顺序执行(需读取配置文件) - 不需要 BackendArchitect / SecurityEngineer 配合,可独立完成 --- ## 执行顺序 -Task 1 → Task 2 → Task 3 → Task 4(串行,每份评审写完即 commit) +Task 1 → Task 2 → Task 3(串行,每步确认后立即 commit)→ Task 4 → Task 5 + +--- + +## 声称 + +- [Claimed: council/DebugAgent] — Task 1-5 全部