fix: 联想建议词改成「统一来源 + 自动兜底」,任何引擎都能出词

问题:除百度/360 外,其它引擎的联想下拉基本是空的。

实测(2026-09-28 在站点服务器上逐个探测):
  百度 sugrec        200 / 0.12s / 1137B  ✅ 最快最稳
  360 sug.so.360.cn  200 / 0.20s /  714B  ✅
  必应 api.bing.com  200 / 0.5~1.6s      ✅ 可用但明显慢
  搜狗 ajajjson      200 / **0 字节**     ❌ 联想域名 w.sugg.sogou.com 已不解析
  谷歌 suggestqueries  连接超时            ❌ 国内不可达
  DuckDuckGo          连接超时            ❌ 国内不可达

按「联想统一取百度、搜索仍走各自引擎」实现:
- 新增设置 suggest_source(设置页可选 百度/必应/360/各引擎自己的接口),默认百度。
- 任何情况下都带自动兜底链:首选来源取不到词(接口挂/超时/返回空)
  就依次回退 百度 → 必应 → 360;单个候选 3 秒超时,绝不让下拉框卡住。
- 候选来源不过滤 is_active —— 作为联想来源的引擎可能在前台被隐藏。
- 响应里带 src 字段标明这次实际用的是谁,便于排查。

顺带修一个更隐蔽的 bug:前台 <form id="search-form"> 原本**没有 data-slug**,
而 JS 只在「点击引擎标签」时才把它写上 → 一进站、没点过引擎时 slug 为空,
suggest.php 查不到引擎,联想**全都不出词**。现在表单自带默认引擎的 slug。

其它:
- 清掉种子里已失效的搜狗联想地址;加一条幂等迁移,只匹配默认种子值,
  用户自己改过的地址不动。谷歌/DDG 也不再预设(前端不可达)。
- app.js:请求加序号,防止先发的慢响应覆盖后发的快响应;切换引擎时
  若输入框已有内容就顺带换一份建议。
- e2e 新增「联想来源」场景(本地桩服务器 + 独立端口,不依赖外网):
  8 条断言覆盖 默认来源 / auto 模式 / 回退 / 空关键词 / 响应头。

验证:本地 e2e 57/57;部署 38 文件哈希逐字节一致、双版本 php -l、37 条
静态断言全过;线上实测 google/sogou/ddg 三个「自家接口不可用」的引擎
均返回 10 条百度建议(src=baidu),中英文都正常。
This commit is contained in:
2026-09-28 17:13:10 +08:00
parent 853e4dd7c6
commit 34473d0120
7 changed files with 184 additions and 44 deletions
+3
View File
@@ -208,6 +208,8 @@ try {
/* ---------- 设置 ---------- */
case 'settings_save': {
$srcRaw = (string)($_POST['suggest_source'] ?? 'baidu');
$src = in_array($srcRaw, ['auto', 'baidu', 'bing', 'so360'], true) ? $srcRaw : 'baidu';
$map = [
'site_name' => trim((string)($_POST['site_name'] ?? '')),
'subtitle' => trim((string)($_POST['subtitle'] ?? '')),
@@ -215,6 +217,7 @@ try {
'primary_color' => preg_match('/^#[0-9a-fA-F]{3,8}$/', (string)($_POST['primary_color'] ?? '')) ? $_POST['primary_color'] : '#2F6BF7',
'open_blank' => isset($_POST['open_blank']) ? '1' : '0',
'icon_services' => isset($_POST['icon_services']) ? '1' : '0',
'suggest_source' => $src,
];
$st = $pdo->prepare('INSERT INTO settings (k,v) VALUES (?,?) ON CONFLICT(k) DO UPDATE SET v=excluded.v');
foreach ($map as $k => $v) { $st->execute([$k, $v]); }
+25
View File
@@ -241,6 +241,11 @@ elseif ($p === 'engines'): ?>
</select>
</label>
<label class="chk"><input type="checkbox" name="is_active" id="e-active"> 启用(显示在首页顶部)</label>
<p class="hint span2">
这两项只在「设置 → 联想词来源」选<b>各搜索引擎自己的接口</b>时才会用到;
默认是统一用百度出建议(搜索本身仍走各自的搜索地址)。
这里填的接口只要通了就会被优先使用,没通就自动回退到百度。
</p>
<div class="span2 form-actions"><button class="btn primary">保存</button><button type="button" class="btn ghost" id="e-reset">清空</button></div>
</form>
</section>
@@ -296,6 +301,26 @@ else: // settings ?>
<?php endforeach; ?>
</select>
</label>
<label>联想词来源
<select name="suggest_source">
<?php
$cur = setting('suggest_source', 'baidu');
$opts = [['baidu', '百度(推荐,最快)'], ['bing', '必应'], ['so360', '360'], ['auto', '各搜索引擎自己的接口']];
$known = array_column($engines, 'slug');
foreach ($opts as $o):
if ($o[0] !== 'auto' && !in_array($o[0], $known, true)) { continue; }
?>
<option value="<?= e($o[0]) ?>" <?= $cur === $o[0] ? 'selected' : '' ?>><?= e($o[1]) ?></option>
<?php endforeach; ?>
</select>
</label>
<p class="hint span2">
搜索时仍然用你选中的那个引擎,这里只决定<b>下拉建议词</b>从哪儿来。
实测(2026-09-28):<b>百度 0.12s、360 0.20s、必应 0.5~1.6s</b> 可用;
<b>搜狗接口已失效</b>(返回 0 字节)、<b>谷歌与 DuckDuckGo 国内连不上</b>。
所以默认「统一用百度」——这样切到任何引擎下拉都有词。选「各搜索引擎自己的接口」则恢复成
各用各的(没恢复好的引擎会自动回退到百度的建议)。
</p>
<label>主题色<input type="text" name="primary_color" value="<?= e(setting('primary_color', '#2F6BF7')) ?>"></label>
<label class="chk"><input type="checkbox" name="open_blank" <?= setting('open_blank', '1') === '1' ? 'checked' : '' ?>> 搜索结果新窗口打开</label>
<label class="chk"><input type="checkbox" name="icon_services" <?= setting('icon_services', '1') === '1' ? 'checked' : '' ?>> 联网搜索高清图标(第三方聚合服务)</label>