CommentController.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. using Hangfire;
  2. using Masuit.MyBlogs.Core.Common;
  3. using Masuit.MyBlogs.Core.Extensions;
  4. using Masuit.MyBlogs.Core.Infrastructure.Services.Interface;
  5. using Masuit.MyBlogs.Core.Models.Command;
  6. using Masuit.MyBlogs.Core.Models.DTO;
  7. using Masuit.MyBlogs.Core.Models.Entity;
  8. using Masuit.MyBlogs.Core.Models.Enum;
  9. using Masuit.MyBlogs.Core.Models.ViewModel;
  10. using Masuit.Tools;
  11. using Masuit.Tools.Core.Net;
  12. using Masuit.Tools.Html;
  13. using Microsoft.AspNetCore.Hosting;
  14. using Microsoft.AspNetCore.Http;
  15. using Microsoft.AspNetCore.Mvc;
  16. using Microsoft.EntityFrameworkCore.Internal;
  17. using Microsoft.Net.Http.Headers;
  18. using System;
  19. using System.Collections.Generic;
  20. using System.ComponentModel.DataAnnotations;
  21. using System.IO;
  22. using System.Linq;
  23. using System.Text.RegularExpressions;
  24. using System.Threading.Tasks;
  25. namespace Masuit.MyBlogs.Core.Controllers
  26. {
  27. /// <summary>
  28. /// 评论管理
  29. /// </summary>
  30. public class CommentController : BaseController
  31. {
  32. public ICommentService CommentService { get; set; }
  33. public IPostService PostService { get; set; }
  34. public IInternalMessageService MessageService { get; set; }
  35. public IWebHostEnvironment HostEnvironment { get; set; }
  36. /// <summary>
  37. /// 发表评论
  38. /// </summary>
  39. /// <param name="dto"></param>
  40. /// <returns></returns>
  41. [HttpPost, ValidateAntiForgeryToken]
  42. public async Task<ActionResult> Submit(CommentCommand dto)
  43. {
  44. if (Regex.Match(dto.NickName + dto.Content, CommonHelper.BanRegex).Length > 0)
  45. {
  46. return ResultData(null, false, "您提交的内容包含敏感词,被禁止发表,请检查您的内容后尝试重新提交!");
  47. }
  48. Post post = await PostService.GetByIdAsync(dto.PostId) ?? throw new NotFoundException("评论失败,文章未找到");
  49. if (post.DisableComment)
  50. {
  51. return ResultData(null, false, "本文已禁用评论功能,不允许任何人回复!");
  52. }
  53. dto.Content = dto.Content.Trim().Replace("<p><br></p>", string.Empty);
  54. if (dto.Content.RemoveHtmlTag().Trim().Equals(HttpContext.Session.Get<string>("comment" + dto.PostId)))
  55. {
  56. return ResultData(null, false, "您刚才已经在这篇文章发表过一次评论了,换一篇文章吧,或者换一下评论内容吧!");
  57. }
  58. var comment = dto.Mapper<Comment>();
  59. if (Regex.Match(dto.NickName + dto.Content, CommonHelper.ModRegex).Length <= 0)
  60. {
  61. comment.Status = Status.Published;
  62. }
  63. comment.CommentDate = DateTime.Now;
  64. var user = HttpContext.Session.Get<UserInfoDto>(SessionKey.UserInfo);
  65. if (user != null)
  66. {
  67. comment.NickName = user.NickName;
  68. comment.QQorWechat = user.QQorWechat;
  69. comment.Email = user.Email;
  70. if (user.IsAdmin)
  71. {
  72. comment.Status = Status.Published;
  73. comment.IsMaster = true;
  74. }
  75. }
  76. comment.Content = dto.Content.HtmlSantinizerStandard().ClearImgAttributes();
  77. comment.Browser = dto.Browser ?? Request.Headers[HeaderNames.UserAgent];
  78. comment.IP = ClientIP;
  79. comment.Location = comment.IP.GetIPLocation().Split("|").Where(s => !int.TryParse(s, out _)).ToHashSet().Join("|");
  80. comment = CommentService.AddEntitySaved(comment);
  81. if (comment == null)
  82. {
  83. return ResultData(null, false, "评论失败");
  84. }
  85. HttpContext.Session.Set("comment" + comment.PostId, comment.Content.RemoveHtmlTag().Trim());
  86. var emails = new HashSet<string>();
  87. var email = CommonHelper.SystemSettings["ReceiveEmail"]; //站长邮箱
  88. emails.Add(email);
  89. var content = (await System.IO.File.ReadAllTextAsync(HostEnvironment.WebRootPath + "/template/notify.html"))
  90. .Replace("{{title}}", post.Title)
  91. .Replace("{{time}}", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"))
  92. .Replace("{{nickname}}", comment.NickName)
  93. .Replace("{{content}}", comment.Content);
  94. if (comment.Status == Status.Published)
  95. {
  96. if (!comment.IsMaster)
  97. {
  98. await MessageService.AddEntitySavedAsync(new InternalMessage()
  99. {
  100. Title = $"来自【{comment.NickName}】的新文章评论",
  101. Content = comment.Content,
  102. Link = Url.Action("Details", "Post", new { id = comment.PostId, cid = comment.Id }, Request.Scheme) + "#comment"
  103. });
  104. }
  105. #if !DEBUG
  106. if (comment.ParentId == 0)
  107. {
  108. emails.Add(post.Email);
  109. emails.Add(post.ModifierEmail);
  110. //新评论,只通知博主和楼主
  111. foreach (var s in emails)
  112. {
  113. BackgroundJob.Enqueue(() => CommonHelper.SendMail(Request.Host + "|博客文章新评论:", content.Replace("{{link}}", Url.Action("Details", "Post", new { id = comment.PostId, cid = comment.Id }, Request.Scheme) + "#comment"), s));
  114. }
  115. }
  116. else
  117. {
  118. //通知博主和上层所有关联的评论访客
  119. var pid = CommentService.GetParentCommentIdByChildId(comment.Id);
  120. emails.AddRange(CommentService.GetSelfAndAllChildrenCommentsByParentId(pid).Select(c => c.Email).ToArray());
  121. emails.AddRange(post.Email, post.ModifierEmail);
  122. emails.Remove(comment.Email);
  123. string link = Url.Action("Details", "Post", new { id = comment.PostId, cid = comment.Id }, Request.Scheme) + "#comment";
  124. foreach (var s in emails)
  125. {
  126. BackgroundJob.Enqueue(() => CommonHelper.SendMail($"{Request.Host}{CommonHelper.SystemSettings["Title"]}文章评论回复:", content.Replace("{{link}}", link), s));
  127. }
  128. }
  129. #endif
  130. return ResultData(null, true, "评论发表成功,服务器正在后台处理中,这会有一定的延迟,稍后将显示到评论列表中");
  131. }
  132. foreach (var s in emails)
  133. {
  134. BackgroundJob.Enqueue(() => CommonHelper.SendMail(Request.Host + "|博客文章新评论(待审核):", content.Replace("{{link}}", Url.Action("Details", "Post", new { id = comment.PostId, cid = comment.Id }, Request.Scheme) + "#comment") + "<p style='color:red;'>(待审核)</p>", s));
  135. }
  136. return ResultData(null, true, "评论成功,待站长审核通过以后将显示");
  137. }
  138. /// <summary>
  139. /// 评论投票
  140. /// </summary>
  141. /// <param name="id"></param>
  142. /// <returns></returns>
  143. [HttpPost]
  144. public async Task<ActionResult> CommentVote(int id)
  145. {
  146. if (HttpContext.Session.Get("cm" + id) != null)
  147. {
  148. return ResultData(null, false, "您刚才已经投过票了,感谢您的参与!");
  149. }
  150. var cm = await CommentService.GetAsync(c => c.Id == id && c.Status == Status.Published) ?? throw new NotFoundException("评论不存在!");
  151. cm.VoteCount++;
  152. bool b = await CommentService.SaveChangesAsync() > 0;
  153. if (b)
  154. {
  155. HttpContext.Session.Set("cm" + id, id.GetBytes());
  156. }
  157. return ResultData(null, b, b ? "投票成功" : "投票失败");
  158. }
  159. /// <summary>
  160. /// 获取评论
  161. /// </summary>
  162. /// <param name="id"></param>
  163. /// <param name="page"></param>
  164. /// <param name="size"></param>
  165. /// <param name="cid"></param>
  166. /// <returns></returns>
  167. public ActionResult GetComments(int? id, [Range(1, int.MaxValue, ErrorMessage = "页码必须大于0")]int page = 1, [Range(1, 50, ErrorMessage = "页大小必须在0到50之间")]int size = 15, int cid = 0)
  168. {
  169. int total; //总条数,用于前台分页
  170. if (cid != 0)
  171. {
  172. int pid = CommentService.GetParentCommentIdByChildId(cid);
  173. var single = CommentService.GetSelfAndAllChildrenCommentsByParentId(pid).ToList();
  174. if (single.Any())
  175. {
  176. total = 1;
  177. return ResultData(new
  178. {
  179. total,
  180. parentTotal = total,
  181. page,
  182. size,
  183. rows = single.Mapper<IList<CommentViewModel>>()
  184. });
  185. }
  186. }
  187. var parent = CommentService.GetPagesNoTracking(page, size, c => c.PostId == id && c.ParentId == 0 && (c.Status == Status.Published || CurrentUser.IsAdmin), c => c.CommentDate, false);
  188. if (!parent.Data.Any())
  189. {
  190. return ResultData(null, false, "没有评论");
  191. }
  192. total = parent.TotalCount;
  193. var result = parent.Data.SelectMany(c => CommentService.GetSelfAndAllChildrenCommentsByParentId(c.Id).Where(x => (x.Status == Status.Published || CurrentUser.IsAdmin))).ToList();
  194. if (total > 0)
  195. {
  196. return ResultData(new
  197. {
  198. total,
  199. parentTotal = total,
  200. page,
  201. size,
  202. rows = result.Mapper<IList<CommentViewModel>>()
  203. });
  204. }
  205. return ResultData(null, false, "没有评论");
  206. }
  207. /// <summary>
  208. /// 审核评论
  209. /// </summary>
  210. /// <param name="id"></param>
  211. /// <returns></returns>
  212. [MyAuthorize]
  213. public async Task<ActionResult> Pass(int id)
  214. {
  215. Comment comment = await CommentService.GetByIdAsync(id) ?? throw new NotFoundException("评论不存在!");
  216. comment.Status = Status.Published;
  217. Post post = await PostService.GetByIdAsync(comment.PostId);
  218. bool b = await CommentService.SaveChangesAsync() > 0;
  219. if (b)
  220. {
  221. var pid = comment.ParentId == 0 ? comment.Id : CommentService.GetParentCommentIdByChildId(id);
  222. #if !DEBUG
  223. var content = (await System.IO.File.ReadAllTextAsync(Path.Combine(HostEnvironment.WebRootPath, "template", "notify.html")))
  224. .Replace("{{title}}", post.Title)
  225. .Replace("{{time}}", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"))
  226. .Replace("{{nickname}}", comment.NickName)
  227. .Replace("{{content}}", comment.Content);
  228. var emails = CommentService.GetSelfAndAllChildrenCommentsByParentId(pid).Select(c => c.Email).Append(post.ModifierEmail).Except(new List<string> { comment.Email, CurrentUser.Email }).ToHashSet();
  229. var link = Url.Action("Details", "Post", new
  230. {
  231. id = comment.PostId,
  232. cid = pid
  233. }, Request.Scheme) + "#comment";
  234. foreach (var email in emails)
  235. {
  236. BackgroundJob.Enqueue(() => CommonHelper.SendMail($"{Request.Host}{CommonHelper.SystemSettings["Title"]}文章评论回复:", content.Replace("{{link}}", link), email));
  237. }
  238. #endif
  239. return ResultData(null, true, "审核通过!");
  240. }
  241. return ResultData(null, false, "审核失败!");
  242. }
  243. /// <summary>
  244. /// 删除评论
  245. /// </summary>
  246. /// <param name="id"></param>
  247. /// <returns></returns>
  248. [MyAuthorize]
  249. public ActionResult Delete(int id)
  250. {
  251. var b = CommentService.DeleteEntitiesSaved(CommentService.GetSelfAndAllChildrenCommentsByParentId(id).ToList());
  252. return ResultData(null, b, b ? "删除成功!" : "删除失败!");
  253. }
  254. /// <summary>
  255. /// 获取未审核的评论
  256. /// </summary>
  257. /// <returns></returns>
  258. [MyAuthorize]
  259. public ActionResult GetPendingComments([Range(1, int.MaxValue, ErrorMessage = "页码必须大于0")]int page = 1, [Range(1, 50, ErrorMessage = "页大小必须在0到50之间")]int size = 15)
  260. {
  261. var pages = CommentService.GetPages<DateTime, CommentDto>(page, size, c => c.Status == Status.Pending, c => c.CommentDate, false);
  262. return Ok(pages);
  263. }
  264. }
  265. }