feat: 增强规则资产管理与审计页面运行时调试

后端新增规则资产版本管理和规则文件 CRUD 接口,优化风险
规则生成模板执行和员工数据模型字段,知识库 RAG 增强本
地回退和文档提取能力,清理旧风险规则文件统一由生成引擎
管理,前端审计页面增加运行时调试面板和规则资产编辑交互,
补充单元测试覆盖。
This commit is contained in:
caoxiaozhu
2026-05-24 21:44:17 +08:00
parent 575f093c74
commit 50b1c3f9a9
113 changed files with 13896 additions and 5044 deletions

View File

@@ -34,6 +34,7 @@ import {
export {
DETAIL_TITLES,
DOMAIN_LABELS,
ENABLED_STATE_OPTIONS,
EXPENSE_RULE_BLOCK_PATTERN,
JSON_RISK_DETAIL_MODE,
LEGACY_RISK_SCENARIO_KEYS,
@@ -43,6 +44,7 @@ export {
REVIEW_META,
RISK_SCENARIO_OPTIONS,
RISK_SCENARIO_VALUES,
RISK_RULE_TABLE_COLUMNS,
RULE_SPREADSHEET_BLOCK_PATTERN,
RULE_TABLE_COLUMNS,
RULE_TAB_TAG_ALIASES,
@@ -51,6 +53,7 @@ export {
SPREADSHEET_DETAIL_MODE,
STATUS_META,
STATUS_OPTIONS,
ONLINE_STATE_OPTIONS,
TAB_META,
TYPE_META,
VERSION_STATE_META
@@ -189,6 +192,17 @@ export function readConfigJson(value) {
return {}
}
export function resolveRiskRuleEnabled(source, rulePayload = null) {
const configJson = readConfigJson(source)
if (isPlainObject(rulePayload) && rulePayload.enabled === false) {
return false
}
if (source?.enabled === false || configJson.enabled === false) {
return false
}
return true
}
export function readRuleDocumentMeta(value) {
const configJson = readConfigJson(value)
return isPlainObject(configJson.rule_document) ? configJson.rule_document : null
@@ -417,6 +431,12 @@ export function buildRiskListSubtitle(text, maxLength = 42) {
export function applyRiskRuleJsonState(target, payload, apiPayload) {
const rulePayload = isPlainObject(payload) ? payload : {}
const metadata = rulePayload.metadata && typeof rulePayload.metadata === 'object'
? rulePayload.metadata
: {}
const apiConfig = apiPayload?.config_json && typeof apiPayload.config_json === 'object'
? apiPayload.config_json
: {}
const fullDescription =
resolveRiskRuleDescription(rulePayload) ||
normalizeText(apiPayload?.description) ||
@@ -427,6 +447,21 @@ export function applyRiskRuleJsonState(target, payload, apiPayload) {
const riskRuleFields = resolveRiskRuleFields(rulePayload)
const riskRuleCreatedAt = resolveRiskRuleCreatedAt(rulePayload, target.createdAt || target.updatedAt)
const statusValue = apiPayload?.status || target.statusValue || 'draft'
const isOnlineLabel = statusValue === 'active' ? '是' : '否'
const isEnabledValue = resolveRiskRuleEnabled(target, rulePayload)
const publisher = apiPayload?.created_by || target.publisher || (apiPayload?.recent_versions && apiPayload.recent_versions[0]?.created_by) || '系统管理员'
let publishedAt = target.publishedAt || '-'
if (apiPayload?.recent_versions) {
const history = buildHistory(apiPayload.recent_versions, { ...target, config_json: payload })
const publishedVersionObj = history.find((item) => item.isPublished || item.lifecycleState === 'published')
publishedAt = publishedVersionObj ? publishedVersionObj.time : (apiPayload?.latest_review?.reviewed_at ? formatDateTime(apiPayload.latest_review.reviewed_at) : '-')
} else if (apiPayload?.latest_review?.reviewed_at) {
publishedAt = formatDateTime(apiPayload.latest_review.reviewed_at)
}
return {
...target,
riskRuleDescription: fullDescription,
@@ -444,6 +479,12 @@ export function applyRiskRuleJsonState(target, payload, apiPayload) {
riskRuleFlow: resolveRiskRuleFlow(rulePayload, riskRuleFields),
riskRuleFlowDiagramSvg:
normalizeText(apiPayload?.flow_diagram_svg) || resolveRiskRuleFlowDiagramSvg(rulePayload),
riskRuleRequiresAttachment: Boolean(
rulePayload.requires_attachment ||
metadata.requires_attachment ||
apiConfig.requires_attachment ||
target.configJson?.requires_attachment
),
riskRuleSummary: {
name: apiPayload?.name || target.name,
evaluator: apiPayload?.evaluator || rulePayload.evaluator || '',
@@ -451,7 +492,13 @@ export function applyRiskRuleJsonState(target, payload, apiPayload) {
inputs: apiPayload?.inputs || rulePayload.inputs || {},
outcomes: apiPayload?.outcomes || rulePayload.outcomes || {}
},
riskRuleJsonText: JSON.stringify(rulePayload, null, 2)
riskRuleJsonText: JSON.stringify(rulePayload, null, 2),
isOnlineLabel,
isEnabledValue,
isEnabledLabel: isEnabledValue ? '是' : '否',
isEnabledTone: isEnabledValue ? 'success' : 'disabled',
publisher,
publishedAt
}
}
@@ -810,6 +857,15 @@ export function buildListItem(asset) {
const listSubtitle = isRiskRule
? buildRiskListSubtitle(asset.description)
: normalizeText(asset.description)
const isOnlineValue = asset.status === 'active'
const isEnabledValue = usesJsonRiskRule ? resolveRiskRuleEnabled(asset) : true
const reviewer = normalizeText(asset.reviewer) || '待分配'
const publisher = isRiskRule
? isOnlineValue
? normalizeText(asset.published_by) || reviewer || modifiedBy || '系统管理员'
: '-'
: ''
const publishedAt = isRiskRule && isOnlineValue ? formatDateTime(asset.published_at || asset.updated_at) : '-'
return {
id: asset.id,
@@ -826,8 +882,8 @@ export function buildListItem(asset) {
summary: listSubtitle,
listSubtitle,
category: resolveDomainLabel(asset.domain),
owner: asset.owner,
reviewer: asset.reviewer || '待分配',
owner: isRiskRule ? reviewer : asset.owner,
reviewer,
scope: typeKey === 'rules' ? ruleScenarioCategory || '通用' : formatScenarioList(asset.scenario_json),
riskCategory: ruleScenarioCategory,
model: buildRowRuntime(asset, typeKey),
@@ -838,10 +894,18 @@ export function buildListItem(asset) {
status: statusMeta.label,
statusValue: asset.status,
statusTone: statusMeta.tone,
hitRate: buildRowMetric({ ...asset, modified_by: modifiedBy }, typeKey),
hitRate: isRiskRule ? publisher : buildRowMetric({ ...asset, modified_by: modifiedBy }, typeKey),
publisher,
publishedAt,
isOnlineValue,
isOnlineLabel: isOnlineValue ? '是' : '否',
isOnlineTone: isOnlineValue ? 'success' : 'disabled',
isEnabledValue,
isEnabledLabel: isEnabledValue ? '是' : '否',
isEnabledTone: isEnabledValue ? 'success' : 'disabled',
modifiedBy,
changeCount,
updatedAt: formatDateTime(asset.updated_at),
updatedAt: isRiskRule ? publishedAt : formatDateTime(asset.updated_at),
badgeTone: tabMeta.badgeTone,
domainValue: asset.domain
}
@@ -1218,6 +1282,7 @@ export function buildDetailViewModel(detail, runs) {
const ruleTemplateLabel = normalizeText(configJson.rule_template_label) || resolveRuleTemplateLabel(ruleTemplateKey)
const runtimeKind = normalizeText(configJson.runtime_kind || previewRuntimeRule.kind) || 'policy_rule_draft'
const ruleScenarioCategory = typeKey === 'rules' ? resolveRuleScenarioCategory(detail, tabId) : ''
const isEnabledValue = usesJsonRiskRule ? resolveRiskRuleEnabled(detail) : true
return {
id: detail.id,
@@ -1258,10 +1323,28 @@ export function buildDetailViewModel(detail, runs) {
riskRuleSeverityLabel: '中风险',
riskRuleCreatedAt: formatDateTime(detail.created_at),
riskRuleAgeLabel: formatRiskRuleAge(detail.created_at),
isOnlineLabel: detail.status === 'active' ? '是' : '否',
isEnabledValue,
isEnabledLabel: isEnabledValue ? '是' : '否',
isEnabledTone: isEnabledValue ? 'success' : 'disabled',
publisher:
detail.status === 'active'
? normalizeText(detail.published_by) ||
detail.latest_review?.reviewer ||
detail.reviewer ||
(detail.recent_versions && detail.recent_versions[0]?.created_by) ||
'系统管理员'
: '-',
publishedAt:
history.find((item) => item.isPublished || item.lifecycleState === 'published')?.time ||
(detail.published_at ? formatDateTime(detail.published_at) : '') ||
(detail.latest_review?.reviewed_at ? formatDateTime(detail.latest_review.reviewed_at) : '-'),
riskRuleFields: [],
riskRuleFieldSummary: '未识别字段',
riskRuleFlow: resolveRiskRuleFlow({}, []),
riskRuleFlowDiagramSvg: normalizeText(configJson.flow_diagram_svg),
riskRuleRequiresAttachment: Boolean(configJson.requires_attachment),
latestTestSummary: detail.latest_test_summary || detail.latestTestSummary || null,
riskCategory: typeKey === 'rules' ? ruleScenarioCategory : '',
ruleDocument,
scenarioList: typeKey === 'rules' && ruleScenarioCategory