fix(news): strip imageless cards in gate without dropping fallback sections
Defer empty-section pruning until after the image gate passes so SearXNG fallback shells survive enrichment; push audit skips empty core sections. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -24,6 +24,7 @@ import {
|
||||
auditNews002ImageCoverage,
|
||||
enrichNews002HtmlWithSourceImages,
|
||||
pruneNews002ImagelessStoryCards,
|
||||
stripNews002ImagelessStoryCards,
|
||||
} from './wechat-news-morning-images.mjs';
|
||||
import {
|
||||
isWechatNewsMorningDraftWorkerEnabled,
|
||||
@@ -71,17 +72,16 @@ function applyNewsMorningDedupe(page, logger) {
|
||||
}
|
||||
}
|
||||
|
||||
function finalizeNews002Html(html, logger, page, label) {
|
||||
const pruned = pruneNews002ImagelessStoryCards(html);
|
||||
if (pruned.removedCards > 0 || pruned.removedSections > 0) {
|
||||
logger.log?.('[NewsMorningDraft] news002 pruned imageless cards', {
|
||||
function stripNews002ForGate(html, logger, page, label) {
|
||||
const stripped = stripNews002ImagelessStoryCards(html);
|
||||
if (stripped.removedCards > 0) {
|
||||
logger.log?.('[NewsMorningDraft] news002 stripped imageless cards', {
|
||||
slug: page.slug,
|
||||
phase: label,
|
||||
removedCards: pruned.removedCards,
|
||||
removedSections: pruned.removedSections,
|
||||
removedCards: stripped.removedCards,
|
||||
});
|
||||
}
|
||||
return pruned.html;
|
||||
return stripped.html;
|
||||
}
|
||||
|
||||
async function applyNews002ImageGate(
|
||||
@@ -95,7 +95,7 @@ async function applyNews002ImageGate(
|
||||
if (!page?.localPath || !fs.existsSync(page.localPath)) {
|
||||
throw new Error('news002 配图闸门无法读取生成页面');
|
||||
}
|
||||
let source = finalizeNews002Html(fs.readFileSync(page.localPath, 'utf8'), logger, page, 'pre-enrich');
|
||||
let source = stripNews002ForGate(fs.readFileSync(page.localPath, 'utf8'), logger, page, 'pre-enrich');
|
||||
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,
|
||||
@@ -155,6 +155,15 @@ async function applyNews002ImageGate(
|
||||
+ `(卡片 ${audit.cardCount},真实配图 ${audit.imageCount},头条配图 ${audit.leadImageCount}/${audit.leadCount})`,
|
||||
);
|
||||
}
|
||||
const display = pruneNews002ImagelessStoryCards(enriched.html);
|
||||
if (display.removedCards > 0 || display.removedSections > 0) {
|
||||
logger.log?.('[NewsMorningDraft] news002 display prune after gate', {
|
||||
slug: page.slug,
|
||||
removedCards: display.removedCards,
|
||||
removedSections: display.removedSections,
|
||||
});
|
||||
fs.writeFileSync(page.localPath, display.html);
|
||||
}
|
||||
return audit;
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ import { countNewsMorningStoryCards } from './wechat-news-morning-dedup.mjs';
|
||||
import {
|
||||
auditNews002ImageCoverage,
|
||||
extractNews002CardImageUrls,
|
||||
pruneNews002ImagelessStoryCards,
|
||||
stripNews002ImagelessStoryCards,
|
||||
} from './wechat-news-morning-images.mjs';
|
||||
import {
|
||||
NEWS001_FRESH_CONTENT_SPEC,
|
||||
@@ -837,8 +837,11 @@ export async function buildDailyNewsWechatDraftArticleForPush({
|
||||
memindLibRoot,
|
||||
});
|
||||
if (/class="[^"]*\bmasthead\b/i.test(String(html ?? ''))) {
|
||||
html = pruneNews002ImagelessStoryCards(String(html ?? '')).html;
|
||||
const audit = auditNews002ImageCoverage(html, { requireTemplateStructure: true });
|
||||
html = stripNews002ImagelessStoryCards(String(html ?? '')).html;
|
||||
const audit = auditNews002ImageCoverage(html, {
|
||||
requireTemplateStructure: true,
|
||||
requireAllCoreSections: false,
|
||||
});
|
||||
if (!audit.ok) {
|
||||
throw new Error(`news002 页面未通过推送闸门:${audit.violations.join(', ')}`);
|
||||
}
|
||||
|
||||
@@ -254,8 +254,8 @@ function pruneEmptyNews002Sections(html) {
|
||||
return { html: output, removedSections };
|
||||
}
|
||||
|
||||
/** 移除无图故事卡并清理空栏目,保证页面只展示有配图的条目。 */
|
||||
export function pruneNews002ImagelessStoryCards(html) {
|
||||
/** 仅移除无图故事卡,不删空栏目(闸门阶段用,避免 fallback 补栏后被误删)。 */
|
||||
export function stripNews002ImagelessStoryCards(html) {
|
||||
let output = String(html ?? '');
|
||||
let removedCards = 0;
|
||||
const imageless = extractNews002StoryCards(output)
|
||||
@@ -264,10 +264,16 @@ export function pruneNews002ImagelessStoryCards(html) {
|
||||
output = output.slice(0, card.start) + output.slice(card.end);
|
||||
removedCards += 1;
|
||||
}
|
||||
const prunedSections = pruneEmptyNews002Sections(output);
|
||||
return { html: output, removedCards, removedSections: 0 };
|
||||
}
|
||||
|
||||
/** 移除无图故事卡并清理空栏目(推送/展示前用,保证页面协调)。 */
|
||||
export function pruneNews002ImagelessStoryCards(html) {
|
||||
const stripped = stripNews002ImagelessStoryCards(html);
|
||||
const prunedSections = pruneEmptyNews002Sections(stripped.html);
|
||||
return {
|
||||
html: prunedSections.html,
|
||||
removedCards,
|
||||
removedCards: stripped.removedCards,
|
||||
removedSections: prunedSections.removedSections,
|
||||
};
|
||||
}
|
||||
@@ -353,22 +359,26 @@ export async function enrichNews002HtmlWithSourceImages(
|
||||
output = output.slice(0, card.start) + replacement + output.slice(card.end);
|
||||
}
|
||||
|
||||
const pruned = pruneNews002ImagelessStoryCards(output);
|
||||
const remainingCards = extractNews002StoryCards(pruned.html).filter((card) => card.title);
|
||||
const stripped = stripNews002ImagelessStoryCards(output);
|
||||
const remainingCards = extractNews002StoryCards(stripped.html).filter((card) => card.title);
|
||||
|
||||
return {
|
||||
html: pruned.html,
|
||||
html: stripped.html,
|
||||
cardCount: remainingCards.length,
|
||||
attemptedCount: unresolved.length,
|
||||
resolvedCount,
|
||||
removedCards: pruned.removedCards,
|
||||
removedSections: pruned.removedSections,
|
||||
removedCards: stripped.removedCards,
|
||||
removedSections: 0,
|
||||
};
|
||||
}
|
||||
|
||||
export function auditNews002ImageCoverage(
|
||||
html,
|
||||
{ maxStoryCards = 28, requireTemplateStructure = false } = {},
|
||||
{
|
||||
maxStoryCards = 28,
|
||||
requireTemplateStructure = false,
|
||||
requireAllCoreSections = true,
|
||||
} = {},
|
||||
) {
|
||||
const source = String(html ?? '');
|
||||
const cards = extractNews002StoryCards(source).filter((card) => card.title);
|
||||
@@ -422,8 +432,10 @@ export function auditNews002ImageCoverage(
|
||||
}
|
||||
return false;
|
||||
};
|
||||
const missingSections = requiredSectionIds.filter((id) => !hasNews002Section(id));
|
||||
if (missingSections.length > 0) violations.push(`missing-sections:${missingSections.join('|')}`);
|
||||
if (requireAllCoreSections) {
|
||||
const missingSections = requiredSectionIds.filter((id) => !hasNews002Section(id));
|
||||
if (missingSections.length > 0) violations.push(`missing-sections:${missingSections.join('|')}`);
|
||||
}
|
||||
if (cards.some((card) => !card.eventKey)) violations.push('missing-event-key');
|
||||
if (cards.some((card) => !card.url)) violations.push('missing-source-url');
|
||||
if (cards.some((card) => !/class="[^"]*\bbody\b/i.test(card.raw))) {
|
||||
|
||||
Reference in New Issue
Block a user