feat: 新增预算后端服务与差旅风险规则库

后端新增预算模型、端点和服务模块,支持预算 CRUD 和余额
查询,清理旧生成规则文件并替换为按严重等级分类的差旅风
险规则库,优化认证权限和报销单访问策略,新增财务规则目
录和演示数据构建脚本,前端预算中心增加对话框交互,完善
审计页面运行时模型和元数据展示,补充单元测试。
This commit is contained in:
caoxiaozhu
2026-05-26 17:29:35 +08:00
parent e1e515ecae
commit e7bef0883d
85 changed files with 6443 additions and 1497 deletions

View File

@@ -113,7 +113,7 @@ export function useAppShell() {
return {
title: isApplicationDocument ? '申请单详情' : '报销单详情',
desc: isApplicationDocument
? '查看申请信息、预计金额审批进度与预算管理口径。'
? '查看申请信息、预计金额审批进度。'
: '查看报销明细、票据材料、审批进度与风险提示。'
}
}

View File

@@ -764,7 +764,7 @@ export function mapExpenseClaimToRequest(claim) {
riskSummary,
attachmentSummary: isApplicationDocument ? '申请单' : (invoiceCount > 0 ? `${invoiceCount} 张票据` : '无'),
expenseTableSummary: isApplicationDocument
? '预计金额已纳入预算管理口径'
? '预计金额已随申请提交'
: expenseItems.length
? (invoiceCount > 0
? `${expenseItems.length} 条费用明细,已关联 ${invoiceCount} 张票据`

View File

@@ -107,7 +107,7 @@ function buildAnonymousUser() {
}
}
function buildLegacyAdminUser(username = '') {
function buildLegacyAdminUser(username = '') {
const normalized = String(username || '').trim()
const name = normalized || DEFAULT_USER_NAME
@@ -129,10 +129,25 @@ function buildLegacyAdminUser(username = '') {
email: '',
avatar: name.slice(0, 1).toUpperCase(),
isAdmin: true
}
}
function readStoredUser() {
}
}
function resolvePlatformAdminFlag(payload, roleCodes = []) {
const username = String(payload?.username || payload?.account || '').trim().toLowerCase()
const role = String(payload?.role || '').trim().toLowerCase()
const normalizedRoleCodes = roleCodes.map((item) => String(item || '').trim().toLowerCase()).filter(Boolean)
return (
Boolean(payload?.isAdmin)
|| username === 'admin'
|| role === 'admin'
|| role === '管理员'
|| role === '系统管理员'
|| normalizedRoleCodes.includes('admin')
)
}
function readStoredUser() {
if (typeof window === 'undefined') {
return buildAnonymousUser()
}
@@ -164,9 +179,9 @@ function readStoredUser() {
roleCodes,
email: String(payload.email || ''),
avatar: String(payload.avatar || name.slice(0, 1).toUpperCase()),
isAdmin: Boolean(payload.isAdmin)
}
}
isAdmin: resolvePlatformAdminFlag(payload, roleCodes)
}
}
} catch {
return buildLegacyAdminUser(readStoredUsername())
}
@@ -609,8 +624,14 @@ async function handleLogin(credentials) {
password: credentials.password
})
const user = response?.user || buildAnonymousUser()
loggedIn.value = true
const responseUser = response?.user || buildAnonymousUser()
const responseRoleCodes = Array.isArray(responseUser.roleCodes) ? responseUser.roleCodes.filter(Boolean) : []
const user = {
...responseUser,
roleCodes: responseRoleCodes,
isAdmin: resolvePlatformAdminFlag(responseUser, responseRoleCodes)
}
loggedIn.value = true
persistAuthState(true, user)
currentUser.value = user
touchAuthActivity(true)