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

@@ -32,6 +32,7 @@ from app.schemas.agent_asset import (
AgentAssetVersionCreate,
)
from app.schemas.reimbursement import TravelReimbursementCalculatorRequest
from app.services import agent_foundation as agent_foundation_module
from app.services.agent_asset_spreadsheet import (
COMPANY_COMMUNICATION_EXPENSE_RULE_CODE,
COMPANY_COMMUNICATION_EXPENSE_RULE_FILENAME,
@@ -43,6 +44,9 @@ from app.services.agent_assets import AgentAssetService
from app.services.agent_runs import AgentRunService
from app.services.audit import AuditLogService
from app.services.expense_rule_runtime import ExpenseRuleRuntimeService
from app.services.finance_rule_catalog import (
DEPRECATED_FINANCE_RULE_CODES,
)
from app.services.settings import OnlyOfficeRuntimeConfig
from app.services.travel_reimbursement_calculator import TravelReimbursementCalculatorService
@@ -80,6 +84,7 @@ def isolate_rule_file_storage(tmp_path, monkeypatch) -> None:
def build_session() -> Session:
agent_foundation_module._foundation_ready_keys.clear()
engine = create_engine(
"sqlite+pysqlite:///:memory:",
connect_args={"check_same_thread": False},
@@ -163,12 +168,59 @@ def test_finance_rules_use_risk_rule_scenario_categories() -> None:
travel_config = travel_rule.config_json or {}
communication_config = communication_rule.config_json or {}
assert travel_rule.scenario_json == ["差旅"]
assert travel_config["scenario_category"] == "差旅"
assert travel_config["ai_review_category"] == "差旅"
assert communication_rule.scenario_json == ["用科目"]
assert communication_config["scenario_category"] == "用科目"
assert communication_config["ai_review_category"] == "用科目"
assert travel_rule.scenario_json == ["差旅"]
assert travel_config["scenario_category"] == "差旅"
assert travel_config["ai_review_category"] == "差旅"
assert communication_rule.scenario_json == ["通信"]
assert communication_config["scenario_category"] == "通信"
assert communication_config["ai_review_category"] == "通信"
def test_non_standard_finance_rule_spreadsheets_are_not_seeded() -> None:
with build_session() as db:
service = AgentAssetService(db)
service.list_assets(asset_type=AgentAssetType.RULE.value)
for code in DEPRECATED_FINANCE_RULE_CODES:
asset = db.scalar(select(AgentAsset).where(AgentAsset.code == code))
assert asset is None or asset.config_json["tag"] == "废弃规则"
def test_demo_budget_risk_rules_sync_with_finance_rule_references() -> None:
with build_session() as db:
service = AgentAssetService(db)
service.list_assets(asset_type=AgentAssetType.RULE.value)
budget_rule = db.scalar(
select(AgentAsset).where(
AgentAsset.code == "risk.budget.available_balance_insufficient"
)
)
marketing_rule = db.scalar(
select(AgentAsset).where(
AgentAsset.code == "risk.application.marketing_without_campaign"
)
)
assert budget_rule is not None
assert "差旅费" in budget_rule.scenario_json
assert "市场推广费" in budget_rule.scenario_json
assert "软件服务费" in budget_rule.scenario_json
assert budget_rule.config_json["budget_required"] is True
assert "marketing" in budget_rule.config_json["expense_types"]
assert budget_rule.config_json["business_stage"] == [
"expense_application",
"reimbursement",
"budget_execution",
]
assert budget_rule.config_json["finance_rule_code"] == "budget.execution.policy"
assert marketing_rule is not None
assert marketing_rule.scenario_json == ["市场推广费"]
assert marketing_rule.config_json["finance_rule_code"] == "expense.application.policy"
assert marketing_rule.config_json["finance_rule_sheet"] == "费用申请前置规则"
assert marketing_rule.config_json["expense_types"] == ["marketing"]
assert marketing_rule.config_json["budget_required"] is True
def test_agent_asset_service_can_activate_rule_after_review() -> None: