From da001797abc7ebd42473585b558176a462a741e2 Mon Sep 17 00:00:00 2001 From: Council Date: Mon, 20 Apr 2026 12:13:29 +0800 Subject: [PATCH] =?UTF-8?q?fix(vr=5Fticket):=20template=5Fsnapshot=20?= =?UTF-8?q?=E9=87=8D=E5=BB=BA=E9=80=BB=E8=BE=91=E9=87=8D=E5=86=99=20+=20?= =?UTF-8?q?=E5=B9=BD=E7=81=B5=E9=85=8D=E7=BD=AE=E8=BF=87=E6=BB=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit === 问题 1: template_snapshot.rooms 为空 === 根因:前端 outputBase64 根本不包含 template_snapshot,导致: - template_snapshot 永远是空的 - 无论 v1/v3 格式,rooms 信息都丢失 修复(AdminGoodsSaveHandle.php): - 条件从「template_snapshot 为空才读 DB」改为「selected_rooms 有值就永远从 DB 重建」 - 读 DB 时同时做 v1→v3 迁移(sections+map → rooms 数组) - ID 匹配支持前端标准化的 "room_0" 格式和 DB 原始 "0" 格式双向兼容 - PHP 7.x 兼容(strpos 而非 str_starts_with) === 问题 2: 幽灵配置(软删除场馆仍出现在表单)=== 根因:AdminGoodsSave.php 查询模板时用 where('status', 1),软删除模板不加载, 但 configs.value 里还保留着旧配置 → 场馆 checkbox 选中但无法操作。 修复(AdminGoodsSave.php): - 加载时用 Set 过滤掉 status=0 模板的配置 - 幽灵配置在编辑表单加载时直接排除,不出现在 UI --- .../plugins/vr_ticket/hook/AdminGoodsSave.php | 8 +- .../vr_ticket/hook/AdminGoodsSaveHandle.php | 87 +++++++++++-------- 2 files changed, 57 insertions(+), 38 deletions(-) diff --git a/shopxo/app/plugins/vr_ticket/hook/AdminGoodsSave.php b/shopxo/app/plugins/vr_ticket/hook/AdminGoodsSave.php index dd25350..1fb92db 100644 --- a/shopxo/app/plugins/vr_ticket/hook/AdminGoodsSave.php +++ b/shopxo/app/plugins/vr_ticket/hook/AdminGoodsSave.php @@ -194,7 +194,13 @@ class AdminGoodsSave // 还原已保存的配置并清洗历史脏数据 if (AppData.vrGoodsConfig && Array.isArray(AppData.vrGoodsConfig)) { - configs.value = AppData.vrGoodsConfig.map(c => { + // 构建有效模板 ID 集合(只含 status=1 的模板) + const validTemplateIds = new Set((AppData.templates || []).map(t => t.id)); + + configs.value = AppData.vrGoodsConfig + // 过滤掉软删除模板的配置(幽灵配置) + .filter(c => validTemplateIds.has(c.template_id)) + .map(c => { // 确保 sessions 结构正确 if (!c.sessions || c.sessions.length === 0) { c.sessions = defaultSessions(); diff --git a/shopxo/app/plugins/vr_ticket/hook/AdminGoodsSaveHandle.php b/shopxo/app/plugins/vr_ticket/hook/AdminGoodsSaveHandle.php index d96477b..6e72975 100644 --- a/shopxo/app/plugins/vr_ticket/hook/AdminGoodsSaveHandle.php +++ b/shopxo/app/plugins/vr_ticket/hook/AdminGoodsSaveHandle.php @@ -67,46 +67,59 @@ class AdminGoodsSaveHandle } if (is_array($configs) && !empty($configs)) { - // 0) 填充 template_snapshot(前端没传、或 rooms 为空时兜底从 vr_seat_templates 读) + // 0) 重建 template_snapshot — 前端不发送 template_snapshot, + // 当 template_snapshot 为空、或 selected_rooms 有值时,从 DB 重建 foreach ($configs as $i => &$config) { - if (empty($config['template_snapshot']) || empty($config['template_snapshot']['rooms'])) { - $templateId = intval($config['template_id'] ?? 0); - if ($templateId > 0) { - $template = Db::name('vr_seat_templates')->find($templateId); - if (empty($template)) { - continue; - } - $seatMap = json_decode($template['seat_map'] ?? '{}', true); - $allRooms = $seatMap['rooms'] ?? []; + $templateId = intval($config['template_id'] ?? 0); + $selectedRooms = $config['selected_rooms'] ?? []; - // ── v1→v3 兼容迁移 ── - // v1 旧格式没有 rooms 嵌套,只有 sections+map 扁平结构 - if (empty($allRooms) && !empty($seatMap['sections'])) { - $v1Sections = $seatMap['sections'] ?? []; - $v1Map = $seatMap['map'] ?? []; - $v1Seats = $seatMap['seats'] ?? []; - $v1RoomId = $config['selected_rooms'][0] ?? 'room_1'; - $allRooms = [[ - 'id' => $v1RoomId, - 'name' => $seatMap['venue']['name'] ?? '主馆', - 'sections' => $v1Sections, - 'map' => $v1Map, - 'seats' => $v1Seats, - ]]; - } - - // 按 selected_rooms 过滤,只存用户选中的房间 - $selectedRoomIds = array_column( - array_filter($allRooms, function ($r) use ($config) { - return isset($r['id']) && in_array($r['id'], $config['selected_rooms'] ?? []); - }), null - ); - - $config['template_snapshot'] = [ - 'venue' => $seatMap['venue'] ?? [], - 'rooms' => $selectedRoomIds, - ]; + // 条件:snapshot 为空,或者前端有 selected_rooms + if ($templateId > 0 && (!empty($selectedRooms) || empty($config['template_snapshot']) || empty($config['template_snapshot']['rooms']))) { + $template = Db::name('vr_seat_templates')->find($templateId); + if (empty($template)) { + continue; } + $seatMap = json_decode($template['seat_map'] ?? '{}', true); + $allRooms = $seatMap['rooms'] ?? []; + + // ── v1→v3 兼容迁移 ── + // v1 旧格式没有 rooms 嵌套,只有 sections+map 扁平结构 + if (empty($allRooms) && !empty($seatMap['sections'])) { + $v1Sections = $seatMap['sections'] ?? []; + $v1Map = $seatMap['map'] ?? []; + $v1Seats = $seatMap['seats'] ?? []; + $v1RoomId = $selectedRooms[0] ?? 'room_1'; + $allRooms = [[ + 'id' => $v1RoomId, + 'name' => $seatMap['venue']['name'] ?? '主馆', + 'sections' => $v1Sections, + 'map' => $v1Map, + 'seats' => $v1Seats, + ]]; + } + + // 按 selected_rooms 过滤(支持前端标准化的 "room_0" 格式双向兼容) + $selectedRoomIds = array_column( + array_filter($allRooms, function ($r) use ($selectedRooms) { + $rid = $r['id'] ?? ''; + if (in_array($rid, $selectedRooms)) { + return true; + } + // 尝试加/减 "room_" 前缀匹配(PHP 7.x 兼容) + if (strpos($rid, 'room_') === 0 && in_array(substr($rid, 5), $selectedRooms)) { + return true; + } + if (in_array('room_' . $rid, $selectedRooms)) { + return true; + } + return false; + }), null + ); + + $config['template_snapshot'] = [ + 'venue' => $seatMap['venue'] ?? [], + 'rooms' => $selectedRoomIds, + ]; } } unset($config); // 解除引用,避免后续误改