true, 'changed' => true]; } } $isList = array_keys($value) === range(0, count($value) - 1); if ($isList) { $newList = []; foreach ($value as $item) { $res = scrub_live_from_value($item); if (is_array($res) && !empty($res['__delete__'])) { $changed = true; continue; } if (is_array($res) && isset($res['value'])) { $newList[] = $res['value']; if (!empty($res['changed'])) { $changed = true; } } else { $newList[] = $item; } } $value = $newList; return ['value' => $value, 'changed' => $changed]; } foreach ($value as $k => &$child) { if ($k === 'path' || $k === 'path_name' || $k === 'pathName' || $k === 'page') { if (is_string($child) && is_live_related_string($child)) { $child = ''; $changed = true; } } $res = scrub_live_from_value($child); if (is_array($res) && !empty($res['__delete__'])) { unset($value[$k]); $changed = true; continue; } if (is_array($res) && array_key_exists('value', $res)) { $value[$k] = $res['value']; if (!empty($res['changed'])) { $changed = true; } } } unset($child); } return ['value' => $value, 'changed' => $changed]; } function log_line($msg) { echo $msg . PHP_EOL; } try { Db::query('SELECT 1'); } catch (\Throwable $e) { fwrite(STDERR, "数据库连接失败,请在已安装 Shopro 的服务器上运行(读取 .env / application/database.php)。\n"); fwrite(STDERR, $e->getMessage() . "\n"); exit(1); } $prefix = config('database.prefix') ?: 'fa_'; $linkTable = $prefix . 'shopro_link'; $contentTable = $prefix . 'shopro_decorate_content'; $configTable = $prefix . 'shopro_config'; $decorateTable = $prefix . 'shopro_decorate'; log_line('=== 清除直播入口(方案 A)==='); log_line('模式: ' . ($apply ? '【执行 --apply】' : '【预览,加 --apply 才写库】')); log_line('表前缀: ' . $prefix); log_line(''); // ---------- 1. shopro_link ---------- $links = Db::table($linkTable) ->whereNull('deletetime') ->where(function ($q) { $q->where('path', 'like', '%live%') ->whereOr('path', 'like', '%直播%') ->whereOr('name', 'like', '%直播%'); }) ->select(); log_line('[1] 系统链接 fa_shopro_link 命中 ' . count($links) . ' 条'); foreach ($links as $row) { log_line(' - id=' . $row['id'] . ' group=' . $row['group'] . ' name=' . $row['name'] . ' path=' . $row['path']); } if ($apply && count($links) > 0) { $ids = array_column($links, 'id'); Db::table($linkTable)->where('id', 'in', $ids)->update(['deletetime' => time()]); log_line(' => 已软删除'); } // 名称/路径含 live 的宽松匹配(path 字段) $links2 = Db::table($linkTable) ->whereNull('deletetime') ->where('path', 'like', '%/pages/live%') ->select(); if (count($links2) > 0) { log_line('[1b] 路径含 /pages/live 再命中 ' . count($links2) . ' 条'); foreach ($links2 as $row) { log_line(' - id=' . $row['id'] . ' name=' . $row['name'] . ' path=' . $row['path']); } if ($apply) { Db::table($linkTable)->where('id', 'in', array_column($links2, 'id'))->update(['deletetime' => time()]); } } log_line(''); // ---------- 2. shopro_decorate_content ---------- $rows = Db::table($contentTable)->select(); $deletedComponents = 0; $updatedComponents = 0; log_line('[2] 装修组件 fa_shopro_decorate_content 共 ' . count($rows) . ' 条'); foreach ($rows as $row) { $type = $row['type'] ?? ''; $id = $row['id']; $decorateId = $row['decorate_id'] ?? ''; $name = $row['name'] ?? ''; if ($type === 'live') { log_line(" - 删除直播组件 id={$id} decorate_id={$decorateId} name={$name}"); if ($apply) { Db::table($contentTable)->where('id', $id)->delete(); } $deletedComponents++; continue; } $contentRaw = $row['content'] ?? ''; if ($contentRaw === '' || $contentRaw === null) { continue; } $content = json_decode($contentRaw, true); if (!is_array($content)) { continue; } $res = scrub_live_from_value($content); if (empty($res['changed'])) { continue; } log_line(" - 清理组件 id={$id} type={$type} decorate_id={$decorateId} name={$name}"); $updatedComponents++; if ($apply) { Db::table($contentTable)->where('id', $id)->update([ 'content' => json_encode($res['value'], JSON_UNESCAPED_UNICODE), ]); } } log_line(" 小结: 删除直播组件 {$deletedComponents} 个,清理其它组件 {$updatedComponents} 个"); log_line(''); // ---------- 3. shopro_config(JSON 配置里可能藏宫格/菜单) ---------- $configRows = Db::table($configTable)->where(function ($q) { $q->where('value', 'like', '%live%')->whereOr('value', 'like', '%直播%'); })->select(); log_line('[3] 配置 fa_shopro_config 命中 ' . count($configRows) . ' 条(value 含 live/直播)'); $configUpdated = 0; foreach ($configRows as $row) { $val = $row['value']; $decoded = json_decode($val, true); if (!is_array($decoded)) { if (is_live_related_string($val)) { log_line(' - 跳过非 JSON 配置 name=' . $row['name'] . '(请人工检查)'); } continue; } $res = scrub_live_from_value($decoded); if (empty($res['changed'])) { continue; } log_line(' - 清理配置 name=' . $row['name']); $configUpdated++; if ($apply) { Db::table($configTable)->where('id', $row['id'])->update([ 'value' => json_encode($res['value'], JSON_UNESCAPED_UNICODE), ]); } } log_line(" 小结: 更新配置 {$configUpdated} 条"); log_line(''); // ---------- 4. 列出仍使用中的装修模板(便于人工核对) ---------- try { $decorates = Db::table($decorateTable)->field('id,name,status,type,platform')->select(); log_line('[4] 当前装修模板(清理后请到后台确认「使用中」的页面已发布)'); foreach ($decorates as $d) { $status = isset($d['status']) ? $d['status'] : ''; log_line(' - id=' . $d['id'] . ' name=' . $d['name'] . ' status=' . $status . ' platform=' . ($d['platform'] ?? '')); } } catch (\Exception $e) { log_line('[4] 读取装修模板列表跳过: ' . $e->getMessage()); } log_line(''); log_line('=== 完成 ==='); if (!$apply) { log_line('以上为预览。确认无误后执行: php scripts/remove-live-for-audit.php --apply'); } log_line('小程序端请重新编译上传(pages.json 已去掉直播页路由)。');