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

@@ -5,6 +5,7 @@ from typing import Any
from sqlalchemy import or_, select
from app.core.logging import get_logger
from app.models.financial_record import ExpenseClaim
from app.services.expense_claim_constants import (
AI_REVIEW_LOOKBACK_DAYS,
@@ -14,6 +15,9 @@ from app.services.expense_claim_constants import (
from app.services.expense_claim_item_sync import ExpenseClaimItemSyncMixin
from app.services.expense_claim_platform_risk import ExpenseClaimPlatformRiskMixin
from app.services.expense_claim_policy_review import ExpenseClaimPolicyReviewMixin
from app.services.risk_observations import RiskObservationService
logger = get_logger("app.services.expense_claim_risk_review")
class ExpenseClaimRiskReviewMixin(
@@ -26,12 +30,16 @@ class ExpenseClaimRiskReviewMixin(
attachment_flags = [
flag
for flag in base_flags
if isinstance(flag, dict) and str(flag.get("source") or "").strip() == "attachment_analysis"
if isinstance(flag, dict)
and str(flag.get("source") or "").strip() == "attachment_analysis"
]
preserved_flags = [
flag
for flag in base_flags
if not (isinstance(flag, dict) and str(flag.get("source") or "").strip() == "submission_review")
if not (
isinstance(flag, dict)
and str(flag.get("source") or "").strip() == "submission_review"
)
]
review_flags: list[dict[str, Any]] = []
@@ -66,7 +74,10 @@ class ExpenseClaimRiskReviewMixin(
"source": "submission_review",
"severity": "medium",
"label": "AI预审提醒",
"message": f"AI预审发现 {len(medium_attachment_flags)} 条中风险附件,已随单流转给审批人复核。",
"message": (
f"AI预审发现 {len(medium_attachment_flags)} 条中风险附件,"
"已随单流转给审批人复核。"
),
}
)
@@ -90,7 +101,8 @@ class ExpenseClaimRiskReviewMixin(
"severity": "medium",
"label": "历史风险偏高",
"message": (
f"{AI_REVIEW_LOOKBACK_DAYS} 天内该员工已有 {historical_risk_count} 笔带风险标记的报销,"
f"{AI_REVIEW_LOOKBACK_DAYS} 天内该员工已有 "
f"{historical_risk_count} 笔带风险标记的报销,"
"本次已追加到审批链重点关注。"
),
}
@@ -102,7 +114,8 @@ class ExpenseClaimRiskReviewMixin(
"severity": "low",
"label": "历史风险提醒",
"message": (
f"{AI_REVIEW_LOOKBACK_DAYS} 天内该员工已有 {historical_risk_count} 笔带风险标记的报销,"
f"{AI_REVIEW_LOOKBACK_DAYS} 天内该员工已有 "
f"{historical_risk_count} 笔带风险标记的报销,"
"建议直属领导重点复核。"
),
}
@@ -118,7 +131,19 @@ class ExpenseClaimRiskReviewMixin(
platform_risk_review = self.evaluate_platform_risk_rules(claim)
attention_reasons.extend(platform_risk_review["blocking_reasons"])
review_flags.extend(platform_risk_review["flags"])
platform_risk_flags = list(platform_risk_review["flags"])
review_flags.extend(platform_risk_flags)
if platform_risk_flags:
try:
RiskObservationService(self.db).upsert_platform_risk_flags(
claim,
platform_risk_flags,
)
except Exception:
logger.exception(
"Failed to persist platform risk observations for claim_id=%s",
claim.id,
)
if attention_reasons:
summary_message = "AI预审发现需审批重点关注事项" + "".join(
@@ -150,7 +175,10 @@ class ExpenseClaimRiskReviewMixin(
if claim.employee is not None:
if claim.employee.manager is not None and claim.employee.manager.name:
return str(claim.employee.manager.name).strip()
if claim.employee.organization_unit is not None and claim.employee.organization_unit.manager_name:
if (
claim.employee.organization_unit is not None
and claim.employee.organization_unit.manager_name
):
return str(claim.employee.organization_unit.manager_name).strip()
return ""