feat(效能分析): 轨迹流转图时间轴支持工作日/自然天切换

- flow 接口加 mode 参数,workdays 模式把时间偏移换算为排除周末/节假日的工作小时
- 轨迹流转图的横轴随顶部按钮(自然天/工作日)切换,休息日被压缩
- 按钮切换自动重新请求 flow
This commit is contained in:
2026-08-28 15:34:17 +08:00
parent 7f1858387c
commit 4447a1f52d
4 changed files with 19 additions and 4 deletions

View File

@ -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)

View File

@ -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)

View File

@ -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 () => {

View File

@ -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;
}