feat: 实现后端核心业务逻辑 (Auth鉴权、二维码服务、看板与产品端点)

This commit is contained in:
2026-08-04 17:09:47 +08:00
parent 2b967afbf6
commit b22de514a1
11 changed files with 407 additions and 0 deletions

View File

@ -0,0 +1 @@
"""API v1 端点"""

View File

@ -0,0 +1,23 @@
"""认证 API — 对接 MOM sys_user"""
from fastapi import APIRouter, Depends
from app.schemas.user import LoginRequest, LoginResponse, UserResponse
from app.services.auth_service import login, get_current_user
router = APIRouter(prefix="/auth", tags=["认证"])
@router.post("/login", response_model=LoginResponse)
def login_endpoint(data: LoginRequest):
"""登录 — 验证 MOM sys_user 表,返回 JWT"""
return login(data.username, data.password)
@router.get("/me", response_model=UserResponse)
def get_me(current_user: dict = Depends(get_current_user)):
"""获取当前用户信息(从 JWT 解析)"""
return UserResponse(
id=current_user["sub"],
username=current_user.get("username", ""),
display_name=current_user.get("display_name", ""),
role=current_user.get("role", "operator"),
)

View File

@ -0,0 +1,12 @@
"""Dashboard API"""
from fastapi import APIRouter, Depends
from sqlalchemy.ext.asyncio import AsyncSession
from app.core.database import get_db
from app.services.dashboard_service import get_dashboard_stats, DashboardStats
router = APIRouter(prefix="/dashboard", tags=["管理看板"])
@router.get("/stats", response_model=DashboardStats)
async def dashboard_stats(db: AsyncSession = Depends(get_db)):
return await get_dashboard_stats(db)

View File

@ -0,0 +1,35 @@
"""生产订单 API 端点"""
from __future__ import annotations
import uuid
from fastapi import APIRouter, Depends, Query
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import selectinload
from app.core.database import get_db
from app.models.production_order import ProductionOrder
from app.schemas.order import OrderCreate, OrderResponse
router = APIRouter(prefix="/orders", tags=["订单管理"])
@router.get("/", response_model=list[OrderResponse])
async def list_orders(
skip: int = Query(0, ge=0),
limit: int = Query(50, ge=1, le=200),
db: AsyncSession = Depends(get_db),
):
result = await db.execute(
select(ProductionOrder).offset(skip).limit(limit).order_by(ProductionOrder.created_at.desc())
)
orders = result.scalars().all()
return [OrderResponse.model_validate(o) for o in orders]
@router.post("/", response_model=OrderResponse, status_code=201)
async def create_order(data: OrderCreate, db: AsyncSession = Depends(get_db)):
order = ProductionOrder(**data.model_dump())
db.add(order)
await db.commit()
await db.refresh(order)
return OrderResponse.model_validate(order)

View File

@ -0,0 +1,98 @@
"""产品 API 端点 — 扫码查询、CRUD、二维码生成"""
from __future__ import annotations
from fastapi import APIRouter, Depends, Query
from fastapi.responses import Response
from sqlalchemy.ext.asyncio import AsyncSession
from app.core.database import get_db
from app.schemas.product import (
ProductCreate,
ProductUpdate,
ProductResponse,
ProductScanResponse,
)
from app.services import product_service
from app.services.qrcode_service import generate_qrcode_png
router = APIRouter(prefix="/products", tags=["产品管理"])
# ============================================================
# 二维码生成 — 根据序列号生成二维码 PNG 图片
# ============================================================
@router.get("/qrcode/{serial_number}")
async def get_product_qrcode(serial_number: str):
"""
生成产品二维码PNG 图片)。
内容为 16 位序列号,扫描后可调用 /scan/{serial_number} 查询产品。
尺寸300×300 px用于 PC 端打印或嵌入标签。
"""
if len(serial_number) != 16:
from fastapi import HTTPException, status
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="序列号必须为 16 位",
)
buf = generate_qrcode_png(serial_number, size_px=300)
return Response(content=buf.getvalue(), media_type="image/png")
# ============================================================
# 扫码查询 — 根据 16 位序列号查产品 + 顶层任务
# ============================================================
@router.get("/scan/{serial_number}", response_model=ProductScanResponse)
async def scan_product(
serial_number: str,
db: AsyncSession = Depends(get_db),
):
"""
扫码接口:根据 16 位序列号查询产品及其当前进度。
返回产品信息、所属订单、以及顶层任务列表。
"""
return await product_service.get_product_by_serial(db, serial_number)
# ============================================================
# 产品 CRUD
# ============================================================
@router.get("/", response_model=list[ProductResponse])
async def list_products(
skip: int = Query(0, ge=0, description="跳过条数"),
limit: int = Query(50, ge=1, le=200, description="返回条数"),
db: AsyncSession = Depends(get_db),
):
"""获取产品列表"""
return await product_service.get_all_products(db, skip=skip, limit=limit)
@router.get("/{product_id}", response_model=ProductResponse)
async def get_product(
product_id: str,
db: AsyncSession = Depends(get_db),
):
"""获取单个产品详情"""
import uuid
return await product_service.get_product(db, uuid.UUID(product_id))
@router.post("/", response_model=ProductResponse, status_code=201)
async def create_product_endpoint(
data: ProductCreate,
db: AsyncSession = Depends(get_db),
):
"""创建产品"""
return await product_service.create_product(db, data)
@router.patch("/{product_id}", response_model=ProductResponse)
async def update_product_endpoint(
product_id: str,
data: ProductUpdate,
db: AsyncSession = Depends(get_db),
):
"""更新产品"""
import uuid
return await product_service.update_product(db, uuid.UUID(product_id), data)