product_review.py 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. """CRUD - 售后评价"""
  2. from typing import List, Optional
  3. from sqlalchemy.orm import Session
  4. from sqlalchemy import and_, func
  5. from app.crud.base import CRUDBase, HistoryCRUD
  6. from app.models.product_review import BizProductReview, BizProductReviewHis
  7. from app.schemas.product_review import ProductReviewCreate, ProductReviewReplyRequest
  8. class ProductReviewCRUD(CRUDBase[BizProductReview]):
  9. """售后评价CRUD操作"""
  10. def get_by_product(
  11. self,
  12. db: Session,
  13. product_id: str,
  14. skip: int = 0,
  15. limit: int = 10,
  16. status: str = "approved",
  17. has_media: int = None
  18. ) -> tuple:
  19. """获取商品的评价列表"""
  20. query = db.query(self.model).filter(
  21. self.model.product_id == product_id,
  22. self.model.is_del == 0
  23. )
  24. if status:
  25. query = query.filter(self.model.status == status)
  26. if has_media == 1:
  27. from app.models.product_review_media import BizProductReviewMedia
  28. review_ids_with_media = db.query(BizProductReviewMedia.review_id).filter(
  29. BizProductReviewMedia.is_del == 0
  30. ).distinct().subquery()
  31. query = query.filter(self.model.id.in_(review_ids_with_media))
  32. total = query.count()
  33. items = query.order_by(
  34. self.model.is_top.desc(),
  35. self.model.create_at.desc()
  36. ).offset(skip).limit(limit).all()
  37. return items, total
  38. def get_admin_list(
  39. self,
  40. db: Session,
  41. skip: int = 0,
  42. limit: int = 10,
  43. product_id: str = None,
  44. product_name: str = None,
  45. user_name: str = None,
  46. status: str = None,
  47. rating: int = None,
  48. start_date: str = None,
  49. end_date: str = None,
  50. ) -> tuple:
  51. """管理员获取评价列表(支持多条件筛选)"""
  52. query = db.query(self.model).filter(self.model.is_del == 0)
  53. if product_id:
  54. query = query.filter(self.model.product_id == product_id)
  55. if user_name:
  56. query = query.filter(self.model.user_name.like(f"%{user_name}%"))
  57. if status:
  58. query = query.filter(self.model.status == status)
  59. if rating:
  60. query = query.filter(self.model.rating == rating)
  61. if start_date:
  62. query = query.filter(self.model.create_at >= start_date)
  63. if end_date:
  64. query = query.filter(self.model.create_at <= end_date)
  65. total = query.count()
  66. items = query.order_by(
  67. self.model.is_top.desc(),
  68. self.model.create_at.desc()
  69. ).offset(skip).limit(limit).all()
  70. return items, total
  71. def get_statistics(self, db: Session, product_id: str = None) -> dict:
  72. """获取评价统计"""
  73. query = db.query(self.model).filter(
  74. self.model.is_del == 0,
  75. self.model.status == "approved"
  76. )
  77. if product_id:
  78. query = query.filter(self.model.product_id == product_id)
  79. total_count = query.count()
  80. if total_count > 0:
  81. avg_rating = db.query(func.avg(self.model.rating)).filter(
  82. self.model.is_del == 0,
  83. self.model.status == "approved",
  84. self.model.product_id == product_id if product_id else True
  85. ).scalar() or 0
  86. else:
  87. avg_rating = 0
  88. rating_dist = {}
  89. for r in range(1, 6):
  90. count = db.query(self.model).filter(
  91. self.model.is_del == 0,
  92. self.model.status == "approved",
  93. self.model.rating == r,
  94. self.model.product_id == product_id if product_id else True
  95. ).count()
  96. rating_dist[str(r)] = count
  97. pending_count = db.query(self.model).filter(
  98. self.model.is_del == 0,
  99. self.model.status == "pending",
  100. self.model.product_id == product_id if product_id else True
  101. ).count()
  102. rejected_count = db.query(self.model).filter(
  103. self.model.is_del == 0,
  104. self.model.status == "rejected",
  105. self.model.product_id == product_id if product_id else True
  106. ).count()
  107. return {
  108. "total_count": total_count,
  109. "pending_count": pending_count,
  110. "approved_count": total_count,
  111. "rejected_count": rejected_count,
  112. "avg_rating": round(float(avg_rating), 1),
  113. "rating_distribution": rating_dist,
  114. }
  115. def reply(
  116. self,
  117. db: Session,
  118. review_id: str,
  119. reply_content: str,
  120. updater: dict = None
  121. ) -> Optional[BizProductReview]:
  122. """回复评价"""
  123. review = self.get(db, review_id)
  124. if review:
  125. review.reply_content = reply_content
  126. review.reply_time = func.now()
  127. if updater:
  128. review.reply_by = updater.get("user_id")
  129. review.reply_by_name = updater.get("real_name")
  130. db.commit()
  131. db.refresh(review)
  132. return review
  133. def reject(
  134. self,
  135. db: Session,
  136. review_id: str,
  137. reject_reason: str,
  138. updater: dict = None
  139. ) -> Optional[BizProductReview]:
  140. """拒绝评价"""
  141. review = self.get(db, review_id)
  142. if review:
  143. review.status = "rejected"
  144. review.reject_reason = reject_reason
  145. db.commit()
  146. db.refresh(review)
  147. return review
  148. def approve(
  149. self,
  150. db: Session,
  151. review_id: str
  152. ) -> Optional[BizProductReview]:
  153. """审核通过评价"""
  154. review = self.get(db, review_id)
  155. if review:
  156. review.status = "approved"
  157. db.commit()
  158. db.refresh(review)
  159. return review
  160. def toggle_top(
  161. self,
  162. db: Session,
  163. review_id: str,
  164. is_top: int
  165. ) -> Optional[BizProductReview]:
  166. """切换置顶状态"""
  167. review = self.get(db, review_id)
  168. if review:
  169. review.is_top = is_top
  170. db.commit()
  171. db.refresh(review)
  172. return review
  173. def like(
  174. self,
  175. db: Session,
  176. review_id: str
  177. ) -> Optional[BizProductReview]:
  178. """点赞评价"""
  179. review = self.get(db, review_id)
  180. if review:
  181. review.like_count = (review.like_count or 0) + 1
  182. db.commit()
  183. db.refresh(review)
  184. return review
  185. def user_can_review(
  186. self,
  187. db: Session,
  188. user_id: str,
  189. product_id: str,
  190. order_id: str = None
  191. ) -> bool:
  192. """检查用户是否已评价过该商品"""
  193. query = db.query(self.model).filter(
  194. self.model.user_id == user_id,
  195. self.model.product_id == product_id,
  196. self.model.is_del == 0
  197. )
  198. if order_id:
  199. query = query.filter(self.model.order_id == order_id)
  200. return query.first() is not None
  201. class ProductReviewHisCRUD(HistoryCRUD[BizProductReviewHis]):
  202. """售后评价历史CRUD操作"""
  203. pass
  204. product_review = ProductReviewCRUD(BizProductReview)
  205. product_review_his = ProductReviewHisCRUD(BizProductReviewHis)