|
|
@@ -0,0 +1,273 @@
|
|
|
+<?php
|
|
|
+
|
|
|
+namespace App\Module\Article\Services;
|
|
|
+
|
|
|
+use App\Module\Article\Enums\STATUS;
|
|
|
+use App\Module\Article\Events\ArticleCreatedEvent;
|
|
|
+use App\Module\Article\Events\ArticleViewedEvent;
|
|
|
+use App\Module\Article\Logics\ArticleLogic;
|
|
|
+use App\Module\Article\Models\Article;
|
|
|
+use App\Module\Article\Models\ArticleCate;
|
|
|
+use Illuminate\Pagination\LengthAwarePaginator;
|
|
|
+use Illuminate\Support\Collection;
|
|
|
+use Illuminate\Support\Facades\Cache;
|
|
|
+
|
|
|
+/**
|
|
|
+ * 文章服务类
|
|
|
+ *
|
|
|
+ * 提供文章相关的服务方法,供其他模块调用
|
|
|
+ */
|
|
|
+class ArticleService
|
|
|
+{
|
|
|
+ /**
|
|
|
+ * 获取文章列表
|
|
|
+ *
|
|
|
+ * @param array $params 查询参数
|
|
|
+ * @param int $page 页码
|
|
|
+ * @param int $perPage 每页数量
|
|
|
+ * @return LengthAwarePaginator
|
|
|
+ */
|
|
|
+ public static function getArticleList(array $params = [], int $page = 1, int $perPage = 10): LengthAwarePaginator
|
|
|
+ {
|
|
|
+ $query = Article::query();
|
|
|
+
|
|
|
+ // 只查询显示状态的文章
|
|
|
+ $query->where('status', STATUS::SHOW->value);
|
|
|
+
|
|
|
+ // 分类筛选
|
|
|
+ if (isset($params['category_id']) && $params['category_id']) {
|
|
|
+ $query->where('category_id', $params['category_id']);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 标题搜索
|
|
|
+ if (isset($params['title']) && $params['title']) {
|
|
|
+ $query->where('title', 'like', "%{$params['title']}%");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 是否置顶
|
|
|
+ if (isset($params['is_top'])) {
|
|
|
+ $query->where('is_top', $params['is_top']);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 是否推荐
|
|
|
+ if (isset($params['is_recommend'])) {
|
|
|
+ $query->where('is_recommend', $params['is_recommend']);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 排序
|
|
|
+ $query->orderBy('is_top', 'desc')
|
|
|
+ ->orderBy('sort_order', 'asc')
|
|
|
+ ->orderBy('created_at', 'desc');
|
|
|
+
|
|
|
+ return $query->paginate($perPage, ['*'], 'page', $page);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取文章详情
|
|
|
+ *
|
|
|
+ * @param int $id 文章ID
|
|
|
+ * @param bool $incrementViews 是否增加浏览量
|
|
|
+ * @return Article|null
|
|
|
+ */
|
|
|
+ public static function getArticleDetail(int $id, bool $incrementViews = true): ?Article
|
|
|
+ {
|
|
|
+ $article = Article::find($id);
|
|
|
+
|
|
|
+ if (!$article || $article->status != STATUS::SHOW->value) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 增加浏览量
|
|
|
+ if ($incrementViews) {
|
|
|
+ self::incrementViewCount($id);
|
|
|
+
|
|
|
+ // 触发文章浏览事件
|
|
|
+ event(new ArticleViewedEvent($article));
|
|
|
+ }
|
|
|
+
|
|
|
+ return $article;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 增加文章浏览量
|
|
|
+ *
|
|
|
+ * @param int $id 文章ID
|
|
|
+ * @return bool
|
|
|
+ */
|
|
|
+ public static function incrementViewCount(int $id): bool
|
|
|
+ {
|
|
|
+ return ArticleLogic::incrementViewCount($id);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取推荐文章列表
|
|
|
+ *
|
|
|
+ * @param int $limit 数量限制
|
|
|
+ * @return Collection
|
|
|
+ */
|
|
|
+ public static function getRecommendArticles(int $limit = 5): Collection
|
|
|
+ {
|
|
|
+ $cacheKey = 'article_recommend_' . $limit;
|
|
|
+
|
|
|
+ return Cache::remember($cacheKey, 3600, function () use ($limit) {
|
|
|
+ return Article::where('status', STATUS::SHOW->value)
|
|
|
+ ->where('is_recommend', 1)
|
|
|
+ ->orderBy('sort_order', 'asc')
|
|
|
+ ->orderBy('created_at', 'desc')
|
|
|
+ ->limit($limit)
|
|
|
+ ->get();
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取置顶文章列表
|
|
|
+ *
|
|
|
+ * @param int $limit 数量限制
|
|
|
+ * @return Collection
|
|
|
+ */
|
|
|
+ public static function getTopArticles(int $limit = 5): Collection
|
|
|
+ {
|
|
|
+ $cacheKey = 'article_top_' . $limit;
|
|
|
+
|
|
|
+ return Cache::remember($cacheKey, 3600, function () use ($limit) {
|
|
|
+ return Article::where('status', STATUS::SHOW->value)
|
|
|
+ ->where('is_top', 1)
|
|
|
+ ->orderBy('sort_order', 'asc')
|
|
|
+ ->orderBy('created_at', 'desc')
|
|
|
+ ->limit($limit)
|
|
|
+ ->get();
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取分类文章列表
|
|
|
+ *
|
|
|
+ * @param int $categoryId 分类ID
|
|
|
+ * @param int $limit 数量限制
|
|
|
+ * @return Collection
|
|
|
+ */
|
|
|
+ public static function getCategoryArticles(int $categoryId, int $limit = 10): Collection
|
|
|
+ {
|
|
|
+ $cacheKey = 'article_category_' . $categoryId . '_' . $limit;
|
|
|
+
|
|
|
+ return Cache::remember($cacheKey, 3600, function () use ($categoryId, $limit) {
|
|
|
+ return Article::where('status', STATUS::SHOW->value)
|
|
|
+ ->where('category_id', $categoryId)
|
|
|
+ ->orderBy('is_top', 'desc')
|
|
|
+ ->orderBy('sort_order', 'asc')
|
|
|
+ ->orderBy('created_at', 'desc')
|
|
|
+ ->limit($limit)
|
|
|
+ ->get();
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取相关文章
|
|
|
+ *
|
|
|
+ * @param int $articleId 当前文章ID
|
|
|
+ * @param int $categoryId 分类ID
|
|
|
+ * @param int $limit 数量限制
|
|
|
+ * @return Collection
|
|
|
+ */
|
|
|
+ public static function getRelatedArticles(int $articleId, int $categoryId, int $limit = 5): Collection
|
|
|
+ {
|
|
|
+ return Article::where('status', STATUS::SHOW->value)
|
|
|
+ ->where('id', '!=', $articleId)
|
|
|
+ ->where('category_id', $categoryId)
|
|
|
+ ->orderBy('created_at', 'desc')
|
|
|
+ ->limit($limit)
|
|
|
+ ->get();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取所有分类
|
|
|
+ *
|
|
|
+ * @param bool $onlyActive 是否只获取激活状态的分类
|
|
|
+ * @return Collection
|
|
|
+ */
|
|
|
+ public static function getAllCategories(bool $onlyActive = true): Collection
|
|
|
+ {
|
|
|
+ $cacheKey = 'article_categories_' . ($onlyActive ? 'active' : 'all');
|
|
|
+
|
|
|
+ return Cache::remember($cacheKey, 3600, function () use ($onlyActive) {
|
|
|
+ $query = ArticleCate::query();
|
|
|
+
|
|
|
+ if ($onlyActive) {
|
|
|
+ $query->where('status', STATUS::SHOW->value);
|
|
|
+ }
|
|
|
+
|
|
|
+ return $query->orderBy('sort_order', 'asc')->get();
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 创建文章
|
|
|
+ *
|
|
|
+ * @param array $data 文章数据
|
|
|
+ * @return Article
|
|
|
+ */
|
|
|
+ public static function createArticle(array $data): Article
|
|
|
+ {
|
|
|
+ $article = ArticleLogic::createArticle($data);
|
|
|
+
|
|
|
+ // 触发文章创建事件
|
|
|
+ event(new ArticleCreatedEvent($article));
|
|
|
+
|
|
|
+ // 清除相关缓存
|
|
|
+ self::clearArticleCache();
|
|
|
+
|
|
|
+ return $article;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 更新文章
|
|
|
+ *
|
|
|
+ * @param int $id 文章ID
|
|
|
+ * @param array $data 文章数据
|
|
|
+ * @return bool
|
|
|
+ */
|
|
|
+ public static function updateArticle(int $id, array $data): bool
|
|
|
+ {
|
|
|
+ $result = ArticleLogic::updateArticle($id, $data);
|
|
|
+
|
|
|
+ // 清除相关缓存
|
|
|
+ self::clearArticleCache();
|
|
|
+
|
|
|
+ return $result;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 删除文章
|
|
|
+ *
|
|
|
+ * @param int $id 文章ID
|
|
|
+ * @return bool
|
|
|
+ */
|
|
|
+ public static function deleteArticle(int $id): bool
|
|
|
+ {
|
|
|
+ $result = ArticleLogic::deleteArticle($id);
|
|
|
+
|
|
|
+ // 清除相关缓存
|
|
|
+ self::clearArticleCache();
|
|
|
+
|
|
|
+ return $result;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 清除文章相关缓存
|
|
|
+ *
|
|
|
+ * @return void
|
|
|
+ */
|
|
|
+ public static function clearArticleCache(): void
|
|
|
+ {
|
|
|
+ Cache::forget('article_recommend_5');
|
|
|
+ Cache::forget('article_top_5');
|
|
|
+ Cache::forget('article_categories_active');
|
|
|
+ Cache::forget('article_categories_all');
|
|
|
+
|
|
|
+ // 清除分类文章缓存
|
|
|
+ $categories = ArticleCate::all();
|
|
|
+ foreach ($categories as $category) {
|
|
|
+ Cache::forget('article_category_' . $category->id . '_10');
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|