feat(backend): 新增 /dashboard/wip-matrix 在制品交叉聚合接口

- 规格型号×人员/工序 的设备数量聚合(WIP/PENDING任务)
- dimension 参数: assignee(中文姓名)/task_name(工序名)
- 返回扁平数组含 count + assignees(该交叉点主负责人列表)
This commit is contained in:
2026-08-28 16:25:20 +08:00
parent d9ec15b72f
commit 7547a886cc
2 changed files with 75 additions and 0 deletions

View File

@ -80,6 +80,13 @@ class OperationDetail(BaseModel):
time: str # 操作时间 ISO(BEIJING_TZ)
class WipMatrixRow(BaseModel):
spec_model: str # 规格型号(Y 轴)
dimension_key: str # 人员姓名 或 工序名称(X 轴)
count: int = 0 # 该交叉点的设备数量
assignees: list[str] = [] # 该交叉点涉及的主负责人(中文名,去重)
class PersonDevice(BaseModel):
product_id: str
serial_number: str # 16位HEX身份证
@ -593,6 +600,64 @@ async def get_user_operations(
return items
# ============================================================
# 在制品分布透视表(WIP Matrix:规格型号 × 人员/工序)
# ============================================================
async def get_wip_matrix(
db: AsyncSession,
dimension: str = "assignee",
) -> list[WipMatrixRow]:
"""在制品交叉聚合:Y=规格型号,X=人员 或 工序,单元格=设备数量。
dimension:
- assignee: 按任务负责人聚合(dimension_key 为中文姓名)
- task_name: 按工序名聚合(dimension_key 为工序名,附主负责人)
"""
from app.models.task import Task, TASK_STATUS_WIP, TASK_STATUS_PENDING
from app.models.product import Product
dim_expr = Task.task_name if dimension == "task_name" else Task.assignee_id
stmt = (
select(
Product.spec_model,
dim_expr,
func.count(Product.id),
func.array_agg(func.distinct(Task.assignee_id)),
)
.join(Task, Task.product_id == Product.id)
.where(Task.status.in_([TASK_STATUS_WIP, TASK_STATUS_PENDING]))
.group_by(Product.spec_model, dim_expr)
.order_by(Product.spec_model, dim_expr)
)
result = await db.execute(stmt)
rows = result.all()
# 收集负责人 ID → 中文名
raw_ids: set[str] = set()
for r in rows:
for aid in (r[3] or []):
if aid:
raw_ids.add(aid)
name_map: dict[str, str] = {}
if raw_ids:
from app.services.mom_cache import get_display_names
name_map = get_display_names(list(raw_ids))
items: list[WipMatrixRow] = []
for spec, dim_key, cnt, assignee_ids in rows:
dim_display = name_map.get(dim_key or "", dim_key or "未分配") if dimension == "assignee" else (dim_key or "—")
assignees = [name_map.get(a, a) for a in (assignee_ids or []) if a] or []
items.append(WipMatrixRow(
spec_model=spec or "未知型号",
dimension_key=dim_display,
count=cnt or 0,
assignees=assignees,
))
return items
# ============================================================
# 人员操作明细(点击数字下钻 — 接收/转交/上传备注)
# ============================================================