feat: 数字员工财务报告体系与定时提醒及看板快照调度

- 新增数字员工财务报告生成、邮件投递与渲染调度器
- 引入员工画像扫描调度与定时提醒任务
- 完善财务看板快照、排行口径与部门人员占比计算
- 优化数字员工工作看板仪表盘与技能目录
- 增强前端总览页图表、工作台摘要与顶部导航栏交互
- 新增差旅申请规划推动提醒与报销创建会话状态管理
- 补充财务报告、看板调度、数字员工工作记录测试覆盖
This commit is contained in:
caoxiaozhu
2026-06-03 09:25:23 +08:00
parent 0c74b4ab4a
commit 15006a05a7
114 changed files with 7356 additions and 650 deletions

View File

@@ -28,7 +28,9 @@ TASK_CODE_TO_TYPE = {
"task.hermes.false_positive_sample_accumulate": "false_positive_sample_accumulate",
"task.hermes.risk_feedback_sample_accumulate": "risk_feedback_sample_accumulate",
"task.hermes.multi_evidence_consistency_evaluate": "multi_evidence_consistency_evaluate",
"task.hermes.travel_spatiotemporal_consistency_evaluate": "travel_spatiotemporal_consistency_evaluate",
"task.hermes.travel_spatiotemporal_consistency_evaluate": (
"travel_spatiotemporal_consistency_evaluate"
),
"task.hermes.budget_overrun_precontrol_evaluate": "budget_overrun_precontrol_evaluate",
"task.hermes.supplier_abnormal_relation_evaluate": "supplier_abnormal_relation_evaluate",
"task.hermes.risk_algorithm_replay_evaluate": "risk_algorithm_replay_evaluate",
@@ -136,6 +138,16 @@ TASK_SPECS: dict[str, dict[str, str]] = {
"category": "升级",
"color": "var(--chart-amber)",
},
"finance_dashboard_snapshot": {
"label": "财务看板指标快照",
"category": "积累",
"color": "var(--chart-blue)",
},
"digital_employee_reminder_scan": {
"label": "定时提醒扫描",
"category": "整理",
"color": "var(--success)",
},
}
CATEGORY_SPECS = {
@@ -203,6 +215,8 @@ class DigitalEmployeeDashboardService:
+ metrics["risk_clues"]
+ metrics["profile_snapshots"]
+ metrics["knowledge_documents"]
+ metrics["finance_snapshots"]
+ metrics["reminders"]
)
return {
@@ -216,6 +230,8 @@ class DigitalEmployeeDashboardService:
"riskClues": metrics["risk_clues"],
"profileSnapshots": metrics["profile_snapshots"],
"knowledgeDocuments": metrics["knowledge_documents"],
"financeDashboardSnapshots": metrics["finance_snapshots"],
"reminders": metrics["reminders"],
"successRate": self._percent(success_runs, total_runs),
"failureRate": self._percent(failed_runs, total_runs),
}
@@ -232,6 +248,8 @@ class DigitalEmployeeDashboardService:
"riskClues": 0,
"profileSnapshots": 0,
"knowledgeDocuments": 0,
"financeDashboardSnapshots": 0,
"reminders": 0,
"businessOutputs": 0,
}
for label in labels
@@ -254,11 +272,15 @@ class DigitalEmployeeDashboardService:
row["riskClues"] += metrics["risk_clues"]
row["profileSnapshots"] += metrics["profile_snapshots"]
row["knowledgeDocuments"] += metrics["knowledge_documents"]
row["financeDashboardSnapshots"] += metrics["finance_snapshots"]
row["reminders"] += metrics["reminders"]
row["businessOutputs"] += (
metrics["risk_observations"]
+ metrics["risk_clues"]
+ metrics["profile_snapshots"]
+ metrics["knowledge_documents"]
+ metrics["finance_snapshots"]
+ metrics["reminders"]
)
return [rows[label] for label in labels]
@@ -374,6 +396,12 @@ class DigitalEmployeeDashboardService:
summary,
("snapshot_count", "profile_snapshot_count", "profile_snapshots"),
)
if self._resolve_task_type(run) == "finance_dashboard_snapshot":
metrics["profile_snapshots"] = 0
metrics["finance_snapshots"] = self._first_int(
summary,
("finance_snapshot_count", "dashboard_snapshot_count"),
)
metrics["knowledge_documents"] = max(
self._first_int(
summary,
@@ -383,9 +411,21 @@ class DigitalEmployeeDashboardService:
self._list_length(route_json, ("document_ids", "requested_document_ids")),
)
metrics["scanned_claims"] = self._first_int(summary, ("scanned_claim_count", "claim_count"))
metrics["target_employees"] = self._first_int(summary, ("target_employee_count", "employee_count"))
metrics["target_employees"] = self._first_int(
summary,
("target_employee_count", "employee_count"),
)
metrics["rule_hits"] = self._first_int(summary, ("rule_hit_count", "rule_hits"))
metrics["facts"] = self._first_int(summary, ("fact_count", "facts"))
metrics["reminders"] = self._first_int(
summary,
(
"reminder_count",
"reminders",
"approval_pending_count",
"budget_reminder_count",
),
)
return metrics
@staticmethod
@@ -394,11 +434,13 @@ class DigitalEmployeeDashboardService:
"risk_observations": 0,
"risk_clues": 0,
"profile_snapshots": 0,
"finance_snapshots": 0,
"knowledge_documents": 0,
"scanned_claims": 0,
"target_employees": 0,
"rule_hits": 0,
"facts": 0,
"reminders": 0,
}
def _extract_run_summary(self, run: AgentRun) -> dict[str, Any]:
@@ -413,7 +455,9 @@ class DigitalEmployeeDashboardService:
def _matched_tool_call(self, run: AgentRun, task_type: str) -> AgentToolCall | None:
digital_tools = [
tool for tool in run.tool_calls if str(tool.tool_name or "").startswith("digital_employee.")
tool
for tool in run.tool_calls
if str(tool.tool_name or "").startswith("digital_employee.")
]
for tool in run.tool_calls:
candidates = [
@@ -441,7 +485,10 @@ class DigitalEmployeeDashboardService:
route_json = run.route_json or {}
if str(route_json.get("selected_agent") or "").strip() == AgentName.HERMES.value:
return True
return any(str(tool.tool_name or "").startswith("digital_employee.") for tool in run.tool_calls)
return any(
str(tool.tool_name or "").startswith("digital_employee.")
for tool in run.tool_calls
)
def _resolve_task_type(self, run: AgentRun) -> str:
route_json = run.route_json or {}
@@ -491,6 +538,8 @@ class DigitalEmployeeDashboardService:
return "global_risk_scan"
if "employee_behavior_profile" in name:
return "employee_behavior_profile_scan"
if "reminder" in name:
return "digital_employee_reminder_scan"
if "finance_policy_knowledge" in name:
return "finance_policy_knowledge_organize"
if "risk_clue" in name: