feat: 效能分析flow区间携带自然/工作日双时长并支持时间过滤
This commit is contained in:
@ -40,12 +40,16 @@ 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(工作小时,排除周末节假日)"),
|
||||
since: str | None = Query(None, description="起始日期 ISO"),
|
||||
until: str | None = Query(None, description="截止日期 ISO"),
|
||||
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, mode=mode)
|
||||
since_dt = datetime.fromisoformat(since) if since else None
|
||||
until_dt = datetime.fromisoformat(until) if until else None
|
||||
return await get_flow_compare(db, product_sns=sns, spec_models=specs, mode=mode, since=since_dt, until=until_dt)
|
||||
|
||||
|
||||
@router.get("/options", response_model=AnalyticsOptions)
|
||||
|
||||
@ -285,6 +285,8 @@ async def get_flow_compare(
|
||||
product_sns: list[str] | None = None,
|
||||
spec_models: list[str] | None = None,
|
||||
mode: str = "natural",
|
||||
since: datetime | None = None,
|
||||
until: datetime | None = None,
|
||||
) -> FlowResponse:
|
||||
"""
|
||||
查询每台设备上各操作人的任务时间区间,拼装为「生命周期时间轴」区间图:
|
||||
@ -296,7 +298,11 @@ async def get_flow_compare(
|
||||
- 仅传 spec_models:这些型号下最近有流转的 20 台设备;
|
||||
- 都未传:返回空。
|
||||
|
||||
data 每项 = [device_index, start_offset, end_offset, task_name, duration](单位小时)。
|
||||
data 每项 = [device_index, task_name, is_main,
|
||||
start_nat, end_nat, start_work, end_work,
|
||||
duration_total(自然), duration_work(工作日)](单位小时)。
|
||||
前端按 isWorkday 切换选取自然/工作日偏移,即时重绘无需重新请求。
|
||||
mode 参数保留兼容(历史调用),不再影响返回内容。
|
||||
"""
|
||||
from app.models.task import Task, TASK_STATUS_WIP, TASK_STATUS_PENDING, TASK_STATUS_COMPLETED
|
||||
from app.models.product import Product
|
||||
@ -382,6 +388,11 @@ async def get_flow_compare(
|
||||
end = _to_bj(completed_at) if completed_at else now
|
||||
if start is None:
|
||||
continue
|
||||
# 时间筛选:任务区间与 [since, until] 有交集
|
||||
if since is not None and end is not None and end < since:
|
||||
continue
|
||||
if until is not None and start is not None and start > until:
|
||||
continue
|
||||
is_main = 0 if task_type == "SPAWN" else 1
|
||||
intervals.append({
|
||||
"idx": idx,
|
||||
@ -430,24 +441,23 @@ async def get_flow_compare(
|
||||
]
|
||||
|
||||
# ── 按人分组,转为相对 T0 的小时偏移区间 ──
|
||||
# 每项同时携带自然天与工作日两套偏移/时长,供前端按 isWorkday 即时切换:
|
||||
# [idx, task_name, is_main, start_nat, end_nat, start_work, end_work, dur_total, dur_work]
|
||||
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}
|
||||
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)
|
||||
start_nat = round((it["start"] - t0).total_seconds() / 3600, 1)
|
||||
end_nat = round((it["end"] - t0).total_seconds() / 3600, 1)
|
||||
start_work = round(_wdh(t0, it["start"], _holidays), 1)
|
||||
end_work = round(_wdh(t0, it["end"], _holidays), 1)
|
||||
by_assignee.setdefault(it["assignee_id"], []).append(
|
||||
[it["idx"], start_offset, end_offset, it["task_name"], duration, it["is_main"]]
|
||||
[it["idx"], it["task_name"], it["is_main"],
|
||||
start_nat, end_nat, start_work, end_work,
|
||||
round(end_nat - start_nat, 1), round(end_work - start_work, 1)]
|
||||
)
|
||||
|
||||
# 翻译人员姓名
|
||||
|
||||
Reference in New Issue
Block a user