|
|
@@ -0,0 +1,193 @@
|
|
|
+"""运输通用地址库 API"""
|
|
|
+from typing import Optional
|
|
|
+
|
|
|
+from fastapi import APIRouter, Depends, HTTPException, Query, status
|
|
|
+from sqlalchemy import case, func, or_
|
|
|
+from sqlalchemy.orm import Session
|
|
|
+
|
|
|
+from app.core.database import get_db
|
|
|
+from app.core.db_auth import DbUserContext, get_db_current_user
|
|
|
+from app.core.delete_guard import assert_row_deletable_by_creator, user_info_from_context
|
|
|
+from app.core.tenant_context import get_tenant_no
|
|
|
+from app.core.utils import generate_uuid
|
|
|
+from app.models.pl_models import PlTransportAddress
|
|
|
+from app.schemas.pl.transport_address import (
|
|
|
+ TransportAddressCreate,
|
|
|
+ TransportAddressListResponse,
|
|
|
+ TransportAddressRead,
|
|
|
+ TransportAddressUpdate,
|
|
|
+)
|
|
|
+from app.services.pl_data_scope import apply_pl_scope, assert_pl_row_in_scope
|
|
|
+
|
|
|
+router = APIRouter(tags=["运输地址库"])
|
|
|
+
|
|
|
+
|
|
|
+def _address_to_read(row: PlTransportAddress) -> TransportAddressRead:
|
|
|
+ return TransportAddressRead.model_validate(row)
|
|
|
+
|
|
|
+
|
|
|
+def _get_address(
|
|
|
+ db: Session, tenant_no: str, address_id: str, user: DbUserContext
|
|
|
+) -> PlTransportAddress:
|
|
|
+ row = (
|
|
|
+ db.query(PlTransportAddress)
|
|
|
+ .filter(
|
|
|
+ PlTransportAddress.id == address_id,
|
|
|
+ PlTransportAddress.tenant_no == tenant_no,
|
|
|
+ PlTransportAddress.del_flag == 0,
|
|
|
+ )
|
|
|
+ .first()
|
|
|
+ )
|
|
|
+ if not row:
|
|
|
+ raise HTTPException(status_code=404, detail="地址不存在")
|
|
|
+ assert_pl_row_in_scope(db, row, PlTransportAddress, user, tenant_no)
|
|
|
+ return row
|
|
|
+
|
|
|
+
|
|
|
+def _build_address_query(db: Session, tenant_no: str, user: DbUserContext):
|
|
|
+ query = db.query(PlTransportAddress).filter(
|
|
|
+ PlTransportAddress.tenant_no == tenant_no,
|
|
|
+ PlTransportAddress.del_flag == 0,
|
|
|
+ )
|
|
|
+ query = apply_pl_scope(query, PlTransportAddress, user, db, tenant_no)
|
|
|
+ return query
|
|
|
+
|
|
|
+
|
|
|
+@router.get("/address", response_model=TransportAddressListResponse)
|
|
|
+def list_transport_addresses(
|
|
|
+ q: Optional[str] = Query(default=None, description="搜索名称或地址"),
|
|
|
+ status: Optional[str] = Query(default=None, description="状态 1-启用 0-停用"),
|
|
|
+ page: int = Query(default=1, ge=1),
|
|
|
+ page_size: int = Query(default=20, ge=1, le=100, alias="pageSize"),
|
|
|
+ db: Session = Depends(get_db),
|
|
|
+ user: DbUserContext = Depends(get_db_current_user),
|
|
|
+) -> TransportAddressListResponse:
|
|
|
+ tenant_no = get_tenant_no(db, user.to_tenant_user_dict())
|
|
|
+ query = _build_address_query(db, tenant_no, user)
|
|
|
+
|
|
|
+ if status:
|
|
|
+ query = query.filter(PlTransportAddress.status == status)
|
|
|
+
|
|
|
+ if q:
|
|
|
+ keyword = q.strip().lower()
|
|
|
+ query = query.filter(
|
|
|
+ or_(
|
|
|
+ func.lower(PlTransportAddress.address_name).contains(keyword),
|
|
|
+ func.lower(PlTransportAddress.full_address).contains(keyword),
|
|
|
+ func.lower(PlTransportAddress.contact_name).contains(keyword),
|
|
|
+ PlTransportAddress.contact_phone.contains(q.strip()),
|
|
|
+ )
|
|
|
+ )
|
|
|
+
|
|
|
+ query = query.order_by(
|
|
|
+ case((PlTransportAddress.order_num.is_(None), 1), else_=0).asc(),
|
|
|
+ PlTransportAddress.create_time.desc(),
|
|
|
+ )
|
|
|
+
|
|
|
+ total = query.count()
|
|
|
+ offset = (page - 1) * page_size
|
|
|
+ rows = query.offset(offset).limit(page_size).all()
|
|
|
+ return TransportAddressListResponse(
|
|
|
+ total=total,
|
|
|
+ list=[_address_to_read(row) for row in rows],
|
|
|
+ )
|
|
|
+
|
|
|
+
|
|
|
+@router.get("/address/all", response_model=TransportAddressListResponse)
|
|
|
+def list_all_transport_addresses(
|
|
|
+ q: Optional[str] = Query(default=None, description="搜索名称或地址"),
|
|
|
+ db: Session = Depends(get_db),
|
|
|
+ user: DbUserContext = Depends(get_db_current_user),
|
|
|
+) -> TransportAddressListResponse:
|
|
|
+ """选择器用:仅返回启用中的地址(不分页)。"""
|
|
|
+ tenant_no = get_tenant_no(db, user.to_tenant_user_dict())
|
|
|
+ query = _build_address_query(db, tenant_no, user).filter(PlTransportAddress.status == "1")
|
|
|
+
|
|
|
+ if q:
|
|
|
+ keyword = q.strip().lower()
|
|
|
+ query = query.filter(
|
|
|
+ or_(
|
|
|
+ func.lower(PlTransportAddress.address_name).contains(keyword),
|
|
|
+ func.lower(PlTransportAddress.full_address).contains(keyword),
|
|
|
+ )
|
|
|
+ )
|
|
|
+
|
|
|
+ query = query.order_by(
|
|
|
+ case((PlTransportAddress.order_num.is_(None), 1), else_=0).asc(),
|
|
|
+ PlTransportAddress.create_time.desc(),
|
|
|
+ )
|
|
|
+ rows = query.limit(200).all()
|
|
|
+ return TransportAddressListResponse(total=len(rows), list=[_address_to_read(row) for row in rows])
|
|
|
+
|
|
|
+
|
|
|
+@router.post("/address", response_model=TransportAddressRead, status_code=status.HTTP_201_CREATED)
|
|
|
+def create_transport_address(
|
|
|
+ payload: TransportAddressCreate,
|
|
|
+ db: Session = Depends(get_db),
|
|
|
+ user: DbUserContext = Depends(get_db_current_user),
|
|
|
+) -> TransportAddressRead:
|
|
|
+ tenant_no = get_tenant_no(db, user.to_tenant_user_dict())
|
|
|
+ row = PlTransportAddress(
|
|
|
+ id=generate_uuid(),
|
|
|
+ tenant_no=tenant_no,
|
|
|
+ address_name=payload.address_name.strip(),
|
|
|
+ full_address=payload.full_address.strip(),
|
|
|
+ lng=payload.lng,
|
|
|
+ lat=payload.lat,
|
|
|
+ contact_name=payload.contact_name,
|
|
|
+ contact_phone=payload.contact_phone,
|
|
|
+ status=payload.status or "1",
|
|
|
+ remark=payload.remark,
|
|
|
+ create_by=user.id,
|
|
|
+ create_dept_id=user.dept_id,
|
|
|
+ )
|
|
|
+ db.add(row)
|
|
|
+ db.commit()
|
|
|
+ db.refresh(row)
|
|
|
+ return _address_to_read(row)
|
|
|
+
|
|
|
+
|
|
|
+@router.get("/address/{id}", response_model=TransportAddressRead)
|
|
|
+def get_transport_address(
|
|
|
+ id: str,
|
|
|
+ db: Session = Depends(get_db),
|
|
|
+ user: DbUserContext = Depends(get_db_current_user),
|
|
|
+) -> TransportAddressRead:
|
|
|
+ tenant_no = get_tenant_no(db, user.to_tenant_user_dict())
|
|
|
+ row = _get_address(db, tenant_no, id, user)
|
|
|
+ return _address_to_read(row)
|
|
|
+
|
|
|
+
|
|
|
+@router.put("/address/{id}", response_model=TransportAddressRead)
|
|
|
+def update_transport_address(
|
|
|
+ id: str,
|
|
|
+ payload: TransportAddressUpdate,
|
|
|
+ db: Session = Depends(get_db),
|
|
|
+ user: DbUserContext = Depends(get_db_current_user),
|
|
|
+) -> TransportAddressRead:
|
|
|
+ tenant_no = get_tenant_no(db, user.to_tenant_user_dict())
|
|
|
+ row = _get_address(db, tenant_no, id, user)
|
|
|
+
|
|
|
+ for key, value in payload.model_dump(exclude_unset=True).items():
|
|
|
+ if key in ("address_name", "full_address") and isinstance(value, str):
|
|
|
+ value = value.strip()
|
|
|
+ setattr(row, key, value)
|
|
|
+
|
|
|
+ row.update_by = user.id
|
|
|
+ db.commit()
|
|
|
+ db.refresh(row)
|
|
|
+ return _address_to_read(row)
|
|
|
+
|
|
|
+
|
|
|
+@router.delete("/address/{id}", status_code=status.HTTP_204_NO_CONTENT)
|
|
|
+def delete_transport_address(
|
|
|
+ id: str,
|
|
|
+ db: Session = Depends(get_db),
|
|
|
+ user: DbUserContext = Depends(get_db_current_user),
|
|
|
+) -> None:
|
|
|
+ tenant_no = get_tenant_no(db, user.to_tenant_user_dict())
|
|
|
+ row = _get_address(db, tenant_no, id, user)
|
|
|
+ assert_row_deletable_by_creator(row, user_info_from_context(user))
|
|
|
+ row.del_flag = 1
|
|
|
+ row.update_by = user.id
|
|
|
+ db.commit()
|