CommentService.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376
  1. <?php
  2. namespace app\common\Service\Goods;
  3. use app\common\model\Comment;
  4. use app\common\model\Order;
  5. use app\common\model\OrderAction;
  6. use app\common\Enum\OrderEnum;
  7. use app\common\Enum\CommentEnum;
  8. use think\Db;
  9. use think\Exception;
  10. use app\common\exception\BusinessException;
  11. use app\common\Enum\StatusEnum;
  12. use think\console\output\formatter\Stack;
  13. class CommentService
  14. {
  15. /**
  16. * 获取评论列表
  17. * @param array $params 查询参数
  18. * @return \think\Paginator
  19. */
  20. public static function getCommentList($params = [])
  21. {
  22. $goods_id = isset($params['goods_id']) ? (int)$params['goods_id'] : 0;
  23. $pid = isset($params['pid']) ? (int)$params['pid'] : 0;
  24. $has_picture = isset($params['has_picture']) ? (int)$params['has_picture'] : '';
  25. $rating_type = isset($params['rating_type']) ? (int)$params['rating_type'] : '';
  26. $orderBy = isset($params['order_by']) ? $params['order_by'] : 'id';
  27. $orderWay = isset($params['order_way']) ? strtolower($params['order_way']) : 'desc';
  28. $page = isset($params['page']) ? (int)$params['page'] : 1;
  29. $pageSize = isset($params['page_size']) ? $params['page_size'] : 10;
  30. $orderWay = in_array($orderWay, ['asc', 'desc']) ? $orderWay : 'desc';
  31. $where = [
  32. 'evaluate_status' => CommentEnum::EVALUATE_STATUS_APPROVED,
  33. 'status' => StatusEnum::ENABLED
  34. ];
  35. if ($goods_id > 0) {
  36. $where['goods_id'] = $goods_id;
  37. }
  38. if ($pid !== '') {
  39. $where['pid'] = $pid;
  40. }
  41. // 添加有图评论过滤
  42. if ($has_picture !== '') {
  43. $where['has_picture'] = $has_picture;
  44. }
  45. // 添加评价类型过滤
  46. if ($rating_type !== '') {
  47. if ($rating_type == CommentEnum::RATING_TYPE_GOOD) {
  48. $where['star'] = ['>=', 4];
  49. } elseif ($rating_type == CommentEnum::RATING_TYPE_MEDIUM) {
  50. $where['star'] = 3;
  51. } elseif ($rating_type == CommentEnum::RATING_TYPE_BAD) {
  52. $where['star'] = ['<=', 2];
  53. }
  54. }
  55. $order = $orderBy == 'rand' ? 'rand()' : (in_array($orderBy, ['pid', 'id', 'createtime', 'updatetime']) ? "{$orderBy} {$orderWay}" : "id {$orderWay}");
  56. $list = Comment::with(['user', 'reply' => function ($query) {
  57. $query->with(['manage' => function ($user) {
  58. $user->field('id,nickname');
  59. }]);
  60. }])
  61. ->where($where)
  62. ->order($order)
  63. ->paginate($pageSize, false, ['page' => $page]);
  64. return $list;
  65. }
  66. /**
  67. * 获取商品好评度
  68. * @param int $goods_id 商品ID
  69. * @return float
  70. */
  71. public static function degree($goods_id)
  72. {
  73. $total = Comment::where('goods_id', $goods_id)
  74. ->where('pid', 0)
  75. ->where('evaluate_status', CommentEnum::EVALUATE_STATUS_APPROVED)
  76. ->where('status', StatusEnum::ENABLED)
  77. ->sum('star');
  78. $favorable = Comment::where('goods_id', $goods_id)
  79. ->where('pid', 0)
  80. ->where('evaluate_status', CommentEnum::EVALUATE_STATUS_APPROVED)
  81. ->where('status', StatusEnum::ENABLED)
  82. ->where('star', '>', 3)
  83. ->sum('star');
  84. if (!$total || !$favorable) {
  85. return 100;
  86. }
  87. return bcmul(bcdiv($favorable, $total, 2), 100);
  88. }
  89. // 获取 总计评论数 和 第一个评论数据
  90. public static function getCommentCountAndFirstComment($goods_id = 0)
  91. {
  92. $total = Comment::where('goods_id', $goods_id)
  93. ->where('pid', 0)
  94. ->where('evaluate_status', CommentEnum::EVALUATE_STATUS_APPROVED)
  95. ->where('status', StatusEnum::ENABLED)
  96. ->count();
  97. $comment = Comment::where('goods_id', $goods_id)
  98. ->with(['user' => function ($query) {
  99. $query->field('id,nickname,avatar,username');
  100. }])
  101. ->where('pid', 0)
  102. ->where('evaluate_status', CommentEnum::EVALUATE_STATUS_APPROVED)
  103. ->where('status', StatusEnum::ENABLED)
  104. ->order('id desc')
  105. ->find();
  106. if (!empty($comment)) {
  107. if(!empty($comment->user->avatar)){
  108. $comment->user->avatar = cdnurl($comment->user->avatar, true);
  109. }
  110. }
  111. return [
  112. 'total' => $total,
  113. 'comment' => $comment
  114. ];
  115. }
  116. /**
  117. * 获取用户评论列表
  118. * @param int $user_id 用户ID
  119. * @param int $page 页码
  120. * @param int $pagesize 每页数量
  121. * @return \think\Paginator
  122. */
  123. public static function getUserCommentList($user_id, $page = 1, $pagesize = 10)
  124. {
  125. $list = Comment::with([
  126. 'Goods' => function ($query) {
  127. $query->field('id,title,image');
  128. }
  129. ])
  130. ->where('user_id', $user_id)
  131. ->where('pid', 0)
  132. ->where('evaluate_status', CommentEnum::EVALUATE_STATUS_APPROVED)
  133. ->where('status', StatusEnum::ENABLED)
  134. ->order('createtime desc')
  135. ->paginate($pagesize, false, ['page' => $page]);
  136. return $list;
  137. }
  138. /**
  139. * 添加评论
  140. * @param int $user_id 用户ID
  141. * @param int $order_id 订单ID
  142. * @param array $remark 评论内容
  143. * @param int $pid 父评论ID
  144. * @return bool
  145. * @throws Exception
  146. */
  147. public static function addComment($user_id, $order_id, $remark, $pid = 0)
  148. {
  149. // 验证订单
  150. $order = Order::with(['OrderGoods'])
  151. ->where('id', $order_id)
  152. ->where('order_status', OrderEnum::STATUS_CONFIRM)
  153. ->where('user_id', $user_id)
  154. ->find();
  155. if (!$order) {
  156. throw new BusinessException('未找到可评论的订单');
  157. }
  158. // 检查是否已评论
  159. $existComment = Comment::where('user_id', $user_id)
  160. ->where('order_id', $order->id)
  161. ->find();
  162. if ($existComment) {
  163. throw new BusinessException('订单已评论');
  164. }
  165. // 获取可评价的商品
  166. $goods_ids = [];
  167. foreach ($order->order_goods as $item) {
  168. $goods_ids[] = $item['goods_id'];
  169. }
  170. // 验证评论数据并组装
  171. $data = [];
  172. foreach ($remark as $item) {
  173. if (!in_array($item['goods_id'], $goods_ids)) {
  174. throw new BusinessException('存在不可评价的商品');
  175. }
  176. // 处理图片
  177. $images = isset($item['images']) ? $item['images'] : [];
  178. $has_picture = !empty($images) ? CommentEnum::HAS_PICTURE_YES : CommentEnum::HAS_PICTURE_NO;
  179. $data[] = [
  180. 'pid' => $pid,
  181. 'order_id' => $order['id'],
  182. 'user_id' => $user_id,
  183. 'goods_id' => $item['goods_id'],
  184. 'star' => $item['star'],
  185. 'content' => $item['content'],
  186. 'images' => is_array($images) ? implode(',', $images) : $images,
  187. 'has_picture' => $has_picture,
  188. 'ip' => request()->ip(),
  189. 'useragent' => substr(request()->server('HTTP_USER_AGENT'), 0, 255),
  190. 'evaluate_status' => CommentEnum::EVALUATE_STATUS_PENDING,
  191. ];
  192. }
  193. // 事务处理
  194. Db::startTrans();
  195. try {
  196. // 批量添加评论
  197. $comment = new Comment();
  198. $comment->saveAll($data);
  199. // 更新订单状态
  200. $order->order_status = OrderEnum::STATUS_COMMENT;
  201. $order->save();
  202. // 更新订单商品评论状态
  203. foreach ($order->order_goods as $item) {
  204. $item->save(['comment_status' => 1]);
  205. }
  206. // 添加订单日志
  207. OrderAction::push($order->order_sn, '系统', '订单已完成');
  208. Db::commit();
  209. return true;
  210. } catch (\Exception $e) {
  211. Db::rollback();
  212. throw new BusinessException('添加评论失败:' . $e->getMessage());
  213. }
  214. }
  215. /**
  216. * 批量保存评论(原方法保留)
  217. * @param array $data 评论数据
  218. * @return bool
  219. */
  220. public static function saveComments($data)
  221. {
  222. $comment = new Comment();
  223. return $comment->saveAll($data);
  224. }
  225. /**
  226. * 审核评论
  227. * @param int $comment_id 评论ID
  228. * @param int $evaluate_status 评价状态
  229. * @return bool
  230. * @throws Exception
  231. */
  232. public static function auditComment($comment_id, $evaluate_status)
  233. {
  234. if (!CommentEnum::isValidEvaluateStatus($evaluate_status)) {
  235. throw new BusinessException('评价状态参数错误');
  236. }
  237. $comment = Comment::where('id', $comment_id)->find();
  238. if (!$comment) {
  239. throw new BusinessException('评论不存在');
  240. }
  241. $status = $evaluate_status == CommentEnum::EVALUATE_STATUS_APPROVED ?
  242. StatusEnum::ENABLED : StatusEnum::DISABLED;
  243. return $comment->save([
  244. 'evaluate_status' => $evaluate_status,
  245. 'status' => $status
  246. ]);
  247. }
  248. /**
  249. * 获取商品评论统计
  250. * @param int $goods_id 商品ID
  251. * @return array
  252. */
  253. public static function getCommentStats($goods_id)
  254. {
  255. $total = Comment::where('goods_id', $goods_id)
  256. ->where('pid', 0)
  257. ->where('evaluate_status', CommentEnum::EVALUATE_STATUS_APPROVED)
  258. ->where('status', StatusEnum::ENABLED)
  259. ->count();
  260. $star_counts = [];
  261. for ($i = 1; $i <= 5; $i++) {
  262. $star_counts[$i] = Comment::where('goods_id', $goods_id)
  263. ->where('pid', 0)
  264. ->where('evaluate_status', CommentEnum::EVALUATE_STATUS_APPROVED)
  265. ->where('status', StatusEnum::ENABLED)
  266. ->where('star', $i)
  267. ->count();
  268. }
  269. // 有图评论统计
  270. $picture_count = Comment::where('goods_id', $goods_id)
  271. ->where('pid', 0)
  272. ->where('evaluate_status', CommentEnum::EVALUATE_STATUS_APPROVED)
  273. ->where('status', StatusEnum::ENABLED)
  274. ->where('has_picture', CommentEnum::HAS_PICTURE_YES)
  275. ->count();
  276. // 好评统计 (4-5星)
  277. $good_count = Comment::where('goods_id', $goods_id)
  278. ->where('pid', 0)
  279. ->where('evaluate_status', CommentEnum::EVALUATE_STATUS_APPROVED)
  280. ->where('status', StatusEnum::ENABLED)
  281. ->where('star', '>=', 4)
  282. ->count();
  283. // 中评统计 (3星)
  284. $medium_count = Comment::where('goods_id', $goods_id)
  285. ->where('pid', 0)
  286. ->where('evaluate_status', CommentEnum::EVALUATE_STATUS_APPROVED)
  287. ->where('status', StatusEnum::ENABLED)
  288. ->where('star', 3)
  289. ->count();
  290. // 差评统计 (1-2星)
  291. $bad_count = Comment::where('goods_id', $goods_id)
  292. ->where('pid', 0)
  293. ->where('evaluate_status', CommentEnum::EVALUATE_STATUS_APPROVED)
  294. ->where('status', StatusEnum::ENABLED)
  295. ->where('star', '<=', 2)
  296. ->count();
  297. // 计算各类型评论占比
  298. $good_percentage = $total > 0 ? round(($good_count / $total) * 100, 1) : 0;
  299. $medium_percentage = $total > 0 ? round(($medium_count / $total) * 100, 1) : 0;
  300. $bad_percentage = $total > 0 ? round(($bad_count / $total) * 100, 1) : 0;
  301. return [
  302. 'total' => $total,
  303. 'star_counts' => $star_counts,
  304. 'picture_count' => $picture_count,
  305. 'rating_stats' => [
  306. 'good' => [
  307. 'count' => $good_count,
  308. 'percentage' => $good_percentage,
  309. 'type' => CommentEnum::RATING_TYPE_GOOD,
  310. 'text' => CommentEnum::getRatingTypeText(CommentEnum::RATING_TYPE_GOOD)
  311. ],
  312. 'medium' => [
  313. 'count' => $medium_count,
  314. 'percentage' => $medium_percentage,
  315. 'type' => CommentEnum::RATING_TYPE_MEDIUM,
  316. 'text' => CommentEnum::getRatingTypeText(CommentEnum::RATING_TYPE_MEDIUM)
  317. ],
  318. 'bad' => [
  319. 'count' => $bad_count,
  320. 'percentage' => $bad_percentage,
  321. 'type' => CommentEnum::RATING_TYPE_BAD,
  322. 'text' => CommentEnum::getRatingTypeText(CommentEnum::RATING_TYPE_BAD)
  323. ]
  324. ],
  325. 'degree' => self::degree($goods_id)
  326. ];
  327. }
  328. }