feat: 新增风险图谱算法与系统仪表盘及操作反馈体系
后端新增风险图谱算法模块、风险观察与反馈服务、规则 DSL 校验器和可解释性引擎,完善系统仪表盘和财务仪表盘统计, 优化 agent 运行和编排执行链路,清理旧开发文档,前端新增 系统趋势、负载热力图等多种仪表盘图表组件,完善操作反馈 对话框和工作台日期选择器,优化报销创建和审批详情交互, 补充单元测试覆盖。
This commit is contained in:
@@ -46,6 +46,41 @@ const RADAR_COLORS = [
|
||||
'#db2777'
|
||||
]
|
||||
|
||||
const FINANCIAL_RADAR_CODES = [
|
||||
'expense_intensity',
|
||||
'application_rhythm',
|
||||
'travel_entertainment',
|
||||
'material_completeness',
|
||||
'process_pressure'
|
||||
]
|
||||
|
||||
const GOVERNANCE_RADAR_CODES = [
|
||||
'ai_collaboration',
|
||||
'approval_efficiency',
|
||||
'approval_control'
|
||||
]
|
||||
|
||||
export const USER_PROFILE_RADAR_VIEW_OPTIONS = [
|
||||
{
|
||||
value: 'financial_risk',
|
||||
label: '财务风险视角',
|
||||
shortLabel: '财务风险',
|
||||
description: '费用、材料和流程相关维度'
|
||||
},
|
||||
{
|
||||
value: 'collaboration_governance',
|
||||
label: '协作治理视角',
|
||||
shortLabel: '协作治理',
|
||||
description: 'AI 协作和审批治理维度'
|
||||
},
|
||||
{
|
||||
value: 'all_behavior',
|
||||
label: '全部行为视角',
|
||||
shortLabel: '全部行为',
|
||||
description: '展示全部可用画像维度'
|
||||
}
|
||||
]
|
||||
|
||||
const TAG_ACCENT_COUNT = 8
|
||||
|
||||
const SOURCE_LABELS = {
|
||||
@@ -89,7 +124,7 @@ const JOB_TYPE_LABELS = {
|
||||
llm_wiki_sync: '知识库归纳同步',
|
||||
employee_behavior_profile_scan: '用户画像测算',
|
||||
workbench_on_demand: '工作台画像测算',
|
||||
global_risk_scan: '全局风险巡检',
|
||||
global_risk_scan: '财务风险图谱巡检',
|
||||
weekly_expense_report: '周费用报告'
|
||||
}
|
||||
|
||||
@@ -98,9 +133,7 @@ export function buildUserProfileMetricCards(profile, runs = [], currentUser = {}
|
||||
const aiMetrics = metricsOf(index.ai_usage)
|
||||
const userRuns = filterRunsByCurrentUser(runs, currentUser)
|
||||
const windowedUserRuns = filterRunsByProfileWindow(userRuns, profile)
|
||||
const durationMs = hasProfileDurationMetric(aiMetrics)
|
||||
? resolveNumber(aiMetrics.ai_run_duration_ms)
|
||||
: sumRunDurationMs(windowedUserRuns)
|
||||
const durationMs = resolveUsageDurationMs(aiMetrics, windowedUserRuns)
|
||||
const durationDisplay = formatDurationMetric(durationMs)
|
||||
const commonAgent = resolveCommonAgent(windowedUserRuns)
|
||||
const tokenCount = resolveNumber(aiMetrics.exact_token_count) || resolveNumber(aiMetrics.estimated_token_count)
|
||||
@@ -113,7 +146,7 @@ export function buildUserProfileMetricCards(profile, runs = [], currentUser = {}
|
||||
label: '使用时长',
|
||||
value: durationDisplay.value,
|
||||
unit: durationDisplay.unit,
|
||||
hint: `近${resolveWindowDays(profile)}天智能体运行累计`,
|
||||
hint: resolveUsageDurationHint(aiMetrics, profile),
|
||||
icon: 'mdi mdi-timer-sand',
|
||||
tone: 'primary'
|
||||
},
|
||||
@@ -157,10 +190,12 @@ export function normalizeUserProfileTags(profile, limit = 8) {
|
||||
code: normalizeText(tag.code || tag.label),
|
||||
label: normalizeText(tag.label),
|
||||
displayLabel: normalizeText(tag.display_label || tag.displayLabel || tag.label),
|
||||
category: normalizeCode(tag.category),
|
||||
tone: resolveTagTone(tag),
|
||||
score: clampScore(tag.score),
|
||||
reason: normalizeText(tag.reason) || '画像算法已识别该行为特征。',
|
||||
confidence: resolveNumber(tag.confidence)
|
||||
confidence: resolveNumber(tag.confidence),
|
||||
radarDimensions: normalizeRadarDimensions(tag)
|
||||
}))
|
||||
.filter((tag) => tag.code && tag.displayLabel)
|
||||
.sort((left, right) => right.score - left.score)
|
||||
@@ -194,6 +229,55 @@ export function normalizeUserProfileRadarDimensions(profile) {
|
||||
)
|
||||
}
|
||||
|
||||
export function filterUserProfileRadarDimensions(dimensions, viewKey) {
|
||||
const items = Array.isArray(dimensions) ? dimensions : []
|
||||
const codes = resolveRadarViewCodes(viewKey)
|
||||
if (!codes.length) {
|
||||
return items
|
||||
}
|
||||
|
||||
const filtered = items.filter((item) => codes.includes(normalizeCode(item?.code)))
|
||||
return filtered.length ? filtered : items
|
||||
}
|
||||
|
||||
export function filterUserProfileTagsByRadarView(tags, viewKey) {
|
||||
const items = Array.isArray(tags) ? tags : []
|
||||
const codes = resolveRadarViewCodes(viewKey)
|
||||
if (!codes.length) {
|
||||
return items
|
||||
}
|
||||
|
||||
return items.filter((tag) => {
|
||||
const dimensions = Array.isArray(tag?.radarDimensions) ? tag.radarDimensions : []
|
||||
if (dimensions.some((code) => codes.includes(normalizeCode(code)))) {
|
||||
return true
|
||||
}
|
||||
return resolveFallbackTagRadarCodes(tag).some((code) => codes.includes(code))
|
||||
})
|
||||
}
|
||||
|
||||
export function resolveUserProfileDefaultRadarView(profile) {
|
||||
const profileTypes = new Set(
|
||||
(Array.isArray(profile?.profiles) ? profile.profiles : [])
|
||||
.map((item) => normalizeCode(item?.profile_type))
|
||||
.filter(Boolean)
|
||||
)
|
||||
if (profileTypes.has('expense') || profileTypes.has('process_quality')) {
|
||||
return 'financial_risk'
|
||||
}
|
||||
if (profileTypes.has('ai_usage') || profileTypes.has('approval')) {
|
||||
return 'collaboration_governance'
|
||||
}
|
||||
|
||||
const dimensions = normalizeUserProfileRadarDimensions(profile)
|
||||
const financialScore = sumRadarScores(dimensions, FINANCIAL_RADAR_CODES)
|
||||
const governanceScore = sumRadarScores(dimensions, GOVERNANCE_RADAR_CODES)
|
||||
if (financialScore > 0 || governanceScore > 0) {
|
||||
return financialScore >= governanceScore ? 'financial_risk' : 'collaboration_governance'
|
||||
}
|
||||
return 'all_behavior'
|
||||
}
|
||||
|
||||
export function buildProfileOperationsFromAgentRuns(runs, currentUser, limit = 5) {
|
||||
const identities = resolveCurrentUserIdentities(currentUser)
|
||||
return (Array.isArray(runs) ? runs : [])
|
||||
@@ -227,8 +311,69 @@ function metricsOf(profile) {
|
||||
return profile?.metrics && typeof profile.metrics === 'object' ? profile.metrics : {}
|
||||
}
|
||||
|
||||
function resolveRadarViewCodes(viewKey) {
|
||||
if (viewKey === 'financial_risk') {
|
||||
return FINANCIAL_RADAR_CODES
|
||||
}
|
||||
if (viewKey === 'collaboration_governance') {
|
||||
return GOVERNANCE_RADAR_CODES
|
||||
}
|
||||
return []
|
||||
}
|
||||
|
||||
function resolveFallbackTagRadarCodes(tag) {
|
||||
const category = normalizeCode(tag?.category)
|
||||
if (['expense', 'travel', 'entertainment', 'process'].includes(category)) {
|
||||
return FINANCIAL_RADAR_CODES
|
||||
}
|
||||
if (['ai', 'approval'].includes(category)) {
|
||||
return GOVERNANCE_RADAR_CODES
|
||||
}
|
||||
return []
|
||||
}
|
||||
|
||||
function normalizeRadarDimensions(tag) {
|
||||
const dimensions = Array.isArray(tag?.radar_dimensions)
|
||||
? tag.radar_dimensions
|
||||
: Array.isArray(tag?.radarDimensions)
|
||||
? tag.radarDimensions
|
||||
: []
|
||||
return dimensions.map((item) => normalizeCode(item)).filter(Boolean)
|
||||
}
|
||||
|
||||
function sumRadarScores(dimensions, codes) {
|
||||
const codeSet = new Set(codes)
|
||||
return (Array.isArray(dimensions) ? dimensions : [])
|
||||
.filter((item) => codeSet.has(normalizeCode(item?.code)))
|
||||
.reduce((total, item) => total + clampScore(item?.score), 0)
|
||||
}
|
||||
|
||||
function hasProfileDurationMetric(metrics) {
|
||||
return Object.prototype.hasOwnProperty.call(metrics || {}, 'ai_run_duration_ms')
|
||||
return (
|
||||
Object.prototype.hasOwnProperty.call(metrics || {}, 'usage_duration_ms')
|
||||
|| Object.prototype.hasOwnProperty.call(metrics || {}, 'online_duration_ms')
|
||||
|| Object.prototype.hasOwnProperty.call(metrics || {}, 'ai_run_duration_ms')
|
||||
)
|
||||
}
|
||||
|
||||
function resolveUsageDurationMs(metrics, fallbackRuns) {
|
||||
if (!hasProfileDurationMetric(metrics)) {
|
||||
return sumRunDurationMs(fallbackRuns)
|
||||
}
|
||||
return resolveNumber(metrics.usage_duration_ms)
|
||||
|| resolveNumber(metrics.online_duration_ms)
|
||||
|| resolveNumber(metrics.ai_run_duration_ms)
|
||||
}
|
||||
|
||||
function resolveUsageDurationHint(metrics, profile) {
|
||||
const days = resolveWindowDays(profile)
|
||||
if (normalizeCode(metrics?.usage_duration_mode) === 'online_session') {
|
||||
return `近${days}天在线会话累计`
|
||||
}
|
||||
if (normalizeCode(metrics?.usage_duration_mode) === 'agent_run_fallback') {
|
||||
return `近${days}天智能体运行累计`
|
||||
}
|
||||
return `近${days}天使用行为累计`
|
||||
}
|
||||
|
||||
function filterRunsByCurrentUser(runs, currentUser) {
|
||||
|
||||
Reference in New Issue
Block a user