no message
This commit is contained in:
@@ -0,0 +1,355 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | likeadmin快速开发前后端分离管理后台(PHP版)
|
||||
// +----------------------------------------------------------------------
|
||||
// | 欢迎阅读学习系统程序代码,建议反馈是我们前进的动力
|
||||
// | 开源版本可自由商用,可去除界面版权logo
|
||||
// | gitee下载:https://gitee.com/likeshop_gitee/likeadmin
|
||||
// | github下载:https://github.com/likeshop-github/likeadmin
|
||||
// | 访问官网:https://www.likeadmin.cn
|
||||
// | likeadmin团队 版权所有 拥有最终解释权
|
||||
// +----------------------------------------------------------------------
|
||||
// | author: likeadminTeam
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace app\api\logic;
|
||||
|
||||
use app\common\enum\user\AccountLogEnum;
|
||||
use app\common\enum\YesNoEnum;
|
||||
use app\common\logic\AccountLogLogic;
|
||||
use app\common\logic\BaseLogic;
|
||||
use app\common\model\article\Article;
|
||||
use app\common\model\article\ArticleCate;
|
||||
use app\common\model\article\ArticleCollect;
|
||||
use app\api\logic\ArticleInteractLogic;
|
||||
use app\common\service\article\ArticleImageProxyService;
|
||||
use app\common\service\ai\AiService;
|
||||
use app\common\service\ai\KbSyncService;
|
||||
use app\common\model\user\User;
|
||||
use think\facade\Db;
|
||||
|
||||
/**
|
||||
* 文章逻辑
|
||||
* Class ArticleLogic
|
||||
* @package app\api\logic
|
||||
*/
|
||||
class ArticleLogic extends BaseLogic
|
||||
{
|
||||
|
||||
/**
|
||||
* @notes 文章详情
|
||||
* @param $articleId
|
||||
* @param $userId
|
||||
* @return array
|
||||
* @author 段誉
|
||||
* @date 2022/9/20 17:09
|
||||
*/
|
||||
public static function detail($articleId, $userId)
|
||||
{
|
||||
$article = Article::getArticleDetailArr($articleId);
|
||||
if (empty($article)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$ext = self::decodeExt($article['ext'] ?? []);
|
||||
$translatedContent = self::getTranslatedContentForUser($articleId, $userId, $ext);
|
||||
if ($translatedContent === '' && (int) ($article['cid'] ?? 0) === 22) {
|
||||
$translatedContent = self::autoTranslateArticle($article);
|
||||
}
|
||||
if ($translatedContent !== '') {
|
||||
$article['translated_content'] = $translatedContent;
|
||||
} elseif (!empty($ext['translated_content'])) {
|
||||
$article['translated_content'] = (string) $ext['translated_content'];
|
||||
}
|
||||
$article['collect'] = ArticleCollect::isCollectArticle($userId, $articleId);
|
||||
$article['vote_status'] = ArticleInteractLogic::getVoteStatus($userId, $articleId);
|
||||
$article = ArticleImageProxyService::rewriteArticlePayload($article);
|
||||
|
||||
return $article;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 加入收藏
|
||||
* @param $userId
|
||||
* @param $articleId
|
||||
* @author 段誉
|
||||
* @date 2022/9/20 16:52
|
||||
*/
|
||||
public static function addCollect($articleId, $userId)
|
||||
{
|
||||
$where = ['user_id' => $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(['<br>', '<br/>', '<br />'], "\n", $content);
|
||||
$content = str_ireplace(['</p>', '</div>', '</li>', '</h1>', '</h2>', '</h3>'], "\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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user