feat: 双Token认证(Access 2h/Refresh 7d) + 通知系统(转交/驳回自动推送)
This commit is contained in:
@ -2,7 +2,7 @@
|
||||
from __future__ import annotations
|
||||
import uuid
|
||||
from fastapi import HTTPException, status
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy import select, or_, cast, String
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy.orm import selectinload
|
||||
|
||||
@ -34,6 +34,7 @@ def _task_to_response(task: Task) -> TaskResponse:
|
||||
status=task.status,
|
||||
notify_parent_on_complete=task.notify_parent_on_complete,
|
||||
is_rework=task.is_rework,
|
||||
task_type=task.task_type,
|
||||
remark=task.remark,
|
||||
reject_reason=task.reject_reason,
|
||||
received_at=task.received_at,
|
||||
@ -277,15 +278,68 @@ async def update_overall_status(db: AsyncSession, serial_number: str, status_val
|
||||
return await get_product_by_serial(db, serial_number)
|
||||
|
||||
|
||||
async def get_all_products(db: AsyncSession, skip: int = 0, limit: int = 50) -> list[ProductResponse]:
|
||||
"""获取产品列表"""
|
||||
result = await db.execute(
|
||||
select(Product)
|
||||
.options(selectinload(Product.order))
|
||||
.offset(skip)
|
||||
.limit(limit)
|
||||
.order_by(Product.created_at.desc())
|
||||
)
|
||||
async def get_all_products(
|
||||
db: AsyncSession,
|
||||
skip: int = 0,
|
||||
limit: int = 50,
|
||||
keyword: str | None = None,
|
||||
status_filter: str | None = None,
|
||||
) -> list[ProductResponse]:
|
||||
"""
|
||||
获取产品列表 — 支持多维 keyword 搜索 + 状态筛选
|
||||
|
||||
keyword: 同时模糊匹配 serial_number (产品身份证)、material_name/id (规格型号)、order_no (订单号)
|
||||
status_filter: 按产品状态过滤 (如 PENDING / WIP / COMPLETED / ARCHIVED)
|
||||
"""
|
||||
stmt = select(Product).options(selectinload(Product.order))
|
||||
|
||||
# keyword 多字段 OR 模糊搜索
|
||||
if keyword and keyword.strip():
|
||||
kw = f"%{keyword.strip()}%"
|
||||
stmt = stmt.outerjoin(ProductionOrder, Product.order_id == ProductionOrder.id).where(
|
||||
or_(
|
||||
Product.serial_number.ilike(kw),
|
||||
Product.material_name.ilike(kw),
|
||||
cast(Product.material_id, String).ilike(kw),
|
||||
Product.spec_model.ilike(kw),
|
||||
ProductionOrder.order_no.ilike(kw),
|
||||
)
|
||||
).distinct()
|
||||
|
||||
# 状态筛选 — 大小写不敏感,支持组合过滤
|
||||
if status_filter and status_filter.strip():
|
||||
from sqlalchemy import func
|
||||
sf = status_filter.strip().upper()
|
||||
if sf == "DONE":
|
||||
# "已完成" 匹配 COMPLETED 或 ARCHIVED
|
||||
stmt = stmt.where(
|
||||
or_(
|
||||
func.upper(Product.status) == "COMPLETED",
|
||||
func.upper(Product.status) == "ARCHIVED",
|
||||
)
|
||||
)
|
||||
elif sf == "PENDING":
|
||||
# "待流转" — 产品状态 PENDING 且所有顶层任务均未分配人
|
||||
stmt = (
|
||||
stmt.outerjoin(Task, Task.product_id == Product.id)
|
||||
.where(func.upper(Product.status) == "PENDING")
|
||||
.where(Task.assignee_id.is_(None))
|
||||
.distinct()
|
||||
)
|
||||
elif sf == "PENDING_ASSIGNED":
|
||||
# "待接收" — 产品状态 PENDING 但已有任务被分配(等待工人扫码)
|
||||
stmt = (
|
||||
stmt.outerjoin(Task, Task.product_id == Product.id)
|
||||
.where(func.upper(Product.status) == "PENDING")
|
||||
.where(Task.assignee_id.isnot(None))
|
||||
.distinct()
|
||||
)
|
||||
else:
|
||||
stmt = stmt.where(func.upper(Product.status) == sf)
|
||||
|
||||
stmt = stmt.offset(skip).limit(limit).order_by(Product.created_at.desc())
|
||||
|
||||
result = await db.execute(stmt)
|
||||
products = result.scalars().all()
|
||||
return [
|
||||
ProductResponse(
|
||||
|
||||
Reference in New Issue
Block a user