后端新增规则资产版本管理和规则文件 CRUD 接口,优化风险 规则生成模板执行和员工数据模型字段,知识库 RAG 增强本 地回退和文档提取能力,清理旧风险规则文件统一由生成引擎 管理,前端审计页面增加运行时调试面板和规则资产编辑交互, 补充单元测试覆盖。
181 lines
5.8 KiB
JavaScript
181 lines
5.8 KiB
JavaScript
export const RISK_RULE_CREATE_DOMAIN_OPTIONS = [
|
|
{ value: 'expense', label: '报销' },
|
|
{ value: 'ar', label: '应收' },
|
|
{ value: 'ap', label: '应付' }
|
|
]
|
|
|
|
export const RISK_RULE_EXPENSE_CATEGORY_OPTIONS = [
|
|
{ value: 'travel', label: '差旅费' },
|
|
{ value: 'hotel', label: '住宿费' },
|
|
{ value: 'transport', label: '交通费' },
|
|
{ value: 'meal', label: '业务招待费' },
|
|
{ value: 'meeting', label: '会务费' },
|
|
{ value: 'office', label: '办公用品费' },
|
|
{ value: 'training', label: '培训费' },
|
|
{ value: 'communication', label: '通讯费' },
|
|
{ value: 'welfare', label: '福利费' }
|
|
]
|
|
|
|
export const RISK_RULE_LEVEL_OPTIONS = [
|
|
{ value: 'medium', label: '中风险' },
|
|
{ value: 'high', label: '高风险' },
|
|
{ value: 'low', label: '低风险' }
|
|
]
|
|
|
|
const RISK_LEVEL_LABELS = {
|
|
low: '低风险',
|
|
medium: '中风险',
|
|
high: '高风险'
|
|
}
|
|
|
|
export function createDefaultRiskRuleForm() {
|
|
return {
|
|
business_domain: 'expense',
|
|
expense_category: 'travel',
|
|
risk_level: 'medium',
|
|
requires_attachment: false,
|
|
natural_language: ''
|
|
}
|
|
}
|
|
|
|
export function normalizeRiskRuleText(value) {
|
|
return String(value || '').trim()
|
|
}
|
|
|
|
export function formatRiskRuleFieldDisplay(field) {
|
|
const key = normalizeRiskRuleText(field?.key)
|
|
const label = normalizeRiskRuleText(field?.label || key)
|
|
if (label && key && label !== key) {
|
|
return `${label}[${key}]`
|
|
}
|
|
return label || key
|
|
}
|
|
|
|
export function resolveRiskRuleSeverity(payload) {
|
|
const outcomes = payload && typeof payload === 'object' ? payload.outcomes || {} : {}
|
|
const fail = outcomes && typeof outcomes.fail === 'object' ? outcomes.fail : {}
|
|
const severity = normalizeRiskRuleText(fail.severity || payload?.severity).toLowerCase()
|
|
return ['low', 'medium', 'high'].includes(severity) ? severity : 'medium'
|
|
}
|
|
|
|
export function resolveRiskRuleSeverityLabel(payload) {
|
|
return RISK_LEVEL_LABELS[resolveRiskRuleSeverity(payload)] || '中风险'
|
|
}
|
|
|
|
export function resolveRiskRuleFields(payload) {
|
|
const inputs = payload && typeof payload === 'object' ? payload.inputs || {} : {}
|
|
const fieldRows = Array.isArray(inputs.fields) ? inputs.fields : []
|
|
if (fieldRows.length) {
|
|
return fieldRows
|
|
.map((item) => ({
|
|
key: normalizeRiskRuleText(item?.key),
|
|
label: normalizeRiskRuleText(item?.label || item?.key),
|
|
display: formatRiskRuleFieldDisplay(item),
|
|
source: normalizeRiskRuleText(item?.source),
|
|
type: normalizeRiskRuleText(item?.type)
|
|
}))
|
|
.filter((item) => item.key || item.label)
|
|
}
|
|
|
|
return Object.entries(inputs)
|
|
.map(([label, key]) => ({
|
|
key: normalizeRiskRuleText(key),
|
|
label: normalizeRiskRuleText(label),
|
|
display: formatRiskRuleFieldDisplay({ key, label }),
|
|
source: '',
|
|
type: ''
|
|
}))
|
|
.filter((item) => item.key || item.label)
|
|
}
|
|
|
|
export function buildRiskRuleFieldSummary(fields) {
|
|
const labels = fields.map(formatRiskRuleFieldDisplay).filter(Boolean)
|
|
if (!labels.length) {
|
|
return '未识别字段'
|
|
}
|
|
if (labels.length <= 4) {
|
|
return labels.join('、')
|
|
}
|
|
return `${labels.slice(0, 4).join('、')} 等 ${labels.length} 项`
|
|
}
|
|
|
|
export function resolveRiskRuleCreatedAt(payload, fallback) {
|
|
const metadata = payload && typeof payload === 'object' ? payload.metadata || {} : {}
|
|
return normalizeRiskRuleText(metadata.created_at || fallback)
|
|
}
|
|
|
|
export function formatRiskRuleAge(value) {
|
|
const date = new Date(value)
|
|
if (Number.isNaN(date.getTime())) {
|
|
return '未记录'
|
|
}
|
|
const diffMs = Date.now() - date.getTime()
|
|
if (diffMs < 0) {
|
|
return '刚刚创建'
|
|
}
|
|
const minutes = Math.floor(diffMs / 60000)
|
|
if (minutes < 1) {
|
|
return '刚刚创建'
|
|
}
|
|
if (minutes < 60) {
|
|
return `${minutes} 分钟`
|
|
}
|
|
const hours = Math.floor(minutes / 60)
|
|
if (hours < 24) {
|
|
return `${hours} 小时`
|
|
}
|
|
const days = Math.floor(hours / 24)
|
|
if (days < 30) {
|
|
return `${days} 天`
|
|
}
|
|
const months = Math.floor(days / 30)
|
|
if (months < 12) {
|
|
return `${months} 个月`
|
|
}
|
|
return `${Math.floor(months / 12)} 年`
|
|
}
|
|
|
|
export function resolveRiskRuleBusinessDescription(payload, fallback) {
|
|
const metadata = payload && typeof payload === 'object' ? payload.metadata || {} : {}
|
|
return (
|
|
normalizeRiskRuleText(metadata.business_explanation) ||
|
|
normalizeRiskRuleText(payload?.description) ||
|
|
normalizeRiskRuleText(fallback)
|
|
)
|
|
}
|
|
|
|
export function resolveRiskRuleFlowDiagramSvg(payload) {
|
|
const metadata = payload && typeof payload === 'object' ? payload.metadata || {} : {}
|
|
return (
|
|
normalizeRiskRuleText(payload?.flow_diagram_svg) ||
|
|
normalizeRiskRuleText(metadata.flow_diagram_svg)
|
|
)
|
|
}
|
|
|
|
export function resolveRiskRuleConditionSummary(payload) {
|
|
const metadata = payload && typeof payload === 'object' ? payload.metadata || {} : {}
|
|
const params = payload && typeof payload === 'object' ? payload.params || {} : {}
|
|
return (
|
|
normalizeRiskRuleText(metadata.condition_summary) ||
|
|
normalizeRiskRuleText(params.condition_summary) ||
|
|
'根据规则字段判断是否命中风险'
|
|
)
|
|
}
|
|
|
|
export function resolveRiskRuleFlow(payload, fields) {
|
|
const metadata = payload && typeof payload === 'object' ? payload.metadata || {} : {}
|
|
const flow = metadata && typeof metadata.flow === 'object' ? metadata.flow : {}
|
|
const fieldSummary = buildRiskRuleFieldSummary(fields)
|
|
const conditionSummary = resolveRiskRuleConditionSummary(payload)
|
|
const severityLabel = resolveRiskRuleSeverityLabel(payload)
|
|
|
|
return {
|
|
start: normalizeRiskRuleText(flow.start) || '业务单据提交',
|
|
evidence: normalizeRiskRuleText(flow.evidence) || `读取 ${fieldSummary}`,
|
|
decision: normalizeRiskRuleText(flow.decision) || conditionSummary,
|
|
basis: conditionSummary,
|
|
pass: normalizeRiskRuleText(flow.pass) || '未命中风险,继续流转',
|
|
fail: normalizeRiskRuleText(flow.fail) || `命中${severityLabel},进入人工复核`
|
|
}
|
|
}
|