$userId, 'article_id' => $articleId]; $collect = ArticleCollect::where($where)->findOrEmpty(); if ($collect->isEmpty()) { ArticleCollect::create([ 'user_id' => $userId, 'article_id' => $articleId, 'status' => YesNoEnum::YES ]); } else { ArticleCollect::update([ 'id' => $collect['id'], 'status' => YesNoEnum::YES ]); } } /** * @notes 取消收藏 * @param $articleId * @param $userId * @author 段誉 * @date 2022/9/20 16:59 */ public static function cancelCollect($articleId, $userId) { ArticleCollect::update(['status' => YesNoEnum::NO], [ 'user_id' => $userId, 'article_id' => $articleId, 'status' => YesNoEnum::YES ]); } /** * @notes 文章分类 * @return array * @throws \think\db\exception\DataNotFoundException * @throws \think\db\exception\DbException * @throws \think\db\exception\ModelNotFoundException * @author 段誉 * @date 2022/9/23 14:11 */ public static function cate() { return ArticleCate::field('id,name') ->where('is_show', '=', 1) ->order(['sort' => 'desc', 'id' => 'desc']) ->select()->toArray(); } /** * @notes 文章翻译 * @param int $articleId * @return array */ public static function translate(int $articleId, int $userId = 0, int $confirm = 0): array { $article = Article::where(['id' => $articleId, 'is_show' => YesNoEnum::YES])->findOrEmpty(); if ($article->isEmpty()) { return ['success' => false, 'error' => '文章不存在']; } $articleData = $article->toArray(); $ext = self::decodeExt($articleData['ext'] ?? []); $translatedContent = self::getTranslatedContentForUser($articleId, $userId, $ext); $translatePoints = 5; $remark = 'AI翻译资讯#' . $articleId; $hasPaid = false; if ($userId) { $hasPaid = Db::name('user_account_log') ->where('user_id', $userId) ->where('change_type', AccountLogEnum::UP_DEC_TRANSLATE_POST) ->where('remark', $remark) ->count() > 0; } if ($hasPaid && $translatedContent !== '') { return [ 'success' => true, 'data' => [ 'translated_content' => $translatedContent, 'from_cache' => true, ], ]; } if ($hasPaid) { $confirm = 1; } if (!$confirm) { return [ 'success' => true, 'data' => [ 'needs_payment' => true, 'cost' => $translatePoints, ], ]; } if (!$userId) { return ['success' => false, 'error' => '请先登录']; } if (!$hasPaid) { $user = User::findOrEmpty($userId); if ($user->isEmpty()) { return ['success' => false, 'error' => '用户不存在']; } if (($user->user_points ?? 0) < $translatePoints) { return ['success' => false, 'error' => '积分不足,当前积分' . ($user->user_points ?? 0) . ',翻译需要' . $translatePoints . '积分']; } } if ($translatedContent !== '') { if (!$hasPaid) { User::where('id', $userId)->dec('user_points', $translatePoints)->update(); AccountLogLogic::add( $userId, AccountLogEnum::UP_DEC_TRANSLATE_POST, AccountLogEnum::DEC, $translatePoints, '', $remark ); } return [ 'success' => true, 'data' => [ 'translated_content' => $translatedContent, 'from_cache' => false, ], ]; } $title = trim((string) ($articleData['title'] ?? '')); $abstract = trim((string) ($articleData['abstract'] ?? $articleData['desc'] ?? '')); $content = self::normalizeContent((string) ($articleData['content'] ?? '')); $result = AiService::translateArticle($title, $abstract, $content); if (!$result['success']) { return ['success' => false, 'error' => $result['error'] ?? '翻译失败']; } $translatedContent = trim((string) ($result['content'] ?? '')); if ($translatedContent === '') { return ['success' => false, 'error' => '翻译结果为空']; } $ext['translated_content'] = $translatedContent; Article::where('id', $articleId)->update([ 'ext' => json_encode($ext, JSON_UNESCAPED_UNICODE), 'update_time' => time(), ]); KbSyncService::enqueue('article', 'article', $articleId, 'upsert', 45); if (!$hasPaid) { User::where('id', $userId)->dec('user_points', $translatePoints)->update(); AccountLogLogic::add( $userId, AccountLogEnum::UP_DEC_TRANSLATE_POST, AccountLogEnum::DEC, $translatePoints, '', $remark ); } return [ 'success' => true, 'data' => [ 'translated_content' => $translatedContent, 'from_cache' => false, ], ]; } /** * @notes 自动翻译并落库文章译文缓存 * @param array $articleData * @return string */ public static function autoTranslateArticle(array $articleData): string { $articleId = (int) ($articleData['id'] ?? 0); if ($articleId <= 0) { return ''; } $ext = self::decodeExt($articleData['ext'] ?? []); if (!empty($ext['translated_content'])) { return (string) $ext['translated_content']; } $title = trim((string) ($articleData['title'] ?? '')); $abstract = trim((string) ($articleData['abstract'] ?? $articleData['desc'] ?? '')); $content = self::normalizeContent((string) ($articleData['content'] ?? '')); if ($title === '' && $abstract === '' && $content === '') { return ''; } $result = AiService::translateArticle($title, $abstract, $content); if (empty($result['success'])) { return ''; } $translatedContent = trim((string) ($result['content'] ?? '')); if ($translatedContent === '') { return ''; } $ext['translated_content'] = $translatedContent; Article::where('id', $articleId)->update([ 'ext' => json_encode($ext, JSON_UNESCAPED_UNICODE), 'update_time' => time(), ]); KbSyncService::enqueue('article', 'article', $articleId, 'upsert', 45); return $translatedContent; } /** * @notes 仅读取已缓存的文章翻译内容 * @param array $articleData * @return string */ public static function getCachedTranslatedContent(array $articleData): string { $ext = self::decodeExt($articleData['ext'] ?? []); return !empty($ext['translated_content']) ? (string) $ext['translated_content'] : ''; } protected static function getTranslatedContentForUser(int $articleId, int $userId, array $ext): string { if (empty($ext['translated_content']) || !$userId) { return ''; } $hasPaid = Db::name('user_account_log') ->where('user_id', $userId) ->where('change_type', AccountLogEnum::UP_DEC_TRANSLATE_POST) ->where('remark', 'AI翻译资讯#' . $articleId) ->count() > 0; return $hasPaid ? (string) $ext['translated_content'] : ''; } protected static function decodeExt($ext): array { if (is_array($ext)) { return $ext; } if (is_string($ext) && $ext !== '') { $data = json_decode($ext, true); return is_array($data) ? $data : []; } return []; } protected static function normalizeContent(string $content): string { if ($content === '') { return ''; } $content = str_ireplace(['
', '
', '
'], "\n", $content); $content = str_ireplace(['

', '', '', '', '', ''], "\n", $content); $content = strip_tags($content); $content = html_entity_decode($content, ENT_QUOTES | ENT_HTML5, 'UTF-8'); $content = preg_replace('/\r\n|\r/u', "\n", $content); $content = preg_replace('/\n{3,}/u', "\n\n", $content); return trim($content); } }