feat: 统一后端分页查询与前端服务层适配
后端新增通用分页模块,为报销单、员工、预算、agent 资产等 端点统一接入分页参数和游标查询,优化 repository 层分页实 现,前端服务层适配分页响应结构,完善预算图表和全局样式, 优化侧边栏和企业选择器组件,引入 Element Plus 插件注册。
This commit is contained in:
@@ -156,6 +156,22 @@ def test_agent_asset_service_seeds_all_foundation_asset_types() -> None:
|
||||
assert len(service.list_assets(asset_type=AgentAssetType.TASK.value)) >= 3
|
||||
|
||||
|
||||
def test_agent_asset_service_supports_backend_pagination() -> None:
|
||||
with build_session() as db:
|
||||
service = AgentAssetService(db)
|
||||
|
||||
page = service.list_assets_page(
|
||||
asset_type=AgentAssetType.RULE.value,
|
||||
page=1,
|
||||
page_size=2,
|
||||
)
|
||||
|
||||
assert len(page.items) <= 2
|
||||
assert page.total >= len(page.items)
|
||||
assert page.page == 1
|
||||
assert page.page_size == 2
|
||||
|
||||
|
||||
def test_finance_rules_use_risk_rule_scenario_categories() -> None:
|
||||
with build_session() as db:
|
||||
service = AgentAssetService(db)
|
||||
|
||||
136
server/tests/test_backend_pagination.py
Normal file
136
server/tests/test_backend_pagination.py
Normal file
@@ -0,0 +1,136 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Generator
|
||||
from datetime import UTC, datetime
|
||||
from decimal import Decimal
|
||||
|
||||
from fastapi.testclient import TestClient
|
||||
from sqlalchemy import create_engine
|
||||
from sqlalchemy.orm import Session, sessionmaker
|
||||
from sqlalchemy.pool import StaticPool
|
||||
|
||||
from app.api.deps import get_db
|
||||
from app.db.base import Base
|
||||
from app.main import create_app
|
||||
from app.models.employee import Employee
|
||||
from app.models.financial_record import ExpenseClaim
|
||||
|
||||
|
||||
def build_client() -> tuple[TestClient, sessionmaker[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)
|
||||
app = create_app()
|
||||
|
||||
def override_db() -> Generator[Session, None, None]:
|
||||
db = session_factory()
|
||||
try:
|
||||
yield db
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
app.dependency_overrides[get_db] = override_db
|
||||
return TestClient(app), session_factory
|
||||
|
||||
|
||||
def seed_claims(db: Session) -> None:
|
||||
employee = Employee(
|
||||
id="emp-page",
|
||||
employee_no="E-PAGE",
|
||||
name="Page User",
|
||||
email="page-user@example.com",
|
||||
position="Analyst",
|
||||
grade="P4",
|
||||
)
|
||||
db.add(employee)
|
||||
for index in range(3):
|
||||
db.add(
|
||||
ExpenseClaim(
|
||||
id=f"claim-page-{index}",
|
||||
claim_no=f"EXP-PAGE-{index}",
|
||||
employee_id=employee.id,
|
||||
employee_name=employee.name,
|
||||
department_id="dept-page",
|
||||
department_name="Market",
|
||||
project_code=None,
|
||||
expense_type="office",
|
||||
reason=f"Office purchase {index}",
|
||||
location="Shanghai",
|
||||
amount=Decimal("100.00"),
|
||||
currency="CNY",
|
||||
invoice_count=0,
|
||||
occurred_at=datetime(2026, 5, 20 + index, tzinfo=UTC),
|
||||
submitted_at=None,
|
||||
status="draft",
|
||||
approval_stage="draft",
|
||||
risk_flags_json=[],
|
||||
created_at=datetime(2026, 5, 20 + index, tzinfo=UTC),
|
||||
updated_at=datetime(2026, 5, 20 + index, tzinfo=UTC),
|
||||
)
|
||||
)
|
||||
db.commit()
|
||||
|
||||
|
||||
def test_expense_claims_support_page_envelope_and_keep_legacy_list() -> None:
|
||||
client, session_factory = build_client()
|
||||
with session_factory() as db:
|
||||
seed_claims(db)
|
||||
|
||||
headers = {"x-auth-username": "E-PAGE", "x-auth-name": "Page User"}
|
||||
legacy_response = client.get("/api/v1/reimbursements/claims", headers=headers)
|
||||
assert legacy_response.status_code == 200
|
||||
assert isinstance(legacy_response.json(), list)
|
||||
|
||||
page_response = client.get(
|
||||
"/api/v1/reimbursements/claims?page=1&page_size=2",
|
||||
headers=headers,
|
||||
)
|
||||
|
||||
assert page_response.status_code == 200
|
||||
payload = page_response.json()
|
||||
assert [key for key in payload if key in {"items", "total", "page", "page_size"}] == [
|
||||
"items",
|
||||
"total",
|
||||
"page",
|
||||
"page_size",
|
||||
]
|
||||
assert len(payload["items"]) == 2
|
||||
assert payload["total"] == 3
|
||||
assert payload["page"] == 1
|
||||
assert payload["page_size"] == 2
|
||||
assert payload["total_pages"] == 2
|
||||
assert payload["has_next"] is True
|
||||
assert payload["has_previous"] is False
|
||||
|
||||
|
||||
def test_employee_directory_supports_backend_pagination() -> None:
|
||||
client, _ = build_client()
|
||||
|
||||
response = client.get("/api/v1/employees?page=2&page_size=10")
|
||||
|
||||
assert response.status_code == 200
|
||||
payload = response.json()
|
||||
assert len(payload["items"]) == 10
|
||||
assert payload["total"] >= 30
|
||||
assert payload["page"] == 2
|
||||
assert payload["page_size"] == 10
|
||||
|
||||
|
||||
def test_budget_allocations_support_backend_pagination() -> None:
|
||||
client, _ = build_client()
|
||||
|
||||
response = client.get(
|
||||
"/api/v1/budgets/allocations?page=1&page_size=2",
|
||||
headers={"x-auth-username": "admin", "x-auth-role-codes": "manager"},
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
payload = response.json()
|
||||
assert len(payload["items"]) <= 2
|
||||
assert payload["total"] >= len(payload["items"])
|
||||
assert payload["page"] == 1
|
||||
assert payload["page_size"] == 2
|
||||
Reference in New Issue
Block a user