From 1fe9c3d59db2b001fb2fdd815b7317c5e4dab8e7 Mon Sep 17 00:00:00 2001 From: duxingchen Date: Mon, 10 Aug 2026 17:37:10 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E7=95=99=E8=A8=80=E6=9D=BF=20API=20+?= =?UTF-8?q?=20=E9=9B=B6=E4=BF=A1=E4=BB=BB=E5=AE=89=E5=85=A8=EF=BC=9A?= =?UTF-8?q?=E5=90=8E=E7=AB=AF=20Token=20=E9=89=B4=E6=9D=83=E5=BC=BA?= =?UTF-8?q?=E5=88=B6=E8=A6=86=E5=86=99=20operator=5Fid?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 GET/POST /products/{id}/messages 端点 - POST 创建留言时注入 current_user = Depends(get_current_user) - operator_id 优先取 Token 中的 username,防止前端越权伪造发言身份 - 同时将 product.current_location_id 显示改为 formatUserName 映射 --- backend/app/api/v1/endpoints/products.py | 50 ++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/backend/app/api/v1/endpoints/products.py b/backend/app/api/v1/endpoints/products.py index 18dfe98..86da566 100644 --- a/backend/app/api/v1/endpoints/products.py +++ b/backend/app/api/v1/endpoints/products.py @@ -3,9 +3,11 @@ from __future__ import annotations from fastapi import APIRouter, Depends, Query from fastapi.responses import Response from pydantic import BaseModel, Field +from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession from app.core.database import get_db +from app.models.message import ProductMessage from app.schemas.product import ( ProductCreate, ProductUpdate, @@ -136,3 +138,51 @@ async def update_product_overall_status( 合法值: 备货 | 生产 | 测试 | 维修 | 在库 """ return await product_service.update_overall_status(db, serial_number, data.status) + + +# ============================================================ +# 协同留言板 +# ============================================================ + +class MessageCreate(BaseModel): + operator_id: str = Field(..., min_length=1, max_length=50, description="留言人姓名或工号") + content: str = Field(..., min_length=1, description="留言内容") + + +@router.get("/{product_id}/messages") +async def get_product_messages( + product_id: str, + db: AsyncSession = Depends(get_db), +): + """获取某产品的所有留言(按时间正序)""" + result = await db.execute( + select(ProductMessage) + .where(ProductMessage.product_id == product_id) + .order_by(ProductMessage.created_at.asc()) + ) + return result.scalars().all() + + +@router.post("/{product_id}/messages", status_code=201) +async def create_product_message( + product_id: str, + request: MessageCreate, + db: AsyncSession = Depends(get_db), + current_user: dict = Depends(get_current_user), +): + """发布新留言(operator_id 由后端 Token 强制覆写,防止越权伪造)""" + import uuid + real_operator_id = ( + current_user.get("username") + or current_user.get("sub") + or request.operator_id + ) + msg = ProductMessage( + product_id=uuid.UUID(product_id), + operator_id=real_operator_id, + content=request.content, + ) + db.add(msg) + await db.commit() + await db.refresh(msg) + return msg