feat: 新增风险图谱算法与系统仪表盘及操作反馈体系

后端新增风险图谱算法模块、风险观察与反馈服务、规则 DSL
校验器和可解释性引擎,完善系统仪表盘和财务仪表盘统计,
优化 agent 运行和编排执行链路,清理旧开发文档,前端新增
系统趋势、负载热力图等多种仪表盘图表组件,完善操作反馈
对话框和工作台日期选择器,优化报销创建和审批详情交互,
补充单元测试覆盖。
This commit is contained in:
caoxiaozhu
2026-05-30 15:46:51 +08:00
parent 4c59941ec6
commit 7989f3a159
314 changed files with 30073 additions and 20626 deletions

View File

@@ -27,6 +27,45 @@ class ExpenseClaimApplicationHandoffMixin:
return normalized.removesuffix("_application") or "other"
return normalized or "other"
@staticmethod
def _resolve_application_detail(application_claim: ExpenseClaim) -> dict[str, str]:
for flag in list(application_claim.risk_flags_json or []):
if not isinstance(flag, dict) or str(flag.get("source") or "").strip() != "application_detail":
continue
detail = flag.get("application_detail") or flag.get("applicationDetail") or {}
if isinstance(detail, dict):
return {str(key): str(value or "").strip() for key, value in detail.items()}
return {}
@staticmethod
def _build_application_handoff_detail(application_claim: ExpenseClaim) -> dict[str, str]:
detail = ExpenseClaimApplicationHandoffMixin._resolve_application_detail(application_claim)
application_time = str(detail.get("time") or "").strip()
if not application_time and application_claim.occurred_at is not None:
application_time = application_claim.occurred_at.isoformat()
application_amount = str(detail.get("amount") or "").strip()
if not application_amount:
application_amount = str(application_claim.amount or Decimal("0.00"))
return {
"application_type": str(detail.get("application_type") or application_claim.expense_type or "").strip(),
"application_content": " / ".join(
item
for item in [
str(detail.get("application_type") or application_claim.expense_type or "").strip(),
str(detail.get("location") or application_claim.location or "").strip(),
]
if item
),
"application_reason": str(detail.get("reason") or application_claim.reason or "").strip(),
"application_days": str(detail.get("days") or "").strip(),
"application_location": str(detail.get("location") or application_claim.location or "").strip(),
"application_amount": application_amount,
"application_time": application_time,
"application_transport_mode": str(detail.get("transport_mode") or "").strip(),
}
def _create_reimbursement_draft_from_application(
self,
*,
@@ -67,6 +106,7 @@ class ExpenseClaimApplicationHandoffMixin:
"application_claim_id": application_claim.id,
"application_claim_no": application_claim.claim_no,
"application_budget_amount": str(application_claim.amount or Decimal("0.00")),
"application_detail": self._build_application_handoff_detail(application_claim),
"application_approval_event_id": str(approval_flag.get("approval_event_id") or ""),
"leader_opinion": str(
approval_flag.get("leader_opinion") or approval_flag.get("opinion") or ""