Files
X-Financial/server/tests/test_hermes_employee_profile_baselines.py
caoxiaozhu 7989f3a159 feat: 新增风险图谱算法与系统仪表盘及操作反馈体系
后端新增风险图谱算法模块、风险观察与反馈服务、规则 DSL
校验器和可解释性引擎,完善系统仪表盘和财务仪表盘统计,
优化 agent 运行和编排执行链路,清理旧开发文档,前端新增
系统趋势、负载热力图等多种仪表盘图表组件,完善操作反馈
对话框和工作台日期选择器,优化报销创建和审批详情交互,
补充单元测试覆盖。
2026-05-30 15:46:51 +08:00

125 lines
4.0 KiB
Python

from __future__ import annotations
from datetime import UTC, date, datetime, timedelta
from decimal import Decimal
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.employee import Employee
from app.models.employee_behavior_profile import EmployeeBehaviorProfileSnapshot
from app.models.financial_record import ExpenseClaim, ExpenseClaimItem
from app.models.organization import OrganizationUnit
from app.services.hermes_employee_profile_scanner import HermesEmployeeProfileScannerService
def test_hermes_employee_profile_scan_returns_profile_baseline_summary() -> None:
session_factory = _build_session_factory()
with session_factory() as db:
_seed_scan_data(db)
summary = HermesEmployeeProfileScannerService(db).scan_employee_profiles(log_id=None)
assert summary["target_employee_count"] == 3
assert db.query(EmployeeBehaviorProfileSnapshot).count() >= 12
baseline_summary = summary["baseline_summary"]
assert baseline_summary["dimension_counts"]["employee"] == 3
assert baseline_summary["dimension_counts"]["department"] == 1
assert baseline_summary["dimension_counts"]["supplier"] == 2
assert baseline_summary["dimension_counts"]["expense_type"] == 2
assert any(
bucket["dimension"] == "supplier" and bucket["key"] == "s-hotel"
for bucket in baseline_summary["buckets"]
)
def _build_session_factory() -> sessionmaker[Session]:
engine = create_engine(
"sqlite+pysqlite:///:memory:",
connect_args={"check_same_thread": False},
poolclass=StaticPool,
)
Base.metadata.create_all(bind=engine)
return sessionmaker(bind=engine, autoflush=False, autocommit=False)
def _seed_scan_data(db: Session) -> None:
org = OrganizationUnit(
id="dept-sales",
unit_code="SALES",
name="市场部",
unit_type="department",
)
employees = [
Employee(
id=f"emp-{index}",
employee_no=f"E10{index}",
name=f"员工{index}",
email=f"emp{index}@example.com",
position="客户经理",
grade="P5",
organization_unit=org,
)
for index in range(1, 4)
]
db.add(org)
db.add_all(employees)
now = datetime.now(UTC)
claims = [
_claim("c1", employees[0], "travel", "600", "s-hotel", "Hotel A", now),
_claim("c2", employees[1], "travel", "900", "s-hotel", "Hotel A", now),
_claim("c3", employees[2], "meal", "300", "s-meal", "Meal B", now),
]
db.add_all(claims)
db.commit()
def _claim(
claim_id: str,
employee: Employee,
expense_type: str,
amount: str,
supplier_id: str,
supplier_name: str,
now: datetime,
) -> ExpenseClaim:
return ExpenseClaim(
id=claim_id,
claim_no=f"EXP-{claim_id}",
employee_id=employee.id,
employee_name=employee.name,
department_id="dept-sales",
department_name="市场部",
project_code="PRJ-001",
expense_type=expense_type,
reason="客户拜访",
location="北京",
amount=Decimal(amount),
currency="CNY",
invoice_count=1,
occurred_at=now - timedelta(days=5),
submitted_at=now - timedelta(days=5),
status="submitted",
approval_stage="直属领导审批",
risk_flags_json=[
{
"supplier_id": supplier_id,
"supplier_name": supplier_name,
}
],
items=[
ExpenseClaimItem(
id=f"item-{claim_id}",
claim_id=claim_id,
item_date=date.today(),
item_type=expense_type,
item_reason="客户拜访",
item_location="北京",
item_amount=Decimal(amount),
invoice_id=f"invoice-{claim_id}",
)
],
)