From 87ed40512c0fb8f080c12e00b865b45d1c45d75d Mon Sep 17 00:00:00 2001 From: wuzilong <704541015@qq.com> Date: Sun, 13 Sep 2026 09:24:09 +0800 Subject: [PATCH] feat(backend): add public pound-qr endpoint by inventory id Expose a read-only payload for the shared weighbridge QR card without auth. Co-authored-by: Cursor --- backend/app/api/db/public_inventory.py | 14 ++++++ backend/app/schemas/db/public_inventory.py | 14 ++++++ backend/app/services/db/pound_qr_public.py | 30 +++++++++++++ backend/tests/test_public_db_pound_qr.py | 50 ++++++++++++++++++++++ 4 files changed, 108 insertions(+) create mode 100644 backend/app/services/db/pound_qr_public.py create mode 100644 backend/tests/test_public_db_pound_qr.py diff --git a/backend/app/api/db/public_inventory.py b/backend/app/api/db/public_inventory.py index b05417be7..5f9e9cfb1 100644 --- a/backend/app/api/db/public_inventory.py +++ b/backend/app/api/db/public_inventory.py @@ -19,6 +19,7 @@ from app.models.db_models import DbInboundOutbound from app.models.pl_models import PlPlan, PlTransportDbLink from app.schemas.db.public_inventory import PublicInventoryFeedback, PublicInventoryPageRead from app.services.db.mappers import inventory_date_to_api +from app.services.db.pound_qr_public import build_public_pound_qr_read router = APIRouter(prefix="/public/db/inventory", tags=["地磅-出入库现场公共"]) @@ -97,6 +98,19 @@ def _build_public_page(db: Session, inventory: DbInboundOutbound) -> PublicInven ) +@router.get("/{inventory_id}/pound-qr") +def get_public_pound_qr(inventory_id: str, db: Session = Depends(get_db)): + row = ( + db.query(DbInboundOutbound) + .filter(DbInboundOutbound.id == inventory_id, DbInboundOutbound.del_flag == 0) + .first() + ) + if not row: + raise HTTPException(status_code=404, detail="链接无效或单据不存在") + read = build_public_pound_qr_read(row, quantity_unit="吨") + return success_response(read.model_dump(by_alias=True)) + + @router.get("/{token}") def get_public_inventory(token: str, db: Session = Depends(get_db)): inventory = _get_inventory_by_token(db, token) diff --git a/backend/app/schemas/db/public_inventory.py b/backend/app/schemas/db/public_inventory.py index 0206246a5..885cd64dc 100644 --- a/backend/app/schemas/db/public_inventory.py +++ b/backend/app/schemas/db/public_inventory.py @@ -34,3 +34,17 @@ class PublicInventoryPageRead(BaseModel): class InventorySiteUrlRead(BaseModel): site_url: str = Field(alias="siteUrl") model_config = {"populate_by_name": True} + + +class PublicPoundQrRead(BaseModel): + id: str + date: Optional[str] = None + business_type: str = Field(alias="businessType") + vehicle_no: str = Field(alias="vehicleNo") + partner_company: str = Field(alias="partnerCompany") + product: str + specification: Optional[str] = None + quantity: float + quantity_unit: str = Field(default="吨", alias="quantityUnit") + remarks: Optional[str] = None + model_config = {"populate_by_name": True} diff --git a/backend/app/services/db/pound_qr_public.py b/backend/app/services/db/pound_qr_public.py new file mode 100644 index 000000000..af13d614b --- /dev/null +++ b/backend/app/services/db/pound_qr_public.py @@ -0,0 +1,30 @@ +from __future__ import annotations + +from typing import Any, Optional + +from app.schemas.db.public_inventory import PublicPoundQrRead +from app.services.db.mappers import inventory_date_to_api + + +def build_pound_qr_label(business_type: str, partner_company: str) -> str: + bt = (business_type or "").strip() + pc = (partner_company or "").strip() + if bt and pc: + return f"{bt} · {pc}" + return bt or pc or "磅单" + + +def build_public_pound_qr_read(row: Any, *, quantity_unit: Optional[str] = None) -> PublicPoundQrRead: + unit = (quantity_unit or "").strip() or "吨" + return PublicPoundQrRead( + id=row.id, + date=inventory_date_to_api(row.date) or None, + business_type=(row.business_type or "").strip() or "", + vehicle_no=(row.vehicle_no or "").strip(), + partner_company=(row.partner_company or "").strip(), + product=(row.product or "").strip(), + specification=(row.specification or None), + quantity=float(row.quantity or 0), + quantity_unit=unit, + remarks=(row.remark or None) if getattr(row, "remark", None) else None, + ) diff --git a/backend/tests/test_public_db_pound_qr.py b/backend/tests/test_public_db_pound_qr.py new file mode 100644 index 000000000..ffce3fef2 --- /dev/null +++ b/backend/tests/test_public_db_pound_qr.py @@ -0,0 +1,50 @@ +"""公开磅单二维码只读接口。""" +from datetime import date +from types import SimpleNamespace + +from fastapi.testclient import TestClient + +from app.main import app +from app.services.db.pound_qr_public import ( + build_pound_qr_label, + build_public_pound_qr_read, +) + +client = TestClient(app) + + +def test_build_pound_qr_label(): + assert build_pound_qr_label("出库", "合肥德泽") == "出库 · 合肥德泽" + assert build_pound_qr_label("入库", "") == "入库" + assert build_pound_qr_label("", "安庆山峡") == "安庆山峡" + + +def test_build_public_pound_qr_read_maps_fields(): + row = SimpleNamespace( + id="inv-1", + date=date(2026, 9, 12), + business_type="出库", + vehicle_no="皖AM8516", + partner_company="合肥德泽化工科技有限公司", + product="30% 工业硫酸", + specification="30%", + quantity=10.0, + remark="注意安全", + ) + read = build_public_pound_qr_read(row, quantity_unit="吨") + dump = read.model_dump(by_alias=True) + assert dump["id"] == "inv-1" + assert dump["date"] == "2026-09-12" + assert dump["businessType"] == "出库" + assert dump["vehicleNo"] == "皖AM8516" + assert dump["partnerCompany"] == "合肥德泽化工科技有限公司" + assert dump["product"] == "30% 工业硫酸" + assert dump["specification"] == "30%" + assert dump["quantity"] == 10.0 + assert dump["quantityUnit"] == "吨" + assert dump["remarks"] == "注意安全" + + +def test_pound_qr_missing_returns_404(): + resp = client.get("/api/public/db/inventory/__missing_pound_qr_id__/pound-qr") + assert resp.status_code == 404