6.6 KiB
DebugAgent 根因分析最终报告
Agent:council/DebugAgent | 日期:2026-04-20 代码版本:bbea35d83(feat: 保存时自动填充 template_snapshot)
执行摘要
通过静态代码分析 + 配置文件验证,确认 "Undefined array key 'id'" 错误的根因位于 AdminGoodsSaveHandle.php 第 77 行。表前缀问题已排除。
一、核心根因:第 77 行 $r['id'] 无空安全
文件:shopxo/app/plugins/vr_ticket/hook/AdminGoodsSaveHandle.php
行号:第 77 行(array_filter 回调内)
代码:
$selectedRoomIds = array_column(
array_filter($allRooms, function ($r) use ($config) {
return in_array($r['id'], $config['selected_rooms'] ?? []); // ← 崩溃点
}), null
);
触发条件:当 $allRooms(来自 $seatMap['rooms'])中存在缺少 'id' key 的房间对象时,访问 $r['id'] 直接抛出 Undefined array key "id"。
对比 Safe 版本:在 SeatSkuService::BatchGenerate 第 100 行有正确的空安全写法:
$roomId = !empty($room['id']) ? $room['id'] : ('room_' . $rIdx); // ✅ 安全
根本原因:AdminGoodsSaveHandle 的 array_filter 回调中,$r 直接访问 'id' 键,没有做存在性检查。
二、表前缀验证:已排除
验证方法
-
install.sql 第 2 行:
CREATE TABLE IF NOT EXISTS `{{prefix}}vr_seat_templates` (...)前缀变量为
{{prefix}}。 -
Admin.php 第 66-67 行(
checkAndInstallTables()方法):$prefix = \think\facade\Config::get('database.connections.mysql.prefix', 'vrt_'); $tableName = $prefix . 'vr_seat_templates'; // → vrt_vr_seat_templates默认前缀为
vrt_。 -
BaseService::table() 第 15-18 行:
public static function table($name) { return 'vr_' . $name; // 'vr_seat_templates' }ThinkPHP 会对
vr_seat_templates应用vrt_前缀 →vrt_vr_seat_templates。
结论
| 方法 | 展开 | 实际表名 |
|---|---|---|
Db::name('vr_seat_templates') |
vrt_ + vr_seat_templates |
vrt_vr_seat_templates ✅ |
BaseService::table('seat_templates') |
'vr_' + 'seat_templates' → ThinkPHP prefix |
vrt_vr_seat_templates ✅ |
两者完全等价,表前缀不是错误来源。
三、find() 返回 null 的次级风险(第 71 行)
$template = Db::name('vr_seat_templates')->find($templateId);
$seatMap = json_decode($template['seat_map'] ?? '{}', true);
风险:若 vr_seat_templates 表中不存在该记录,find() 返回 null,访问 $template['seat_map'] 抛出 Undefined array key "seat_map"。
注意:报错不是 "id" 而是 "seat_map",所以这不是 Primary 根因。
PHP 8+ ?? 行为关键点:?? 只防御 $template === null,不防御 $template = [](空数组):
$template = []; // find() 查不到记录时,理论上也可能返回空数组(取决于 ThinkPHP 版本)
$template['seat_map'] ?? '{}'; // PHP 8+: Undefined array key "seat_map"
四、selected_rooms 类型不匹配(静默错误,第 77 行)
return in_array($r['id'], $config['selected_rooms'] ?? []);
风险:前端传来的 selected_rooms 元素是字符串(如 "room_id_xxx"),而 $r['id'] 可能是字符串或整数(取决于模板创建时的数据格式)。PHP 的 in_array() 默认使用松散比较(==),所以 1 == '1' 为 true,但 1 === '1' 为 false。这种不匹配会导致过滤逻辑静默失效,不会触发 PHP 错误,但用户选择的房间可能全部丢失。
修复建议:
// 方案 1:严格类型比较
in_array($r['id'], $config['selected_rooms'] ?? [], true)
// 方案 2:统一字符串化
in_array((string)($r['id'] ?? ''), array_map('strval', $config['selected_rooms'] ?? []))
五、$data['item_type'] 访问安全性
if ($goodsId > 0 && ($data['item_type'] ?? '') === 'ticket') {
结论:安全。?? '' 提供默认值,'' === 'ticket' 为 false,不会误入票务分支。
六、SeatSkuService::BatchGenerate 审计结论
BackendArchitect 报告已确认:
- 第 100 行:
$roomId = !empty($room['id']) ? $room['id'] : ('room_' . $rIdx)✅ 有空安全 - 第 55-57 行:
if (empty($template)) { return ...; }✅ 有空安全
结论:SeatSkuService 无 "Undefined array key 'id'" 风险。
七、根因概率汇总
| # | 位置 | 错误信息 | 概率 | 结论 |
|---|---|---|---|---|
| P1 | AdminGoodsSaveHandle.php:77 $r['id'] |
"Undefined array key 'id'" | 99% | Primary |
| P2 | AdminGoodsSaveHandle.php:71 $template['seat_map'] |
"Undefined array key 'seat_map'" | 5%(不是 "id") | Secondary |
| P3 | AdminGoodsSaveHandle.php:77 in_array 类型 |
静默失效 | 高 | Tertiary |
表前缀问题:已排除 ✅
八、修复方案
P1 必须修复(对应 BackendArchitect P1)
// 修改前(AdminGoodsSaveHandle.php:75-79)
$selectedRoomIds = array_column(
array_filter($allRooms, function ($r) use ($config) {
return in_array($r['id'], $config['selected_rooms'] ?? []);
}), null
);
// 修改后(参考 BatchGenerate 第 100 行写法)
$selectedRoomIds = array_column(
array_filter($allRooms, function ($r, $idx) use ($config) {
$roomId = !empty($r['id']) ? $r['id'] : ('room_' . $idx);
return in_array($roomId, array_map('strval', $config['selected_rooms'] ?? []));
}), null
);
P2 必须修复(对应 BackendArchitect P2)
// 修改前(AdminGoodsSaveHandle.php:70-72)
$template = Db::name('vr_seat_templates')->find($templateId);
$seatMap = json_decode($template['seat_map'] ?? '{}', true);
// 修改后
$template = Db::name('vr_seat_templates')->find($templateId);
if (empty($template)) {
continue; // 或 return ['code' => -1, 'msg' => '座位模板不存在'];
}
$seatMap = json_decode($template['seat_map'] ?? '{}', true);
P3 建议修复(对应 BackendArchitect P3)
已在 P1 的修复方案中一并解决(array_map('strval', ...) 统一字符串化)。
九、报告结论
根因已确认:AdminGoodsSaveHandle.php:77 的 $r['id'] 无空安全,当 room 数据缺少 id 字段时触发 "Undefined array key 'id'"。
表前缀已排除:两者均查询 vrt_vr_seat_templates,等价。
优先级:P1 > P2 > P3,与 BackendArchitect 报告一致。
[APPROVE] — 与 BackendArchitect 报告结论一致,建议按 P1→P2→P3 顺序修复。