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

@@ -46,11 +46,13 @@ function readCurrentUserHeaders() {
const username = String(payload?.username || '').trim()
const name = String(payload?.name || username).trim()
const roleCodes = Array.isArray(payload?.roleCodes) ? payload.roleCodes.filter(Boolean) : []
const isAdmin = Boolean(payload?.isAdmin)
const isAdmin = resolveStoredUserAdminFlag(payload, roleCodes)
const department = String(payload?.department || payload?.departmentName || '').trim()
const costCenter = String(payload?.costCenter || payload?.cost_center || '').trim()
const safeUsername = pickSafeHeaderValue(username)
const safeName = pickSafeHeaderValue(name)
const safeDepartment = pickSafeHeaderValue(department)
const safeCostCenter = pickSafeHeaderValue(costCenter)
if (!safeUsername && !safeName) {
return {}
@@ -73,11 +75,30 @@ function readCurrentUserHeaders() {
headers['x-auth-department'] = safeDepartment
}
if (safeCostCenter) {
headers['x-auth-cost-center'] = safeCostCenter
}
return headers
} catch {
return {}
}
}
function resolveStoredUserAdminFlag(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 normalizeApiBaseUrl(value) {
return String(value || '/api/v1').replace(/\/$/, '')

View File

@@ -0,0 +1,30 @@
import { apiRequest } from './api.js'
function buildQuery(params = {}) {
const search = new URLSearchParams()
Object.entries(params || {}).forEach(([key, value]) => {
if (typeof value === 'undefined' || value === null || value === '') return
search.set(key, String(value))
})
const query = search.toString()
return query ? `?${query}` : ''
}
export function fetchBudgetSummary(params = {}) {
return apiRequest(`/budgets/summary${buildQuery(params)}`)
}
export function fetchBudgetAllocations(params = {}) {
return apiRequest(`/budgets/allocations${buildQuery(params)}`)
}
export function createBudgetAllocation(payload = {}) {
return apiRequest('/budgets/allocations', {
method: 'POST',
body: JSON.stringify(payload)
})
}
export function fetchBudgetTransactions(allocationId) {
return apiRequest(`/budgets/allocations/${encodeURIComponent(String(allocationId || '').trim())}/transactions`)
}