// +---------------------------------------------------------------------- declare (strict_types = 1); namespace addon\articles\service; use addon\articles\model\Article; use addon\articles\model\ArticleComment; use addon\articles\model\ArticleLike; use think\facade\Db; /** * 点赞服务(文章 / 评论) * * 规则: * - 必须登录(游客不允许点赞,避免刷量) * - 同一用户对同一目标只能赞一次(唯一键保证),再次调用即取消 * - 计数冗余在目标表的 like_count,取消时用 dec 并保证不为负 */ class LikeService { /** * 文章点赞/取消 * * @return array ['liked' => bool, 'count' => int] */ public static function toggleArticle(int $uid, int $articleId): array { if ($uid <= 0 || $articleId <= 0) { return ['liked' => false, 'count' => 0]; } // 文章必须存在且已发布 $exists = Article::where('id', $articleId)->where('status', 1)->where('delete_at', 0)->count(); if (!$exists) { return ['liked' => false, 'count' => 0]; } return self::toggle($uid, $articleId, ArticleLike::TYPE_ARTICLE, 'articles_article', $articleId); } /** * 评论点赞/取消 * * @return array ['liked' => bool, 'count' => int] */ public static function toggleComment(int $uid, int $commentId): array { if ($uid <= 0 || $commentId <= 0) { return ['liked' => false, 'count' => 0]; } $exists = ArticleComment::where('id', $commentId)->where('delete_at', 0)->count(); if (!$exists) { return ['liked' => false, 'count' => 0]; } return self::toggle($uid, $commentId, ArticleLike::TYPE_COMMENT, 'articles_comment', $commentId); } /** * 是否已点赞 */ public static function isLiked(int $uid, int $targetId, int $type): bool { if ($uid <= 0) { return false; } return (bool) ArticleLike::where('uid', $uid) ->where('target_id', $targetId) ->where('type', $type) ->count(); } /** * 批量判断文章是否已赞(列表/详情页用,避免 N+1) * * @param array $ids 文章ID数组 * @return array 已赞的文章ID => true */ public static function likedArticleMap(int $uid, array $ids): array { if ($uid <= 0 || empty($ids)) { return []; } $rows = ArticleLike::where('uid', $uid) ->where('type', ArticleLike::TYPE_ARTICLE) ->whereIn('target_id', $ids) ->column('target_id'); $map = []; foreach ($rows as $id) { $map[(int) $id] = true; } return $map; } /** * 通用切换逻辑 */ private static function toggle(int $uid, int $targetId, int $type, string $countTable, int $targetKey): array { $record = ArticleLike::where('uid', $uid) ->where('target_id', $targetId) ->where('type', $type) ->find(); if ($record) { // 已赞 → 取消 $record->delete(); Db::name($countTable)->where('id', $targetKey) ->where('like_count', '>', 0) ->dec('like_count') ->update(); return ['liked' => false, 'count' => self::count($countTable, $targetKey)]; } ArticleLike::create([ 'uid' => $uid, 'target_id' => $targetId, 'type' => $type, 'create_at' => time(), ]); Db::name($countTable)->where('id', $targetKey)->inc('like_count')->update(); return ['liked' => true, 'count' => self::count($countTable, $targetKey)]; } /** * 读取最新计数 */ private static function count(string $table, int $id): int { return (int) Db::name($table)->where('id', $id)->value('like_count'); } }