- 新增数字员工财务报告生成、邮件投递与渲染调度器 - 引入员工画像扫描调度与定时提醒任务 - 完善财务看板快照、排行口径与部门人员占比计算 - 优化数字员工工作看板仪表盘与技能目录 - 增强前端总览页图表、工作台摘要与顶部导航栏交互 - 新增差旅申请规划推动提醒与报销创建会话状态管理 - 补充财务报告、看板调度、数字员工工作记录测试覆盖
249 lines
10 KiB
Python
249 lines
10 KiB
Python
from __future__ import annotations
|
|
|
|
from datetime import UTC, datetime, timedelta
|
|
|
|
from sqlalchemy import create_engine
|
|
from sqlalchemy.orm import Session, sessionmaker
|
|
from sqlalchemy.pool import StaticPool
|
|
|
|
from app.db.base import Base
|
|
from app.models.agent_run import AgentRun, AgentToolCall
|
|
from app.services.digital_employee_dashboard import DigitalEmployeeDashboardService
|
|
|
|
|
|
def build_session() -> Session:
|
|
engine = create_engine(
|
|
"sqlite+pysqlite:///:memory:",
|
|
connect_args={"check_same_thread": False},
|
|
poolclass=StaticPool,
|
|
)
|
|
Base.metadata.create_all(bind=engine)
|
|
session_factory = sessionmaker(bind=engine, autoflush=False, autocommit=False)
|
|
return session_factory()
|
|
|
|
|
|
def test_digital_employee_dashboard_aggregates_daily_work_from_agent_runs() -> None:
|
|
now = datetime.now(UTC)
|
|
|
|
with build_session() as db:
|
|
db.add_all(
|
|
[
|
|
AgentRun(
|
|
run_id="run-digital-risk-001",
|
|
agent="hermes",
|
|
source="schedule",
|
|
user_id="system",
|
|
status="succeeded",
|
|
route_json={"task_code": "task.hermes.global_risk_scan"},
|
|
result_summary="财务风险图谱巡检完成。",
|
|
started_at=now - timedelta(hours=4),
|
|
finished_at=now - timedelta(hours=4, minutes=-3),
|
|
tool_calls=[
|
|
AgentToolCall(
|
|
run_id="run-digital-risk-001",
|
|
tool_type="rule_engine",
|
|
tool_name="digital_employee.financial_risk_graph.scan",
|
|
request_json={"task_type": "global_risk_scan"},
|
|
response_json={
|
|
"scanned_claim_count": 18,
|
|
"risk_observation_count": 3,
|
|
},
|
|
status="succeeded",
|
|
duration_ms=1200,
|
|
created_at=now - timedelta(hours=4),
|
|
)
|
|
],
|
|
),
|
|
AgentRun(
|
|
run_id="run-digital-clue-001",
|
|
agent="hermes",
|
|
source="schedule",
|
|
user_id="system",
|
|
status="failed",
|
|
route_json={"report_type": "risk_clue_collect"},
|
|
result_summary="风险线索归集失败。",
|
|
started_at=now - timedelta(hours=3),
|
|
finished_at=now - timedelta(hours=3, minutes=-1),
|
|
tool_calls=[
|
|
AgentToolCall(
|
|
run_id="run-digital-clue-001",
|
|
tool_type="database",
|
|
tool_name="digital_employee.risk_clue.collect",
|
|
request_json={"task_type": "risk_clue_collect"},
|
|
response_json={
|
|
"fact_count": 12,
|
|
"rule_hit_count": 5,
|
|
"risk_clue_count": 2,
|
|
},
|
|
status="failed",
|
|
duration_ms=800,
|
|
error_message="collector failed",
|
|
created_at=now - timedelta(hours=3),
|
|
)
|
|
],
|
|
),
|
|
AgentRun(
|
|
run_id="run-digital-knowledge-001",
|
|
agent="hermes",
|
|
source="user_message",
|
|
user_id="admin",
|
|
status="running",
|
|
route_json={
|
|
"job_type": "knowledge_index_sync",
|
|
"requested_document_ids": ["doc-1", "doc-2"],
|
|
},
|
|
result_summary="知识归纳任务已入队。",
|
|
started_at=now - timedelta(hours=1),
|
|
),
|
|
AgentRun(
|
|
run_id="run-user-001",
|
|
agent="user_agent",
|
|
source="user_message",
|
|
user_id="employee",
|
|
status="succeeded",
|
|
result_summary="普通报销预审。",
|
|
started_at=now - timedelta(hours=2),
|
|
finished_at=now - timedelta(hours=2, minutes=-1),
|
|
tool_calls=[
|
|
AgentToolCall(
|
|
run_id="run-user-001",
|
|
tool_type="llm",
|
|
tool_name="expense_claim.review",
|
|
request_json={},
|
|
response_json={},
|
|
status="succeeded",
|
|
duration_ms=500,
|
|
created_at=now - timedelta(hours=2),
|
|
)
|
|
],
|
|
),
|
|
]
|
|
)
|
|
db.commit()
|
|
|
|
dashboard = DigitalEmployeeDashboardService(db).build_dashboard(days=7)
|
|
|
|
assert dashboard.has_real_data is True
|
|
assert dashboard.totals["totalRuns"] == 3
|
|
assert dashboard.totals["successRuns"] == 1
|
|
assert dashboard.totals["failedRuns"] == 1
|
|
assert dashboard.totals["runningRuns"] == 1
|
|
assert dashboard.totals["toolCalls"] == 2
|
|
assert dashboard.totals["riskObservations"] == 3
|
|
assert dashboard.totals["riskClues"] == 2
|
|
assert dashboard.totals["knowledgeDocuments"] == 2
|
|
assert dashboard.totals["businessOutputs"] == 7
|
|
assert dashboard.totals["successRate"] == 33.3
|
|
|
|
category_counts = {item["name"]: item["count"] for item in dashboard.category_distribution}
|
|
assert category_counts["评估"] == 1
|
|
assert category_counts["升级"] == 1
|
|
assert category_counts["整理"] == 1
|
|
assert category_counts["积累"] == 0
|
|
|
|
task_names = {item["name"] for item in dashboard.task_distribution}
|
|
assert task_names == {"财务风险图谱巡检", "风险线索归集", "知识制度整理"}
|
|
|
|
assert sum(item["total"] for item in dashboard.daily_work) == 3
|
|
assert dashboard.recent_runs[0]["runId"] == "run-digital-knowledge-001"
|
|
assert dashboard.recent_runs[0]["statusLabel"] == "运行中"
|
|
|
|
|
|
def test_digital_employee_dashboard_keeps_empty_payload_without_fake_data() -> None:
|
|
with build_session() as db:
|
|
dashboard = DigitalEmployeeDashboardService(db).build_dashboard(days=7)
|
|
|
|
assert dashboard.has_real_data is False
|
|
assert dashboard.totals["totalRuns"] == 0
|
|
assert dashboard.daily_work
|
|
assert dashboard.task_distribution == []
|
|
|
|
|
|
def test_digital_employee_dashboard_counts_finance_dashboard_snapshots() -> None:
|
|
now = datetime.now(UTC)
|
|
|
|
with build_session() as db:
|
|
db.add(
|
|
AgentRun(
|
|
run_id="run-finance-snapshot-001",
|
|
agent="hermes",
|
|
source="schedule",
|
|
user_id="digital_employee",
|
|
status="succeeded",
|
|
route_json={"task_type": "finance_dashboard_snapshot"},
|
|
result_summary="finance dashboard snapshot generated",
|
|
started_at=now - timedelta(minutes=3),
|
|
finished_at=now - timedelta(minutes=2),
|
|
tool_calls=[
|
|
AgentToolCall(
|
|
run_id="run-finance-snapshot-001",
|
|
tool_type="database",
|
|
tool_name="digital_employee.finance_dashboard.snapshot",
|
|
request_json={"task_type": "finance_dashboard_snapshot"},
|
|
response_json={
|
|
"summary": {
|
|
"finance_snapshot_count": 1,
|
|
"reimbursement_count": 534,
|
|
}
|
|
},
|
|
status="succeeded",
|
|
duration_ms=1200,
|
|
created_at=now - timedelta(minutes=3),
|
|
)
|
|
],
|
|
)
|
|
)
|
|
db.commit()
|
|
|
|
dashboard = DigitalEmployeeDashboardService(db).build_dashboard(days=7)
|
|
|
|
assert dashboard.totals["financeDashboardSnapshots"] == 1
|
|
assert dashboard.totals["businessOutputs"] == 1
|
|
assert dashboard.daily_work[-1]["financeDashboardSnapshots"] == 1
|
|
assert dashboard.task_distribution[0]["taskType"] == "finance_dashboard_snapshot"
|
|
|
|
|
|
def test_digital_employee_dashboard_counts_reminder_outputs() -> None:
|
|
now = datetime.now(UTC)
|
|
|
|
with build_session() as db:
|
|
db.add(
|
|
AgentRun(
|
|
run_id="run-reminder-scan-001",
|
|
agent="hermes",
|
|
source="schedule",
|
|
user_id="digital_employee",
|
|
status="succeeded",
|
|
route_json={"task_type": "digital_employee_reminder_scan"},
|
|
result_summary="reminder scan generated",
|
|
started_at=now - timedelta(minutes=3),
|
|
finished_at=now - timedelta(minutes=2),
|
|
tool_calls=[
|
|
AgentToolCall(
|
|
run_id="run-reminder-scan-001",
|
|
tool_type="database",
|
|
tool_name="digital_employee.reminder.scan",
|
|
request_json={"task_type": "digital_employee_reminder_scan"},
|
|
response_json={
|
|
"summary": {
|
|
"recipient_count": 3,
|
|
"reminder_count": 8,
|
|
"approval_pending_count": 2,
|
|
}
|
|
},
|
|
status="succeeded",
|
|
duration_ms=900,
|
|
created_at=now - timedelta(minutes=3),
|
|
)
|
|
],
|
|
)
|
|
)
|
|
db.commit()
|
|
|
|
dashboard = DigitalEmployeeDashboardService(db).build_dashboard(days=7)
|
|
|
|
assert dashboard.totals["reminders"] == 8
|
|
assert dashboard.totals["businessOutputs"] == 8
|
|
assert dashboard.daily_work[-1]["reminders"] == 8
|
|
assert dashboard.task_distribution[0]["taskType"] == "digital_employee_reminder_scan"
|