feat(backend): 新增 /dashboard/wip-matrix 在制品交叉聚合接口
- 规格型号×人员/工序 的设备数量聚合(WIP/PENDING任务) - dimension 参数: assignee(中文姓名)/task_name(工序名) - 返回扁平数组含 count + assignees(该交叉点主负责人列表)
This commit is contained in:
@ -12,6 +12,7 @@ from app.services.dashboard_service import (
|
||||
get_rejected_tasks, RejectedTask,
|
||||
get_user_operations, UserOperation,
|
||||
get_user_operation_detail, OperationDetail,
|
||||
get_wip_matrix, WipMatrixRow,
|
||||
get_people_workload, PersonWorkload,
|
||||
get_people_history, PersonHistoryRecord,
|
||||
search_product_messages, ProductMessageList,
|
||||
@ -100,6 +101,15 @@ async def user_operations_detail(
|
||||
)
|
||||
|
||||
|
||||
@router.get("/wip-matrix", response_model=list[WipMatrixRow])
|
||||
async def wip_matrix(
|
||||
dimension: str = Query("assignee", description="聚合维度: assignee(人员) / task_name(工序)"),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
"""在制品分布透视表 — 规格型号 × 人员/工序 的设备数量交叉聚合"""
|
||||
return await get_wip_matrix(db, dimension=dimension)
|
||||
|
||||
|
||||
@router.get("/people-workload", response_model=list[PersonWorkload])
|
||||
async def people_workload(
|
||||
db: AsyncSession = Depends(get_db),
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
# ============================================================
|
||||
# 人员操作明细(点击数字下钻 — 接收/转交/上传备注)
|
||||
# ============================================================
|
||||
|
||||
Reference in New Issue
Block a user