import type { VersionWithContext } from './derive'; import type { XiaobaoRiskInsight } from './xiaobao-risk-cache'; import type { XiaobaoVersionRisk } from './xiaobao-risk'; import { WORK_HOURS } from './work-hours'; const UNFINISHED_VERSION_STATUSES = new Set(['planned', 'developing', 'paused']); const HIGH_RISK_LEVELS = new Set(['at_risk', 'likely_delayed', 'blocked']); const PAGE_REFRESH_ADVICE_PATTERNS = [ /(刷新|重新加载|重载).*(页面|浏览器|小宝|预警)/i, /(页面|浏览器|小宝|预警).*(刷新|重新加载|重载)/i, /(手动|主动).*(触发|刷新).*(更新|预警|分析)/i, /(manual|manually).*(refresh|reload|trigger)/i, /(refresh|reload).*(page|browser|xiaobao|warning)/i, ]; export interface XiaobaoWarningVersionFilter { canManage: boolean; userName?: string; } export type XiaobaoWarningRiskFilter = 'all' | 'attention' | 'high'; export interface XiaobaoWarningRiskListFilter { riskFilter?: XiaobaoWarningRiskFilter; productId?: string; projectId?: string; } export function filterXiaobaoWarningVersions( versions: VersionWithContext[], filter: XiaobaoWarningVersionFilter, ): VersionWithContext[] { return versions.filter((version) => { if (!UNFINISHED_VERSION_STATUSES.has(version.status)) return false; if (filter.canManage) return true; if (!filter.userName) return false; return (version.members ?? []).some((member) => member.name === filter.userName); }); } export function filterXiaobaoRiskWarnings( risks: XiaobaoVersionRisk[], filter: XiaobaoWarningRiskListFilter = {}, ): XiaobaoVersionRisk[] { return risks.filter((risk) => { if (risk.riskLevel === 'on_track') return false; if (filter.productId && risk.productId !== filter.productId) return false; if (filter.projectId && risk.projectId !== filter.projectId) return false; if (filter.riskFilter === 'attention') return true; if (filter.riskFilter === 'high') return HIGH_RISK_LEVELS.has(risk.riskLevel); return true; }); } export function getXiaobaoWarningRiskCount(risks: XiaobaoVersionRisk[]): number { return filterXiaobaoRiskWarnings(risks).length; } export function formatRemainingWork(hours: number): string { const safeHours = Number.isFinite(hours) && hours > 0 ? hours : 0; const days = safeHours / WORK_HOURS.hoursPerDay; return `${formatNumber(safeHours)}h / ${formatNumber(days)}天`; } export function sanitizeRiskInsight(insight: XiaobaoRiskInsight): XiaobaoRiskInsight { return { ...insight, suggestedActions: insight.suggestedActions.filter((action) => !isPageRefreshAdvice(action)), ownerHints: insight.ownerHints.filter((hint) => !isPageRefreshAdvice(hint)), }; } function formatNumber(value: number): string { if (Number.isInteger(value)) return String(value); return value.toFixed(1).replace(/\.0$/, ''); } function isPageRefreshAdvice(text: string): boolean { return PAGE_REFRESH_ADVICE_PATTERNS.some((pattern) => pattern.test(text)); }