feat: 扩展风险规则体系、审批动态路由与预算中心列表化改造
- 新增 25+ 条风险规则(预算/报销/申请/通用类),完善风险规则模拟与反馈发布机制 - 引入费用审批动态路由、平台风险分级、预审与风险阶段管理 - 预算中心列表化改造,优化票据夹仪表盘与数字员工工作看板 - 新增 Hermes 风险线索收集器、Agent 链路追踪中心 - 扩展数字员工能力库(18 个领域 Skill)与交通费用自动预估 - 完善报销申请快速预览、权限控制与前端测试覆盖
This commit is contained in:
@@ -105,6 +105,10 @@ function parseYear(rawText) {
|
||||
return match ? Number(match[1]) : 2026
|
||||
}
|
||||
|
||||
function hasExplicitYear(rawText) {
|
||||
return /(20\d{2})/.test(String(rawText || ''))
|
||||
}
|
||||
|
||||
function resolvePreviousPeriod(year, quarter) {
|
||||
if (quarter > 1) {
|
||||
return { year, quarter: quarter - 1 }
|
||||
@@ -117,35 +121,52 @@ export function shouldUseBudgetCompileReport(rawText, options = {}) {
|
||||
return false
|
||||
}
|
||||
const text = normalizeBudgetText(rawText)
|
||||
const hasTargetPeriod = parseQuarter(rawText) || hasExplicitYear(rawText)
|
||||
return Boolean(
|
||||
text &&
|
||||
/(预算|budget)/.test(text) &&
|
||||
/(编制|制定|测算|生成|规划|预算一下|compile|create|plan)/.test(text) &&
|
||||
parseQuarter(rawText)
|
||||
hasTargetPeriod
|
||||
)
|
||||
}
|
||||
|
||||
export function buildBudgetCompileReport(rawText, user = {}) {
|
||||
const targetYear = parseYear(rawText)
|
||||
const targetQuarter = parseQuarter(rawText) || 3
|
||||
const previous = resolvePreviousPeriod(targetYear, targetQuarter)
|
||||
const totalSpend = PREVIOUS_QUARTER_SPEND.reduce((sum, item) => sum + item.value, 0)
|
||||
const totalBudget = 1320000
|
||||
const recommendedTotal = PREVIOUS_QUARTER_SPEND.reduce((sum, item) => sum + item.recommendedBudget, 0)
|
||||
const parsedQuarter = parseQuarter(rawText)
|
||||
const isAnnualBudget = !parsedQuarter
|
||||
const targetQuarter = parsedQuarter || 1
|
||||
const previous = isAnnualBudget
|
||||
? { year: targetYear - 1, quarter: 0 }
|
||||
: resolvePreviousPeriod(targetYear, targetQuarter)
|
||||
const periodMultiplier = isAnnualBudget ? 4 : 1
|
||||
const totalSpend = PREVIOUS_QUARTER_SPEND.reduce((sum, item) => sum + item.value * periodMultiplier, 0)
|
||||
const totalBudget = 1320000 * periodMultiplier
|
||||
const recommendedTotal = PREVIOUS_QUARTER_SPEND.reduce((sum, item) => sum + item.recommendedBudget * periodMultiplier, 0)
|
||||
const departmentName = String(user.departmentName || user.department || '').trim() || '当前部门'
|
||||
|
||||
const items = PREVIOUS_QUARTER_SPEND.map((item) => {
|
||||
const value = item.value * periodMultiplier
|
||||
const previousValue = item.previousValue * periodMultiplier
|
||||
const recommendedBudget = item.recommendedBudget * periodMultiplier
|
||||
const trendValue = item.previousValue
|
||||
? ((item.value - item.previousValue) / item.previousValue) * 100
|
||||
? ((value - previousValue) / previousValue) * 100
|
||||
: 0
|
||||
return {
|
||||
...item,
|
||||
amountDisplay: compactCurrency(item.value),
|
||||
display: percent(item.value, totalSpend),
|
||||
share: percent(item.value, totalSpend),
|
||||
value,
|
||||
previousValue,
|
||||
recommendedBudget,
|
||||
amountDisplay: compactCurrency(value),
|
||||
display: percent(value, totalSpend),
|
||||
share: percent(value, totalSpend),
|
||||
trend: `${trendValue >= 0 ? '+' : ''}${trendValue.toFixed(1)}%`,
|
||||
trendTone: trendValue >= 10 ? 'risk' : trendValue >= 0 ? 'warn' : 'stable',
|
||||
recommendedDisplay: compactCurrency(item.recommendedBudget)
|
||||
recommendedDisplay: compactCurrency(recommendedBudget),
|
||||
editableBudget: recommendedBudget,
|
||||
reminderThreshold: item.key === 'communication' || item.key === 'office' ? 60 : 70,
|
||||
alertThreshold: item.key === 'communication' || item.key === 'office' ? 70 : 80,
|
||||
riskThreshold: item.key === 'communication' || item.key === 'office' ? 80 : 90,
|
||||
editNote: item.suggestion
|
||||
}
|
||||
})
|
||||
|
||||
@@ -158,13 +179,18 @@ export function buildBudgetCompileReport(rawText, user = {}) {
|
||||
|
||||
return {
|
||||
type: 'budget_compile_analysis',
|
||||
title: `${targetYear}年${targetQuarter}季度预算编制前置分析报告`,
|
||||
subtitle: `基于${previous.year}年${previous.quarter}季度预算执行模拟数据`,
|
||||
title: isAnnualBudget
|
||||
? `${targetYear}年度预算编制前置分析报告`
|
||||
: `${targetYear}年${targetQuarter}季度预算编制前置分析报告`,
|
||||
subtitle: isAnnualBudget
|
||||
? `基于${previous.year}年度预算执行模拟数据`
|
||||
: `基于${previous.year}年${previous.quarter}季度预算执行模拟数据`,
|
||||
departmentName,
|
||||
targetPeriod: `${targetYear}年${QUARTER_NAME_MAP[targetQuarter]}`,
|
||||
basePeriod: `${previous.year}年${QUARTER_NAME_MAP[previous.quarter]}`,
|
||||
targetPeriod: isAnnualBudget ? `${targetYear}年度` : `${targetYear}年${QUARTER_NAME_MAP[targetQuarter]}`,
|
||||
basePeriod: isAnnualBudget ? `${previous.year}年度` : `${previous.year}年${QUARTER_NAME_MAP[previous.quarter]}`,
|
||||
periodType: isAnnualBudget ? '年度预算' : '季度预算',
|
||||
centerValue: compactCurrency(totalSpend),
|
||||
centerLabel: '上季度开销',
|
||||
centerLabel: isAnnualBudget ? '去年开销' : '上季度开销',
|
||||
summary: {
|
||||
totalBudget: compactCurrency(totalBudget),
|
||||
totalSpend: compactCurrency(totalSpend),
|
||||
@@ -172,13 +198,25 @@ export function buildBudgetCompileReport(rawText, user = {}) {
|
||||
recommendedTotal: compactCurrency(recommendedTotal)
|
||||
},
|
||||
macroInsights: [
|
||||
`${previous.year}年${previous.quarter}季度实际开销 ${compactCurrency(totalSpend)},预算使用率 ${percent(totalSpend, totalBudget)},整体仍在可控区间。`,
|
||||
`${topItem.name}是最大开销项,占 ${topItem.share},建议作为${targetYear}年${targetQuarter}季度预算编制的第一优先级。`,
|
||||
`${isAnnualBudget ? `${previous.year}年度` : `${previous.year}年${previous.quarter}季度`}实际开销 ${compactCurrency(totalSpend)},预算使用率 ${percent(totalSpend, totalBudget)},整体仍在可控区间。`,
|
||||
`${topItem.name}是最大开销项,占 ${topItem.share},建议作为${isAnnualBudget ? `${targetYear}年度` : `${targetYear}年${targetQuarter}季度`}预算编制的第一优先级。`,
|
||||
`${growthItem.name}环比增长 ${growthItem.trend},需要在预算说明中提前解释业务驱动,避免后续报销阶段反复补充材料。`
|
||||
],
|
||||
items,
|
||||
editableDraft: {
|
||||
status: 'editing',
|
||||
rows: items.map((item) => ({
|
||||
key: item.key,
|
||||
name: item.name,
|
||||
budgetAmount: item.editableBudget,
|
||||
reminderThreshold: item.reminderThreshold,
|
||||
alertThreshold: item.alertThreshold,
|
||||
riskThreshold: item.riskThreshold,
|
||||
note: item.editNote
|
||||
}))
|
||||
},
|
||||
recommendations: [
|
||||
`建议${targetYear}年${targetQuarter}季度总预算先按 ${compactCurrency(recommendedTotal)} 编制,再预留 5%-8% 部门机动池。`,
|
||||
`建议${isAnnualBudget ? `${targetYear}年度` : `${targetYear}年${targetQuarter}季度`}总预算先按 ${compactCurrency(recommendedTotal)} 编制,再预留 5%-8% 部门机动池。`,
|
||||
'差旅和招待费采用更早的提醒阈值,通信和办公用品保持稳定额度,避免把预算过度分散到低波动项目。',
|
||||
'正式编制时建议把重点项目、客户活动和集中采购计划写入预算说明,后续费用控制会更容易解释。'
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user