Comment.php 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  1. <?php
  2. namespace addons\cms\model;
  3. use addons\cms\library\CommentException;
  4. use addons\cms\library\Service;
  5. use app\common\library\Auth;
  6. use app\common\library\Email;
  7. use app\common\model\User;
  8. use think\Db;
  9. use think\Exception;
  10. use think\Model;
  11. use think\Validate;
  12. use traits\model\SoftDelete;
  13. /**
  14. * 评论模型
  15. */
  16. class Comment extends Model
  17. {
  18. use SoftDelete;
  19. protected $name = "cms_comment";
  20. // 开启自动写入时间戳字段
  21. protected $autoWriteTimestamp = 'int';
  22. // 定义时间戳字段名
  23. protected $createTime = 'createtime';
  24. protected $updateTime = 'updatetime';
  25. protected $deleteTime = 'deletetime';
  26. protected static $tagCount = 0;
  27. // 追加属性
  28. protected $append = [
  29. 'create_date',
  30. ];
  31. public function getCreateDateAttr($value, $data)
  32. {
  33. return human_date($data['createtime']);
  34. }
  35. /**
  36. * 发表评论
  37. * @param array $params
  38. * @return bool
  39. * @throws CommentException
  40. * @throws Exception
  41. */
  42. public static function postComment($params = [])
  43. {
  44. $config = get_addon_config('cms');
  45. $request = request();
  46. $useragent = substr($request->server('HTTP_USER_AGENT', ''), 0, 255);
  47. $ip = $request->ip(0, false);
  48. $auth = Auth::instance();
  49. $content = $params['content'];
  50. if (!$auth->id) {
  51. throw new Exception("请登录后发表评论");
  52. }
  53. if ($auth->score < $config['limitscore']['postcomment']) {
  54. throw new Exception("积分必须大于{$config['limitscore']['postcomment']}才可以发表评论");
  55. }
  56. if (!isset($params['aid']) || !isset($params['content'])) {
  57. throw new Exception("内容不能为空");
  58. }
  59. $params['user_id'] = $auth->id;
  60. $params['type'] = isset($params['type']) ? $params['type'] : 'archives';
  61. $params['content'] = nl2br($params['content']);
  62. $params['content'] = preg_replace("/(@([\s\S]*?))\s+/i", '<em>$1</em> ', $params['content']);
  63. $archives = $params['type'] == 'archives' ? Archives::get($params['aid']) : ($params['type'] == 'special' ? Special::get($params['aid']) : Page::get($params['aid']));
  64. if (!$archives || $archives['status'] == 'hidden') {
  65. throw new Exception("文档未找到或正在审核");
  66. }
  67. if (!$archives['iscomment']) {
  68. throw new Exception("文档评论功能已关闭");
  69. }
  70. $rule = [
  71. 'type' => 'require|in:archives,page,special',
  72. 'pid' => 'require|number',
  73. 'user_id' => 'require|number',
  74. 'content|内容' => 'require|length:3,250',
  75. '__token__' => 'require|token',
  76. ];
  77. $message = [
  78. 'content.length' => '评论最少输入3个字符'
  79. ];
  80. $validate = new Validate($rule, $message);
  81. $result = $validate->check($params);
  82. if (!$result) {
  83. throw new Exception($validate->getError());
  84. }
  85. //查找最后评论
  86. $lastComment = self::where(['type' => $params['type'], 'aid' => $params['aid'], 'ip' => $ip])->order('id', 'desc')->find();
  87. if ($lastComment && time() - $lastComment['createtime'] < 30) {
  88. throw new Exception("对不起!您发表评论的速度过快!");
  89. }
  90. if ($lastComment && $lastComment['content'] == $params['content']) {
  91. throw new Exception("您可能连续了相同的评论,请不要重复提交");
  92. }
  93. //审核状态
  94. $status = 'normal';
  95. if ($config['iscommentaudit'] == 1) {
  96. $status = 'hidden';
  97. } elseif ($config['iscommentaudit'] == 0) {
  98. $status = 'normal';
  99. } elseif ($config['iscommentaudit'] == -1) {
  100. if (!Service::isContentLegal($content)) {
  101. $status = 'hidden';
  102. }
  103. }
  104. $params['ip'] = $ip;
  105. $params['useragent'] = $useragent;
  106. $params['status'] = $status;
  107. Db::startTrans();
  108. try {
  109. $model = new static();
  110. $model->allowField(true)->save($params);
  111. //评论正常则增加积分和统计
  112. if ($status == 'normal') {
  113. $archives->setInc('comments');
  114. //增加积分
  115. $status == 'normal' && User::score($config['score']['postcomment'], $auth->id, '发表评论');
  116. }
  117. Db::commit();
  118. } catch (\Exception $e) {
  119. Db::rollback();
  120. throw new Exception("发表评论失败");
  121. }
  122. //发送通知
  123. if ($status === 'hidden') {
  124. Service::notice('CMS收到一条待审核评论', $config['auditnotice'], $config['noticetemplateid']);
  125. throw new CommentException("发表评论成功,但评论需要显示审核后才会展示", 1);
  126. }
  127. if (isset($params['pid'])) {
  128. //查找父评论,是否并发邮件通知
  129. $parentComment = self::get($params['pid'], 'user');
  130. if ($parentComment && $parentComment['subscribe'] && Validate::is($parentComment->user->email, 'email')) {
  131. $domain = $request->domain();
  132. $config = get_addon_config('cms');
  133. $title = "{$parentComment->user->nickname},您发表在《{$archives['title']}》上的评论有了新回复 - {$config['sitename']}";
  134. $archivesurl = $domain . $archives['url'];
  135. $unsubscribe_url = addon_url("cms/comment/unsubscribe", ['id' => $parentComment['id'], 'key' => md5($parentComment['id'] . $parentComment->user->email)], true, true);
  136. $content = "亲爱的{$parentComment->user->nickname}:<br />您于" . date("Y-m-d H:i:s") .
  137. "在《<a href='{$archivesurl}' target='_blank'>{$archives['title']}</a>》上发表的评论<br /><blockquote>{$parentComment['content']}</blockquote>" .
  138. "<br />{$auth->nickname}发表了回复,内容是<br /><br />您可以<a href='{$archivesurl}'>点击查看评论详情</a>。" .
  139. "<br /><br />如果你不愿意再接受最新评论的通知,<a href='{$unsubscribe_url}'>请点击这里取消</a>";
  140. try {
  141. $email = new Email;
  142. $result = $email
  143. ->to($parentComment->user->email)
  144. ->subject($title)
  145. ->message('<div style="min-height:550px; padding: 100px 55px 200px;">' . $content . '</div>')
  146. ->send();
  147. } catch (\think\Exception $e) {
  148. }
  149. }
  150. }
  151. return self::with('user')->find($model->id);
  152. }
  153. /**
  154. * 获取评论列表
  155. * @param $params
  156. * @return \think\Paginator
  157. * @throws \think\exception\DbException
  158. */
  159. public static function getCommentList($params)
  160. {
  161. $type = empty($params['type']) ? 'archives' : $params['type'];
  162. $aid = empty($params['aid']) ? 0 : $params['aid'];
  163. $pid = empty($params['pid']) ? 0 : $params['pid'];
  164. $condition = empty($params['condition']) ? '' : $params['condition'];
  165. $field = empty($params['field']) ? '*' : $params['field'];
  166. $fragment = empty($params['fragment']) ? 'comments' : $params['fragment'];
  167. $row = empty($params['row']) ? 10 : (int)$params['row'];
  168. $orderby = empty($params['orderby']) ? 'createtime' : $params['orderby'];
  169. $orderway = empty($params['orderway']) ? 'desc' : strtolower($params['orderway']);
  170. $pagesize = empty($params['pagesize']) ? $row : $params['pagesize'];
  171. $cache = !isset($params['cache']) ? false : (int)$params['cache'];
  172. $orderway = in_array($orderway, ['asc', 'desc']) ? $orderway : 'desc';
  173. $paginate = !isset($params['paginate']) ? false : $params['paginate'];
  174. $cache = !$cache ? false : $cache;
  175. self::$tagCount++;
  176. $where = ['status' => 'normal'];
  177. if ($type) {
  178. $where['type'] = $type;
  179. }
  180. if ($aid) {
  181. $where['aid'] = $aid;
  182. }
  183. if ($pid) {
  184. $where['pid'] = $pid;
  185. }
  186. $order = $orderby == 'rand' ? Db::raw('rand()') : (preg_match("/\,|\s/", $orderby) ? $orderby : "{$orderby} {$orderway}");
  187. $list = self::with('user')
  188. ->where($where)
  189. ->where($condition)
  190. ->field($field)
  191. ->order($order)
  192. ->cache($cache)
  193. ->paginate($pagesize, false, ['type' => '\\addons\\cms\\library\\Bootstrap', 'var_page' => 'cp', 'fragment' => $fragment]);
  194. self::render($list);
  195. return $list;
  196. }
  197. public static function render(&$list)
  198. {
  199. foreach ($list as $k => &$v) {
  200. }
  201. return $list;
  202. }
  203. /**
  204. * 关联会员模型
  205. */
  206. public function user()
  207. {
  208. return $this->belongsTo("app\common\model\User", "user_id", "id", [], "LEFT")->field('id,nickname,avatar,bio,email')->setEagerlyType(1);
  209. }
  210. /**
  211. * 关联文章模型
  212. */
  213. public function archives()
  214. {
  215. return $this->belongsTo("addons\cms\model\Archives", 'aid', 'id', [], 'LEFT')->field('id,title,image,style,diyname,model_id,channel_id,likes,dislikes,tags,createtime')->setEagerlyType(1);
  216. }
  217. /**
  218. * 关联单页模型
  219. */
  220. public function spage()
  221. {
  222. return $this->belongsTo("addons\cms\model\Page", 'aid', 'id', [], 'LEFT')->field('id,title,createtime')->setEagerlyType(1);
  223. }
  224. /**
  225. * 关联模型
  226. */
  227. public function source()
  228. {
  229. $type = $this->getData('type');
  230. $modelArr = ['page' => 'Page', 'archives' => 'Archives', 'special' => 'Special'];
  231. $model = isset($modelArr[$type]) ? $modelArr[$type] : $modelArr['archives'];
  232. return $this->belongsTo($model, "aid");
  233. }
  234. }