feat: 实现后端核心业务逻辑 (Auth鉴权、二维码服务、看板与产品端点)
This commit is contained in:
1
backend/app/api/v1/endpoints/__init__.py
Normal file
1
backend/app/api/v1/endpoints/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
"""API v1 端点"""
|
||||
23
backend/app/api/v1/endpoints/auth.py
Normal file
23
backend/app/api/v1/endpoints/auth.py
Normal 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"),
|
||||
)
|
||||
12
backend/app/api/v1/endpoints/dashboard.py
Normal file
12
backend/app/api/v1/endpoints/dashboard.py
Normal 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)
|
||||
35
backend/app/api/v1/endpoints/orders.py
Normal file
35
backend/app/api/v1/endpoints/orders.py
Normal 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)
|
||||
98
backend/app/api/v1/endpoints/products.py
Normal file
98
backend/app/api/v1/endpoints/products.py
Normal 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)
|
||||
Reference in New Issue
Block a user