feat: 新增风险图谱算法与系统仪表盘及操作反馈体系
后端新增风险图谱算法模块、风险观察与反馈服务、规则 DSL 校验器和可解释性引擎,完善系统仪表盘和财务仪表盘统计, 优化 agent 运行和编排执行链路,清理旧开发文档,前端新增 系统趋势、负载热力图等多种仪表盘图表组件,完善操作反馈 对话框和工作台日期选择器,优化报销创建和审批详情交互, 补充单元测试覆盖。
This commit is contained in:
@@ -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 || '未指定目录'}`
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user