diff --git a/backend/app/services/product_service.py b/backend/app/services/product_service.py index 1037646..92d8436 100644 --- a/backend/app/services/product_service.py +++ b/backend/app/services/product_service.py @@ -392,10 +392,6 @@ async def get_all_products( result = await db.execute(stmt) products = result.scalars().all() - # 批量查询当前位置对应的真实姓名 - location_ids = [p.current_location_id for p in products if p.current_location_id] - name_map = _lookup_display_names(location_ids) - # 🔧 批量预计算 macro_status:一次性查出所有产品关联的任务状态 product_ids = [p.id for p in products] macro_map: dict[uuid.UUID, str] = {} @@ -420,52 +416,55 @@ async def get_all_products( for row in task_result: macro_map[row[0]] = prio_to_status.get(row[1], None) - # 🔧 动态宏观状态:只从主干任务中获取最高优先级任务的 task_name + # 🔧 动态主干状态+位置:只从主干任务中获取最高优先级任务的 task_name + assignee_id overall_names: dict[uuid.UUID, str] = {} + main_assignees: dict[uuid.UUID, str] = {} if product_ids: from sqlalchemy import func as sa_func2, case as sa_case2 - # 先找每个产品中优先级最高的主干任务ID - main_prio_stmt = ( - select( - Task.product_id, - sa_func2.max(sa_case2( - (Task.status == "WIP", 3), - (Task.status == "PENDING", 2), - (Task.status == "COMPLETED", 1), - else_=0, - )).label("prio"), - ) - .where( - Task.product_id.in_(product_ids), - sa_func2.or_( - Task.parent_task_id.is_(None), - Task.task_type.in_(["TRANSFER", "RECOVERY"]), - ), - ) - .group_by(Task.product_id) - ).subquery() - # 再 join 回去拿 task_name - name_stmt = ( - select(Task.product_id, Task.task_name) - .join(main_prio_stmt, sa_func2.and_( - Task.product_id == main_prio_stmt.c.product_id, - sa_case2( - (Task.status == "WIP", 3), - (Task.status == "PENDING", 2), - (Task.status == "COMPLETED", 1), - else_=0, - ) == main_prio_stmt.c.prio, - sa_func2.or_( - Task.parent_task_id.is_(None), - Task.task_type.in_(["TRANSFER", "RECOVERY"]), - ), - )) - .order_by(Task.created_at.desc()) - .limit(1) + main_where = sa_func2.and_( + Task.product_id.in_(product_ids), + sa_func2.or_( + Task.parent_task_id.is_(None), + Task.task_type.in_(["TRANSFER", "RECOVERY"]), + ), ) - name_result = await db.execute(name_stmt) - for row in name_result: - overall_names[row[0]] = row[1] + prio_expr = sa_case2( + (Task.status == "WIP", 3), + (Task.status == "PENDING", 2), + (Task.status == "COMPLETED", 1), + else_=0, + ) + # 子查询:每个产品最高优先级主干任务 + max_prio = ( + select(Task.product_id, sa_func2.max(prio_expr).label("prio")) + .where(main_where) + .group_by(Task.product_id) + ).subquery("mp") + # JOIN 回 tasks 拿 task_name + assignee_id(同优先级取最新创建的) + main_stmt = ( + select(Task.product_id, Task.task_name, Task.assignee_id) + .join(max_prio, sa_func2.and_( + Task.product_id == max_prio.c.product_id, + prio_expr == max_prio.c.prio, + )) + .where(main_where) + .order_by(Task.product_id, Task.created_at.desc()) + .distinct(Task.product_id) # PostgreSQL DISTINCT ON + ) + main_result = await db.execute(main_stmt) + for row in main_result: + pid, tname, assignee = row[0], row[1], row[2] + overall_names[pid] = tname + if assignee: main_assignees[pid] = assignee + + # 🔧 动态主干的 assignee_id → 查中文姓名 + dynamic_location_ids = list(main_assignees.values()) + dynamic_name_map = _lookup_display_names(dynamic_location_ids) + + # 🔧 合并:静态位置姓名(兜底)+ 动态主干位置姓名(优先) + static_location_ids = [p.current_location_id for p in products if p.current_location_id] + merged_location_ids = list(set(static_location_ids + dynamic_location_ids)) + merged_name_map = _lookup_display_names(merged_location_ids) return [ ProductResponse( @@ -480,14 +479,19 @@ async def get_all_products( category=p.category, material_type=p.material_type, parent_product_id=p.parent_product_id, - current_location_id=p.current_location_id, + # 🔧 当前位置:动态主干assignee优先 → 静态兜底 + current_location_id=( + main_assignees.get(p.id) # 动态主干 + or p.current_location_id # 静态兜底 + ), current_location_name=( - "仓库" if p.current_location_id == "virtual_warehouse" - else name_map.get(p.current_location_id) if p.current_location_id + "仓库" if (main_assignees.get(p.id) or p.current_location_id) == "virtual_warehouse" + else dynamic_name_map.get(main_assignees.get(p.id, "")) + or merged_name_map.get(p.current_location_id) if p.current_location_id else None ), macro_status=macro_map.get(p.id) or p.status, - overall_status=overall_names.get(p.id) or p.overall_status, # 🔧 动态主干任务名优先 + overall_status=overall_names.get(p.id) or p.overall_status, status=p.status, created_at=p.created_at, )