vr-shopxo-plugin/reviews/BackendArchitect-on-Issue-1...

6.4 KiB
Raw Blame History

评审报告Issue #13 "Undefined array key 'id'" 根因分析

评审人council/BackendArchitect | 日期2026-04-20 代码版本bbea35d83feat: 保存时自动填充 template_snapshot


一、"Undefined array key 'id'" 根因定位

Primary Bug — 99% 是这行(第 77 行)

文件shopxo/app/plugins/vr_ticket/hook/AdminGoodsSaveHandle.php 行号:第 77 行(array_filter 回调内) 代码

return in_array($r['id'], $config['selected_rooms'] ?? []);

根因:当 $rrooms 数组元素)缺少 'id' key 时,访问 $r['id'] 直接抛出 Undefined array key "id"

何时触发:当 vr_seat_templates.seat_map.rooms[] 中存在任何一个没有 id 字段的房间对象时,在 template_snapshot 填充逻辑中崩溃。

对比SeatSkuService::BatchGenerate 第 100 行做了正确防护:

// ✅ 安全写法
$roomId = !empty($room['id']) ? $room['id'] : ('room_' . $rIdx);

AdminGoodsSaveHandle 第 77 行没有这个防护。


二、Db::name('vr_seat_templates') 表前缀问题

结论:两者等价,不存在前缀错误

验证依据admin/Admin.php 第 66 行):

$prefix = \think\facade\Config::get('database.connections.mysql.prefix', 'vrt_');
$tableName = $prefix . 'vr_seat_templates';  // → vrt_vr_seat_templates

ShopXO 默认表前缀为 vrt_。因此:

  • Db::name('vr_seat_templates')vrt_vr_seat_templates
  • BaseService::table('seat_templates')vr_seat_templates + ShopXO 前缀 → vrt_vr_seat_templates

两者查询同一张表,不是错误来源

⚠️AdminGoodsSaveHandle 使用裸 Db::name() 而非 SeatSkuService 使用的 BaseService::table(),风格不统一。建议统一。


三、find() 返回 null 的空安全问题

Secondary Bug — 触发概率 5%(第 71 行)

代码

$template = Db::name('vr_seat_templates')->find($templateId);
$seatMap  = json_decode($template['seat_map'] ?? '{}', true);  // ❌ $template 可能是 null

根因:若 vr_seat_templates 表中不存在 id = $templateId 的记录,find() 返回 null,访问 $template['seat_map'] 抛出 Undefined array key "seat_map"(虽然报错信息不是 "id",但属于同类空安全问题)。

对比SeatSkuService::BatchGenerate 第 55-57 行做了正确防护:

if (empty($template)) {
    return ['code' => -2, 'msg' => "座位模板 {$seatTemplateId} 不存在"];
}

AdminGoodsSaveHandle 第 71 行没有等效检查。


四、selected_rooms 类型不匹配问题

Tertiary Bug — 静默失败(第 77 行)

代码

return in_array($r['id'], $config['selected_rooms'] ?? []);

根因selected_rooms[] 从前端传来是字符串(如 "room_0"),而 $r['id']vr_seat_templates.seat_map.rooms[] 中可能是整数或字符串,取决于模板创建时的数据。

影响:类型不匹配时 in_array() 永远返回 false,导致 selectedRoomIds 永远为空数组,前端无法正确展示选中的房间。但不会抛出 PHP 错误,属于静默逻辑错误。

修复建议

// 使用严格模式 (bool) 第三个参数
in_array($r['id'], $config['selected_rooms'] ?? [], true)
// 或统一为字符串比较
in_array((string)($r['id'] ?? ''), array_map('strval', $config['selected_rooms'] ?? []))

五、SeatSkuService::BatchGenerate 审计结论

无 "id" 访问问题

位置 代码 结论
第 100 行 $roomId = !empty($room['id']) ? $room['id'] : ('room_' . $rIdx) 有 null-safe fallback
第 103 行 in_array($roomId, $selectedRooms) 基于安全的 $roomId
第 127-128 行 in_array($char, $selectedSections[$roomId]) 先检查 !empty()
第 278-280 行 json_decode($existingItems, true) ?: [] 有 fallback
第 283 行 array_column($existingItems, 'name') ⚠️$existingItems 不是数组,抛出 Warning

六、$data['item_type'] 访问安全分析

安全(第 59 行)

if ($goodsId > 0 && ($data['item_type'] ?? '') === 'ticket') {

使用 ?? '' 提供默认值,'' === 'ticket'false,不会误入票务分支。


七、修复建议汇总

高优先级(必须修复)

# 位置 问题 修复方案
P1 AdminGoodsSaveHandle.php:77 $r['id'] 无空安全 参考 BatchGenerate 第 100 行:(($r['id'] ?? null) ?: ('room_' . $rIdx))
P2 AdminGoodsSaveHandle.php:71 $template null 访问 find() 后加 if (empty($template)) { continue; }
P3 AdminGoodsSaveHandle.php:77 类型不匹配静默失败 加严格类型比较或统一字符串化

建议优化(非必须)

# 位置 问题 建议
S1 AdminGoodsSaveHandle.php:70 Db::name() 不统一 改用 SeatSkuServiceBaseService::table() 风格一致
S2 AdminGoodsSaveHandle.php:91 goods 表写回时机 确认 save_thing_end 时机 goods 已落表,可以直接 update

八、最终根因结论

"Undefined array key 'id'" 错误 99% 来自 AdminGoodsSaveHandle.php 第 77 行

return in_array($r['id'], $config['selected_rooms'] ?? []);
//              ^^^^^^^^ 当 $r 无 'id' key 时崩溃

触发条件vr_seat_templates.seat_map.rooms[] 中存在至少一个没有 id 字段的房间对象(这在前端手动构造 seat_map 或某些旧模板数据中很可能发生)。

修复后代码建议

$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
);

九、审查结论

审查项 结论
错误根因 已定位AdminGoodsSaveHandle.php:77
表前缀问题 确认无前缀错误,两者等价
null 安全 存在两处 null 安全问题P1/P2
类型匹配 ⚠️ 存在静默类型不匹配P3
SeatSkuService BatchGenerate 已正确处理
建议修复优先级 P1 > P2 > P3

[APPROVE] — 根因已确认,建议按 P1→P2→P3 顺序修复