feat: 新增风险图谱算法与系统仪表盘及操作反馈体系
后端新增风险图谱算法模块、风险观察与反馈服务、规则 DSL 校验器和可解释性引擎,完善系统仪表盘和财务仪表盘统计, 优化 agent 运行和编排执行链路,清理旧开发文档,前端新增 系统趋势、负载热力图等多种仪表盘图表组件,完善操作反馈 对话框和工作台日期选择器,优化报销创建和审批详情交互, 补充单元测试覆盖。
This commit is contained in:
@@ -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 ""
|
||||
|
||||
|
||||
Reference in New Issue
Block a user