feat(api): 核心端点 — 接收/驳回/转交/打印/上传/用户/物料/记录CRUD
新增端点:
- POST /tasks/{id}/receive — 确认接收 (PENDING→WIP)
- POST /tasks/{id}/reject — 品质驳回 + 自动返工
- POST /tasks/{id}/transfer — 完工裂变转交 (多路分支+入库)
- PATCH /tasks/{id}/records — 追加图文记录
- PATCH /products/scan/{sn}/status — 宏观状态定调
- POST /upload/ + GET /upload/files/{name} — 文件上传
- GET /users/ — MOM用户列表
- GET /materials/groups + /materials/items — BOM物料手风琴
- POST /print/preview + /execute — 标签预览/打印
- PUT/DELETE /records/{id} — 记录编辑/删除
- GET /tasks/ 新增 assignee_id 过滤
This commit is contained in:
145
backend/app/api/v1/endpoints/materials.py
Normal file
145
backend/app/api/v1/endpoints/materials.py
Normal file
@ -0,0 +1,145 @@
|
||||
"""物料选择器 — 读 MOM material_base,按成品/半成品 category 手风琴分组"""
|
||||
from fastapi import APIRouter, Query, HTTPException, status
|
||||
from pydantic import BaseModel
|
||||
from app.core.mom_database import MomSessionLocal
|
||||
from sqlalchemy import text
|
||||
|
||||
router = APIRouter(prefix="/materials", tags=["物料选择"])
|
||||
|
||||
# 只展示成品 / 半成品(category 字段区分,如 IRIS/成品/… / IRIS/半成品/…)
|
||||
TYPE_FILTER = "category ILIKE '%成品%' OR category ILIKE '%半成品%'"
|
||||
|
||||
|
||||
# ============================================================
|
||||
# 响应模型
|
||||
# ============================================================
|
||||
|
||||
class MaterialGroup(BaseModel):
|
||||
category: str
|
||||
count: int
|
||||
|
||||
|
||||
class MaterialItem(BaseModel):
|
||||
id: int
|
||||
name: str
|
||||
spec: str
|
||||
category: str
|
||||
type: str
|
||||
unit: str
|
||||
is_enabled: bool
|
||||
|
||||
|
||||
# ============================================================
|
||||
# 端点
|
||||
# ============================================================
|
||||
|
||||
@router.get("/groups", response_model=list[MaterialGroup])
|
||||
def get_material_groups(
|
||||
keyword: str = Query("", description="搜索(按名称/规格)"),
|
||||
):
|
||||
"""
|
||||
按 category 分组汇总,前端渲染手风琴外层。
|
||||
只返回成品/半成品分类。
|
||||
"""
|
||||
db = MomSessionLocal()
|
||||
try:
|
||||
if keyword.strip():
|
||||
sql = text(
|
||||
f"""
|
||||
SELECT category, COUNT(*) AS count
|
||||
FROM material_base
|
||||
WHERE is_enabled = TRUE
|
||||
AND ({TYPE_FILTER})
|
||||
AND (name ILIKE :kw OR spec_model ILIKE :kw)
|
||||
GROUP BY category
|
||||
ORDER BY category
|
||||
"""
|
||||
)
|
||||
result = db.execute(sql, {"kw": f"%{keyword.strip()}%"})
|
||||
else:
|
||||
sql = text(
|
||||
f"""
|
||||
SELECT category, COUNT(*) AS count
|
||||
FROM material_base
|
||||
WHERE is_enabled = TRUE
|
||||
AND ({TYPE_FILTER})
|
||||
GROUP BY category
|
||||
ORDER BY category
|
||||
"""
|
||||
)
|
||||
result = db.execute(sql)
|
||||
|
||||
rows = result.fetchall()
|
||||
return [MaterialGroup(category=row.category, count=row.count) for row in rows]
|
||||
except Exception as e:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_502_BAD_GATEWAY,
|
||||
detail=f"MOM material_base 查询失败: {str(e)}",
|
||||
)
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
@router.get("/items", response_model=list[MaterialItem])
|
||||
def get_material_items(
|
||||
category: str = Query(..., description="物料分类"),
|
||||
keyword: str = Query("", description="分组内搜索"),
|
||||
limit: int = Query(500, ge=1, le=9999),
|
||||
):
|
||||
"""
|
||||
获取指定 category 下的物料条目,前端展开手风琴时懒加载。
|
||||
"""
|
||||
db = MomSessionLocal()
|
||||
try:
|
||||
if keyword.strip():
|
||||
sql = text(
|
||||
f"""
|
||||
SELECT id, name, spec_model AS spec, category, material_type AS type,
|
||||
COALESCE(unit, '') AS unit, is_enabled
|
||||
FROM material_base
|
||||
WHERE is_enabled = TRUE
|
||||
AND ({TYPE_FILTER})
|
||||
AND category = :cat
|
||||
AND (name ILIKE :kw OR spec_model ILIKE :kw)
|
||||
ORDER BY name
|
||||
LIMIT :lim
|
||||
"""
|
||||
)
|
||||
result = db.execute(
|
||||
sql, {"cat": category, "kw": f"%{keyword.strip()}%", "lim": limit}
|
||||
)
|
||||
else:
|
||||
sql = text(
|
||||
f"""
|
||||
SELECT id, name, spec_model AS spec, category, material_type AS type,
|
||||
COALESCE(unit, '') AS unit, is_enabled
|
||||
FROM material_base
|
||||
WHERE is_enabled = TRUE
|
||||
AND ({TYPE_FILTER})
|
||||
AND category = :cat
|
||||
ORDER BY name
|
||||
LIMIT :lim
|
||||
"""
|
||||
)
|
||||
result = db.execute(sql, {"cat": category, "lim": limit})
|
||||
|
||||
rows = result.fetchall()
|
||||
return [
|
||||
MaterialItem(
|
||||
id=row.id,
|
||||
name=row.name,
|
||||
spec=row.spec,
|
||||
category=row.category,
|
||||
type=row.type,
|
||||
unit=row.unit,
|
||||
is_enabled=row.is_enabled,
|
||||
)
|
||||
for row in rows
|
||||
]
|
||||
except Exception as e:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_502_BAD_GATEWAY,
|
||||
detail=f"MOM material_base 查询失败: {str(e)}",
|
||||
)
|
||||
finally:
|
||||
db.close()
|
||||
Reference in New Issue
Block a user