council(draft): DebugAgent - plan.md: debug "Undefined array key id" error
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>council/ProductManager
parent
98bd6a52cf
commit
0385f79106
110
plan.md
110
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 项目三份文档进行评审:
|
调查 ShopXO 后台编辑票务商品(goods_id=118)保存时报错:
|
||||||
1. `docs/14_TEMPLATE_RENDER_INVESTIGATION.md`
|
```
|
||||||
2. `docs/PHASE2_PLAN.md`
|
Undefined array key "id"
|
||||||
3. `docs/DEVELOPMENT_LOG.md`(第十一、十二章)
|
```
|
||||||
|
|
||||||
评审维度:准确性、完整性、可操作性、一致性、误导风险。
|
|
||||||
**不读代码文件,只读文档。输出到 `reviews/` 目录。**
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 任务清单
|
## 根因分析摘要(Round 1 快速结论)
|
||||||
|
|
||||||
- [x] **Task 1**: 评审 `docs/14_TEMPLATE_RENDER_INVESTIGATION.md` → `reviews/Architect-on-doc14.md`
|
### 1. 最可能触发点
|
||||||
- [Done: council/Architect]
|
|
||||||
|
|
||||||
- [x] **Task 2**: 评审 `docs/PHASE2_PLAN.md` → `reviews/Architect-on-PHASE2_PLAN.md`
|
**AdminGoodsSaveHandle.php 第 71 行**:
|
||||||
- [Done: council/Architect]
|
```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`
|
- `find()` 查不到记录时返回 `null`
|
||||||
- [Done: council/Architect]
|
- `??` 操作符只防御 `$template` 为 `null`,**不防御** `$template` 存在但键 `'seat_map'` 缺失
|
||||||
|
- PHP 8+ 会报 `Undefined array key "seat_map"`,而非 "id"
|
||||||
|
|
||||||
- [x] **Task 4**: 综合三份评审,输出 Top 3 修正建议 → `reviews/Architect-DOC-SUMMARY.md`
|
**真正报 "id" 的位置**——第 77 行 `array_filter` 回调内:
|
||||||
- [Done: council/Architect]
|
```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:逐份文档输出独立评审报告 |
|
| **Draft** | Round 1:代码静态分析,定位可疑行 | ✅ 完成 |
|
||||||
| **Review** | ✅ Task 4:综合汇总,Top 3 修正建议 |
|
| **Review** | Round 2:读取配置文件确认表前缀,输出根因报告 | 待做 |
|
||||||
| **Finalize** | ✅ 提交到 main,标注完成 |
|
| **Finalize** | Round 3:合并报告到 main,提交调试结论 | 待做 |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 依赖
|
## 依赖
|
||||||
|
|
||||||
- 三份文档已读取完毕,无需额外探索
|
- Task 1-3 必须按顺序执行(需读取配置文件)
|
||||||
- 不需要 BackendArchitect / SecurityEngineer 配合,可独立完成
|
- 不需要 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 全部
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue