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

@@ -1,4 +1,5 @@
import { buildApplicationFieldsFromOntology } from './expenseApplicationOntology.js'
import { getTodayDateValue } from './workbenchComposerDate.js'
const APPLICATION_SESSION_TYPE = 'application'
const APPLICATION_CREATE_PATTERN = /申请|事前|前置|预算|出差|差旅|采购|会务|会议|培训|办公用品|交通|住宿/
@@ -54,11 +55,11 @@ function formatIsoDate(date) {
}
function buildEndDateFromDays(startText, daysText = '') {
const days = Number(String(daysText || '').replace(/[^\d]/g, ''))
const days = parseApplicationDaysValue(daysText)
const start = parseIsoDate(startText)
if (!days || !start) return ''
const end = new Date(start.getTime())
end.setUTCDate(end.getUTCDate() + days)
end.setUTCDate(end.getUTCDate() + Math.max(days - 1, 0))
return formatIsoDate(end)
}
@@ -69,7 +70,16 @@ function resolveDaysFromDateRange(rangeText) {
const end = parseIsoDate(match[2])
if (!start || !end) return ''
const diffDays = Math.round((end.getTime() - start.getTime()) / 86400000)
return diffDays > 0 ? `${diffDays}` : '1天'
return diffDays >= 0 ? `${diffDays + 1}` : ''
}
function resolvePreviewToday(options = {}) {
const explicitToday = String(options.today || options.currentDate || '').trim()
if (parseIsoDate(explicitToday)) return normalizeDateText(explicitToday)
if (options.now instanceof Date && !Number.isNaN(options.now.getTime())) {
return getTodayDateValue(options.now)
}
return getTodayDateValue()
}
function resolveApplicationType(text) {
@@ -106,10 +116,35 @@ function resolveCurrentUserGrade(currentUser = {}) {
function parseApplicationDaysValue(value) {
const match = String(value || '').match(/\d+/)
const days = match ? Number(match[0]) : 0
const days = match ? Number(match[0]) : parseChineseNumber(value)
return Number.isFinite(days) && days > 0 ? Math.max(1, Math.floor(days)) : 0
}
function parseChineseNumber(value) {
const digits = {
: 1,
: 2,
: 2,
: 3,
: 4,
: 5,
: 6,
: 7,
: 8,
: 9
}
const text = String(value || '').match(/[一二两三四五六七八九十]{1,3}/)?.[0] || ''
if (!text) return 0
if (text === '十') return 10
if (text.includes('十')) {
const [left, right] = text.split('十')
const tens = left ? digits[left] || 0 : 1
const ones = right ? digits[right] || 0 : 0
return tens * 10 + ones
}
return digits[text] || 0
}
function parseMoneyNumber(value) {
const normalized = String(value ?? '').replace(/[^\d.-]/g, '')
const amount = Number(normalized)
@@ -161,7 +196,7 @@ function resolveApplicationDays(text) {
return value ? `${value}` : ''
}
function resolveApplicationTime(text, daysText = '') {
function resolveApplicationTime(text, daysText = '', options = {}) {
const range = text.match(
/(20\d{2}[-/.]\d{1,2}[-/.]\d{1,2})\s*(?:至|到|~|—||--)\s*(20\d{2}[-/.]\d{1,2}[-/.]\d{1,2})/u
)
@@ -176,7 +211,18 @@ function resolveApplicationTime(text, daysText = '') {
if (!single) return ''
const normalized = normalizeDateText(single)
const endDate = buildEndDateFromDays(normalized, daysText)
return endDate ? `${normalized}${endDate}` : normalized
return endDate && endDate !== normalized ? `${normalized}${endDate}` : normalized
}
function resolveApplicationTimeWithDefault(text, daysText = '', options = {}) {
const resolvedTime = resolveApplicationTime(text, daysText)
if (resolvedTime || !parseApplicationDaysValue(daysText)) {
return resolvedTime
}
const startDate = resolvePreviewToday(options)
const endDate = buildEndDateFromDays(startDate, daysText)
return endDate && endDate !== startDate ? `${startDate}${endDate}` : startDate
}
function resolveApplicationLocation(text) {
@@ -449,10 +495,10 @@ export function buildApplicationPreviewSubmitText(preview = {}) {
].join('\n')
}
export function buildLocalApplicationPreview(rawText, currentUser = {}) {
export function buildLocalApplicationPreview(rawText, currentUser = {}, options = {}) {
const sourceText = String(rawText || '').trim()
const explicitDays = resolveApplicationDays(sourceText)
const time = resolveApplicationTime(sourceText, explicitDays)
const time = resolveApplicationTimeWithDefault(sourceText, explicitDays, options)
const days = explicitDays || resolveDaysFromDateRange(time)
const location = resolveApplicationLocation(sourceText)
const fields = {