first commot

This commit is contained in:
2026-01-26 09:29:36 +08:00
commit dbb40c38c4
189 changed files with 20198 additions and 0 deletions
@@ -0,0 +1,605 @@
<?php
declare (strict_types = 1);
namespace app\admin\controller\Article;
use app\admin\BaseController;
use think\exception\ValidateException;
use think\facade\Db;
use think\facade\Request;
use think\facade\Session;
use think\response\Json;
use think\db\exception\DbException;
class ArticleController extends BaseController
{
/**
* 获取文章列表
* @param string $keyword 文章标题关键词(默认空)
* @param string $cate 文章分类(默认空)
* @param int $page 页码(默认1
* @param int $pageSize 每页条数(默认10
* @return Json
*/
public function getArticlesList(string $keyword = '', string $cate = '', int $page = 1, int $pageSize = 10): Json
{
try {
// 安全处理参数(防止非法参数)
$page = max(1, $page); // 页码最小为1
$pageSize = max(1, min(100, $pageSize)); // 每页条数限制1-100条
$keyword = trim($keyword);
$cate = trim($cate);
// 输出参数值
trace('查询参数:', 'debug');
trace([
'keyword' => $keyword,
'cate' => $cate,
'page' => $page,
'pageSize' => $pageSize
], 'debug');
// 构建查询条件,合并表连接
$query = Db::name('mete_articles')->alias('a')
->where('a.delete_time', null)
->leftJoin('mete_admin_user u', 'a.publisher = u.id')
->leftJoin('mete_articles_category c', 'a.cate = c.id');
// 关键词筛选
if ($keyword) {
$query->where('a.title', 'like', '%' . $keyword . '%');
}
// 分类筛选
if ($cate) {
$query->where('a.cate', $cate);
}
// 获取总条数
$total = $query->count();
// 执行分页查询,合并order语句
$articles = $query
->field('a.id, a.title, c.name as cate, a.author, a.status, a.views, a.likes, a.publishdate,a.recommend,a.top,a.update_time, u.name as publisher')
->order('a.status, a.top desc, a.recommend desc, a.sort desc, a.id desc')
->page($page, $pageSize)
->select()
->toArray();
return json([
'code' => 200,
'msg' => 'success',
'data' => [
'list' => $articles,
'total' => $total,
'page' => $page,
'pageSize' => $pageSize
]
]);
} catch (\Exception $e) {
// 简化异常处理
trace('文章列表查询失败: ' . $e->getMessage(), 'error');
return json([
'code' => 500,
'msg' => '文章列表查询失败,请稍后重试',
'data' => ['list' => [], 'total' => 0, 'page' => $page, 'pageSize' => $pageSize]
]);
}
}
/**
* 检查标题相似度
* @param string $title 新标题
* @return array|null 相似文章列表,null表示没有相似标题
*/
private function checkTitleSimilarity(string $title)
{
$newTitleLower = strtolower($title);
$newTitleLength = strlen($newTitleLower);
// 获取所有文章标题
$allTitles = Db::name('mete_articles')
->field('id, title')
->select();
$similarArticles = [];
// 计算每个标题的相似度
foreach ($allTitles as $article) {
$existingTitleLower = strtolower($article['title']);
$existingTitleLength = strlen($existingTitleLower);
// 使用Levenshtein距离计算相似度
$distance = levenshtein($newTitleLower, $existingTitleLower);
$maxLength = max($newTitleLength, $existingTitleLength);
$similarity = $maxLength > 0 ? (1 - $distance / $maxLength) * 100 : 0;
// 相似度超过50%才返回
if ($similarity >= 50) {
$similarArticles[] = [
'id' => $article['id'],
'title' => $article['title'],
'similarity' => round($similarity, 2) // 保留两位小数
];
}
}
// 按相似度降序排序
usort($similarArticles, function($a, $b) {
return $b['similarity'] - $a['similarity'];
});
return !empty($similarArticles) ? $similarArticles : null;
}
public function createArticle(): Json
{
try {
$data = request()->only(['title','cate','image','desc','author','content','sort','status','is_trans','transurl','ignore_similarity']);
// 基础校验
if (empty($data['title'])) {
throw new ValidateException('文章标题不能为空');
} elseif (empty($data['cate'])) {
throw new ValidateException('请选择文章分类');
} elseif (empty($data['author'])) {
throw new ValidateException('请填写作者');
} elseif (empty($data['content'])) {
throw new ValidateException('请填写内容');
}
// 标题相似度检测(仅新增时检测)
if (empty($data['ignore_similarity'])) {
$similarArticles = $this->checkTitleSimilarity($data['title']);
if ($similarArticles) {
return json([
'code' => 409,
'msg' => '检测到相似标题',
'data' => [
'similar_articles' => $similarArticles
]
]);
}
}
// 获取当前登录用户ID
$userId = Request::middleware('user_id', '');
// 移除 ignore_similarity,不保存到数据库
unset($data['ignore_similarity']);
// 准备写入数据库的数据
$insertData = [
'title' => $data['title'],
'cate' => $data['cate'],
'image' => $data['image'] ?? '',
'desc' => $data['desc'] ?? '',
'author' => $data['author'],
'content' => $data['content'],
'sort' => isset($data['sort']) ? (int)$data['sort'] : 0,
'status' => isset($data['status']) ? (int)$data['status'] : 0,
'is_trans' => $data['is_trans'] ?? 0,
'transurl' => $data['is_trans'] == 1 ? ($data['transurl'] ?? '') : '',
'publisher' => $userId,
'create_time' => date('Y-m-d H:i:s'),
];
$id = Db::name('mete_articles')->insertGetId($insertData);
// 记录成功日志
$this->logSuccess('文章管理', '创建文章', ['id' => $id]);
return json(['code'=>200,'msg'=>'success','data'=>['id'=>$id]]);
} catch (ValidateException $ve) {
return json(['code'=>422,'msg'=>$ve->getMessage(),'data'=>null]);
} catch (\Exception $e) {
// 记录失败日志
$this->logFail('文章管理', '创建文章', $e->getMessage());
return json(['code'=>500,'msg'=>'fail'.$e->getMessage(),'data'=>$e->getTraceAsString()]);
}
}
/**
* 获取文章详情
* @param int $id 文章ID
* @return Json
*/
public function getArticle(int $id): Json
{
try {
$article = Db::name('mete_articles')
->where('id', $id)
->field('id,title,cate,image,desc,author,content,is_trans,transurl,views,likes,publisher,create_time,publishdate,update_time')
->find();
if (!$article) {
return json(['code' => 404, 'msg' => '文章不存在', 'data' => null]);
}
return json(['code' => 200, 'msg' => 'success', 'data' => $article]);
} catch (\Exception $e) {
return json(['code' => 500, 'msg' => 'fail' . $e->getMessage(), 'data' => $e->getTraceAsString()]);
}
}
/**
* 编辑文章
* @param int $id
* @return Json
*/
public function editArticle(int $id): Json
{
try {
$data = request()->only(['title','cate','image','desc','author','content','sort','status','is_trans','transurl']);
if (empty($data)) {
return json(['code'=>400,'msg'=>'无更新数据','data'=>null]);
}
$data['update_time'] = date('Y-m-d H:i:s');
$affected = Db::name('mete_articles')->where('id',$id)->update($data);
if ($affected) {
return json(['code'=>200,'msg'=>'success','data'=>null]);
}
// 记录成功日志
$this->logSuccess('文章管理', '编辑文章', ['id' => $id]);
return json(['code'=>404,'msg'=>'not found','data'=>null]);
} catch (\Exception $e) {
// 记录失败日志
$this->logFail('文章管理', '编辑文章', $e->getMessage());
return json(['code'=>500,'msg'=>'fail'.$e->getMessage(),'data'=>$e->getTraceAsString()]);
}
}
/**
* 删除文章分类(软删除)
* @param int $id
* @return Json
*/
public function deleteArticle(int $id): Json
{
try {
$affected = Db::name('mete_articles')
->where('id', $id)
->update(['delete_time' => date('Y-m-d H:i:s')]);
if ($affected) {
// 记录成功日志
$this->logSuccess('文章管理', '删除文章', ['id' => $id]);
return json(['code' => 200, 'msg' => 'success', 'data' => null]);
}
return json(['code' => 404, 'msg' => 'not found', 'data' => null]);
} catch (\Exception $e) {
// 记录失败日志
$this->logFail('文章管理', '删除文章', $e->getMessage());
return json([
'code' => 500,
'msg' => 'fail' . $e->getMessage(),
'data' => $e->getTraceAsString()
]);
}
}
/**
* 更新分类状态
* @param int $id
* @return Json
*/
public function updateCategoryStatus(int $id): Json
{
try {
$data = request()->only(['status']);
if (empty($data)) {
return json(['code'=>400,'msg'=>'无更新数据','data'=>null]);
}
$data['update_time'] = date('Y-m-d H:i:s');
$affected = Db::name('mete_articles')->where('id',$id)->update($data);
if ($affected) {
// 记录成功日志
$this->logSuccess('文章管理', '更新文章分类状态', ['id' => $id]);
return json(['code'=>200,'msg'=>'success','data'=>null]);
}
return json(['code'=>404,'msg'=>'not found','data'=>null]);
} catch (\Exception $e) {
// 记录失败日志
$this->logFail('文章管理', '更新文章分类状态', $e->getMessage());
return json(['code'=>500,'msg'=>'fail'.$e->getMessage(),'data'=>$e->getTraceAsString()]);
}
}
/**
* 审核发布文章
* @param int $id 文章ID
* @return Json
*/
public function publishArticle(int $id): Json
{
try {
// 获取请求对象
$request = Request::instance();
$uid = $request->post('uid');
$currentTime = date('Y-m-d H:i:s');
if (!isset($uid) || empty($uid)){
return json(['code'=>400,'msg'=>'用户ID不能为空','data'=>null]);
}else{
$user_id = $uid;
}
$data = [
'status' => 2, // 已发布状态
'publishdate' => $currentTime,
'update_time' => $currentTime,
'publisher' => $user_id,
];
$affected = Db::name('mete_articles')
->where('id', $id)
->update($data);
if ($affected) {
// 记录成功日志
$this->logSuccess('文章管理', '审核发布文章', ['id' => $id]);
return json(['code' => 200, 'msg' => '发布成功', 'data' => null]);
}
return json(['code' => 404, 'msg' => '文章不存在或更新失败', 'data' => null]);
} catch (\Exception $e) {
// 记录失败日志
$this->logFail('文章管理', '审核发布文章', $e->getMessage());
return json([
'code' => 500,
'msg' => '发布失败:' . $e->getMessage(),
'data' => null
]);
}
}
/**
* 下架文章
* @param int $id 文章ID
* @return Json
*/
public function unPublishArticle(int $id): Json
{
try {
$currentTime = date('Y-m-d H:i:s');
$data = [
'status' => 1, // 已发布状态
'publishdate' => null,
'update_time' => $currentTime
];
$affected = Db::name('mete_articles')
->where('id', $id)
->update($data);
if ($affected) {
// 记录成功日志
$this->logSuccess('文章管理', '下架文章', ['id' => $id]);
return json(['code' => 200, 'msg' => '下架成功', 'data' => null]);
}
return json(['code' => 404, 'msg' => '文章不存在或更新失败', 'data' => null]);
} catch (\Exception $e) {
// 记录失败日志
$this->logFail('文章管理', '下架文章', $e->getMessage());
return json([
'code' => 500,
'msg' => '发布失败:' . $e->getMessage(),
'data' => null
]);
}
}
/**
* 文章推荐
* @param int $id 文章ID
* @return Json
*/
public function articleRecommend(int $id): Json
{
try {
$data = [
'recommend' => 1, // 推荐状态
];
$affected = Db::name('mete_articles')
->where('id', $id)
->update($data);
if ($affected) {
// 记录成功日志
$this->logSuccess('文章管理', '文章推荐', ['id' => $id]);
return json(['code' => 200, 'msg' => '推荐成功', 'data' => null]);
}
return json(['code' => 404, 'msg' => '文章不存在或更新失败', 'data' => null]);
} catch (\Exception $e) {
// 记录失败日志
$this->logFail('文章管理', '文章推荐', $e->getMessage());
return json([
'code' => 500,
'msg' => '推荐失败:' . $e->getMessage(),
'data' => null
]);
}
}
/**
* 文章取消推荐
* @param int $id 文章ID
* @return Json
*/
public function unArticleRecommend(int $id): Json
{
try {
$data = [
'recommend' => 0, // 取消推荐状态
];
$affected = Db::name('mete_articles')
->where('id', $id)
->update($data);
if ($affected) {
return json(['code' => 200, 'msg' => '取消推荐成功', 'data' => null]);
}
return json(['code' => 404, 'msg' => '文章不存在或更新失败', 'data' => null]);
} catch (\Exception $e) {
return json([
'code' => 500,
'msg' => '取消推荐失败:' . $e->getMessage(),
'data' => null
]);
}
}
/**
* 文章置顶
* @param int $id 文章ID
* @return Json
*/
public function articleTop(int $id): Json
{
try {
$data = [
'top' => 1, // 置顶状态
];
$affected = Db::name('mete_articles')
->where('id', $id)
->update($data);
if ($affected) {
return json(['code' => 200, 'msg' => '置顶成功', 'data' => null]);
}
return json(['code' => 404, 'msg' => '文章不存在或更新失败', 'data' => null]);
} catch (\Exception $e) {
return json([
'code' => 500,
'msg' => '置顶失败:' . $e->getMessage(),
'data' => null
]);
}
}
/**
* 文章取消置顶
* @param int $id 文章ID
* @return Json
*/
public function unArticleTop(int $id): Json
{
try {
$data = [
'top' => 0, // 取消置顶状态
];
$affected = Db::name('mete_articles')
->where('id', $id)
->update($data);
if ($affected) {
return json(['code' => 200, 'msg' => '取消置顶成功', 'data' => null]);
}
return json(['code' => 404, 'msg' => '文章不存在或更新失败', 'data' => null]);
} catch (\Exception $e) {
return json([
'code' => 500,
'msg' => '取消置顶失败:' . $e->getMessage(),
'data' => null
]);
}
}
/**
* 获取文章列表接口(带条件筛选和分页)
* @param string $keyword 文章标题关键词(默认空)
* @param string $cate 文章分类(默认空)
* @param int $page 页码(默认1
* @param int $pageSize 每页条数(默认10
* @return Json
*/
public function getAllArticles(string $keyword = '', string $cate = '', int $page = 1, int $pageSize = 10): Json
{
try {
// 安全处理参数(防止非法参数)
$page = max(1, $page); // 页码最小为1
$pageSize = max(1, min(100, $pageSize)); // 每页条数限制1-100条
$keyword = trim($keyword);
$cate = trim($cate);
// 输出参数值
trace('查询参数:', 'debug');
trace([
'keyword' => $keyword,
'cate' => $cate,
'page' => $page,
'pageSize' => $pageSize
], 'debug');
// 构建查询条件
$query = Db::name('mete_articles')
->alias('a')
->where('a.delete_time', null);
// 关键词筛选(标题模糊匹配)
if (!empty($keyword)) {
$query->where('a.title', 'like', '%' . $keyword . '%');
}
// 分类筛选
if (!empty($cate)) {
$query->where('a.cate', $cate);
}
// 克隆查询对象用于获取总数
$countQuery = clone $query;
// 获取总条数(必须在分页前获取)
$total = $countQuery->count();
// 执行分页查询
$articles = $query
->alias('a')
->leftJoin('mete_admin_user u', 'a.publisher = u.id')
->leftJoin('mete_articles_category c', 'a.cate = c.id')
->field('a.*, u.name as publisher, c.name as cate')
->order('a.status', 'asc')
->order('a.top', 'desc')
->order('a.recommend', 'desc')
->order('a.sort', 'desc')
->order('a.id', 'desc')
->page($page, $pageSize)
->select()
->toArray();
return json([
'code' => 200,
'msg' => 'success',
'data' => [
'list' => $articles, // 分页数据列表
'total' => $total, // 总条数
'page' => $page, // 当前页码
'pageSize' => $pageSize // 每页条数
]
]);
} catch (DbException $e) {
// 数据库异常,记录详细日志但返回友好提示
trace('文章查询失败: ' . $e->getMessage() . ' | ' . $e->getTraceAsString(), 'error');
return json([
'code' => 500,
'msg' => '文章列表查询失败,请稍后重试',
'data' => []
]);
} catch (\Exception $e) {
// 其他异常处理
trace('系统异常: ' . $e->getMessage() . ' | ' . $e->getTraceAsString(), 'error');
return json([
'code' => 500,
'msg' => '服务器内部错误,请稍后重试',
'data' => []
]);
}
}
}