# 费用审批动态路由概念文档

## 功能一句话

让费用申请和报账在直属领导审批后，按预算风险、规则风险和员工历史风险动态决定是否进入预算管理者复核。

## 背景与问题

当前申请单默认进入预算管理者审批，报账单默认进入财务审批，审批路径偏固定。业务上更合理的方式是：预算充足、当前无风险、历史画像正常的单据减少审批层级；存在超预算、规则命中、超标或历史风险异常的单据交给预算管理者做二次确认。

## 目标与非目标

目标：

- 申请环节：低风险且预算充足时，直属领导审批后直接完成申请并生成报销草稿。
- 申请环节：超预算、预算预警、当前风险或历史风险异常时，进入预算管理者审批。
- 报账环节：低风险且预算充足时，直属领导审批后进入财务审批。
- 报账环节：超预算、超标、当前风险或历史风险异常时，先进入预算管理者审批，再进入财务审批。
- 审批记录中保留路由决策依据，便于追溯。

非目标：

- 不改预算占用、释放、核销的资金动作语义。
- 不引入新的审批流配置页面。
- 不让大模型参与最终审批路由裁判。

## 用户与场景

- 普通员工：提交费用申请或报账。
- 直属领导：确认业务必要性。
- 预算管理者：只复核有预算或风险关注项的单据。
- 财务人员：处理报账财务终审和付款前流程。

## 功能能力

路由决策输入：

- 单据基本信息：金额、费用类型、发生时间、部门、项目、申请人。
- 预算测算：预算池匹配、可用余额、预算使用率、预警阈值、超预算金额。
- 当前风险：预算标记、规则中心风险、提交预审风险、票据/附件风险、超标风险。
- 历史风险：同一员工近一段时间内的实质风险记录。

路由决策输出：

- `requires_budget_review`：是否需要预算管理者复核。
- `route`：下一环节建议。
- `reasons`：触发预算复核或跳过的原因。
- `budget_result`：预算模型摘要。
- `current_risk_count`、`historical_risk_count`：当前和历史风险计数。

## 方案设计

后端新增独立审批路由决策模块，避免在审批主流程中堆条件。

直属领导审批时：

1. 调用预算服务计算当前单据预算影响。
2. 读取当前单据风险标记，过滤审批记录等非风险事件。
3. 查询同一员工近期历史单据，统计实质风险记录。
4. 生成路由决策标记并写入 `risk_flags_json`。
5. 根据结果决定下一环节：
   - 申请单：预算复核或审批完成。
   - 报账单：预算复核或财务审批。

预算管理者审批时：

- 申请单进入审批完成，并生成报销草稿。
- 报账单进入财务审批。

## 算法与公式

路由决策不是单一分数，而是规则化闸口：

$$
requires\_budget\_review =
budget\_risk \lor current\_risk \lor historical\_risk
$$

其中：

- `budget_risk = rating in {block, review, caution} or risk_level in {medium, high, critical}`
- `current_risk = 当前单据存在 medium/high/critical 实质风险标记`
- `historical_risk = 同一员工近期存在实质风险记录`

实质风险标记排除审批通过、退回、付款、路由说明等流程记录，只保留预算、规则、AI 预审、附件、政策超标等风险来源。

## 测试方案

- 单元测试：低风险申请跳过预算管理者并生成报销草稿。
- 单元测试：高风险报账进入预算管理者审批，预算审批后进入财务审批。
- 回归测试：原有风险规则生成、申请提交、阶段化风险规则执行继续通过。
- 容器验证：在 `x-financial-main:/app/server` 内运行定向 pytest。

## 指标与验收

- 低风险申请不会固定进入预算管理者审批。
- 风险报账会进入预算管理者审批。
- 报账经过预算管理者审批后仍需进入财务终审。
- 每次动态路由都有可追溯的 `approval_routing` 标记。
- 预算资金动作仍由原有提交、退回、财务终审链路处理。

## 风险与开放问题

- 历史风险的口径会影响预算管理者工作量，当前一期采用“存在实质风险即复核”的严格口径。
- 缺失预算池时是否全部进入预算复核，当前按预算风险处理。
- 后续如要支持可配置路由阈值，应新增配置表或策略服务，而不是继续改审批流分支。
