feat: 扩展风险规则体系、审批动态路由与预算中心列表化改造

- 新增 25+ 条风险规则(预算/报销/申请/通用类),完善风险规则模拟与反馈发布机制
- 引入费用审批动态路由、平台风险分级、预审与风险阶段管理
- 预算中心列表化改造,优化票据夹仪表盘与数字员工工作看板
- 新增 Hermes 风险线索收集器、Agent 链路追踪中心
- 扩展数字员工能力库(18 个领域 Skill)与交通费用自动预估
- 完善报销申请快速预览、权限控制与前端测试覆盖
This commit is contained in:
caoxiaozhu
2026-06-01 17:07:14 +08:00
parent 7989f3a159
commit 92444e7eae
285 changed files with 25075 additions and 2986 deletions

View File

@@ -230,6 +230,41 @@ def test_generate_expense_application_risk_rule_marks_business_stage(tmp_path) -
assert payload["params"]["business_stage_label"] == "费用申请"
def test_generate_risk_rule_asset_supports_all_expense_category(tmp_path) -> None:
with build_session() as db:
manager = AgentAssetRuleLibraryManager(rule_root=tmp_path / "rules")
service = RiskRuleGenerationService(
db,
rule_library_manager=manager,
runtime_chat_service=NullRuntimeChatService(),
)
asset_id = service.generate_rule_asset(
AgentAssetRiskRuleGenerateRequest(
business_domain=AgentAssetDomain.EXPENSE,
business_stage="expense_application",
expense_category="all",
rule_title="预算可用余额不足",
natural_language="费用申请时,如果申请金额超过预算可用余额,则提示预算风险并要求补充说明。",
),
actor="pytest",
)
asset = db.get(AgentAsset, asset_id)
assert asset is not None
assert asset.config_json["expense_category"] == "all"
assert asset.config_json["expense_category_label"] == "全部"
assert asset.scenario_json == ["全部"]
payload = manager.read_rule_library_json(
library=RISK_RULES_LIBRARY,
file_name=asset.config_json["rule_document"]["file_name"],
)
assert payload["applies_to"]["expense_categories"] == ["all"]
assert payload["metadata"]["expense_category"] == "all"
assert payload["metadata"]["expense_category_label"] == "全部"
def test_risk_score_model_keeps_explicit_low_control_rules_low() -> None:
field_keys = ["attachment.invoice_no", "attachment.goods_name", "claim.reason"]
result = calculate_risk_rule_score(
@@ -476,6 +511,44 @@ def test_platform_risk_applies_to_chinese_expense_type_labels() -> None:
)
def test_platform_risk_all_expense_scope_matches_any_budget_category() -> None:
class PlatformRiskProbe(ExpenseClaimPlatformRiskMixin):
pass
claim = ExpenseClaim(
claim_no="TEST-COMMUNICATION-RISK",
employee_name="测试员工",
department_name="市场部",
expense_type="通信费",
reason="客户支持电话费",
amount=Decimal("300.00"),
currency="CNY",
invoice_count=1,
occurred_at=datetime.now(UTC),
status="draft",
)
manifest = {
"applies_to": {
"domains": ["expense"],
"expense_types": ["all"],
}
}
assert PlatformRiskProbe()._risk_manifest_applies_to_claim(
manifest,
claim=claim,
contexts=[],
)
manifest["applies_to"] = {"domains": ["expense"], "expense_categories": ["全部"]}
assert PlatformRiskProbe()._risk_manifest_applies_to_claim(
manifest,
claim=claim,
contexts=[],
)
def test_risk_rule_flow_diagram_uses_risk_level_palette() -> None:
renderer = RiskRuleFlowDiagramRenderer()