feat(approval): add safe risk disposition workflow

This commit is contained in:
caoxiaozhu
2026-07-16 15:34:58 +08:00
parent ee88a36baf
commit 4940ebc419
64 changed files with 6602 additions and 631 deletions

View File

@@ -0,0 +1,66 @@
import assert from 'node:assert/strict'
import { readFileSync } from 'node:fs'
import test from 'node:test'
import { fileURLToPath } from 'node:url'
import {
createRiskDispositionRequestId,
normalizeRiskDisposition,
normalizeRiskObservation
} from '../src/services/riskObservations.js'
const evidenceCard = readFileSync(
fileURLToPath(new URL('../src/components/travel/RiskObservationEvidenceCard.vue', import.meta.url)),
'utf8'
)
test('risk observation normalizes persisted json evidence and typed disposition', () => {
const observation = normalizeRiskObservation({
id: 'risk-1',
contribution_scores_json: { S_rule: 90 },
evidence_json: [{ title: '重复票据' }],
graph_node_keys_json: ['claim:1'],
similar_case_claim_ids_json: ['redacted-for-reviewer-only'],
decision_trace_json: { action: 'manual_review' },
disposition: {
id: 'disposition-1',
observation_id: 'risk-1',
adjudication: 'confirmed',
lifecycle_status: 'remediation_in_progress',
version: 2,
events: [{ id: 'event-1', action: 'confirm', version: 1 }]
}
})
assert.equal(observation.contributionScores.S_rule, 90)
assert.equal(observation.evidence[0].title, '重复票据')
assert.deepEqual(observation.graphNodeKeys, ['claim:1'])
assert.equal(observation.decisionTrace.action, 'manual_review')
assert.equal(observation.disposition.lifecycleStatus, 'remediation_in_progress')
assert.equal(observation.disposition.events[0].action, 'confirm')
})
test('risk disposition normalizer rejects absent projection and keeps version', () => {
assert.equal(normalizeRiskDisposition(null), null)
assert.equal(normalizeRiskDisposition({}), null)
assert.equal(normalizeRiskDisposition({ id: 'd1', version: 4 }).version, 4)
})
test('risk evidence card exposes typed actions and never offers direct approval', () => {
assert.match(evidenceCard, /executeRiskDispositionAction/)
assert.match(evidenceCard, /确认风险/)
assert.match(evidenceCard, /标记误报/)
assert.match(evidenceCard, /请求补材料/)
assert.match(evidenceCard, /启动整改/)
assert.match(evidenceCard, /申请豁免/)
assert.match(evidenceCard, /确认已解决/)
assert.match(evidenceCard, /expectedVersion: currentDisposition\.value\.version/)
assert.doesNotMatch(evidenceCard, /自动审批|直接通过|batchApprove/)
assert.match(evidenceCard, /RISK_DISPOSITION_VERSION_CONFLICT/)
assert.match(evidenceCard, /lifecycle !== 'supplement_requested'/)
})
test('risk action request ids bind action and observation', () => {
const requestId = createRiskDispositionRequestId('confirm', 'risk-1')
assert.match(requestId, /^risk:confirm:risk-1:/)
})