feat(backend): 生产总天数返回自然天+工作日两个口径
- ProductResponse 加 production_days(自然天)/production_days_workdays(工作日) - get_all_products 计算:自然天自创建至今,工作日用 working_duration_hours 排除周末/节假日 - FlowDevice 加 total_days/total_workdays(设备生产总天数,自最早介入至今)
This commit is contained in:
@ -63,6 +63,9 @@ class ProductResponse(BaseModel):
|
||||
latest_record_assignee_name: str | None = None
|
||||
# 🔧 当前人滞留时长 — 活跃任务(WIP/PENDING)最早接手时间到现在的时长(小时)
|
||||
active_duration_hours: float | None = None
|
||||
# 🔧 生产总天数(自创建至今)
|
||||
production_days: int = 0 # 自然天
|
||||
production_days_workdays: int = 0 # 工作日(排除周末/节假日)
|
||||
|
||||
model_config = {"from_attributes": True}
|
||||
|
||||
|
||||
@ -54,6 +54,8 @@ class FlowDevice(BaseModel):
|
||||
spec_model: str
|
||||
lead_time: float # 设备生命周期总时长(小时)= max_end - min_start
|
||||
started_at: str # 设备最早介入时间(T0),格式 MM-DD HH:mm
|
||||
total_days: int = 0 # 设备生产总天数(自然天,自最早介入至今)
|
||||
total_workdays: int = 0 # 设备生产总天数(工作日,排除周末/节假日)
|
||||
|
||||
|
||||
class FlowSeries(BaseModel):
|
||||
@ -394,12 +396,30 @@ async def get_flow_compare(
|
||||
idx: round((max_end_by_device[idx] - t0_by_device[idx]).total_seconds() / 3600, 1)
|
||||
for idx in t0_by_device
|
||||
}
|
||||
# 🔧 设备生产总天数(自然天 + 工作日,自最早介入至今)
|
||||
import math
|
||||
from app.core.time_utils import to_beijing as _tb, 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}
|
||||
|
||||
def _total_days(t0):
|
||||
t0_bj = _tb(t0)
|
||||
if not t0_bj:
|
||||
return 1, 1
|
||||
natural = max(1, math.ceil((now - t0_bj).total_seconds() / 86400))
|
||||
workdays = max(1, math.ceil(_wdh(t0_bj, now, _holidays) / 24))
|
||||
return natural, workdays
|
||||
|
||||
days_map = {i: _total_days(t0_by_device[i]) for i in t0_by_device}
|
||||
devices = [
|
||||
FlowDevice(
|
||||
product_sn=d.product_sn, external_serial=d.external_serial,
|
||||
material_name=d.material_name, spec_model=d.spec_model,
|
||||
lead_time=lead_time_by_index.get(i, 0.0),
|
||||
started_at=t0_by_device[i].strftime("%m-%d %H:%M") if i in t0_by_device else "",
|
||||
total_days=days_map.get(i, (1, 1))[0],
|
||||
total_workdays=days_map.get(i, (1, 1))[1],
|
||||
)
|
||||
for i, d in enumerate(devices)
|
||||
]
|
||||
|
||||
@ -569,6 +569,27 @@ async def get_all_products(
|
||||
start_bj = to_beijing(start) # 🚀 naive 按 UTC 转北京时间(修复多算8小时)
|
||||
active_duration_map[row[0]] = working_duration_hours(start_bj, now, holidays)
|
||||
|
||||
# 🔧 生产总天数(自然天 + 工作日):自创建至今
|
||||
import math
|
||||
from app.core.time_utils import to_beijing as _tb, working_duration_hours as _wdh
|
||||
from app.models.holiday import Holiday as _Holiday
|
||||
hres2 = await db.execute(select(_Holiday.day))
|
||||
holidays2 = {r[0] for r in hres2}
|
||||
now2 = get_beijing_time()
|
||||
|
||||
def _prod_days(created_at):
|
||||
created = _tb(created_at)
|
||||
if not created:
|
||||
return 1, 1
|
||||
natural = max(1, math.ceil((now2 - created).total_seconds() / 86400))
|
||||
work_hours = _wdh(created, now2, holidays2)
|
||||
workdays = max(1, math.ceil(work_hours / 24))
|
||||
return natural, workdays
|
||||
|
||||
production_days_map: dict = {}
|
||||
for _p in products:
|
||||
production_days_map[_p.id] = _prod_days(_p.created_at)
|
||||
|
||||
return [
|
||||
ProductResponse(
|
||||
id=p.id,
|
||||
@ -604,6 +625,8 @@ async def get_all_products(
|
||||
if latest_record_map.get(p.id, (None, None, False, None))[3] else None
|
||||
),
|
||||
active_duration_hours=active_duration_map.get(p.id),
|
||||
production_days=production_days_map.get(p.id, (1, 1))[0],
|
||||
production_days_workdays=production_days_map.get(p.id, (1, 1))[1],
|
||||
)
|
||||
for p in products
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user