const NO_RISK_SUMMARY_VALUES = new Set(['无', '暂无异常', '无异常', '暂无风险']) const NON_RISK_SOURCES = new Set([ 'manual_approval', 'finance_approval', 'approval', 'approval_log', 'expense_claim_approval', 'expense_claim_finance_approval', 'payment' ]) const NON_RISK_EVENTS = new Set([ 'expense_claim_approval', 'expense_claim_finance_approval', 'expense_claim_payment_completed' ]) const NON_RISK_TONES = new Set(['info', 'pass', 'success', 'approved', 'ok', 'none']) const RISK_SOURCES = new Set([ 'attachment_analysis', 'submission_review', 'manual_return', 'platform_risk', 'policy_review', 'scene_policy_review' ]) function normalizeText(value) { return String(value || '').trim() } function normalizeKey(value) { return normalizeText(value).toLowerCase() } function isApprovalOnlyText(value) { const text = normalizeText(value) if (!text) { return true } return ( /^(同意|通过|审批通过|审核通过|已同意|无意见)$/.test(text) || /已审批通过/.test(text) || /已完成财务审核/.test(text) || /进入待付款/.test(text) || /已确认付款/.test(text) || /进入归档入账/.test(text) || /流转至/.test(text) ) } export function normalizeRiskFlagTone(flag) { if (!flag || typeof flag !== 'object') { return normalizeText(flag) ? 'medium' : 'none' } const tone = normalizeKey(flag.severity || flag.tone || flag.level || flag.riskTone || flag.risk_tone) if (['high', 'medium', 'low'].includes(tone)) { return tone } if (NON_RISK_TONES.has(tone)) { return 'none' } const source = normalizeKey(flag.source) if (source === 'manual_return') { return 'medium' } if (RISK_SOURCES.has(source)) { return 'medium' } const riskText = normalizeText(flag.message || flag.reason || flag.summary || flag.label || flag.description || flag.title) if (riskText && !isApprovalOnlyText(riskText)) { return 'medium' } return 'none' } export function isActionableRiskFlag(flag) { if (!flag || typeof flag !== 'object') { const text = normalizeText(flag) return Boolean(text && !isApprovalOnlyText(text)) } const source = normalizeKey(flag.source) const eventType = normalizeKey(flag.event_type || flag.eventType) if (NON_RISK_SOURCES.has(source) || NON_RISK_EVENTS.has(eventType)) { return false } const tone = normalizeRiskFlagTone(flag) if (tone === 'high' || tone === 'medium' || tone === 'low') { return true } return false } export function filterActionableRiskFlags(riskFlags) { return (Array.isArray(riskFlags) ? riskFlags : []).filter((flag) => isActionableRiskFlag(flag)) } export function isRiskSummaryWithRisk(riskSummary) { const summary = normalizeText(riskSummary) if (!summary || NO_RISK_SUMMARY_VALUES.has(summary) || isApprovalOnlyText(summary)) { return false } return true }