ArticleService.cs 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500
  1. using Infrastructure;
  2. using Infrastructure.Attribute;
  3. using Infrastructure.Extensions;
  4. using Mapster;
  5. using ZR.Common;
  6. using ZR.Model.Content;
  7. using ZR.Model.Content.Dto;
  8. using ZR.Model.Enum;
  9. using ZR.Model.System;
  10. using ZR.Repository;
  11. using ZR.Service.Content.IService;
  12. using ZR.ServiceCore.Services;
  13. namespace ZR.Service.Content
  14. {
  15. /// <summary>
  16. ///
  17. /// </summary>
  18. [AppService(ServiceType = typeof(IArticleService), ServiceLifetime = LifeTime.Transient)]
  19. public class ArticleService : BaseService<Article>, IArticleService
  20. {
  21. private readonly IArticleCategoryService _categoryService;
  22. private readonly IArticleTopicService _topicService;
  23. private readonly ISysUserMsgService _userMsgService;
  24. /// <summary>
  25. ///
  26. /// </summary>
  27. /// <param name="categoryService"></param>
  28. /// <param name="topicService"></param>
  29. /// <param name="userMsgService"></param>
  30. public ArticleService(
  31. IArticleCategoryService categoryService,
  32. IArticleTopicService topicService,
  33. ISysUserMsgService userMsgService)
  34. {
  35. _categoryService = categoryService;
  36. _topicService = topicService;
  37. _userMsgService = userMsgService;
  38. }
  39. /// <summary>
  40. /// 查询文章管理列表
  41. /// </summary>
  42. /// <param name="parm"></param>
  43. /// <returns></returns>
  44. public PagedInfo<ArticleDto> GetList(ArticleQueryDto parm)
  45. {
  46. var predicate = Expressionable.Create<Article>();
  47. predicate = predicate.AndIF(!string.IsNullOrEmpty(parm.Title), m => m.Title.Contains(parm.Title));
  48. predicate = predicate.AndIF(!string.IsNullOrEmpty(parm.AbstractText), m => m.AbstractText.Contains(parm.AbstractText));
  49. predicate = predicate.AndIF(!string.IsNullOrEmpty(parm.Status), m => m.Status == parm.Status);
  50. predicate = predicate.AndIF(parm.IsPublic != null, m => m.IsPublic == parm.IsPublic);
  51. predicate = predicate.AndIF(parm.IsTop != null, m => m.IsTop == parm.IsTop);
  52. predicate = predicate.AndIF(parm.ArticleType != null, m => (int)m.ArticleType == parm.ArticleType);
  53. predicate = predicate.AndIF(parm.TopicId != null, m => m.TopicId == parm.TopicId);
  54. predicate = predicate.AndIF(parm.AuditStatus != null, m => m.AuditStatus == parm.AuditStatus);
  55. if (parm.CategoryId != null)
  56. {
  57. var allChildCategory = Context.Queryable<ArticleCategory>()
  58. .ToChildList(m => m.ParentId, parm.CategoryId);
  59. var categoryIdList = allChildCategory.Select(x => x.CategoryId).ToArray();
  60. predicate = predicate.And(m => categoryIdList.Contains(m.CategoryId));
  61. }
  62. var response = Queryable()
  63. .WithCache(60 * 24)
  64. .IgnoreColumns(x => new { x.Content })
  65. .Includes(x => x.CategoryNav) //填充子对象
  66. .Where(predicate.ToExpression())
  67. //.OrderBy(x => x.CreateTime, OrderByType.Desc)
  68. .ToPage<Article, ArticleDto>(parm);
  69. return response;
  70. }
  71. /// <summary>
  72. /// 前台查询文章列表
  73. /// </summary>
  74. /// <param name="parm"></param>
  75. /// <returns></returns>
  76. public PagedInfo<ArticleDto> GetArticleList(ArticleQueryDto parm)
  77. {
  78. var predicate = Expressionable.Create<Article>();
  79. predicate = predicate.And(m => m.Status == "1");
  80. predicate = predicate.And(m => m.IsPublic == 1);
  81. predicate = predicate.And(m => m.AuditStatus == AuditStatusEnum.Passed);
  82. predicate = predicate.And(m => m.ArticleType == ArticleTypeEnum.Article);
  83. predicate = predicate.AndIF(parm.SearchText.IsNotEmpty(), m => m.Title.Contains(parm.SearchText) || m.AbstractText.Contains(parm.SearchText));
  84. predicate = predicate.AndIF(parm.TopicId != null, m => m.TopicId == parm.TopicId);
  85. if (parm.CategoryId != null)
  86. {
  87. var allChildCategory = Context.Queryable<ArticleCategory>()
  88. .ToChildList(m => m.ParentId, parm.CategoryId);
  89. var categoryIdList = allChildCategory.Select(x => x.CategoryId).ToArray();
  90. predicate = predicate.And(m => categoryIdList.Contains(m.CategoryId));
  91. }
  92. var response = Queryable()
  93. .WithCache(60 * 30)
  94. .Includes(x => x.CategoryNav)
  95. .LeftJoin<SysUser>((m, u) => m.UserId == u.UserId).Filter(null, true)
  96. .Where(predicate.ToExpression())
  97. .OrderByDescending(m => m.Cid)
  98. .Select((m, u) => new ArticleDto()
  99. {
  100. User = new ArticleUser()
  101. {
  102. Avatar = u.Avatar,
  103. NickName = u.NickName,
  104. Sex = u.Sex,
  105. },
  106. Content = string.Empty,
  107. UserIP = string.Empty
  108. }, true)
  109. .ToPage(parm);
  110. if (parm.UserId > 0)
  111. {
  112. Context.ThenMapper(response.Result, item =>
  113. {
  114. item.IsPraise = Context.Queryable<ArticlePraise>()
  115. .Where(f => f.UserId == parm.UserId && f.IsDelete == 0)
  116. .SetContext(scl => scl.ArticleId, () => item.Cid, item).Any() ? 1 : 0;
  117. });
  118. }
  119. return response;
  120. }
  121. /// <summary>
  122. /// 查询最新文章列表
  123. /// </summary>
  124. /// <returns></returns>
  125. public List<ArticleDto> GetNewArticleList()
  126. {
  127. var predicate = Expressionable.Create<Article>();
  128. predicate = predicate.And(m => m.Status == "1");
  129. predicate = predicate.And(m => m.IsPublic == 1);
  130. predicate = predicate.And(m => m.ArticleType == ArticleTypeEnum.Article);
  131. predicate = predicate.And(m => m.AuditStatus == AuditStatusEnum.Passed);
  132. var response = Queryable()
  133. .Where(predicate.ToExpression())
  134. .Includes(x => x.CategoryNav) //填充子对象
  135. .Take(10)
  136. .OrderBy(f => f.CreateTime, OrderByType.Desc)
  137. .ToList();
  138. return response.Adapt<List<ArticleDto>>();
  139. }
  140. /// <summary>
  141. /// 前台查询动态列表
  142. /// </summary>
  143. /// <param name="parm"></param>
  144. /// <returns></returns>
  145. public PagedInfo<ArticleDto> GetMonentList(ArticleQueryDto parm)
  146. {
  147. var predicate = Expressionable.Create<Article>();
  148. predicate = predicate.And(m => m.Status == "1");
  149. predicate = predicate.And(m => m.IsPublic == 1);
  150. predicate = predicate.And(m => m.ArticleType == ArticleTypeEnum.Monent);
  151. predicate = predicate.And(m => m.AuditStatus == AuditStatusEnum.Passed);
  152. predicate = predicate.AndIF(parm.TopicId != null, m => m.TopicId == parm.TopicId);
  153. predicate = predicate.AndIF(parm.CategoryId != null, m => m.CategoryId == parm.CategoryId);
  154. //获取我的圈子
  155. if (parm.QueryMyJoin)
  156. {
  157. var myJoin = Context.Queryable<ArticleUserCircles>()
  158. .Where(m => m.UserId == parm.UserId)
  159. .Select(m => m.CategoryId)
  160. .ToList();
  161. predicate = predicate.And(m => myJoin.Contains(m.CategoryId));
  162. }
  163. var response = Queryable()
  164. .Includes(x => x.CategoryNav) //填充子对象
  165. .LeftJoin<SysUser>((m, u) => m.UserId == u.UserId).Filter(null, true)
  166. .Where(predicate.ToExpression())
  167. .OrderByIF(parm.OrderBy == 1, m => new { m.PraiseNum, m.CommentNum }, OrderByType.Desc)
  168. .OrderByIF(parm.OrderBy == 2, m => new { m.Cid }, OrderByType.Desc)
  169. .OrderBy(m => m.Cid, OrderByType.Desc)
  170. .Select((m, u) => new ArticleDto()
  171. {
  172. User = new ArticleUser()
  173. {
  174. Avatar = u.Avatar,
  175. NickName = u.NickName,
  176. Sex = u.Sex,
  177. },
  178. }, true)
  179. .ToPage(parm);
  180. if (parm.UserId > 0)
  181. {
  182. Context.ThenMapper(response.Result, item =>
  183. {
  184. item.IsPraise = Context.Queryable<ArticlePraise>()
  185. .Where(f => f.UserId == parm.UserId && f.IsDelete == 0)
  186. .SetContext(scl => scl.ArticleId, () => item.Cid, item).Any() ? 1 : 0;
  187. });
  188. }
  189. return response;
  190. }
  191. /// <summary>
  192. /// 查询我的文章列表
  193. /// </summary>
  194. /// <param name="parm"></param>
  195. /// <returns></returns>
  196. public PagedInfo<ArticleDto> GetMyList(ArticleQueryDto parm)
  197. {
  198. var predicate = Expressionable.Create<Article>();
  199. predicate = predicate.AndIF(!string.IsNullOrEmpty(parm.Title), m => m.Title.Contains(parm.Title));
  200. predicate = predicate.AndIF(!string.IsNullOrEmpty(parm.Status), m => m.Status == parm.Status);
  201. predicate = predicate.AndIF(parm.BeginTime != null, m => m.CreateTime >= parm.BeginTime);
  202. predicate = predicate.AndIF(parm.EndTime != null, m => m.CreateTime <= parm.EndTime);
  203. predicate = predicate.And(m => m.UserId == parm.UserId);
  204. predicate = predicate.AndIF(parm.ArticleType != null, m => (int)m.ArticleType == parm.ArticleType);
  205. predicate = predicate.AndIF(parm.TabId == 2, m => m.IsPublic == 0 && m.UserId == parm.UserId);
  206. var response = Queryable()
  207. //.IgnoreColumns(x => new { x.Content })
  208. .Includes(x => x.CategoryNav)
  209. .Where(predicate.ToExpression())
  210. .OrderByIF(parm.TabId == 3, m => new { m.IsTop, m.PraiseNum }, OrderByType.Desc)
  211. .OrderByDescending(m => new { m.IsTop, m.Cid })
  212. .Select((x) => new ArticleDto()
  213. {
  214. Content = x.ArticleType == 0 ? string.Empty : x.Content,
  215. }, true)
  216. .ToPage(parm);
  217. return response;
  218. }
  219. /// <summary>
  220. /// 修改文章管理
  221. /// </summary>
  222. /// <param name="model"></param>
  223. /// <returns></returns>
  224. public int UpdateArticle(Article model)
  225. {
  226. var response = Update(w => w.Cid == model.Cid, it => new Article()
  227. {
  228. Title = model.Title,
  229. Content = model.Content,
  230. Status = model.Status,
  231. Tags = model.Tags,
  232. UpdateTime = DateTime.Now,
  233. CoverUrl = model.CoverUrl,
  234. CategoryId = model.CategoryId,
  235. IsPublic = model.IsPublic,
  236. AbstractText = model.AbstractText,
  237. });
  238. return response;
  239. }
  240. /// <summary>
  241. /// 置顶文章
  242. /// </summary>
  243. /// <param name="model"></param>
  244. /// <returns></returns>
  245. public int TopArticle(Article model)
  246. {
  247. var response = Update(w => w.Cid == model.Cid, it => new Article()
  248. {
  249. IsTop = model.IsTop,
  250. });
  251. return response;
  252. }
  253. /// <summary>
  254. /// 评论权限
  255. /// </summary>
  256. /// <param name="model"></param>
  257. /// <returns></returns>
  258. public int ChangeComment(Article model)
  259. {
  260. var response = Update(w => w.Cid == model.Cid, it => new Article()
  261. {
  262. CommentSwitch = model.CommentSwitch,
  263. });
  264. return response;
  265. }
  266. /// <summary>
  267. /// 是否公开
  268. /// </summary>
  269. /// <param name="model"></param>
  270. /// <returns></returns>
  271. public int ChangeArticlePublic(Article model)
  272. {
  273. var response = Update(w => w.Cid == model.Cid, it => new Article()
  274. {
  275. IsPublic = model.IsPublic,
  276. });
  277. return response;
  278. }
  279. /// <summary>
  280. /// 修改文章访问量
  281. /// </summary>
  282. /// <param name="cid"></param>
  283. /// <returns></returns>
  284. public int UpdateArticleHit(long cid)
  285. {
  286. var response = Update(w => w.Cid == cid, it => new Article() { Hits = it.Hits + 1 });
  287. return response;
  288. }
  289. /// <summary>
  290. /// 点赞
  291. /// </summary>
  292. /// <param name="cid"></param>
  293. /// <returns></returns>
  294. public int PraiseArticle(long cid)
  295. {
  296. return Update(w => w.Cid == cid, it => new Article() { PraiseNum = it.PraiseNum + 1 });
  297. }
  298. public int CancelPraise(long cid)
  299. {
  300. return Update(w => w.Cid == cid, it => new Article() { PraiseNum = it.PraiseNum - 1 });
  301. }
  302. public Article PublishArticle(Article article)
  303. {
  304. return Publish(article);
  305. }
  306. /// <summary>
  307. /// 前端-发布动态,文章
  308. /// </summary>
  309. /// <param name="article"></param>
  310. /// <returns></returns>
  311. public Article Publish(Article article)
  312. {
  313. var httpContext = App.HttpContext;
  314. article.AuthorName = HttpContextExtension.GetName(httpContext);
  315. article.UserId = HttpContextExtension.GetUId(httpContext);
  316. article.UserIP = HttpContextExtension.GetClientUserIp(httpContext);
  317. article.Location = HttpContextExtension.GetIpInfo(article.UserIP);
  318. article.AuditStatus = AuditStatusEnum.Passed;
  319. article = InsertReturnEntity(article);
  320. if (article.Status == "1")
  321. {
  322. //跟新话题加入数
  323. if (article.Cid > 0 && article.TopicId > 0)
  324. {
  325. _topicService.Update(w => w.TopicId == article.TopicId, it => new ArticleTopic() { JoinNum = it.JoinNum + 1 });
  326. }
  327. //更新发布文章数
  328. if (article.Cid > 0 && article.CategoryId > 0)
  329. {
  330. _categoryService.Update(w => w.CategoryId == article.CategoryId, it => new ArticleCategory() { ArticleNum = it.ArticleNum + 1 });
  331. }
  332. }
  333. return article;
  334. }
  335. /// <summary>
  336. /// 获取文章详情
  337. /// </summary>
  338. /// <param name="cid">内容id</param>
  339. /// <param name="userId">当前用户id</param>
  340. /// <returns></returns>
  341. /// <exception cref="CustomException"></exception>
  342. public ArticleDto GetArticle(long cid, long userId)
  343. {
  344. var response = GetId(cid);
  345. var model = response.Adapt<ArticleDto>() ?? throw new CustomException(ResultCode.FAIL, "内容不存在");
  346. if (model.IsPublic == 0 && userId != model.UserId)
  347. {
  348. throw new CustomException(ResultCode.CUSTOM_ERROR, "访问失败");
  349. }
  350. if (model != null)
  351. {
  352. model.CategoryNav = _categoryService.GetById(model.CategoryId).Adapt<ArticleCategoryDto>();
  353. }
  354. var webContext = App.HttpContext;
  355. var CK = "ARTICLE_DETAILS_" + userId + HttpContextExtension.GetClientUserIp(webContext);
  356. if (!CacheHelper.Exists(CK))
  357. {
  358. UpdateArticleHit(cid);
  359. var userIP = HttpContextExtension.GetClientUserIp(webContext);
  360. var location = HttpContextExtension.GetIpInfo(userIP);
  361. itenant.InsertableWithAttr(new ArticleBrowsingLog()
  362. {
  363. ArticleId = cid,
  364. UserId = userId,
  365. Location = location,
  366. UserIP = userIP,
  367. AddTime = DateTime.Now,
  368. }).ExecuteReturnSnowflakeId();
  369. }
  370. CacheHelper.SetCache(CK, 1, 10);
  371. if (userId > 0)
  372. {
  373. model.IsPraise = Context.Queryable<ArticlePraise>()
  374. .Where(f => f.UserId == userId && f.ArticleId == cid && f.IsDelete == 0)
  375. .Any() ? 1 : 0;
  376. }
  377. return model;
  378. }
  379. /// <summary>
  380. /// 审核通过
  381. /// </summary>
  382. /// <param name="idsArr"></param>
  383. /// <returns></returns>
  384. public int Passed(long[] idsArr)
  385. {
  386. int result = 0;
  387. List<Article> monents = [];
  388. foreach (var item in idsArr)
  389. {
  390. var model = GetFirst(x => x.Cid == item && x.AuditStatus == 0);
  391. if (model == null) continue;
  392. //model.Auditor = HttpContext.GetName();
  393. model.AuditStatus = AuditStatusEnum.Passed;
  394. var response = Update(w => w.Cid == model.Cid, it => new Article()
  395. {
  396. AuditStatus = model.AuditStatus,
  397. //Auditor = model.Auditor,
  398. });
  399. result += response;
  400. monents.Add(model);
  401. }
  402. var data = from a in monents
  403. group a by new { a.UserId } into grp
  404. select new
  405. {
  406. userid = grp.Key.UserId,
  407. num = grp.Count()
  408. };
  409. foreach (var item in data)
  410. {
  411. _userMsgService.AddSysUserMsg(item.userid, "您发布的内容已通过审核", UserMsgType.SYSTEM);
  412. }
  413. return result;
  414. }
  415. /// <summary>
  416. /// 审核不通过
  417. /// </summary>
  418. /// <param name="reason"></param>
  419. /// <param name="idsArr"></param>
  420. /// <returns></returns>
  421. public int Reject(string reason, long[] idsArr)
  422. {
  423. int result = 0;
  424. List<Article> monents = new();
  425. foreach (var item in idsArr)
  426. {
  427. var model = GetFirst(x => x.Cid == item && x.AuditStatus == 0);
  428. if (model == null) continue;
  429. //model.Auditor = HttpContext.GetName();
  430. model.AuditStatus = AuditStatusEnum.Reject;
  431. var response = Update(w => w.Cid == model.Cid, it => new Article()
  432. {
  433. AuditStatus = model.AuditStatus,
  434. //Auditor = model.Auditor,
  435. });
  436. result += response;
  437. monents.Add(model);
  438. }
  439. var data = from a in monents
  440. group a by new { a.UserId } into grp
  441. select new
  442. {
  443. userid = grp.Key.UserId,
  444. num = grp.Count()
  445. };
  446. foreach (var item in data)
  447. {
  448. //Console.WriteLine(item.useridx +"," + item.num);
  449. string content = $"您发布的内容未通过审核。";
  450. if (!string.IsNullOrEmpty(reason))
  451. {
  452. content += $"原因:{reason}";
  453. }
  454. _userMsgService.AddSysUserMsg(item.userid, content, UserMsgType.SYSTEM);
  455. }
  456. return result;
  457. }
  458. }
  459. }