Files
track/backend/app/api/v1/endpoints/analytics.py
duxingchen 54b5ea00f1 feat: 新增效能分析看板(个人能力图谱 + 设备人员耗时对比)
- 前端:AnalyticsDashboard 页面 + BaseEChart 封装 + analyticsApi,路由与导航
- 后端:/analytics 系列接口(capability/flow/options/device-records)
- 能力图谱单机颗粒度、流转对比按人堆叠识别瓶颈、点击下钻备注、筛选动态联动
- 依赖:引入 echarts,移除 echarts-for-react
2026-08-14 10:19:44 +08:00

70 lines
3.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""效能分析 API — ECharts 数据源(个人能力图谱 / 设备流转对比 / 筛选选项)"""
from datetime import datetime
from fastapi import APIRouter, Depends, Query
from sqlalchemy.ext.asyncio import AsyncSession
from app.core.database import get_db
from app.services.analytics_service import (
get_capability_profile, CapabilityResponse,
get_flow_compare, FlowResponse,
get_analytics_options, AnalyticsOptions,
get_device_records, DeviceRecord,
)
router = APIRouter(prefix="/analytics", tags=["效能分析"])
@router.get("/capability", response_model=CapabilityResponse)
async def capability_profile(
assignee_ids: str | None = Query(None, description="负责人ID逗号分隔"),
spec_models: str | None = Query(None, description="规格型号,逗号分隔(可选)"),
since: str | None = Query(None, description="起始日期 ISO"),
until: str | None = Query(None, description="截止日期 ISO"),
db: AsyncSession = Depends(get_db),
):
"""个人能力图谱 — X 轴=设备身份证,分组柱状图(单台设备总耗时)。"""
since_dt = datetime.fromisoformat(since) if since else None
until_dt = datetime.fromisoformat(until) if until else None
ids = [s.strip() for s in assignee_ids.split(",") if s.strip()] if assignee_ids else None
specs = [s.strip() for s in spec_models.split(",") if s.strip()] if spec_models else None
return await get_capability_profile(
db, assignee_ids=ids, spec_models=specs,
since=since_dt, until=until_dt,
)
@router.get("/flow", response_model=FlowResponse)
async def flow_compare(
product_sns: str | None = Query(None, description="身份证,逗号分隔"),
spec_models: str | None = Query(None, description="规格型号,逗号分隔(无 product_sns 时按型号取最近设备)"),
db: AsyncSession = Depends(get_db),
):
"""设备流转对比 — 每台设备各操作人耗时(堆叠柱状,按人堆叠,识别瓶颈)。"""
sns = [s.strip() for s in product_sns.split(",") if s.strip()] if product_sns else None
specs = [s.strip() for s in spec_models.split(",") if s.strip()] if spec_models else None
return await get_flow_compare(db, product_sns=sns, spec_models=specs)
@router.get("/options", response_model=AnalyticsOptions)
async def analytics_options(
assignee_ids: str | None = Query(None, description="负责人ID逗号分隔联动过滤型号"),
spec_models: str | None = Query(None, description="规格型号,逗号分隔(联动过滤人员)"),
db: AsyncSession = Depends(get_db),
):
"""顶部筛选栏选项 — 负责人 + 规格型号,支持动态联动。"""
ids = [s.strip() for s in assignee_ids.split(",") if s.strip()] if assignee_ids else None
specs = [s.strip() for s in spec_models.split(",") if s.strip()] if spec_models else None
return await get_analytics_options(db, assignee_ids=ids, spec_models=specs)
@router.get("/device-records", response_model=list[DeviceRecord])
async def device_records(
product_sn: str = Query(..., description="设备身份证"),
assignee_ids: str | None = Query(None, description="负责人ID逗号分隔可选用于过滤"),
db: AsyncSession = Depends(get_db),
):
"""某台设备的任务备注记录(含图片);可选按负责人过滤。"""
ids = [s.strip() for s in assignee_ids.split(",") if s.strip()] if assignee_ids else None
return await get_device_records(db, product_sn, assignee_ids=ids)