ArticleService.php 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  1. <?php
  2. namespace App\Module\Article\Services;
  3. use App\Module\Article\Enums\STATUS;
  4. use App\Module\Article\Events\ArticleCreatedEvent;
  5. use App\Module\Article\Events\ArticleViewedEvent;
  6. use App\Module\Article\Logics\ArticleLogic;
  7. use App\Module\Article\Models\Article;
  8. use App\Module\Article\Models\ArticleCate;
  9. use Illuminate\Pagination\LengthAwarePaginator;
  10. use Illuminate\Support\Collection;
  11. use Illuminate\Support\Facades\Cache;
  12. /**
  13. * 文章服务类
  14. *
  15. * 提供文章相关的服务方法,供其他模块调用
  16. */
  17. class ArticleService
  18. {
  19. /**
  20. * 获取文章列表
  21. *
  22. * @param array $params 查询参数
  23. * @param int $page 页码
  24. * @param int $perPage 每页数量
  25. * @return LengthAwarePaginator
  26. */
  27. public static function getArticleList(array $params = [], int $page = 1, int $perPage = 10): LengthAwarePaginator
  28. {
  29. $query = Article::query();
  30. // 只查询显示状态的文章
  31. $query->where('status', STATUS::SHOW->value);
  32. // 分类筛选
  33. if (isset($params['category_id']) && $params['category_id']) {
  34. $query->where('category_id', $params['category_id']);
  35. }
  36. // 标题搜索
  37. if (isset($params['title']) && $params['title']) {
  38. $query->where('title', 'like', "%{$params['title']}%");
  39. }
  40. // 是否置顶
  41. if (isset($params['is_top'])) {
  42. $query->where('is_top', $params['is_top']);
  43. }
  44. // 是否推荐
  45. if (isset($params['is_recommend'])) {
  46. $query->where('is_recommend', $params['is_recommend']);
  47. }
  48. // 排序
  49. $query->orderBy('is_top', 'desc')
  50. ->orderBy('sort_order', 'asc')
  51. ->orderBy('created_at', 'desc');
  52. return $query->paginate($perPage, ['*'], 'page', $page);
  53. }
  54. /**
  55. * 获取文章详情
  56. *
  57. * @param int $id 文章ID
  58. * @param bool $incrementViews 是否增加浏览量
  59. * @return Article|null
  60. */
  61. public static function getArticleDetail(int $id, bool $incrementViews = true): ?Article
  62. {
  63. $article = Article::find($id);
  64. if (!$article || $article->status != STATUS::SHOW->value) {
  65. return null;
  66. }
  67. // 增加浏览量
  68. if ($incrementViews) {
  69. self::incrementViewCount($id);
  70. // 触发文章浏览事件
  71. event(new ArticleViewedEvent($article));
  72. }
  73. return $article;
  74. }
  75. /**
  76. * 增加文章浏览量
  77. *
  78. * @param int $id 文章ID
  79. * @return bool
  80. */
  81. public static function incrementViewCount(int $id): bool
  82. {
  83. return ArticleLogic::incrementViewCount($id);
  84. }
  85. /**
  86. * 获取推荐文章列表
  87. *
  88. * @param int $limit 数量限制
  89. * @return Collection
  90. */
  91. public static function getRecommendArticles(int $limit = 5): Collection
  92. {
  93. $cacheKey = 'article_recommend_' . $limit;
  94. return Cache::remember($cacheKey, 3600, function () use ($limit) {
  95. return Article::where('status', STATUS::SHOW->value)
  96. ->where('is_recommend', 1)
  97. ->orderBy('sort_order', 'asc')
  98. ->orderBy('created_at', 'desc')
  99. ->limit($limit)
  100. ->get();
  101. });
  102. }
  103. /**
  104. * 获取置顶文章列表
  105. *
  106. * @param int $limit 数量限制
  107. * @return Collection
  108. */
  109. public static function getTopArticles(int $limit = 5): Collection
  110. {
  111. $cacheKey = 'article_top_' . $limit;
  112. return Cache::remember($cacheKey, 3600, function () use ($limit) {
  113. return Article::where('status', STATUS::SHOW->value)
  114. ->where('is_top', 1)
  115. ->orderBy('sort_order', 'asc')
  116. ->orderBy('created_at', 'desc')
  117. ->limit($limit)
  118. ->get();
  119. });
  120. }
  121. /**
  122. * 获取分类文章列表
  123. *
  124. * @param int $categoryId 分类ID
  125. * @param int $limit 数量限制
  126. * @return Collection
  127. */
  128. public static function getCategoryArticles(int $categoryId, int $limit = 10): Collection
  129. {
  130. $cacheKey = 'article_category_' . $categoryId . '_' . $limit;
  131. return Cache::remember($cacheKey, 3600, function () use ($categoryId, $limit) {
  132. return Article::where('status', STATUS::SHOW->value)
  133. ->where('category_id', $categoryId)
  134. ->orderBy('is_top', 'desc')
  135. ->orderBy('sort_order', 'asc')
  136. ->orderBy('created_at', 'desc')
  137. ->limit($limit)
  138. ->get();
  139. });
  140. }
  141. /**
  142. * 获取相关文章
  143. *
  144. * @param int $articleId 当前文章ID
  145. * @param int $categoryId 分类ID
  146. * @param int $limit 数量限制
  147. * @return Collection
  148. */
  149. public static function getRelatedArticles(int $articleId, int $categoryId, int $limit = 5): Collection
  150. {
  151. return Article::where('status', STATUS::SHOW->value)
  152. ->where('id', '!=', $articleId)
  153. ->where('category_id', $categoryId)
  154. ->orderBy('created_at', 'desc')
  155. ->limit($limit)
  156. ->get();
  157. }
  158. /**
  159. * 获取所有分类
  160. *
  161. * @param bool $onlyActive 是否只获取激活状态的分类
  162. * @return Collection
  163. */
  164. public static function getAllCategories(bool $onlyActive = true): Collection
  165. {
  166. $cacheKey = 'article_categories_' . ($onlyActive ? 'active' : 'all');
  167. return Cache::remember($cacheKey, 3600, function () use ($onlyActive) {
  168. $query = ArticleCate::query();
  169. if ($onlyActive) {
  170. $query->where('status', STATUS::SHOW->value);
  171. }
  172. return $query->orderBy('sort_order', 'asc')->get();
  173. });
  174. }
  175. /**
  176. * 创建文章
  177. *
  178. * @param array $data 文章数据
  179. * @return Article
  180. */
  181. public static function createArticle(array $data): Article
  182. {
  183. $article = ArticleLogic::createArticle($data);
  184. // 触发文章创建事件
  185. event(new ArticleCreatedEvent($article));
  186. // 清除相关缓存
  187. self::clearArticleCache();
  188. return $article;
  189. }
  190. /**
  191. * 更新文章
  192. *
  193. * @param int $id 文章ID
  194. * @param array $data 文章数据
  195. * @return bool
  196. */
  197. public static function updateArticle(int $id, array $data): bool
  198. {
  199. $result = ArticleLogic::updateArticle($id, $data);
  200. // 清除相关缓存
  201. self::clearArticleCache();
  202. return $result;
  203. }
  204. /**
  205. * 删除文章
  206. *
  207. * @param int $id 文章ID
  208. * @return bool
  209. */
  210. public static function deleteArticle(int $id): bool
  211. {
  212. $result = ArticleLogic::deleteArticle($id);
  213. // 清除相关缓存
  214. self::clearArticleCache();
  215. return $result;
  216. }
  217. /**
  218. * 清除文章相关缓存
  219. *
  220. * @return void
  221. */
  222. public static function clearArticleCache(): void
  223. {
  224. Cache::forget('article_recommend_5');
  225. Cache::forget('article_top_5');
  226. Cache::forget('article_categories_active');
  227. Cache::forget('article_categories_all');
  228. // 清除分类文章缓存
  229. $categories = ArticleCate::all();
  230. foreach ($categories as $category) {
  231. Cache::forget('article_category_' . $category->id . '_10');
  232. }
  233. }
  234. }