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

@@ -1,5 +1,7 @@
from __future__ import annotations
from typing import Any
COMPOSITE_RULE_TEMPLATE_KEY = "composite_rule_v1"
COMPOSITE_RULE_OPERATORS = {
@@ -12,6 +14,62 @@ COMPOSITE_RULE_OPERATORS = {
"overlap",
"not_overlap",
"date_outside_range",
"numeric_compare",
"duplicate_value",
"contains_any",
"not_contains_any",
}
def build_dsl_from_semantic_plan(semantic_plan: dict[str, Any]) -> dict[str, Any]:
"""把模型语义计划转换成可交给 validator 继续规范化的 DSL 草稿。"""
if not isinstance(semantic_plan, dict):
return {}
text_parts = _semantic_text_parts(semantic_plan)
field_keys = _semantic_field_keys(semantic_plan)
if not text_parts and not field_keys:
return {}
return {
"template_key": COMPOSITE_RULE_TEMPLATE_KEY,
"field_keys": field_keys,
"description": str(semantic_plan.get("rule_intent") or "").strip(),
"condition_summary": "".join(text_parts)[:800],
"keywords": [],
"rule_ir": {
"facts": field_keys,
"conditions": text_parts,
"hit_logic": "由 DSL validator 根据字段本体和语义步骤生成受控条件",
},
}
def _semantic_text_parts(semantic_plan: dict[str, Any]) -> list[str]:
parts: list[str] = []
for key in ("rule_intent", "scope", "judgment_steps", "exception_conditions", "risk_action"):
parts.extend(_flatten_semantic_text(semantic_plan.get(key)))
return [item for index, item in enumerate(parts) if item and item not in parts[:index]]
def _semantic_field_keys(semantic_plan: dict[str, Any]) -> list[str]:
keys: list[str] = []
for value in (semantic_plan.get("required_fields"), semantic_plan.get("fields")):
for item in value if isinstance(value, list) else []:
key = item if isinstance(item, str) else next(
(item.get(name) for name in ("field", "key", "field_key") if isinstance(item, dict) and item.get(name)),
"",
)
text = str(key or "").strip()
if "." in text and text not in keys:
keys.append(text)
return keys
def _flatten_semantic_text(value: Any) -> list[str]:
if isinstance(value, str):
return [value.strip()] if value.strip() else []
if isinstance(value, list):
return [item for value_item in value for item in _flatten_semantic_text(value_item)]
if isinstance(value, dict):
return [item for value_item in value.values() for item in _flatten_semantic_text(value_item)]
return []