fix(小宝预警): 保留可复用小宝建议

This commit is contained in:
Script Generator
2026-06-30 12:05:41 +08:00
parent 92e230d303
commit a6a4d7d3d9
2 changed files with 171 additions and 1 deletions

View File

@@ -148,7 +148,8 @@ export function getReusableInsight(
const currentSignature = buildRiskInsightSignature(risk);
return (
findCachedInsight(cache, risk.versionId, currentSignature) ??
cache.find((item) => item.versionId === risk.versionId && normalizeCachedRiskSignature(item.riskSignature) === currentSignature)
cache.find((item) => item.versionId === risk.versionId && normalizeCachedRiskSignature(item.riskSignature) === currentSignature) ??
findLatestDisplayCompatibleInsight(cache, risk.versionId, currentSignature)
);
}
@@ -309,6 +310,103 @@ function normalizeCachedRiskSignature(signature: string): string | undefined {
}
}
function findLatestDisplayCompatibleInsight(
cache: XiaobaoRiskInsightCacheItem[],
versionId: string,
currentSignature: string,
): XiaobaoRiskInsightCacheItem | undefined {
const currentKey = buildDisplayCompatibilityKey(currentSignature);
if (!currentKey) return undefined;
return cache
.filter((item) => item.versionId === versionId && buildDisplayCompatibilityKey(item.riskSignature) === currentKey)
.sort((a, b) => getTime(b.generatedAt) - getTime(a.generatedAt))[0];
}
function buildDisplayCompatibilityKey(signature: string): string | undefined {
try {
const parsed = JSON.parse(signature) as Record<string, unknown>;
const signals = readRecord(parsed.signals);
const trend = readRecord(parsed.trend);
const dailyEvidence = readRecord(parsed.dailyEvidence);
return JSON.stringify({
versionId: readString(parsed.versionId),
riskScore: normalizeInteger(parsed.riskScore),
riskLevel: readString(parsed.riskLevel),
expectedReleaseDate: normalizeDateKey(readString(parsed.expectedReleaseDate)),
delayDays: normalizeInteger(parsed.delayDays),
confidence: normalizeInteger(parsed.confidence),
signals: {
unfinishedCount: normalizeInteger(signals.unfinishedCount),
openBugCount: normalizeInteger(signals.openBugCount),
criticalBugCount: normalizeInteger(signals.criticalBugCount),
failedTestCount: normalizeInteger(signals.failedTestCount),
blockedCount: normalizeInteger(signals.blockedCount),
silentRiskCount: normalizeInteger(signals.silentRiskCount),
},
trend: {
direction: readString(trend.direction),
delta: normalizeInteger(trend.delta),
pattern: readString(trend.pattern),
},
reasons: normalizeCompatibilityReasons(parsed.reasons),
silentRisks: normalizeCompatibilitySilentRisks(parsed.silentRisks),
dailyEvidence: {
todayDeliveries: normalizeCompatibilityEvidence(dailyEvidence.todayDeliveries),
todayProgress: normalizeCompatibilityEvidence(dailyEvidence.todayProgress),
todayRisks: normalizeCompatibilityEvidence(dailyEvidence.todayRisks),
progressNotes: normalizeCompatibilityEvidence(dailyEvidence.progressNotes),
needsProgressItems: normalizeCompatibilityEvidence(dailyEvidence.needsProgressItems),
},
});
} catch {
return undefined;
}
}
function normalizeCompatibilityReasons(rows: unknown) {
return asRecordArray(rows)
.map((row) => ({
key: readString(row.key),
severity: readString(row.severity),
detail: readString(row.detail),
count: normalizeInteger(row.count),
}))
.sort(compareSignatureRows);
}
function normalizeCompatibilitySilentRisks(rows: unknown) {
return asRecordArray(rows)
.map((row) => ({
key: readString(row.key),
itemId: readString(row.itemId),
itemType: readString(row.itemType),
}))
.sort(compareSignatureRows);
}
function normalizeCompatibilityEvidence(rows: unknown) {
return asRecordArray(rows)
.map((row) => ({
id: readString(row.id),
title: readString(row.title),
summary: readString(row.summary),
}))
.sort(compareSignatureRows);
}
function asRecordArray(rows: unknown): Array<Record<string, unknown>> {
return Array.isArray(rows) ? rows.map(readRecord).filter((row) => Object.keys(row).length > 0) : [];
}
function readRecord(value: unknown): Record<string, unknown> {
return value && typeof value === 'object' && !Array.isArray(value) ? value as Record<string, unknown> : {};
}
function readString(value: unknown): string | null {
return typeof value === 'string' ? value : null;
}
function normalizeDateKey(value: string | null | undefined): string | null {
if (!value) return null;
const raw = value.trim();
@@ -325,6 +423,10 @@ function normalizeDaysToExpectedRelease(value: number | null | undefined): numbe
return value >= 0 ? Math.ceil(value) : Math.floor(value);
}
function normalizeInteger(value: unknown): number | null {
return typeof value === 'number' && Number.isFinite(value) ? Math.round(value) : null;
}
function getTime(value: string): number {
const time = new Date(value).getTime();
return Number.isFinite(time) ? time : Number.NaN;