feat(效能分析): 轨迹流转图时间轴支持工作日/自然天切换
- flow 接口加 mode 参数,workdays 模式把时间偏移换算为排除周末/节假日的工作小时 - 轨迹流转图的横轴随顶部按钮(自然天/工作日)切换,休息日被压缩 - 按钮切换自动重新请求 flow
This commit is contained in:
@ -38,12 +38,13 @@ async def capability_profile(
|
||||
async def flow_compare(
|
||||
product_sns: str | None = Query(None, description="身份证,逗号分隔"),
|
||||
spec_models: str | None = Query(None, description="规格型号,逗号分隔(无 product_sns 时按型号取最近设备)"),
|
||||
mode: str = Query("natural", description="时间口径: natural(自然小时) / workdays(工作小时,排除周末节假日)"),
|
||||
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)
|
||||
return await get_flow_compare(db, product_sns=sns, spec_models=specs, mode=mode)
|
||||
|
||||
|
||||
@router.get("/options", response_model=AnalyticsOptions)
|
||||
|
||||
@ -280,6 +280,7 @@ async def get_flow_compare(
|
||||
db: AsyncSession,
|
||||
product_sns: list[str] | None = None,
|
||||
spec_models: list[str] | None = None,
|
||||
mode: str = "natural",
|
||||
) -> FlowResponse:
|
||||
"""
|
||||
查询每台设备上各操作人的任务时间区间,拼装为「生命周期时间轴」区间图:
|
||||
@ -426,8 +427,18 @@ async def get_flow_compare(
|
||||
|
||||
# ── 按人分组,转为相对 T0 的小时偏移区间 ──
|
||||
by_assignee: dict[str, list[list]] = {}
|
||||
# 工作日模式:把时间偏移换算为「排除周末/节假日的工作小时」,压缩休息日
|
||||
if mode == "workdays":
|
||||
from app.core.time_utils import working_duration_hours as _wdh
|
||||
from app.models.holiday import Holiday as _Holiday
|
||||
hres = await db.execute(select(_Holiday.day))
|
||||
_holidays = {r[0] for r in hres}
|
||||
for it in intervals:
|
||||
t0 = t0_by_device[it["idx"]]
|
||||
if mode == "workdays":
|
||||
start_offset = _wdh(t0, it["start"], _holidays)
|
||||
end_offset = _wdh(t0, it["end"], _holidays)
|
||||
else:
|
||||
start_offset = round((it["start"] - t0).total_seconds() / 3600, 1)
|
||||
end_offset = round((it["end"] - t0).total_seconds() / 3600, 1)
|
||||
duration = round(end_offset - start_offset, 1)
|
||||
|
||||
@ -127,8 +127,9 @@ export default function AnalyticsDashboard() {
|
||||
const q: FlowQuery = {};
|
||||
if (productSns.length) q.product_sns = productSns;
|
||||
if (specModels.length) q.spec_models = specModels;
|
||||
q.mode = totalDaysMode; // 自然天 / 工作日,控制轨迹流转图时间口径
|
||||
return q;
|
||||
}, [productSns, specModels]);
|
||||
}, [productSns, specModels, totalDaysMode]);
|
||||
|
||||
// ─── 能力图谱(需选择人员才发起) ──
|
||||
const loadCapability = useCallback(async () => {
|
||||
|
||||
@ -103,6 +103,7 @@ export interface CapabilityQuery {
|
||||
export interface FlowQuery {
|
||||
product_sns?: string[];
|
||||
spec_models?: string[];
|
||||
mode?: "natural" | "workdays";
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
@ -123,6 +124,7 @@ export async function fetchFlowData(query: FlowQuery): Promise<FlowResponse> {
|
||||
const params: Record<string, string> = {};
|
||||
if (query.product_sns?.length) params.product_sns = query.product_sns.join(",");
|
||||
if (query.spec_models?.length) params.spec_models = query.spec_models.join(",");
|
||||
if (query.mode) params.mode = query.mode;
|
||||
const { data } = await api.get<FlowResponse>("/analytics/flow", { params });
|
||||
return data;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user