feat: 增强风险规则生成引擎与预算中心页面

后端拆分风险规则生成为解释器、语义分析、本体对齐等子模块,
优化模板执行和流程图生成,完善员工种子数据和导入逻辑,增强
报销单权限策略和草稿持久化,前端新增预算中心视图和趋势图
组件,重构审计页面和风险规则测试对话框交互,完善文档中心
和报销创建页面细节,补充单元测试覆盖。
This commit is contained in:
caoxiaozhu
2026-05-26 09:15:14 +08:00
parent d0e946cf47
commit 0e861d8fa6
150 changed files with 14953 additions and 4099 deletions

View File

@@ -1,5 +1,6 @@
from __future__ import annotations
import re
from datetime import UTC, datetime, timedelta
from decimal import Decimal
@@ -208,11 +209,11 @@ def test_user_agent_application_context_uses_application_language() -> None:
assert "| 字段 | 内容 |" in response.answer
assert "| 发生时间 | 2026-05-25 至 2026-05-28 |" in response.answer
assert "支持上海国网服务器部署" in response.answer
assert "当前还需要补充:出行方式、预计金额/预算" in response.answer
assert "当前还需要补充:出行方式、用户预估费用" in response.answer
assert "请先在下面选择报销场景" not in response.answer
assert response.review_payload is None
assert [item.label for item in response.suggested_actions] == ["一次性补充申请信息"]
assert response.suggested_actions[0].payload["prompt_prefill"] == "出行方式:\n预计总费用:"
assert response.suggested_actions[0].payload["prompt_prefill"] == "出行方式:\n用户预估费用:"
def test_user_agent_application_infers_natural_reason_and_expands_single_date() -> None:
@@ -222,13 +223,38 @@ def test_user_agent_application_infers_natural_reason_and_expands_single_date()
response = build_application_user_agent_response(db, message)
assert "| 发生时间 | 2026-05-25 至 2026-05-28 |" in response.answer
assert "| 地点 | 上海 |" in response.answer
assert "| 地点 | 上海 |" in response.answer
assert "| 事由 | 支撑上海国网服务器部署 |" in response.answer
assert "当前还需要先补充:申请事由" not in response.answer
assert "当前还需要补充:出行方式、预计金额/预算" in response.answer
assert "当前还需要补充:出行方式、用户预估费用" in response.answer
assert [item.label for item in response.suggested_actions] == ["一次性补充申请信息"]
def test_user_agent_application_normalizes_location_to_region_city() -> None:
session_factory = build_session_factory()
yili_message = (
"发生时间2026-05-25\n"
"地点:伊犁\n"
"事由:支撑新疆电力仿生产部署\n"
"天数3天"
)
beijing_message = (
"发生时间2026-05-25\n"
"地点:北京\n"
"事由:支撑总部系统部署\n"
"天数3天"
)
with session_factory() as db:
yili_response = build_application_user_agent_response(db, yili_message)
beijing_response = build_application_user_agent_response(db, beijing_message)
assert "| 发生时间 | 2026-05-25 至 2026-05-28 |" in yili_response.answer
assert "| 地点 | 新疆,伊犁 |" in yili_response.answer
assert "| 事由 | 支撑新疆电力仿生产部署 |" in yili_response.answer
assert "伊犁出差" not in yili_response.answer
assert "| 地点 | 北京市 |" in beijing_response.answer
def test_user_agent_application_uses_selected_time_and_natural_language_fields() -> None:
session_factory = build_session_factory()
message = "出差上海,支撑国网服务器上线部署"
@@ -262,12 +288,12 @@ def test_user_agent_application_uses_selected_time_and_natural_language_fields()
)
assert "| 发生时间 | 2026-05-25 |" in response.answer
assert "| 地点 | 上海 |" in response.answer
assert "| 地点 | 上海 |" in response.answer
assert "| 事由 | 支撑国网服务器上线部署 |" in response.answer
assert "当前还需要补充:出行方式、预计金额/预算" in response.answer
assert "当前还需要补充:出行方式、用户预估费用" in response.answer
assert [item.label for item in response.suggested_actions] == ["一次性补充申请信息"]
assert response.suggested_actions[0].action_type == "prefill_composer"
assert response.suggested_actions[0].payload["prompt_prefill"] == "出行方式:\n预计总费用:"
assert response.suggested_actions[0].payload["prompt_prefill"] == "出行方式:\n用户预估费用:"
def test_user_agent_application_asks_amount_after_transport_choice() -> None:
@@ -286,10 +312,10 @@ def test_user_agent_application_asks_amount_after_transport_choice() -> None:
)
assert "| 出行方式 | 飞机 |" in response.answer
assert "当前还需要补充:预计金额/预算" in response.answer
assert "当前还需要补充:用户预估费用" in response.answer
assert [item.label for item in response.suggested_actions] == ["一次性补充申请信息"]
assert response.suggested_actions[0].action_type == "prefill_composer"
assert response.suggested_actions[0].payload["prompt_prefill"] == "预计总费用:"
assert response.suggested_actions[0].payload["prompt_prefill"] == "用户预估费用:"
def test_user_agent_application_missing_base_actions_prefill_composer() -> None:
@@ -300,10 +326,10 @@ def test_user_agent_application_missing_base_actions_prefill_composer() -> None:
"地点:上海\n事由:支撑国网服务器部署\n天数3天",
)
assert "当前还需要补充:发生时间、出行方式、预计金额/预算" in response.answer
assert "当前还需要补充:发生时间、出行方式、用户预估费用" in response.answer
assert [item.label for item in response.suggested_actions] == ["一次性补充申请信息"]
assert response.suggested_actions[0].action_type == "prefill_composer"
assert response.suggested_actions[0].payload["prompt_prefill"] == "申请时间段:\n出行方式:\n预计总费用:"
assert response.suggested_actions[0].payload["prompt_prefill"] == "申请时间段:\n出行方式:\n用户预估费用:"
def test_user_agent_application_builds_preview_when_amount_is_ready() -> None:
@@ -328,7 +354,7 @@ def test_user_agent_application_builds_preview_when_amount_is_ready() -> None:
assert "| 字段 | 内容 |" in response.answer
assert "| 事由 | 支持上海国网服务器部署 |" in response.answer
assert "| 出行方式 | 飞机 |" in response.answer
assert "| 预计总费用 | 12000元 |" in response.answer
assert "| 用户预估费用 | 12000元 |" in response.answer
assert "请核对上述信息无误" in response.answer
assert "[确认](#application-submit)" in response.answer
assert response.requires_confirmation is True
@@ -349,11 +375,11 @@ def test_user_agent_application_submit_enters_leader_review() -> None:
"| --- | --- |\n"
"| 申请类型 | 差旅费用申请 |\n"
"| 发生时间 | 2026-05-25 |\n"
"| 地点 | 上海 |\n"
"| 地点 | 上海 |\n"
"| 事由 | 支持上海国网服务器部署 |\n"
"| 天数 | 3天 |\n"
"| 出行方式 | 飞机 |\n"
"| 预计总费用 | 12000元 |\n\n"
"| 用户预估费用 | 12000元 |\n\n"
"请核对上述信息无误,确认无误后 [确认](#application-submit) 提交至审批流程。"
)
with session_factory() as db:
@@ -372,9 +398,9 @@ def test_user_agent_application_submit_enters_leader_review() -> None:
assert "当前操作已完成,单据已经推送给 陈硕 进行审核,请耐心等待" in response.answer
assert "当前状态:陈硕审核中" in response.answer
assert "预算占用参考" in response.answer
assert "APP-20260525-" in response.answer
assert re.search(r"AP-\d{14}-[A-HJ-NP-Z2-9]{8}", response.answer)
assert response.suggested_actions == []
claim = db.query(ExpenseClaim).filter(ExpenseClaim.claim_no.like("APP-20260525-%")).one()
claim = db.query(ExpenseClaim).filter(ExpenseClaim.claim_no.like("AP-%")).one()
assert claim.status == "submitted"
assert claim.approval_stage == "直属领导审批"
assert claim.expense_type == "travel_application"