product_banner.py 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. """API - 商品轮播图管理"""
  2. from fastapi import APIRouter, Depends, HTTPException, Request
  3. from pydantic import ValidationError
  4. from sqlalchemy.orm import Session
  5. from app.core.database import get_db
  6. from app.core.auth import get_current_user
  7. from app.core.admin_request_json import parse_admin_request_json
  8. from app.core.response import success_response, error_response
  9. from app.crud.product_banner import product_banner
  10. from app.schemas.product_banner import (
  11. ProductBannerCreate,
  12. ProductBannerUpdate,
  13. ProductBannerSortUpdate,
  14. )
  15. router = APIRouter(prefix="/web/admin/product-banners", tags=["商品轮播图管理"])
  16. def require_admin(current_user: dict = Depends(get_current_user)):
  17. """验证运营人员权限"""
  18. if current_user.get("role") not in {"admin", "super_admin"}:
  19. raise HTTPException(status_code=403, detail="需要运营人员权限")
  20. return current_user
  21. @router.get("/product/{product_id}", response_model=dict)
  22. def list_banners(
  23. product_id: str,
  24. db: Session = Depends(get_db),
  25. current_user: dict = Depends(require_admin)
  26. ):
  27. """获取商品的所有轮播图"""
  28. banners = product_banner.get_by_product(db, product_id)
  29. return success_response({
  30. "list": [{
  31. "id": b.id,
  32. "productId": b.product_id,
  33. "mediaType": b.media_type,
  34. "fileUrl": b.file_url,
  35. "thumbnailUrl": b.thumbnail_url,
  36. "sortOrder": b.sort_order,
  37. "createAt": str(b.create_at) if b.create_at else None
  38. } for b in banners],
  39. "total": len(banners)
  40. })
  41. @router.post("/product/{product_id}", response_model=dict)
  42. async def create_banners(
  43. product_id: str,
  44. request: Request,
  45. db: Session = Depends(get_db),
  46. current_user: dict = Depends(require_admin)
  47. ):
  48. """批量添加轮播图(请求体为 JSON 数组;兼容密文信封与 ApiCryptoMiddleware 未改写 body 的场景)"""
  49. items_raw, err = await parse_admin_request_json(
  50. request, current_user, source="admin_product_banner"
  51. )
  52. if err:
  53. return error_response(400, err)
  54. if not isinstance(items_raw, list):
  55. return error_response(400, "请求体应为轮播图条目数组")
  56. try:
  57. items = [ProductBannerCreate.model_validate(x) for x in items_raw]
  58. except ValidationError as exc:
  59. return error_response(400, str(exc))
  60. creator = {"user_id": current_user.get("user_id"), "real_name": current_user.get("real_name")}
  61. items_data = [item.model_dump() for item in items]
  62. banners = product_banner.create_batch(db, product_id, items_data, creator)
  63. return success_response({
  64. "list": [{
  65. "id": b.id,
  66. "productId": b.product_id,
  67. "mediaType": b.media_type,
  68. "fileUrl": b.file_url,
  69. "thumbnailUrl": b.thumbnail_url,
  70. "sortOrder": b.sort_order
  71. } for b in banners],
  72. "total": len(banners)
  73. })
  74. @router.put("/product/{product_id}/sort", response_model=dict)
  75. async def update_sort(
  76. product_id: str,
  77. request: Request,
  78. db: Session = Depends(get_db),
  79. current_user: dict = Depends(require_admin)
  80. ):
  81. """更新轮播图排序(兼容密文信封)"""
  82. data, err = await parse_admin_request_json(
  83. request, current_user, source="admin_product_banner_sort"
  84. )
  85. if err:
  86. return error_response(400, err)
  87. if not isinstance(data, dict):
  88. return error_response(400, "请求体应为 JSON 对象且包含 items")
  89. try:
  90. sort_data = ProductBannerSortUpdate.model_validate(data)
  91. except ValidationError as exc:
  92. return error_response(400, str(exc))
  93. items_data = [item.model_dump() for item in sort_data.items]
  94. product_banner.update_sort(db, items_data)
  95. return success_response({"message": "排序更新成功"})
  96. @router.put("/{banner_id}", response_model=dict)
  97. def update_banner(
  98. banner_id: str,
  99. update_data: ProductBannerUpdate,
  100. db: Session = Depends(get_db),
  101. current_user: dict = Depends(require_admin)
  102. ):
  103. """更新轮播图"""
  104. banner = product_banner.update(db, banner_id, update_data.model_dump(exclude_unset=True))
  105. if not banner:
  106. return error_response(404, "轮播图不存在")
  107. return success_response({
  108. "id": banner.id,
  109. "productId": banner.product_id,
  110. "mediaType": banner.media_type,
  111. "fileUrl": banner.file_url,
  112. "thumbnailUrl": banner.thumbnail_url,
  113. "sortOrder": banner.sort_order
  114. })
  115. @router.delete("/{banner_id}", response_model=dict)
  116. def delete_banner(
  117. banner_id: str,
  118. db: Session = Depends(get_db),
  119. current_user: dict = Depends(require_admin)
  120. ):
  121. """删除轮播图"""
  122. result = product_banner.delete(db, banner_id)
  123. if not result:
  124. return error_response(404, "轮播图不存在")
  125. return success_response({"message": "删除成功"})