product_review.py 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. """API - 售后评价管理"""
  2. from typing import Optional, List
  3. from fastapi import APIRouter, Depends, HTTPException, Query
  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.response import success_response, error_response
  8. from app.crud.product_review import product_review
  9. from app.crud.product_review_media import product_review_media
  10. from app.schemas.product_review import (
  11. ProductReviewReplyRequest,
  12. ProductReviewRejectRequest,
  13. ProductReviewTopRequest,
  14. ProductReviewResponse,
  15. ProductReviewListResponse,
  16. ProductReviewStatisticsResponse
  17. )
  18. router = APIRouter(prefix="/web/admin/reviews", tags=["售后评价管理"])
  19. def require_admin(current_user: dict = Depends(get_current_user)):
  20. """验证运营人员权限"""
  21. if current_user.get("role") not in {"admin", "super_admin"}:
  22. raise HTTPException(status_code=403, detail="需要运营人员权限")
  23. return current_user
  24. @router.get("", response_model=dict)
  25. def list_reviews(
  26. page: int = Query(1, ge=1, description="页码"),
  27. pageSize: int = Query(10, ge=1, le=100, description="每页条数"),
  28. productId: Optional[str] = Query(None, description="商品ID"),
  29. productName: Optional[str] = Query(None, description="商品名称"),
  30. userName: Optional[str] = Query(None, description="用户名称"),
  31. status: Optional[str] = Query(None, description="状态:pending/approved/rejected"),
  32. rating: Optional[int] = Query(None, ge=1, le=5, description="评分"),
  33. startDate: Optional[str] = Query(None, description="开始日期"),
  34. endDate: Optional[str] = Query(None, description="结束日期"),
  35. db: Session = Depends(get_db),
  36. current_user: dict = Depends(require_admin)
  37. ):
  38. """获取评价列表"""
  39. items, total = product_review.get_admin_list(
  40. db,
  41. skip=(page - 1) * pageSize,
  42. limit=pageSize,
  43. product_id=productId,
  44. user_name=userName,
  45. status=status,
  46. rating=rating,
  47. start_date=startDate,
  48. end_date=endDate
  49. )
  50. # 获取每个评价的媒体列表
  51. result_list = []
  52. for item in items:
  53. media_list = product_review_media.get_by_review(db, item.id)
  54. result_list.append({
  55. "id": item.id,
  56. "productId": item.product_id,
  57. "orderId": item.order_id,
  58. "orderItemId": item.order_item_id,
  59. "userId": item.user_id,
  60. "userName": "匿名用户" if item.is_anonymous else item.user_name,
  61. "rating": item.rating,
  62. "ratingTags": item.rating_tags,
  63. "content": item.content,
  64. "isAnonymous": item.is_anonymous,
  65. "replyContent": item.reply_content,
  66. "replyTime": str(item.reply_time) if item.reply_time else None,
  67. "replyBy": item.reply_by,
  68. "replyByName": item.reply_by_name,
  69. "likeCount": item.like_count,
  70. "isTop": item.is_top,
  71. "status": item.status,
  72. "rejectReason": item.reject_reason,
  73. "createAt": str(item.create_at) if item.create_at else None,
  74. "mediaList": [{
  75. "id": m.id,
  76. "mediaType": m.media_type,
  77. "fileUrl": m.file_url,
  78. "thumbnailUrl": m.thumbnail_url,
  79. "sortOrder": m.sort_order
  80. } for m in media_list]
  81. })
  82. return success_response({
  83. "list": result_list,
  84. "total": total,
  85. "page": page,
  86. "pageSize": pageSize
  87. })
  88. @router.get("/statistics", response_model=dict)
  89. def get_review_statistics(
  90. productId: Optional[str] = Query(None, description="商品ID"),
  91. db: Session = Depends(get_db),
  92. current_user: dict = Depends(require_admin)
  93. ):
  94. """获取评价统计"""
  95. stats = product_review.get_statistics(db, productId)
  96. return success_response(stats)
  97. @router.get("/{review_id}", response_model=dict)
  98. def get_review(
  99. review_id: str,
  100. db: Session = Depends(get_db),
  101. current_user: dict = Depends(require_admin)
  102. ):
  103. """获取评价详情"""
  104. review = product_review.get(db, review_id)
  105. if not review:
  106. return error_response(404, "评价不存在")
  107. media_list = product_review_media.get_by_review(db, review_id)
  108. return success_response({
  109. "id": review.id,
  110. "productId": review.product_id,
  111. "orderId": review.order_id,
  112. "orderItemId": review.order_item_id,
  113. "userId": review.user_id,
  114. "userName": "匿名用户" if review.is_anonymous else review.user_name,
  115. "rating": review.rating,
  116. "ratingTags": review.rating_tags,
  117. "content": review.content,
  118. "isAnonymous": review.is_anonymous,
  119. "replyContent": review.reply_content,
  120. "replyTime": str(review.reply_time) if review.reply_time else None,
  121. "replyBy": review.reply_by,
  122. "replyByName": review.reply_by_name,
  123. "likeCount": review.like_count,
  124. "isTop": review.is_top,
  125. "status": review.status,
  126. "rejectReason": review.reject_reason,
  127. "createAt": str(review.create_at) if review.create_at else None,
  128. "mediaList": [{
  129. "id": m.id,
  130. "mediaType": m.media_type,
  131. "fileUrl": m.file_url,
  132. "thumbnailUrl": m.thumbnail_url,
  133. "sortOrder": m.sort_order
  134. } for m in media_list]
  135. })
  136. @router.put("/{review_id}/approve", response_model=dict)
  137. def approve_review(
  138. review_id: str,
  139. db: Session = Depends(get_db),
  140. current_user: dict = Depends(require_admin)
  141. ):
  142. """审核通过评价"""
  143. review = product_review.approve(db, review_id)
  144. if not review:
  145. return error_response(404, "评价不存在")
  146. return success_response({"message": "审核通过", "id": review.id})
  147. @router.put("/{review_id}/reject", response_model=dict)
  148. def reject_review(
  149. review_id: str,
  150. reject_data: ProductReviewRejectRequest,
  151. db: Session = Depends(get_db),
  152. current_user: dict = Depends(require_admin)
  153. ):
  154. """拒绝评价"""
  155. updater = {"user_id": current_user.get("user_id"), "real_name": current_user.get("real_name")}
  156. review = product_review.reject(db, review_id, reject_data.reject_reason, updater)
  157. if not review:
  158. return error_response(404, "评价不存在")
  159. return success_response({"message": "已拒绝", "id": review.id})
  160. @router.put("/{review_id}/reply", response_model=dict)
  161. def reply_review(
  162. review_id: str,
  163. reply_data: ProductReviewReplyRequest,
  164. db: Session = Depends(get_db),
  165. current_user: dict = Depends(require_admin)
  166. ):
  167. """回复评价"""
  168. updater = {"user_id": current_user.get("user_id"), "real_name": current_user.get("real_name")}
  169. review = product_review.reply(db, review_id, reply_data.reply_content, updater)
  170. if not review:
  171. return error_response(404, "评价不存在")
  172. return success_response({
  173. "message": "回复成功",
  174. "id": review.id,
  175. "replyContent": review.reply_content,
  176. "replyTime": str(review.reply_time) if review.reply_time else None
  177. })
  178. @router.put("/{review_id}/top", response_model=dict)
  179. def toggle_top_review(
  180. review_id: str,
  181. top_data: ProductReviewTopRequest,
  182. db: Session = Depends(get_db),
  183. current_user: dict = Depends(require_admin)
  184. ):
  185. """置顶/取消置顶评价"""
  186. review = product_review.toggle_top(db, review_id, top_data.is_top)
  187. if not review:
  188. return error_response(404, "评价不存在")
  189. return success_response({
  190. "message": "操作成功",
  191. "id": review.id,
  192. "isTop": review.is_top
  193. })