diff --git a/wechat-news-morning-draft-worker.mjs b/wechat-news-morning-draft-worker.mjs index 92f94f5..8269933 100644 --- a/wechat-news-morning-draft-worker.mjs +++ b/wechat-news-morning-draft-worker.mjs @@ -10,7 +10,11 @@ import { isNewsMorningPageForToday, isWithinNewsMorningGenerateLeadWindow, } from './wechat-news-morning-draft.mjs'; -import { prefetchNewsMorningSearxngBrief } from './wechat-news-morning-search.mjs'; +import { prefetchNewsMorningSearxngBundle } from './wechat-news-morning-search.mjs'; +import { + shouldUseNewsMorningSearxngFallback, + supplementNews002HtmlFromSearxngGroups, +} from './wechat-news-morning-searxng-fallback.mjs'; import { dedupeNewsMorningHtml, formatNewsMorningDedupeSummary, @@ -65,18 +69,49 @@ function applyNewsMorningDedupe(page, logger) { } } -async function applyNews002ImageGate(page, config, logger, env = process.env) { +async function applyNews002ImageGate( + page, + config, + logger, + env = process.env, + { searchGroups = [] } = {}, +) { if (config?.templateId !== 'news002') return null; if (!page?.localPath || !fs.existsSync(page.localPath)) { throw new Error('news002 配图闸门无法读取生成页面'); } - const source = fs.readFileSync(page.localPath, 'utf8'); - const enriched = await enrichNews002HtmlWithSourceImages(source, { + let source = fs.readFileSync(page.localPath, 'utf8'); + let enriched = await enrichNews002HtmlWithSourceImages(source, { timeoutMs: Number(env.MEMIND_NEWS_MORNING_IMAGE_META_TIMEOUT_MS ?? 6000) || 6000, concurrency: Number(env.MEMIND_NEWS_MORNING_IMAGE_META_CONCURRENCY ?? 8) || 8, }); fs.writeFileSync(page.localPath, enriched.html); - const audit = auditNews002ImageCoverage(enriched.html, { requireTemplateStructure: true }); + let audit = auditNews002ImageCoverage(enriched.html, { requireTemplateStructure: true }); + + if ( + !audit.ok + && searchGroups.length > 0 + && shouldUseNewsMorningSearxngFallback(env) + ) { + const supplemented = supplementNews002HtmlFromSearxngGroups( + enriched.html, + searchGroups, + audit, + ); + if (supplemented.changed) { + logger.log?.('[NewsMorningDraft] searxng fallback supplement', { + slug: page.slug, + ...supplemented.stats, + }); + enriched = await enrichNews002HtmlWithSourceImages(supplemented.html, { + timeoutMs: Number(env.MEMIND_NEWS_MORNING_IMAGE_META_TIMEOUT_MS ?? 6000) || 6000, + concurrency: Number(env.MEMIND_NEWS_MORNING_IMAGE_META_CONCURRENCY ?? 8) || 8, + }); + fs.writeFileSync(page.localPath, enriched.html); + audit = auditNews002ImageCoverage(enriched.html, { requireTemplateStructure: true }); + } + } + logger.log?.('[NewsMorningDraft] news002 image audit', { slug: page.slug, attempted: enriched.attemptedCount, @@ -121,7 +156,7 @@ export function startWechatNewsMorningDraftWorker({ h5Root = null, env = process.env, executeTask = executeScheduledTask, - prefetchNewsSearch = prefetchNewsMorningSearxngBrief, + prefetchNewsSearch = prefetchNewsMorningSearxngBundle, logger = console, intervalMs = wechatNewsMorningDraftWorkerIntervalMs(env), executionTimeoutMs = wechatNewsMorningDraftExecutionTimeoutMs(env), @@ -152,12 +187,15 @@ export function startWechatNewsMorningDraftWorker({ archiveTodayPageForForce(config, h5Root, now, logger); } let searchBrief = ''; + let searchGroups = []; try { - searchBrief = await prefetchNewsSearch({ + const searchBundle = await prefetchNewsSearch({ now, timezone: config.timezone, env, }); + searchBrief = String(searchBundle?.brief ?? searchBundle ?? '').trim(); + searchGroups = Array.isArray(searchBundle?.groups) ? searchBundle.groups : []; } catch (prefetchError) { logger.warn?.( '[NewsMorningDraft] searxng prefetch failed:', @@ -181,7 +219,7 @@ export function startWechatNewsMorningDraftWorker({ throw new Error('新闻早报页面生成未完成'); } applyNewsMorningDedupe(page, logger); - await applyNews002ImageGate(page, config, logger, env); + await applyNews002ImageGate(page, config, logger, env, { searchGroups }); await wechatNewsMorningDraftService.recordAutoGenerationRun({ status: 'success', pageSlug: page.slug, diff --git a/wechat-news-morning-draft-worker.test.mjs b/wechat-news-morning-draft-worker.test.mjs index bbe4e38..787e1b0 100644 --- a/wechat-news-morning-draft-worker.test.mjs +++ b/wechat-news-morning-draft-worker.test.mjs @@ -212,7 +212,10 @@ test('generateToday skips existing page unless force is set', async () => { executeCalls.push(task.taskSpec); fs.writeFileSync(path.join(publicDir, 'daily-news-0911.html'), `${SAMPLE_HTML}`); }, - prefetchNewsSearch: async () => '【专用联网搜索预取】国内国际热门事件', + prefetchNewsSearch: async () => ({ + brief: '【专用联网搜索预取】国内国际热门事件', + groups: [], + }), intervalMs: 30_000, runOnStart: false, setIntervalFn: () => ({ unref() {} }), diff --git a/wechat-news-morning-images.mjs b/wechat-news-morning-images.mjs index 3b1b590..d6e0b87 100644 --- a/wechat-news-morning-images.mjs +++ b/wechat-news-morning-images.mjs @@ -329,6 +329,11 @@ export function auditNews002ImageCoverage( if (!/#C8362F/i.test(source) || !/#FAF7F2/i.test(source) || !/#1B3A6B/i.test(source)) { violations.push('noncanonical-palette'); } + const sectionAliases = { + brief: ['brief'], + business: ['business', 'finance'], + deepdive: ['deepdive', 'deep'], + }; const requiredSectionIds = [ 'brief', 'headlines', @@ -341,10 +346,22 @@ export function auditNews002ImageCoverage( 'knowledge', 'history', ]; - const missingSections = requiredSectionIds.filter((id) => ( - !new RegExp(`
