feat: 新增风险图谱算法与系统仪表盘及操作反馈体系

后端新增风险图谱算法模块、风险观察与反馈服务、规则 DSL
校验器和可解释性引擎,完善系统仪表盘和财务仪表盘统计,
优化 agent 运行和编排执行链路,清理旧开发文档,前端新增
系统趋势、负载热力图等多种仪表盘图表组件,完善操作反馈
对话框和工作台日期选择器,优化报销创建和审批详情交互,
补充单元测试覆盖。
This commit is contained in:
caoxiaozhu
2026-05-30 15:46:51 +08:00
parent 4c59941ec6
commit 7989f3a159
314 changed files with 30073 additions and 20626 deletions

View File

@@ -18,6 +18,47 @@ const KNOWLEDGE_JOB_TYPES = new Set([
'finance_policy_knowledge_organize'
])
const TASK_TYPE_LABELS = {
global_risk_scan: '财务风险图谱巡检',
employee_behavior_profile_scan: '员工行为画像巡检',
finance_policy_knowledge_organize: '知识制度整理',
knowledge_index_sync: '知识制度整理',
llm_wiki_sync: '知识制度整理',
llm_wiki_rule_formation: '知识制度整理'
}
const TASK_CODE_TO_TYPE = {
'task.hermes.global_risk_scan': 'global_risk_scan',
'task.hermes.employee_behavior_profile_scan': 'employee_behavior_profile_scan',
'task.hermes.finance_policy_knowledge_organize': 'finance_policy_knowledge_organize'
}
function toObject(value) {
return value && typeof value === 'object' && !Array.isArray(value) ? value : {}
}
function normalizeTaskType(value) {
const normalized = String(value || '').trim()
if (!normalized) {
return ''
}
return TASK_CODE_TO_TYPE[normalized] || normalized
}
function resolveTaskTypeFromToolName(value) {
const name = String(value || '').trim()
if (name.includes('financial_risk_graph')) {
return 'global_risk_scan'
}
if (name.includes('employee_behavior_profile')) {
return 'employee_behavior_profile_scan'
}
if (name.includes('finance_policy_knowledge')) {
return 'finance_policy_knowledge_organize'
}
return ''
}
export function formatWorkRecordDateTime(value) {
if (!value) {
return '未结束'
@@ -46,8 +87,89 @@ export function resolveWorkRecordSourceLabel(source) {
return SOURCE_LABELS[source] || source || '未标记'
}
export function resolveWorkRecordTaskType(run) {
const routeJson = toObject(run?.route_json)
const routeCandidates = [
routeJson.job_type,
routeJson.task_type,
routeJson.report_type,
routeJson.task_code
].map(normalizeTaskType)
for (const candidate of routeCandidates) {
if (candidate) {
return candidate
}
}
for (const toolCall of run?.tool_calls || []) {
const requestJson = toObject(toolCall?.request_json)
const responseJson = toObject(toolCall?.response_json)
const candidates = [
requestJson.task_type,
requestJson.job_type,
responseJson.report_type,
responseJson.task_type,
responseJson.job_type,
resolveTaskTypeFromToolName(toolCall?.tool_name)
].map(normalizeTaskType)
const matched = candidates.find(Boolean)
if (matched) {
return matched
}
}
return ''
}
export function resolveWorkRecordTaskLabel(run) {
const taskType = resolveWorkRecordTaskType(run)
return TASK_TYPE_LABELS[taskType] || ''
}
export function resolveWorkRecordProductKind(run) {
const taskType = resolveWorkRecordTaskType(run)
if (taskType === 'global_risk_scan') {
return 'risk_graph'
}
if (taskType === 'employee_behavior_profile_scan') {
return 'employee_profile'
}
if (KNOWLEDGE_JOB_TYPES.has(taskType)) {
return 'knowledge'
}
return ''
}
export function extractWorkRecordToolSummary(run) {
const taskType = resolveWorkRecordTaskType(run)
const toolCalls = Array.isArray(run?.tool_calls) ? run.tool_calls : []
const matchedCall = toolCalls.find((toolCall) => {
const requestJson = toObject(toolCall?.request_json)
const responseJson = toObject(toolCall?.response_json)
const candidates = [
requestJson.task_type,
requestJson.job_type,
responseJson.report_type,
responseJson.task_type,
responseJson.job_type,
resolveTaskTypeFromToolName(toolCall?.tool_name)
].map(normalizeTaskType)
return candidates.includes(taskType)
}) || toolCalls[0]
const responseJson = toObject(matchedCall?.response_json)
const nestedSummary = toObject(responseJson.summary)
return Object.keys(nestedSummary).length ? nestedSummary : responseJson
}
export function resolveWorkRecordModuleLabel(run) {
const routeJson = run?.route_json || {}
const taskLabel = resolveWorkRecordTaskLabel(run)
if (taskLabel) {
return taskLabel
}
if (KNOWLEDGE_JOB_TYPES.has(routeJson.job_type)) {
return '知识制度整理'
}
@@ -62,6 +184,11 @@ export function resolveWorkRecordModuleLabel(run) {
export function resolveWorkRecordTitle(run) {
const routeJson = run?.route_json || {}
const taskLabel = resolveWorkRecordTaskLabel(run)
if (taskLabel) {
const suffix = String(routeJson.task_name || routeJson.folder || '本次运行').trim()
return suffix && suffix !== taskLabel ? `${taskLabel} · ${suffix}` : taskLabel
}
if (KNOWLEDGE_JOB_TYPES.has(routeJson.job_type)) {
return `知识制度整理 · ${routeJson.folder || '未指定目录'}`
}