transport_address.py 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. """运输通用地址库 API"""
  2. from typing import Optional
  3. from fastapi import APIRouter, Depends, HTTPException, Query, status
  4. from sqlalchemy import case, func, or_
  5. from sqlalchemy.orm import Session
  6. from app.core.database import get_db
  7. from app.core.db_auth import DbUserContext, get_db_current_user
  8. from app.core.delete_guard import assert_row_deletable_by_creator, user_info_from_context
  9. from app.core.tenant_context import get_tenant_no
  10. from app.core.utils import generate_uuid
  11. from app.models.pl_models import PlTransportAddress
  12. from app.schemas.pl.transport_address import (
  13. TransportAddressCreate,
  14. TransportAddressListResponse,
  15. TransportAddressRead,
  16. TransportAddressUpdate,
  17. )
  18. router = APIRouter(tags=["运输地址库"])
  19. def _address_to_read(row: PlTransportAddress) -> TransportAddressRead:
  20. return TransportAddressRead.model_validate(row)
  21. def _get_address(
  22. db: Session, tenant_no: str, address_id: str, user: DbUserContext
  23. ) -> PlTransportAddress:
  24. row = (
  25. db.query(PlTransportAddress)
  26. .filter(
  27. PlTransportAddress.id == address_id,
  28. PlTransportAddress.tenant_no == tenant_no,
  29. PlTransportAddress.del_flag == 0,
  30. )
  31. .first()
  32. )
  33. if not row:
  34. raise HTTPException(status_code=404, detail="地址不存在")
  35. return row
  36. def _build_address_query(db: Session, tenant_no: str, user: DbUserContext):
  37. # 运输地址为主数据选项,不做 create_by 数据范围过滤
  38. _ = user
  39. query = db.query(PlTransportAddress).filter(
  40. PlTransportAddress.tenant_no == tenant_no,
  41. PlTransportAddress.del_flag == 0,
  42. )
  43. return query
  44. @router.get("/address", response_model=TransportAddressListResponse)
  45. def list_transport_addresses(
  46. q: Optional[str] = Query(default=None, description="搜索名称或地址"),
  47. status: Optional[str] = Query(default=None, description="状态 1-启用 0-停用"),
  48. page: int = Query(default=1, ge=1),
  49. page_size: int = Query(default=20, ge=1, le=100, alias="pageSize"),
  50. db: Session = Depends(get_db),
  51. user: DbUserContext = Depends(get_db_current_user),
  52. ) -> TransportAddressListResponse:
  53. tenant_no = get_tenant_no(db, user.to_tenant_user_dict())
  54. query = _build_address_query(db, tenant_no, user)
  55. if status:
  56. query = query.filter(PlTransportAddress.status == status)
  57. if q:
  58. keyword = q.strip().lower()
  59. query = query.filter(
  60. or_(
  61. func.lower(PlTransportAddress.address_name).contains(keyword),
  62. func.lower(PlTransportAddress.full_address).contains(keyword),
  63. func.lower(PlTransportAddress.contact_name).contains(keyword),
  64. PlTransportAddress.contact_phone.contains(q.strip()),
  65. )
  66. )
  67. query = query.order_by(
  68. case((PlTransportAddress.order_num.is_(None), 1), else_=0).asc(),
  69. PlTransportAddress.create_time.desc(),
  70. )
  71. total = query.count()
  72. offset = (page - 1) * page_size
  73. rows = query.offset(offset).limit(page_size).all()
  74. return TransportAddressListResponse(
  75. total=total,
  76. list=[_address_to_read(row) for row in rows],
  77. )
  78. @router.get("/address/all", response_model=TransportAddressListResponse)
  79. def list_all_transport_addresses(
  80. q: Optional[str] = Query(default=None, description="搜索名称或地址"),
  81. db: Session = Depends(get_db),
  82. user: DbUserContext = Depends(get_db_current_user),
  83. ) -> TransportAddressListResponse:
  84. """选择器用:仅返回启用中的地址(不分页)。"""
  85. tenant_no = get_tenant_no(db, user.to_tenant_user_dict())
  86. query = _build_address_query(db, tenant_no, user).filter(PlTransportAddress.status == "1")
  87. if q:
  88. keyword = q.strip().lower()
  89. query = query.filter(
  90. or_(
  91. func.lower(PlTransportAddress.address_name).contains(keyword),
  92. func.lower(PlTransportAddress.full_address).contains(keyword),
  93. )
  94. )
  95. query = query.order_by(
  96. case((PlTransportAddress.order_num.is_(None), 1), else_=0).asc(),
  97. PlTransportAddress.create_time.desc(),
  98. )
  99. rows = query.limit(200).all()
  100. return TransportAddressListResponse(total=len(rows), list=[_address_to_read(row) for row in rows])
  101. @router.post("/address", response_model=TransportAddressRead, status_code=status.HTTP_201_CREATED)
  102. def create_transport_address(
  103. payload: TransportAddressCreate,
  104. db: Session = Depends(get_db),
  105. user: DbUserContext = Depends(get_db_current_user),
  106. ) -> TransportAddressRead:
  107. tenant_no = get_tenant_no(db, user.to_tenant_user_dict())
  108. row = PlTransportAddress(
  109. id=generate_uuid(),
  110. tenant_no=tenant_no,
  111. address_name=payload.address_name.strip(),
  112. full_address=payload.full_address.strip(),
  113. lng=payload.lng,
  114. lat=payload.lat,
  115. contact_name=payload.contact_name,
  116. contact_phone=payload.contact_phone,
  117. status=payload.status or "1",
  118. remark=payload.remark,
  119. create_by=user.id,
  120. create_dept_id=user.dept_id,
  121. )
  122. db.add(row)
  123. db.commit()
  124. db.refresh(row)
  125. return _address_to_read(row)
  126. @router.get("/address/{id}", response_model=TransportAddressRead)
  127. def get_transport_address(
  128. id: str,
  129. db: Session = Depends(get_db),
  130. user: DbUserContext = Depends(get_db_current_user),
  131. ) -> TransportAddressRead:
  132. tenant_no = get_tenant_no(db, user.to_tenant_user_dict())
  133. row = _get_address(db, tenant_no, id, user)
  134. return _address_to_read(row)
  135. @router.put("/address/{id}", response_model=TransportAddressRead)
  136. def update_transport_address(
  137. id: str,
  138. payload: TransportAddressUpdate,
  139. db: Session = Depends(get_db),
  140. user: DbUserContext = Depends(get_db_current_user),
  141. ) -> TransportAddressRead:
  142. tenant_no = get_tenant_no(db, user.to_tenant_user_dict())
  143. row = _get_address(db, tenant_no, id, user)
  144. for key, value in payload.model_dump(exclude_unset=True).items():
  145. if key in ("address_name", "full_address") and isinstance(value, str):
  146. value = value.strip()
  147. setattr(row, key, value)
  148. row.update_by = user.id
  149. db.commit()
  150. db.refresh(row)
  151. return _address_to_read(row)
  152. @router.delete("/address/{id}", status_code=status.HTTP_204_NO_CONTENT)
  153. def delete_transport_address(
  154. id: str,
  155. db: Session = Depends(get_db),
  156. user: DbUserContext = Depends(get_db_current_user),
  157. ) -> None:
  158. tenant_no = get_tenant_no(db, user.to_tenant_user_dict())
  159. row = _get_address(db, tenant_no, id, user)
  160. assert_row_deletable_by_creator(row, user_info_from_context(user))
  161. row.del_flag = 1
  162. row.update_by = user.id
  163. db.commit()