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

@@ -14,6 +14,7 @@ from app.core.agent_enums import (
AgentReviewStatus,
)
from app.models.agent_asset import AgentAsset, AgentAssetReview, AgentAssetTestRun
from app.models.employee import Employee
from app.models.financial_record import ExpenseClaim, ExpenseClaimItem
from app.schemas.agent_asset import (
AgentAssetRiskRuleLatestTestSummary,
@@ -25,6 +26,7 @@ from app.schemas.agent_asset import (
)
from app.services.expense_claims import ExpenseClaimService
from app.services.risk_rule_template_executor import RiskRuleTemplateExecutor
from app.services.risk_rule_manifest_normalizer import normalize_risk_rule_manifest
class AgentAssetRiskRuleTestingMixin:
@@ -333,6 +335,7 @@ class AgentAssetRiskRuleTestingMixin:
library=rule_library,
file_name=file_name,
)
manifest = normalize_risk_rule_manifest(manifest)
return asset, target_version, manifest
def _create_test_run(
@@ -451,6 +454,13 @@ class AgentAssetRiskRuleTestingMixin:
item_amount=self._to_decimal(values.get("item.item_amount") or claim.amount),
)
claim.items = [item]
if values.get("employee.location"):
claim.employee = Employee(
employee_no="TEST-EMPLOYEE",
name=claim.employee_name,
email="risk-rule-test@example.com",
location=str(values.get("employee.location") or ""),
)
attachment_fields = []
document_info: dict[str, Any] = {"fields": attachment_fields}
@@ -585,7 +595,14 @@ class AgentAssetRiskRuleTestingMixin:
def _default_value_for_field(field_key: str) -> Any:
if field_key.endswith("amount"):
return "100.00"
if field_key.endswith("issue_date"):
if (
field_key.endswith("issue_date")
or field_key.endswith("stay_start_date")
or field_key.endswith("stay_end_date")
or field_key.endswith("trip_start_date")
or field_key.endswith("trip_end_date")
or field_key.endswith("item_date")
):
return date.today().isoformat()
if field_key.endswith("route_cities"):
return ["北京"]