import { apiRequest } from './api.js' function toNumber(value, fallback = 0) { const number = Number(value) return Number.isFinite(number) ? number : fallback } function toArray(value) { return Array.isArray(value) ? value : [] } function toObject(value) { return value && typeof value === 'object' && !Array.isArray(value) ? value : {} } export function normalizeRiskObservation(item = {}) { return { id: String(item.id || '').trim(), observationKey: String(item.observation_key || item.observationKey || '').trim(), claimId: String(item.claim_id || item.claimId || '').trim(), claimNo: String(item.claim_no || item.claimNo || '').trim(), riskType: String(item.risk_type || item.riskType || '').trim(), riskSignal: String(item.risk_signal || item.riskSignal || '').trim(), title: String(item.title || '').trim(), description: String(item.description || '').trim(), riskScore: toNumber(item.risk_score ?? item.riskScore), riskLevel: String(item.risk_level || item.riskLevel || '').trim(), confidenceScore: toNumber(item.confidence_score ?? item.confidenceScore), controlStage: String(item.control_stage || item.controlStage || '').trim(), controlMode: String(item.control_mode || item.controlMode || '').trim(), automationMode: String(item.automation_mode || item.automationMode || '').trim(), source: String(item.source || '').trim(), algorithmVersion: String(item.algorithm_version || item.algorithmVersion || '').trim(), status: String(item.status || '').trim(), feedbackStatus: String(item.feedback_status || item.feedbackStatus || '').trim(), contributionScores: toObject(item.contribution_scores || item.contributionScores), baseline: toObject(item.baseline), evidence: toArray(item.evidence), graphNodeKeys: toArray(item.graph_node_keys || item.graphNodeKeys), graphEdgeKeys: toArray(item.graph_edge_keys || item.graphEdgeKeys), policyRefs: toArray(item.policy_refs || item.policyRefs), similarCaseClaimIds: toArray(item.similar_case_claim_ids || item.similarCaseClaimIds), ontology: toObject(item.ontology_json || item.ontologyJson), decisionTrace: toObject(item.decision_trace || item.decisionTrace), feedbackItems: toArray(item.feedback_items || item.feedbackItems), createdAt: String(item.created_at || item.createdAt || '').trim(), updatedAt: String(item.updated_at || item.updatedAt || '').trim() } } export function normalizeRiskObservationDashboard(payload = {}) { return { windowDays: toNumber(payload.window_days ?? payload.windowDays, 30), totalObservations: toNumber(payload.total_observations ?? payload.totalObservations), pendingCount: toNumber(payload.pending_count ?? payload.pendingCount), highOrAboveCount: toNumber(payload.high_or_above_count ?? payload.highOrAboveCount), confirmedCount: toNumber(payload.confirmed_count ?? payload.confirmedCount), falsePositiveCount: toNumber(payload.false_positive_count ?? payload.falsePositiveCount), totalAmount: toNumber(payload.total_amount ?? payload.totalAmount), averageScore: toNumber(payload.average_score ?? payload.averageScore), confirmationRate: toNumber(payload.confirmation_rate ?? payload.confirmationRate), falsePositiveRate: toNumber(payload.false_positive_rate ?? payload.falsePositiveRate), candidateRuleCount: toNumber(payload.candidate_rule_count ?? payload.candidateRuleCount), levelDistribution: toObject(payload.level_distribution || payload.levelDistribution), statusDistribution: toObject(payload.status_distribution || payload.statusDistribution), signalDistribution: toObject(payload.signal_distribution || payload.signalDistribution), sourceDistribution: toObject(payload.source_distribution || payload.sourceDistribution), automationDistribution: toObject( payload.automation_distribution || payload.automationDistribution ), departmentDistribution: toObject( payload.department_distribution || payload.departmentDistribution ), expenseTypeDistribution: toObject( payload.expense_type_distribution || payload.expenseTypeDistribution ), riskTypeDistribution: toObject(payload.risk_type_distribution || payload.riskTypeDistribution), supplierDistribution: toObject(payload.supplier_distribution || payload.supplierDistribution), employeeGradeDistribution: toObject( payload.employee_grade_distribution || payload.employeeGradeDistribution ), dailyTrend: toArray(payload.daily_trend || payload.dailyTrend), topRiskSignals: toArray(payload.top_risk_signals || payload.topRiskSignals), topDepartments: toArray(payload.top_departments || payload.topDepartments), topEmployees: toArray(payload.top_employees || payload.topEmployees), topSuppliers: toArray(payload.top_suppliers || payload.topSuppliers), topExpenseTypes: toArray(payload.top_expense_types || payload.topExpenseTypes), topRules: toArray(payload.top_rules || payload.topRules), recentHighObservations: toArray( payload.recent_high_observations || payload.recentHighObservations ).map(normalizeRiskObservation) } } export async function fetchRiskObservationDashboard(options = {}) { const windowDays = Math.max(1, Math.min(toNumber(options.windowDays || 30), 365)) const limit = Math.max(1, Math.min(toNumber(options.limit || 500), 2000)) const search = new URLSearchParams() search.set('window_days', String(windowDays)) search.set('limit', String(limit)) const payload = await apiRequest(`/risk-observations/dashboard?${search.toString()}`, { timeoutMs: toNumber(options.timeoutMs || 3500), timeoutMessage: '风险看板数据加载超时,请稍后重试。' }) return normalizeRiskObservationDashboard(payload) } export async function fetchClaimRiskObservations(claimId, options = {}) { const normalizedClaimId = String(claimId || '').trim() if (!normalizedClaimId) { return [] } const payload = await apiRequest( `/risk-observations/claim/${encodeURIComponent(normalizedClaimId)}`, { timeoutMs: toNumber(options.timeoutMs || 3500), timeoutMessage: '风险证据链加载超时,请稍后重试。' } ) return toArray(payload).map(normalizeRiskObservation) } export async function fetchRunRiskObservations(runId, options = {}) { const normalizedRunId = String(runId || '').trim() if (!normalizedRunId) { return [] } const search = new URLSearchParams() search.set('run_id', normalizedRunId) search.set('limit', String(Math.max(1, Math.min(toNumber(options.limit || 100), 200)))) const payload = await apiRequest(`/risk-observations?${search.toString()}`, { timeoutMs: toNumber(options.timeoutMs || 3500), timeoutMessage: '本次运行风险观察加载超时,请稍后重试。' }) return toArray(payload.items || payload).map(normalizeRiskObservation) }