fix(小宝预警): 保留可复用小宝建议
This commit is contained in:
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user