first commot
This commit is contained in:
@@ -0,0 +1,165 @@
|
||||
<?php
|
||||
declare (strict_types = 1);
|
||||
|
||||
namespace app\admin;
|
||||
|
||||
use think\App;
|
||||
use think\exception\ValidateException;
|
||||
use think\Validate;
|
||||
use think\facade\Request;
|
||||
use think\facade\Cache;
|
||||
use app\admin\controller\OperationLog\OperationLogger;
|
||||
use app\service\JwtService;
|
||||
|
||||
/**
|
||||
* 控制器基础类
|
||||
*/
|
||||
abstract class BaseController
|
||||
{
|
||||
/**
|
||||
* Request实例
|
||||
* @var \think\Request
|
||||
*/
|
||||
protected $request;
|
||||
|
||||
/**
|
||||
* 应用实例
|
||||
* @var \think\App
|
||||
*/
|
||||
protected $app;
|
||||
|
||||
/**
|
||||
* 是否批量验证
|
||||
* @var bool
|
||||
*/
|
||||
protected $batchValidate = false;
|
||||
|
||||
/**
|
||||
* 控制器中间件
|
||||
* @var array
|
||||
*/
|
||||
protected $middleware = [];
|
||||
|
||||
/**
|
||||
* 构造方法
|
||||
* @access public
|
||||
* @param App $app 应用对象
|
||||
*/
|
||||
public function __construct(App $app)
|
||||
{
|
||||
$this->app = $app;
|
||||
$this->request = $this->app->request;
|
||||
|
||||
// 控制器初始化
|
||||
$this->initialize();
|
||||
}
|
||||
|
||||
// 初始化
|
||||
protected function initialize()
|
||||
{}
|
||||
|
||||
/**
|
||||
* 验证数据
|
||||
* @access protected
|
||||
* @param array $data 数据
|
||||
* @param string|array $validate 验证器名或者验证规则数组
|
||||
* @param array $message 提示信息
|
||||
* @param bool $batch 是否批量验证
|
||||
* @return array|string|true
|
||||
* @throws ValidateException
|
||||
*/
|
||||
protected function validate(array $data, string|array $validate, array $message = [], bool $batch = false)
|
||||
{
|
||||
if (is_array($validate)) {
|
||||
$v = new Validate();
|
||||
$v->rule($validate);
|
||||
} else {
|
||||
if (strpos($validate, '.')) {
|
||||
// 支持场景
|
||||
[$validate, $scene] = explode('.', $validate);
|
||||
}
|
||||
$class = false !== strpos($validate, '\\') ? $validate : $this->app->parseClass('validate', $validate);
|
||||
$v = new $class();
|
||||
if (!empty($scene)) {
|
||||
$v->scene($scene);
|
||||
}
|
||||
}
|
||||
|
||||
$v->message($message);
|
||||
|
||||
// 是否批量验证
|
||||
if ($batch || $this->batchValidate) {
|
||||
$v->batch(true);
|
||||
}
|
||||
|
||||
return $v->failException(true)->check($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录操作日志
|
||||
*
|
||||
* @param string $module 模块名称(如:文件管理)
|
||||
* @param string $action 操作名称(如:创建文件分组)
|
||||
* @param array $responseData 响应数据(可选)
|
||||
* @param array $userInfo 用户信息(可选,如果不提供则从 Session 获取)
|
||||
* @return bool
|
||||
*/
|
||||
protected function logSuccess(string $module, string $action, array $responseData = [], array $userInfo = []): bool
|
||||
{
|
||||
return OperationLogger::success($module, $action, $responseData, $userInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录失败日志
|
||||
*
|
||||
* @param string $module 模块名称
|
||||
* @param string $action 操作名称
|
||||
* @param string $errorMessage 错误信息
|
||||
* @param array $userInfo 用户信息(可选,如果不提供则从 Session 获取)
|
||||
* @return bool
|
||||
*/
|
||||
protected function logFail(string $module, string $action, string $errorMessage, array $userInfo = []): bool
|
||||
{
|
||||
return OperationLogger::fail($module, $action, $errorMessage, $userInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录操作日志
|
||||
*
|
||||
* @param string $module 操作模块
|
||||
* @param string $action 操作动作
|
||||
* @param array $requestData 请求数据(为空则自动获取)
|
||||
* @param array $responseData 响应数据
|
||||
* @param int $status 操作状态:1-成功,0-失败
|
||||
* @param string $errorMessage 错误信息
|
||||
* @return bool
|
||||
*/
|
||||
protected function logOperation(
|
||||
string $module,
|
||||
string $action,
|
||||
array $requestData = [],
|
||||
array $responseData = [],
|
||||
int $status = 1,
|
||||
string $errorMessage = ''
|
||||
): bool {
|
||||
return OperationLogger::record(
|
||||
$module,
|
||||
$action,
|
||||
$requestData,
|
||||
$responseData,
|
||||
$status,
|
||||
$errorMessage
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前登录管理员信息
|
||||
*
|
||||
* @return array 用户信息数组,包含 id、account、name 等字段
|
||||
*/
|
||||
protected function getAdminUserInfo(): array
|
||||
{
|
||||
return JwtService::getUserFromHeader($this->request->header('Authorization', ''));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
<?php
|
||||
// 这是系统自动生成的公共文件
|
||||
@@ -0,0 +1,197 @@
|
||||
<?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\Session;
|
||||
use think\response\Json;
|
||||
use think\db\exception\DbException;
|
||||
|
||||
class ArticleCategoryController extends BaseController
|
||||
{
|
||||
|
||||
/**
|
||||
* 获取文章分类列表接口
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function getAllArticleCategories(string $keyword)
|
||||
{
|
||||
try {
|
||||
//判断keyword是否存在
|
||||
$keyword = $keyword ?? '';
|
||||
|
||||
// 获取文章分类列表
|
||||
$categories = Db::name('mete_articles_category')
|
||||
->where('delete_time', null)
|
||||
->where('status', 1)
|
||||
->where('name', 'like', '%' . $keyword . '%')
|
||||
->order('sort', 'desc')
|
||||
->order('id', 'asc')
|
||||
->select()
|
||||
->toArray(); // 转换为数组
|
||||
|
||||
return json([
|
||||
'code' => 200,
|
||||
'msg' => 'success',
|
||||
'data' => $categories
|
||||
]);
|
||||
} catch (DbException $e) {
|
||||
return json([
|
||||
'code' => 500,
|
||||
'msg' => 'fail:' . $e->getMessage(),
|
||||
'data' => $e->getTraceAsString()
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文章分类列表接口
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function getArticleCategories()
|
||||
{
|
||||
try {
|
||||
// 获取文章分类列表
|
||||
$categories = Db::name('mete_articles_category')
|
||||
->where('delete_time', null)
|
||||
->where('status', 1)
|
||||
->field('id,cid,name,image,sort')
|
||||
->order('sort', 'desc')
|
||||
->order('id', 'asc')
|
||||
->select()
|
||||
->toArray(); // 转换为数组
|
||||
|
||||
return json([
|
||||
'code' => 200,
|
||||
'msg' => 'success',
|
||||
'data' => $categories
|
||||
]);
|
||||
} catch (DbException $e) {
|
||||
return json([
|
||||
'code' => 500,
|
||||
'msg' => 'fail:' . $e->getMessage(),
|
||||
'data' => $e->getTraceAsString()
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增文章分类
|
||||
* @return Json
|
||||
*/
|
||||
public function createCategory(): Json
|
||||
{
|
||||
try {
|
||||
$data = request()->only(['cid','name','image','desc','sort','status']);
|
||||
// 基础校验
|
||||
if (empty($data['name'])) {
|
||||
throw new ValidateException('分类名称不能为空');
|
||||
}
|
||||
$data['cid'] = isset($data['cid']) ? (int)$data['cid'] : 0;
|
||||
$data['sort'] = isset($data['sort']) ? (int)$data['sort'] : 0;
|
||||
$data['status']= isset($data['status']) ? (int)$data['status'] : 1;
|
||||
$data['create_time'] = date('Y-m-d H:i:s');
|
||||
$data['update_time'] = $data['create_time'];
|
||||
|
||||
$id = Db::name('mete_articles_category')->insertGetId($data);
|
||||
return json(['code'=>200,'msg'=>'success','data'=>['id'=>$id]]);
|
||||
} catch (ValidateException $ve) {
|
||||
// 记录失败日志
|
||||
$this->logFail('文章分类管理', '新增文章分类', $ve->getMessage());
|
||||
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
|
||||
* @return Json
|
||||
*/
|
||||
public function editCategory(int $id): Json
|
||||
{
|
||||
try {
|
||||
$data = request()->only(['cid','name','image','desc','sort','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_category')->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
|
||||
* @return Json
|
||||
*/
|
||||
public function deleteCategory(int $id): Json
|
||||
{
|
||||
try {
|
||||
$affected = Db::name('mete_articles_category')
|
||||
->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['status'] = isset($data['status']) ? (int)$data['status'] : 1;
|
||||
$data['update_time'] = date('Y-m-d H:i:s');
|
||||
$affected = Db::name('mete_articles_category')->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()]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -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' => []
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,232 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\admin\controller;
|
||||
|
||||
use app\admin\BaseController;
|
||||
use think\exception\ValidateException;
|
||||
use think\facade\Db;
|
||||
use think\facade\Session;
|
||||
use think\response\Json;
|
||||
use think\db\exception\DbException;
|
||||
use app\model\Banner;
|
||||
|
||||
class BannerController extends BaseController
|
||||
{
|
||||
/**
|
||||
* 获取所有Banner信息
|
||||
* @return Json
|
||||
*/
|
||||
public function getAllBanners()
|
||||
{
|
||||
try {
|
||||
$banners = Banner::where('delete_time', null)
|
||||
->order('sort', 'asc')
|
||||
->field('id, title, desc, url, image, sort, create_time, update_time')
|
||||
->select()
|
||||
->toArray();
|
||||
// 记录操作日志
|
||||
$this->logSuccess('Banner管理', '获取所有Banner', ['data' => $banners]);
|
||||
return json([
|
||||
'code' => 200,
|
||||
'msg' => '获取成功',
|
||||
'data' => $banners
|
||||
]);
|
||||
} catch (DbException $e) {
|
||||
// 记录失败日志
|
||||
$this->logFail('Banner管理', '获取所有Banner', $e->getMessage());
|
||||
return json([
|
||||
'code' => 500,
|
||||
'msg' => '获取失败:' . $e->getMessage(),
|
||||
'data' => []
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建Banner
|
||||
* @return Json
|
||||
*/
|
||||
public function createBanner()
|
||||
{
|
||||
try {
|
||||
// 获取请求参数
|
||||
$data = $this->request->param();
|
||||
|
||||
// 验证参数
|
||||
$this->validate($data, [
|
||||
'title|标题' => 'require|max:255',
|
||||
'desc|简介' => 'max:65535',
|
||||
'url|跳转地址' => 'max:65535',
|
||||
'image|图片地址' => 'max:65535',
|
||||
'sort|排序号' => 'integer',
|
||||
]);
|
||||
|
||||
// 准备Banner数据
|
||||
$bannerData = [
|
||||
'title' => $data['title'],
|
||||
'desc' => $data['desc'] ?? '',
|
||||
'url' => $data['url'] ?? '',
|
||||
'image' => $data['image'] ?? '',
|
||||
'sort' => $data['sort'] ?? 0,
|
||||
'create_time' => date('Y-m-d H:i:s'),
|
||||
];
|
||||
|
||||
// 创建Banner
|
||||
$result = Banner::insertGetId($bannerData);
|
||||
|
||||
if ($result === false) {
|
||||
return json([
|
||||
'code' => 500,
|
||||
'msg' => '创建失败'
|
||||
]);
|
||||
}
|
||||
|
||||
// 记录操作日志
|
||||
$this->logSuccess('Banner管理', '创建Banner', ['id' => $result]);
|
||||
return json([
|
||||
'code' => 200,
|
||||
'msg' => '创建成功',
|
||||
'data' => ['id' => $result]
|
||||
]);
|
||||
} catch (ValidateException $e) {
|
||||
// 记录失败日志
|
||||
$this->logFail('Banner管理', '创建Banner', $e->getMessage());
|
||||
return json([
|
||||
'code' => 400,
|
||||
'msg' => $e->getError()
|
||||
]);
|
||||
} catch (DbException $e) {
|
||||
// 记录失败日志
|
||||
$this->logFail('Banner管理', '创建Banner', $e->getMessage());
|
||||
return json([
|
||||
'code' => 500,
|
||||
'msg' => '创建失败:' . $e->getMessage()
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑Banner
|
||||
* @param int $id Banner ID
|
||||
* @return Json
|
||||
*/
|
||||
public function editBanner(int $id)
|
||||
{
|
||||
try {
|
||||
// 获取请求参数
|
||||
$data = $this->request->param();
|
||||
|
||||
// 验证参数
|
||||
$this->validate($data, [
|
||||
'title|标题' => 'require|max:255',
|
||||
'desc|简介' => 'max:65535',
|
||||
'url|跳转地址' => 'max:65535',
|
||||
'image|图片地址' => 'max:65535',
|
||||
'sort|排序号' => 'integer',
|
||||
]);
|
||||
|
||||
// 检查Banner是否存在
|
||||
$banner = Banner::where('id', $id)
|
||||
->where('delete_time', null)
|
||||
->find();
|
||||
|
||||
if (!$banner) {
|
||||
return json([
|
||||
'code' => 404,
|
||||
'msg' => 'Banner不存在'
|
||||
]);
|
||||
}
|
||||
|
||||
// 准备更新数据
|
||||
$updateData = [
|
||||
'title' => $data['title'],
|
||||
'desc' => $data['desc'] ?? null,
|
||||
'url' => $data['url'] ?? null,
|
||||
'image' => $data['image'] ?? null,
|
||||
'sort' => $data['sort'] ?? 0,
|
||||
'update_time' => date('Y-m-d H:i:s'),
|
||||
];
|
||||
|
||||
// 执行更新
|
||||
Banner::where('id', $id)->update($updateData);
|
||||
|
||||
// 获取更新后的Banner信息
|
||||
$updatedBanner = Banner::where('id', $id)->find();
|
||||
|
||||
// 记录操作日志
|
||||
$this->logSuccess('Banner管理', '更新Banner', ['id' => $id]);
|
||||
return json([
|
||||
'code' => 200,
|
||||
'msg' => '更新成功',
|
||||
'data' => $updatedBanner ? $updatedBanner->toArray() : []
|
||||
]);
|
||||
} catch (ValidateException $e) {
|
||||
// 记录失败日志
|
||||
$this->logFail('Banner管理', '更新Banner', $e->getMessage());
|
||||
return json([
|
||||
'code' => 400,
|
||||
'msg' => $e->getError()
|
||||
]);
|
||||
} catch (DbException $e) {
|
||||
// 记录失败日志
|
||||
$this->logFail('Banner管理', '更新Banner', $e->getMessage());
|
||||
return json([
|
||||
'code' => 500,
|
||||
'msg' => '更新失败:' . $e->getMessage()
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除Banner
|
||||
* @param int $id Banner ID
|
||||
* @return Json
|
||||
*/
|
||||
public function deleteBanner(int $id)
|
||||
{
|
||||
try {
|
||||
// 检查Banner是否存在
|
||||
$banner = Banner::where('id', $id)
|
||||
->where('delete_time', null)
|
||||
->find();
|
||||
|
||||
if (!$banner) {
|
||||
return json([
|
||||
'code' => 404,
|
||||
'msg' => 'Banner不存在'
|
||||
]);
|
||||
}
|
||||
|
||||
// 逻辑删除Banner
|
||||
$result = Banner::where('id', $id)
|
||||
->where('delete_time', null)
|
||||
->update([
|
||||
'delete_time' => time(),
|
||||
'update_time' => time()
|
||||
]);
|
||||
|
||||
if ($result === false) {
|
||||
return json([
|
||||
'code' => 500,
|
||||
'msg' => '删除失败'
|
||||
]);
|
||||
}
|
||||
|
||||
// 记录操作日志
|
||||
$this->logSuccess('Banner管理', '删除Banner', ['id' => $id]);
|
||||
return json([
|
||||
'code' => 200,
|
||||
'msg' => '删除成功'
|
||||
]);
|
||||
} catch (DbException $e) {
|
||||
// 记录失败日志
|
||||
$this->logFail('Banner管理', '删除Banner', $e->getMessage());
|
||||
return json([
|
||||
'code' => 500,
|
||||
'msg' => '删除失败:' . $e->getMessage()
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,390 @@
|
||||
<?php
|
||||
|
||||
namespace app\admin\controller;
|
||||
|
||||
use app\admin\BaseController;
|
||||
use think\facade\Filesystem;
|
||||
use think\facade\Request;
|
||||
use think\facade\Db;
|
||||
use think\Response;
|
||||
|
||||
use app\model\FilesCategory;
|
||||
use app\model\Files;
|
||||
|
||||
class FileController extends BaseController
|
||||
{
|
||||
// 文件类型映射
|
||||
protected $fileTypes = [
|
||||
'image' => 1,
|
||||
'document' => 2,
|
||||
'video' => 3,
|
||||
'audio' => 4
|
||||
];
|
||||
|
||||
// 允许的文件类型
|
||||
protected $allowedExtensions = [
|
||||
'image' => ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp'],
|
||||
'document' => ['pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'txt'],
|
||||
'video' => ['mp4', 'webm', 'mov'],
|
||||
'audio' => ['mp3', 'wav', 'ogg']
|
||||
];
|
||||
|
||||
// 获取所有文件
|
||||
public function getAllFiles()
|
||||
{
|
||||
try {
|
||||
$page = Request::param('page/d', 1);
|
||||
$pageSize = Request::param('pageSize/d', 10);
|
||||
$cate = Request::param('cate/d', 0);
|
||||
$keyword = Request::param('keyword/s', '');
|
||||
|
||||
$query = Files::where('delete_time', null);
|
||||
|
||||
if ($cate) {
|
||||
$query->where('cate', $cate);
|
||||
}
|
||||
|
||||
if ($keyword) {
|
||||
$query->whereLike('name', "%{$keyword}%");
|
||||
}
|
||||
|
||||
$total = $query->count();
|
||||
$list = $query->page($page, $pageSize)
|
||||
->order('create_time', 'desc')
|
||||
->select();
|
||||
|
||||
return json([
|
||||
'code' => 200,
|
||||
'msg' => 'success',
|
||||
'data' => [
|
||||
'list' => $list,
|
||||
'total' => $total,
|
||||
'page' => $page,
|
||||
'pageSize' => $pageSize
|
||||
]
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
return json([
|
||||
'code' => 500,
|
||||
'msg' => '获取文件列表失败: ' . $e->getMessage()
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
//获取用户分类
|
||||
public function getUserCate()
|
||||
{
|
||||
try {
|
||||
$cate = FilesCategory::where('delete_time', null)->field('id,name')->select();
|
||||
|
||||
// 获取每个分类下的文件数量
|
||||
foreach ($cate as &$c) {
|
||||
$c['total'] = Files::where('cate', $c['id'])->where('delete_time', null)->count();
|
||||
}
|
||||
|
||||
return json([
|
||||
'code' => 200,
|
||||
'msg' => 'success',
|
||||
'data' => $cate
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
return json([
|
||||
'code' => 500,
|
||||
'msg' => '获取用户分类失败: ' . $e->getMessage()
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
//新建文件分组
|
||||
public function createFileCate()
|
||||
{
|
||||
try {
|
||||
$data = Request::param();
|
||||
$data['create_time'] = date('Y-m-d H:i:s');
|
||||
$id = FilesCategory::insertGetId($data);
|
||||
|
||||
// 记录操作日志
|
||||
$this->logSuccess('文件管理', '创建文件分组', ['id' => $id]);
|
||||
|
||||
return json([
|
||||
'code' => 200,
|
||||
'msg' => '新建文件分组成功',
|
||||
'data' => [
|
||||
'id' => $id
|
||||
]
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
// 记录失败日志
|
||||
$this->logFail('文件管理', '创建文件分组', $e->getMessage());
|
||||
|
||||
return json([
|
||||
'code' => 500,
|
||||
'msg' => '新建文件分组失败: ' . $e->getMessage()
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
//重命名文件分组
|
||||
public function renameFileCate($id)
|
||||
{
|
||||
try {
|
||||
$data = Request::param();
|
||||
$result = FilesCategory::where('id', $id)->update(['name' => $data['name']]);
|
||||
// 记录操作日志
|
||||
$this->logSuccess('文件管理', '重命名文件分组', ['id' => $id]);
|
||||
return json([
|
||||
'code' => 200,
|
||||
'msg' => '重命名文件分组成功'
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
// 记录失败日志
|
||||
$this->logFail('文件管理', '重命名文件分组', $e->getMessage());
|
||||
return json([
|
||||
'code' => 500,
|
||||
'msg' => '重命名文件分组失败: ' . $e->getMessage()
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
//删除文件分组
|
||||
public function deleteFileCate($id)
|
||||
{
|
||||
try {
|
||||
$result = FilesCategory::where('id', $id)->update(['delete_time' => date('Y-m-d H:i:s')]);
|
||||
// 记录操作日志
|
||||
$this->logSuccess('文件管理', '删除文件分组', ['id' => $id]);
|
||||
return json([
|
||||
'code' => 200,
|
||||
'msg' => '删除文件分组成功'
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
// 记录失败日志
|
||||
$this->logFail('文件管理', '删除文件分组', $e->getMessage());
|
||||
return json([
|
||||
'code' => 500,
|
||||
'msg' => '删除文件分组失败: ' . $e->getMessage()
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
//获取分类文件
|
||||
public function getCateFiles(int $id)
|
||||
{
|
||||
try {
|
||||
$page = Request::param('page/d', 1);
|
||||
$pageSize = Request::param('pageSize/d', 10);
|
||||
$keyword = Request::param('keyword/s', '');
|
||||
|
||||
$query = Files::where('cate', $id)->where('delete_time', null);
|
||||
|
||||
if ($keyword) {
|
||||
$query->whereLike('name', "%{$keyword}%");
|
||||
}
|
||||
|
||||
$list = $query->page($page, $pageSize)
|
||||
->order('create_time', 'desc')
|
||||
->select();
|
||||
|
||||
// 格式化文件信息
|
||||
foreach ($list as &$file) {
|
||||
$file['createTime'] = $file['create_time'];
|
||||
$file['groupId'] = $file['cate'];
|
||||
$file['url'] = $file['src'];
|
||||
}
|
||||
return json([
|
||||
'code' => 200,
|
||||
'msg' => 'success',
|
||||
'data' => [
|
||||
'list' => $list,
|
||||
'page' => $page,
|
||||
'pageSize' => $pageSize,
|
||||
'categoryId' => $id
|
||||
]
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
return json([
|
||||
'code' => 500,
|
||||
'msg' => '获取分类文件失败: ' . $e->getMessage()
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
public function uploadFile()
|
||||
{
|
||||
try {
|
||||
$file = Request::file('file');
|
||||
if (!$file) {
|
||||
return json(['code' => 400, 'msg' => '请选择要上传的文件']);
|
||||
}
|
||||
|
||||
// 验证文件大小和类型
|
||||
$maxSize = 50 * 1024 * 1024; // 50MB
|
||||
$fileExt = strtolower($file->getOriginalExtension());
|
||||
|
||||
if ($file->getSize() > $maxSize) {
|
||||
return json(['code' => 400, 'msg' => '文件大小不能超过50MB']);
|
||||
}
|
||||
|
||||
// 计算文件MD5
|
||||
$fileMd5 = md5_file($file->getRealPath());
|
||||
|
||||
// 检查是否已存在相同文件
|
||||
$existFile = Files::where('md5', $fileMd5)->where('delete_time', null)->find();
|
||||
|
||||
if ($existFile) {
|
||||
return json([
|
||||
'code' => 201,
|
||||
'msg' => '文件已存在',
|
||||
'data' => [
|
||||
'url' => $existFile['src'],
|
||||
'id' => $existFile['id'],
|
||||
'name' => $existFile['name']
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
// 确定文件类型
|
||||
$fileType = 2; // 默认为文件
|
||||
foreach ($this->allowedExtensions as $type => $extensions) {
|
||||
if (in_array($fileExt, $extensions)) {
|
||||
$fileType = $this->fileTypes[$type];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$cate = Request::param('cate/d', 0);
|
||||
// 生成按日期分类的目录结构
|
||||
$datePath = date('Y/m/d');
|
||||
$saveName = $datePath . '/' . uniqid() . '.' . $fileExt;
|
||||
$fullPath = Filesystem::disk('public')->putFileAs('uploads', $file, $saveName);
|
||||
$fileUrl = '/storage/' . str_replace('\\', '/', $fullPath);
|
||||
|
||||
// 获取当前登录用户ID
|
||||
$userId = Request::middleware('user_id', '');
|
||||
|
||||
// 保存文件信息到数据库
|
||||
$fileData = [
|
||||
'name' => $file->getOriginalName(),
|
||||
'type' => $fileType,
|
||||
'cate' => $cate,
|
||||
'size' => $file->getSize(),
|
||||
'src' => $fileUrl,
|
||||
'md5' => $fileMd5,
|
||||
'uploader' => $userId,
|
||||
'create_time' => date('Y-m-d H:i:s'),
|
||||
];
|
||||
|
||||
$fileId = Files::insertGetId($fileData);
|
||||
|
||||
// 记录操作日志
|
||||
$this->logSuccess('文件管理', '上传文件', ['id' => $fileId]);
|
||||
return json([
|
||||
'code' => 200,
|
||||
'msg' => '上传成功',
|
||||
'data' => [
|
||||
'url' => $fileUrl,
|
||||
'id' => $fileId,
|
||||
'name' => $fileData['name']
|
||||
]
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
// 记录失败日志
|
||||
$this->logFail('文件管理', '上传文件', $e->getMessage());
|
||||
return json([
|
||||
'code' => 500,
|
||||
'msg' => '上传失败: ' . $e->getMessage()
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
// 更新文件信息
|
||||
public function updateFile($id)
|
||||
{
|
||||
try {
|
||||
$data = Request::only(['name', 'cate']);
|
||||
if (empty($data)) {
|
||||
return json(['code' => 400, 'msg' => '无更新数据']);
|
||||
}
|
||||
|
||||
$data['update_time'] = date('Y-m-d H:i:s');
|
||||
|
||||
$result = Files::where('id', $id)->where('delete_time', null)->update($data);
|
||||
|
||||
if ($result) {
|
||||
// 记录操作日志
|
||||
$this->logSuccess('文件管理', '更新文件', ['id' => $id]);
|
||||
return json(['code' => 200, 'msg' => '更新成功']);
|
||||
}
|
||||
|
||||
return json(['code' => 404, 'msg' => '文件不存在']);
|
||||
} catch (\Exception $e) {
|
||||
// 记录失败日志
|
||||
$this->logFail('文件管理', '更新文件', $e->getMessage());
|
||||
return json(['code' => 500, 'msg' => '更新失败: ' . $e->getMessage()]);
|
||||
}
|
||||
}
|
||||
|
||||
// 删除文件
|
||||
public function deleteFile($id)
|
||||
{
|
||||
try {
|
||||
$result = Files::where('id', $id)->update(['delete_time' => date('Y-m-d H:i:s')]);
|
||||
|
||||
if ($result) {
|
||||
// 记录操作日志
|
||||
$this->logSuccess('文件管理', '删除文件', ['id' => $id]);
|
||||
return json(['code' => 200, 'msg' => '删除成功']);
|
||||
}
|
||||
|
||||
return json(['code' => 404, 'msg' => '文件不存在']);
|
||||
} catch (\Exception $e) {
|
||||
// 记录失败日志
|
||||
$this->logFail('文件管理', '删除文件', $e->getMessage());
|
||||
return json(['code' => 500, 'msg' => '删除失败: ' . $e->getMessage()]);
|
||||
}
|
||||
}
|
||||
|
||||
// 下载文件
|
||||
public function download($id)
|
||||
{
|
||||
try {
|
||||
$file = Files::where('id', $id)->where('delete_time', null)->find();
|
||||
|
||||
if (!$file) {
|
||||
return json(['code' => 404, 'msg' => '文件不存在']);
|
||||
}
|
||||
|
||||
$filePath = public_path() . $file['src'];
|
||||
|
||||
if (!file_exists($filePath)) {
|
||||
return json(['code' => 404, 'msg' => '文件不存在']);
|
||||
}
|
||||
|
||||
return download($filePath, $file['name']);
|
||||
} catch (\Exception $e) {
|
||||
// 记录失败日志
|
||||
$this->logFail('文件管理', '下载文件', $e->getMessage());
|
||||
return json(['code' => 500, 'msg' => '下载失败: ' . $e->getMessage()]);
|
||||
}
|
||||
}
|
||||
|
||||
// 移动文件
|
||||
public function moveFile($id)
|
||||
{
|
||||
try {
|
||||
$cate = Request::param('cate/d', 0);
|
||||
$result = Files::where('id', $id)->update(['cate' => $cate]);
|
||||
if ($result) {
|
||||
// 记录操作日志
|
||||
$this->logSuccess('文件管理', '移动文件', ['id' => $id]);
|
||||
return json(['code' => 200, 'msg' => '移动成功']);
|
||||
}
|
||||
|
||||
return json(['code' => 404, 'msg' => '文件不存在']);
|
||||
} catch (\Exception $e) {
|
||||
// 记录失败日志
|
||||
$this->logFail('文件管理', '移动文件', $e->getMessage());
|
||||
return json(['code' => 500, 'msg' => '移动失败: ' . $e->getMessage()]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,261 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\admin\controller;
|
||||
|
||||
use app\admin\BaseController;
|
||||
use think\exception\ValidateException;
|
||||
use think\facade\Db;
|
||||
use think\facade\Session;
|
||||
use think\response\Json;
|
||||
use think\db\exception\DbException;
|
||||
use app\model\FrontMenu;
|
||||
|
||||
class FrontMenuController extends BaseController
|
||||
{
|
||||
/**
|
||||
* 获取前端导航接口
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function getFrontMenus()
|
||||
{
|
||||
try {
|
||||
// 获取所有未删除的菜单
|
||||
$frontMenus = FrontMenu::where('delete_time', null)
|
||||
->field('id,pid,title,type,image,path,component_path,sort,desc')
|
||||
->order('sort', 'desc')
|
||||
->select()
|
||||
->toArray(); // 转换为数组
|
||||
|
||||
// 使用 buildFrontMenuTree 构建树形结构
|
||||
$treeFrontMenus = $this->buildFrontMenuTree($frontMenus);
|
||||
|
||||
return json([
|
||||
'code' => 200,
|
||||
'msg' => 'success',
|
||||
'data' => $treeFrontMenus
|
||||
]);
|
||||
} catch (DbException $e) {
|
||||
return json([
|
||||
'code' => 500,
|
||||
'msg' => 'fail:' . $e->getMessage(),
|
||||
'data' => $e->getTraceAsString()
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建新菜单
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function createFrontMenu()
|
||||
{
|
||||
try {
|
||||
// 获取请求参数
|
||||
$data = $this->request->param();
|
||||
|
||||
// 验证参数
|
||||
$this->validate($data, [
|
||||
'title|菜单名称' => 'require|max:50',
|
||||
'pid|上级菜单ID' => 'integer',
|
||||
'type|菜单类型' => 'require|in:1,2,3,4',
|
||||
'sort|排序号' => 'integer',
|
||||
'path|路由路径' => 'max:200',
|
||||
'component_path|组件路径' => 'max:200',
|
||||
'desc|描述' => 'max:500',
|
||||
]);
|
||||
|
||||
// 准备菜单数据
|
||||
$menuData = [
|
||||
'pid' => $data['pid'] ?? 0,
|
||||
'title' => $data['title'],
|
||||
'type' => $data['type'],
|
||||
'sort' => $data['sort'] ?? 0,
|
||||
'path' => $data['path'] ?? '',
|
||||
'component_path' => $data['component_path'] ?? '',
|
||||
'image' => $data['image'] ?? '',
|
||||
'desc' => $data['desc'] ?? '',
|
||||
'create_time' => date('Y-m-d H:i:s')
|
||||
];
|
||||
|
||||
// 创建菜单
|
||||
$result = FrontMenu::insertGetId($menuData);
|
||||
|
||||
if ($result === false) {
|
||||
return json([
|
||||
'code' => 500,
|
||||
'msg' => 'fail'
|
||||
]);
|
||||
}
|
||||
|
||||
// 记录操作日志
|
||||
$this->logSuccess('前端导航管理', '创建前端导航', ['id' => $result]);
|
||||
return json([
|
||||
'code' => 200,
|
||||
'msg' => 'success'
|
||||
]);
|
||||
} catch (ValidateException $e) {
|
||||
// 记录失败日志
|
||||
$this->logFail('前端导航管理', '创建前端导航', $e->getMessage());
|
||||
return json([
|
||||
'code' => 500,
|
||||
'msg' => 'fail:' . $e->getMessage()
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑前端导航
|
||||
* @param int $id 前端导航ID
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function editFrontMenu(int $id)
|
||||
{
|
||||
try {
|
||||
// 获取请求参数
|
||||
$data = $this->request->param();
|
||||
|
||||
// 验证参数
|
||||
$this->validate($data, [
|
||||
'title|菜单名称' => 'require|max:50',
|
||||
'pid|上级菜单ID' => 'integer',
|
||||
'type|菜单类型' => 'require|in:1,2,3,4',
|
||||
'sort|排序号' => 'integer',
|
||||
'path|路由路径' => 'max:200',
|
||||
'component_path|组件路径' => 'max:200',
|
||||
'desc|描述' => 'max:500',
|
||||
]);
|
||||
|
||||
// 准备更新数据
|
||||
$updateData = [
|
||||
'title' => $data['title'],
|
||||
'pid' => $data['pid'] ?? null,
|
||||
'type' => $data['type'],
|
||||
'path' => $data['path'] ?? null,
|
||||
'component_path' => $data['component_path'] ?? null,
|
||||
'image' => $data['image'] ?? null,
|
||||
'desc' => $data['desc'] ?? null,
|
||||
'sort' => $data['sort'] ?? 0,
|
||||
'update_time' => date('Y-m-d H:i:s')
|
||||
];
|
||||
|
||||
// 执行更新
|
||||
FrontMenu::where('id', $id)->update($updateData);
|
||||
|
||||
// 获取更新后的菜单信息
|
||||
$menu = FrontMenu::where('id', $id)->find();
|
||||
|
||||
// 记录操作日志
|
||||
$this->logSuccess('前端导航管理', '更新前端导航', ['id' => $id]);
|
||||
return json([
|
||||
'code' => 200,
|
||||
'msg' => 'success',
|
||||
'data' => $menu ? $menu->toArray() : []
|
||||
]);
|
||||
} catch (ValidateException $e) {
|
||||
// 记录失败日志
|
||||
$this->logFail('前端导航管理', '更新前端导航', $e->getMessage());
|
||||
return json([
|
||||
'code' => 400,
|
||||
'msg' => $e->getError()
|
||||
]);
|
||||
} catch (DbException $e) {
|
||||
// 记录失败日志
|
||||
$this->logFail('前端导航管理', '更新前端导航', $e->getMessage());
|
||||
return json([
|
||||
'code' => 500,
|
||||
'msg' => 'fail:' . $e->getMessage(),
|
||||
'data' => $e->getTraceAsString()
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function deleteFrontMenu(int $id)
|
||||
{
|
||||
try {
|
||||
// 检查是否有子前端导航
|
||||
$hasChildren = FrontMenu::where('pid', $id)
|
||||
->where('pid', $id)
|
||||
->where('delete_time', null)
|
||||
->count() > 0;
|
||||
|
||||
if ($hasChildren) {
|
||||
return json([
|
||||
'code' => 400,
|
||||
'msg' => '该前端导航下有子导航,无法删除'
|
||||
]);
|
||||
}
|
||||
|
||||
// 逻辑删除前端导航
|
||||
$result = FrontMenu::where('id', $id)
|
||||
->where('id', $id)
|
||||
->where('delete_time', null)
|
||||
->update([
|
||||
'delete_time' => date('Y-m-d H:i:s'),
|
||||
'update_time' => date('Y-m-d H:i:s')
|
||||
]);
|
||||
|
||||
if ($result === false) {
|
||||
return json([
|
||||
'code' => 500,
|
||||
'msg' => 'fail'
|
||||
]);
|
||||
}
|
||||
|
||||
// 记录操作日志
|
||||
$this->logSuccess('前端导航管理', '删除前端导航', ['id' => $id]);
|
||||
return json([
|
||||
'code' => 200,
|
||||
'msg' => 'success'
|
||||
]);
|
||||
} catch (ValidateException $e) {
|
||||
// 记录失败日志
|
||||
$this->logFail('前端导航管理', '删除前端导航', $e->getMessage());
|
||||
return json([
|
||||
'code' => 500,
|
||||
'msg' => 'fail:' . $e->getMessage()
|
||||
]);
|
||||
} catch (DbException $e) {
|
||||
// 记录失败日志
|
||||
$this->logFail('前端导航管理', '删除前端导航', $e->getMessage());
|
||||
return json([
|
||||
'code' => 500,
|
||||
'msg' => 'fail:' . $e->getMessage()
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建前端导航树形结构
|
||||
* @param array $frontMenus 前端导航列表
|
||||
* @param int $pid 父前端导航ID
|
||||
* @return array
|
||||
*/
|
||||
private function buildFrontMenuTree(array $frontMenus, int $pid = 0): array
|
||||
{
|
||||
$tree = [];
|
||||
|
||||
foreach ($frontMenus as $frontMenu) {
|
||||
// 将null的pid视为0处理
|
||||
$menuPid = is_null($frontMenu['pid']) ? 0 : $frontMenu['pid'];
|
||||
if ($menuPid == $pid) {
|
||||
$children = $this->buildFrontMenuTree($frontMenus, $frontMenu['id']);
|
||||
if (!empty($children)) {
|
||||
$frontMenu['children'] = $children;
|
||||
}
|
||||
$tree[] = $frontMenu;
|
||||
}
|
||||
}
|
||||
|
||||
// 按 sort 字段降序排序
|
||||
usort($tree, function ($a, $b) {
|
||||
$sortA = $a['sort'] ?? 0;
|
||||
$sortB = $b['sort'] ?? 0;
|
||||
return $sortB - $sortA;
|
||||
});
|
||||
|
||||
return $tree;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
declare (strict_types = 1);
|
||||
|
||||
namespace app\admin\controller;
|
||||
use app\admin\BaseController;
|
||||
|
||||
class Index extends BaseController
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
return '后端运行中...';
|
||||
}
|
||||
|
||||
public function hello($name = 'ThinkPHP8')
|
||||
{
|
||||
return 'hello,' . $name;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,191 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\admin\controller;
|
||||
|
||||
use app\admin\BaseController;
|
||||
use think\exception\ValidateException;
|
||||
use think\facade\Db;
|
||||
use think\facade\Cache;
|
||||
use think\response\Json;
|
||||
use app\model\AdminUser;
|
||||
use app\service\JwtService;
|
||||
|
||||
class LoginController extends BaseController
|
||||
{
|
||||
private function generateToken($userInfo): string
|
||||
{
|
||||
return JwtService::generateToken($userInfo);
|
||||
}
|
||||
|
||||
private function verifyToken($token): ?array
|
||||
{
|
||||
return JwtService::verifyToken($token);
|
||||
}
|
||||
|
||||
/**
|
||||
* 登录接口
|
||||
* @return Json
|
||||
*/
|
||||
public function login(): Json
|
||||
{
|
||||
$data = $this->request->param();
|
||||
|
||||
if (isset($data['email'])) {
|
||||
$data['account'] = $data['email'];
|
||||
} elseif (isset($data['phone'])) {
|
||||
$data['account'] = $data['phone'];
|
||||
}
|
||||
|
||||
try {
|
||||
$this->validate($data, [
|
||||
'account|账号' => 'require|length:3,32',
|
||||
'password|密码' => 'require|length:6,32'
|
||||
]);
|
||||
} catch (ValidateException $e) {
|
||||
$this->logFail('登录管理', '登录', $e->getMessage());
|
||||
return json([
|
||||
'code' => 400,
|
||||
'msg' => $e->getError()
|
||||
]);
|
||||
}
|
||||
|
||||
$user = AdminUser::where('account', $data['account'])
|
||||
->where('status', 1)
|
||||
->where('delete_time', null)
|
||||
->find();
|
||||
|
||||
if (!$user) {
|
||||
return json([
|
||||
'code' => 401,
|
||||
'msg' => '账号不存在或已禁用'
|
||||
]);
|
||||
}
|
||||
|
||||
if (md5($data['password']) !== $user['password']) {
|
||||
return json([
|
||||
'code' => 401,
|
||||
'msg' => '密码错误'
|
||||
]);
|
||||
}
|
||||
|
||||
AdminUser::where('id', $user['id'])->update([
|
||||
'login_count' => $user['login_count'] + 1,
|
||||
'last_login_ip' => $this->request->ip()
|
||||
]);
|
||||
|
||||
$userInfo = [
|
||||
'id' => $user['id'],
|
||||
'account' => $user['account'],
|
||||
'name' => $user['name'],
|
||||
'group_id' => $user['group_id']
|
||||
];
|
||||
|
||||
$token = $this->generateToken($userInfo);
|
||||
|
||||
$this->logSuccess('登录管理', '登录', ['id' => $user['id']], $userInfo);
|
||||
return json([
|
||||
'code' => 200,
|
||||
'msg' => '登录成功',
|
||||
'data' => [
|
||||
'token' => $token,
|
||||
'user' => $userInfo
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 退出登录
|
||||
* @return Json
|
||||
*/
|
||||
public function logout(): Json
|
||||
{
|
||||
$authHeader = $this->request->header('Authorization', '');
|
||||
$userInfo = null;
|
||||
|
||||
if (preg_match('/Bearer\s+(.+)/i', $authHeader, $matches)) {
|
||||
$tokenData = $this->verifyToken($matches[1]);
|
||||
if ($tokenData && isset($tokenData['user'])) {
|
||||
$userInfo = (array)$tokenData['user'];
|
||||
}
|
||||
}
|
||||
|
||||
if ($userInfo && isset($userInfo['id'])) {
|
||||
$this->logSuccess('登录管理', '退出登录', ['result' => 'success'], $userInfo);
|
||||
} else {
|
||||
\app\model\OperationLog::create([
|
||||
'user_id' => 0,
|
||||
'user_account' => '',
|
||||
'user_name' => '未知用户',
|
||||
'module' => '登录管理',
|
||||
'action' => '退出登录',
|
||||
'method' => 'POST',
|
||||
'url' => $this->request->url(true),
|
||||
'ip' => $this->request->ip(),
|
||||
'user_agent' => $this->request->header('user-agent', ''),
|
||||
'request_data' => null,
|
||||
'response_data' => json_encode(['result' => 'success'], JSON_UNESCAPED_UNICODE),
|
||||
'status' => 1,
|
||||
'error_message' => '',
|
||||
'execution_time' => 0.0,
|
||||
]);
|
||||
}
|
||||
|
||||
return json([
|
||||
'code' => 200,
|
||||
'msg' => '退出成功'
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前登录用户信息
|
||||
* @return Json
|
||||
*/
|
||||
public function userInfo(): Json
|
||||
{
|
||||
$authHeader = $this->request->header('Authorization', '');
|
||||
|
||||
if (!preg_match('/Bearer\s+(.+)/i', $authHeader, $matches)) {
|
||||
return json([
|
||||
'code' => 401,
|
||||
'msg' => '未登录'
|
||||
]);
|
||||
}
|
||||
|
||||
$tokenData = $this->verifyToken($matches[1]);
|
||||
|
||||
if (!$tokenData || !isset($tokenData['user'])) {
|
||||
return json([
|
||||
'code' => 401,
|
||||
'msg' => 'Token无效'
|
||||
]);
|
||||
}
|
||||
|
||||
$user = (array)$tokenData['user'];
|
||||
$user_id = $user['id'];
|
||||
|
||||
$userData = AdminUser::where('id', $user_id)
|
||||
->where('delete_time', null)
|
||||
->field('id, account, name, phone, qq, sex, group_id, status, create_time, update_time')
|
||||
->find();
|
||||
|
||||
if (!$userData) {
|
||||
return json([
|
||||
'code' => 404,
|
||||
'msg' => '用户不存在'
|
||||
]);
|
||||
}
|
||||
|
||||
return json([
|
||||
'code' => 200,
|
||||
'msg' => '获取成功',
|
||||
'data' => $userData->toArray()
|
||||
]);
|
||||
}
|
||||
|
||||
public function getAdminUserFromToken(): array
|
||||
{
|
||||
return JwtService::getUserFromHeader($this->request->header('Authorization', ''));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,418 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\admin\controller;
|
||||
|
||||
use app\admin\BaseController;
|
||||
use think\exception\ValidateException;
|
||||
use think\facade\Db;
|
||||
use think\facade\Session;
|
||||
use think\response\Json;
|
||||
use think\db\exception\DbException;
|
||||
use app\model\SystemMenu;
|
||||
use app\model\AdminUser;
|
||||
use app\model\AdminUserGroup;
|
||||
|
||||
class MenuController extends BaseController
|
||||
{
|
||||
/**
|
||||
* 获取用户菜单接口
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function getMenus(int $id)
|
||||
{
|
||||
try {
|
||||
$user = AdminUser::where('id', $id)
|
||||
->where('id', $id)
|
||||
->find();
|
||||
|
||||
if (!$user) {
|
||||
return json([
|
||||
'code' => 404,
|
||||
'msg' => '用户不存在',
|
||||
'data' => null
|
||||
]);
|
||||
}
|
||||
|
||||
// 获取用户组权限信息
|
||||
$userGroup = AdminUserGroup::where('id', $user['group_id'])
|
||||
->find();
|
||||
|
||||
// var_dump($userGroup);
|
||||
|
||||
if (!$userGroup) {
|
||||
return json([
|
||||
'code' => 404,
|
||||
'msg' => '用户组不存在',
|
||||
'data' => null
|
||||
]);
|
||||
}
|
||||
|
||||
// 解析权限数组
|
||||
$menuIds = [];
|
||||
if (!empty($userGroup['rights'])) {
|
||||
$menuIds = is_array($userGroup['rights']) ? $userGroup['rights'] : json_decode($userGroup['rights'], true);
|
||||
}
|
||||
// var_dump($menuIds);
|
||||
|
||||
// 如果权限为空,返回空数组
|
||||
if (empty($menuIds)) {
|
||||
return json([
|
||||
'code' => 200,
|
||||
'msg' => 'success',
|
||||
'data' => []
|
||||
]);
|
||||
}
|
||||
|
||||
// 获取有权限的菜单
|
||||
$menus = SystemMenu::where('delete_time', null)
|
||||
->where('status', 1)
|
||||
->whereIn('id', $menuIds)
|
||||
->field('id,pid,title,path,component_path,icon,sort')
|
||||
->order('sort', 'asc')
|
||||
->select();
|
||||
|
||||
// 将菜单转换为树形结构
|
||||
$treeMenus = $this->buildMenuTree($menus->toArray());
|
||||
|
||||
return json([
|
||||
'code' => 200,
|
||||
'msg' => 'success',
|
||||
'data' => $treeMenus
|
||||
]);
|
||||
} catch (DbException $e) {
|
||||
return json([
|
||||
'code' => 500,
|
||||
'msg' => 'fail:' . $e->getMessage(),
|
||||
'data' => $e->getTraceAsString()
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有菜单接口
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function getAllMenus()
|
||||
{
|
||||
try {
|
||||
// 获取所有未删除的菜单
|
||||
$menus = SystemMenu::where('delete_time', null)
|
||||
->order('sort', 'asc')
|
||||
->select()
|
||||
->toArray(); // 转换为数组
|
||||
|
||||
// 构建树形结构
|
||||
$tree = [];
|
||||
$map = [];
|
||||
|
||||
// 首先创建所有菜单的映射
|
||||
foreach ($menus as $menu) {
|
||||
$menu['children'] = []; // 初始化子菜单数组
|
||||
$map[$menu['id']] = $menu;
|
||||
}
|
||||
|
||||
// 构建树形结构
|
||||
foreach ($menus as $menu) {
|
||||
$pid = $menu['pid'];
|
||||
if ($pid == 0) {
|
||||
// 顶级菜单
|
||||
$tree[] = &$map[$menu['id']];
|
||||
} else {
|
||||
// 子菜单
|
||||
if (isset($map[$pid])) {
|
||||
$map[$pid]['children'][] = &$map[$menu['id']];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 递归函数:对所有层级的菜单按 sort 降序排序
|
||||
$sortChildren = function (&$items) use (&$sortChildren) {
|
||||
// 再递归处理每个节点的子菜单
|
||||
foreach ($items as &$item) {
|
||||
if (!empty($item['children'])) {
|
||||
$sortChildren($item['children']);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 对整个树形结构进行排序(包括顶级菜单)
|
||||
$sortChildren($tree);
|
||||
|
||||
return json([
|
||||
'code' => 200,
|
||||
'msg' => 'success',
|
||||
'data' => $tree
|
||||
]);
|
||||
} catch (DbException $e) {
|
||||
return json([
|
||||
'code' => 500,
|
||||
'msg' => 'fail:' . $e->getMessage(),
|
||||
'data' => $e->getTraceAsString()
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新菜单状态
|
||||
* @param int $id 菜单ID
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function updateMenuStatus(int $id)
|
||||
{
|
||||
try {
|
||||
// 获取请求参数
|
||||
$data = $this->request->param();
|
||||
|
||||
// 验证参数
|
||||
$this->validate($data, [
|
||||
'status|菜单状态' => 'require|in:0,1'
|
||||
]);
|
||||
|
||||
// 更新菜单状态
|
||||
$result = SystemMenu::where('id', $id)
|
||||
->where('delete_time', null)
|
||||
->update([
|
||||
'status' => $data['status'],
|
||||
'update_time' => time()
|
||||
]);
|
||||
|
||||
if ($result === false) {
|
||||
return json([
|
||||
'code' => 500,
|
||||
'msg' => 'fail'
|
||||
]);
|
||||
}
|
||||
|
||||
// 记录操作日志
|
||||
$this->logSuccess('菜单管理', '更新菜单状态', ['id' => $id]);
|
||||
return json([
|
||||
'code' => 200,
|
||||
'msg' => 'success'
|
||||
]);
|
||||
} catch (ValidateException $e) {
|
||||
// 记录失败日志
|
||||
$this->logFail('菜单管理', '更新菜单状态', $e->getMessage());
|
||||
return json([
|
||||
'code' => 400,
|
||||
'msg' => $e->getError()
|
||||
]);
|
||||
} catch (DbException $e) {
|
||||
// 记录失败日志
|
||||
$this->logFail('菜单管理', '更新菜单状态', $e->getMessage());
|
||||
return json([
|
||||
'code' => 500,
|
||||
'msg' => 'fail:' . $e->getMessage()
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建新菜单
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function createMenu()
|
||||
{
|
||||
try {
|
||||
// 获取请求参数
|
||||
$data = $this->request->param();
|
||||
|
||||
// 验证参数
|
||||
$this->validate($data, [
|
||||
'title|菜单名称' => 'require|max:50',
|
||||
'type|菜单类型' => 'require|in:1,2,3',
|
||||
'status|菜单状态' => 'require|in:0,1',
|
||||
'sort|排序号' => 'integer',
|
||||
'path|路由路径' => 'max:200',
|
||||
'icon|菜单图标' => 'max:100',
|
||||
'permission|权限标识' => 'max:100',
|
||||
'remark|备注' => 'max:500'
|
||||
]);
|
||||
|
||||
// 准备菜单数据
|
||||
$menuData = [
|
||||
'pid' => $data['pid'] ?? 0,
|
||||
'title' => $data['title'],
|
||||
'type' => $data['type'],
|
||||
'status' => $data['status'] ?? 1,
|
||||
'sort' => $data['sort'] ?? 0,
|
||||
'path' => $data['path'] ?? '',
|
||||
'component_path' => $data['component_path'] ?? '',
|
||||
'icon' => $data['icon'] ?? '',
|
||||
'permission' => $data['permission'] ?? '',
|
||||
'remark' => $data['remark'] ?? '',
|
||||
'creater' => Session::get('name') ?? 'system',
|
||||
'create_time' => date('Y-m-d H:i:s')
|
||||
];
|
||||
|
||||
// 创建菜单
|
||||
$result = SystemMenu::insertGetId($menuData);
|
||||
|
||||
if ($result) {
|
||||
// 记录操作日志
|
||||
$this->logSuccess('菜单管理', '创建菜单', ['id' => $result]);
|
||||
return json([
|
||||
'code' => 200,
|
||||
'msg' => '创建成功',
|
||||
'data' => ['id' => $result]
|
||||
]);
|
||||
} else {
|
||||
throw new \Exception('创建菜单失败');
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
// 记录失败日志
|
||||
$this->logFail('菜单管理', '创建菜单', $e->getMessage());
|
||||
return json([
|
||||
'code' => 500,
|
||||
'msg' => '创建失败:' . $e->getMessage(),
|
||||
'data' => []
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新菜单信息
|
||||
* @param int $id 菜单ID
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function updateMenu(int $id)
|
||||
{
|
||||
try {
|
||||
// 获取请求参数
|
||||
$data = $this->request->param();
|
||||
|
||||
// 验证参数
|
||||
$this->validate($data, [
|
||||
'title|菜单名称' => 'require|max:50',
|
||||
'pid|上级菜单ID' => 'integer',
|
||||
'type|菜单类型' => 'require|in:1,2,3',
|
||||
'status|菜单状态' => 'require|in:0,1',
|
||||
'sort|排序号' => 'integer',
|
||||
'path|路由路径' => 'max:200',
|
||||
'icon|菜单图标' => 'max:100',
|
||||
'permission|权限标识' => 'max:100',
|
||||
'remark|备注' => 'max:500'
|
||||
]);
|
||||
|
||||
// 准备更新数据
|
||||
$updateData = [
|
||||
'title' => $data['title'],
|
||||
'pid' => $data['pid'] ?? null,
|
||||
'type' => $data['type'],
|
||||
'path' => $data['path'] ?? null,
|
||||
'component_path' => $data['component_path'] ?? null,
|
||||
'icon' => $data['icon'] ?? null,
|
||||
'sort' => $data['sort'] ?? 0,
|
||||
'status' => $data['status'],
|
||||
'permission' => $data['permission'] ?? null,
|
||||
'remark' => $data['remark'] ?? null,
|
||||
'update_time' => date('Y-m-d H:i:s')
|
||||
];
|
||||
|
||||
// 执行更新
|
||||
SystemMenu::where('id', $id)->update($updateData);
|
||||
|
||||
// 获取更新后的菜单信息
|
||||
$menu = SystemMenu::where('id', $id)->find();
|
||||
|
||||
// 记录操作日志
|
||||
$this->logSuccess('菜单管理', '更新菜单信息', ['id' => $id]);
|
||||
return json([
|
||||
'code' => 200,
|
||||
'msg' => 'success',
|
||||
'data' => $menu ? $menu->toArray() : []
|
||||
]);
|
||||
} catch (ValidateException $e) {
|
||||
// 记录失败日志
|
||||
$this->logFail('菜单管理', '更新菜单信息', $e->getMessage());
|
||||
return json([
|
||||
'code' => 400,
|
||||
'msg' => $e->getError()
|
||||
]);
|
||||
} catch (DbException $e) {
|
||||
// 记录失败日志
|
||||
$this->logFail('菜单管理', '更新菜单信息', $e->getMessage());
|
||||
return json([
|
||||
'code' => 500,
|
||||
'msg' => 'fail:' . $e->getMessage()
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除菜单
|
||||
* @param int $id 菜单ID
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function deleteMenu(int $id)
|
||||
{
|
||||
try {
|
||||
// 检查是否有子菜单
|
||||
$hasChildren = SystemMenu::where('pid', $id)
|
||||
->where('delete_time', null)
|
||||
->count() > 0;
|
||||
|
||||
if ($hasChildren) {
|
||||
return json([
|
||||
'code' => 400,
|
||||
'msg' => '该菜单下有子菜单,无法删除'
|
||||
]);
|
||||
}
|
||||
|
||||
// 逻辑删除菜单
|
||||
$result = SystemMenu::where('id', $id)
|
||||
->where('delete_time', null)
|
||||
->update([
|
||||
'delete_time' => time(),
|
||||
'update_time' => time()
|
||||
]);
|
||||
|
||||
if ($result === false) {
|
||||
return json([
|
||||
'code' => 500,
|
||||
'msg' => 'fail'
|
||||
]);
|
||||
}
|
||||
|
||||
// 记录操作日志
|
||||
$this->logSuccess('菜单管理', '删除菜单', ['id' => $id]);
|
||||
return json([
|
||||
'code' => 200,
|
||||
'msg' => 'success'
|
||||
]);
|
||||
} catch (DbException $e) {
|
||||
// 记录失败日志
|
||||
$this->logFail('菜单管理', '删除菜单', $e->getMessage());
|
||||
return json([
|
||||
'code' => 500,
|
||||
'msg' => 'fail:' . $e->getMessage()
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建菜单树形结构
|
||||
* @param array $menus 菜单列表
|
||||
* @param int $pid 父菜单ID
|
||||
* @return array
|
||||
*/
|
||||
private function buildMenuTree(array $menus, int $pid = 0): array
|
||||
{
|
||||
$tree = [];
|
||||
|
||||
foreach ($menus as $menu) {
|
||||
// 将null的pid视为0处理
|
||||
$menuPid = is_null($menu['pid']) ? 0 : $menu['pid'];
|
||||
if ($menuPid == $pid) {
|
||||
$children = $this->buildMenuTree($menus, $menu['id']);
|
||||
if (!empty($children)) {
|
||||
$menu['children'] = $children;
|
||||
}
|
||||
$tree[] = $menu;
|
||||
}
|
||||
}
|
||||
|
||||
return $tree;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,268 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\admin\controller;
|
||||
|
||||
use app\admin\BaseController;
|
||||
use think\exception\ValidateException;
|
||||
use think\facade\Db;
|
||||
use think\facade\Session;
|
||||
use think\response\Json;
|
||||
use think\db\exception\DbException;
|
||||
use app\model\OnePage;
|
||||
|
||||
class OnePageController extends BaseController
|
||||
{
|
||||
/**
|
||||
* 获取所有单页信息
|
||||
* @return Json
|
||||
*/
|
||||
public function getAllOnePages()
|
||||
{
|
||||
try {
|
||||
$onePages = OnePage::where('delete_time', null)
|
||||
->order('sort', 'asc')
|
||||
->field('id, title, content, path, sort, status, create_time, update_time')
|
||||
->select()
|
||||
->toArray();
|
||||
return json([
|
||||
'code' => 200,
|
||||
'msg' => '获取成功',
|
||||
'data' => $onePages
|
||||
]);
|
||||
} catch (DbException $e) {
|
||||
return json([
|
||||
'code' => 500,
|
||||
'msg' => '获取失败:' . $e->getMessage(),
|
||||
'data' => []
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建单页
|
||||
* @return Json
|
||||
*/
|
||||
public function createOnePage()
|
||||
{
|
||||
try {
|
||||
// 获取请求参数
|
||||
$data = $this->request->param();
|
||||
|
||||
// 验证参数
|
||||
$this->validate($data, [
|
||||
'title|标题' => 'require|max:255',
|
||||
'content|内容' => 'require',
|
||||
'path|路由' => 'require|max:200',
|
||||
'sort|排序号' => 'integer',
|
||||
'status|状态' => 'integer|in:0,1',
|
||||
]);
|
||||
|
||||
// 验证路由格式(必须以 / 开头)
|
||||
if (!preg_match('/^\/[a-zA-Z0-9\/_-]*$/', $data['path'])) {
|
||||
return json([
|
||||
'code' => 400,
|
||||
'msg' => '路由必须以 / 开头,只能包含字母、数字、下划线、横线和斜线'
|
||||
]);
|
||||
}
|
||||
|
||||
// 检查路由是否已存在
|
||||
$exists = OnePage::where('path', $data['path'])
|
||||
->where('delete_time', null)
|
||||
->find();
|
||||
if ($exists) {
|
||||
return json([
|
||||
'code' => 400,
|
||||
'msg' => '路由已存在,请使用其他路由'
|
||||
]);
|
||||
}
|
||||
|
||||
// 准备单页数据
|
||||
$onePageData = [
|
||||
'title' => $data['title'],
|
||||
'content' => $data['content'],
|
||||
'path' => $data['path'],
|
||||
'sort' => $data['sort'] ?? 0,
|
||||
'status' => $data['status'] ?? 1,
|
||||
'create_time' => date('Y-m-d H:i:s'),
|
||||
];
|
||||
|
||||
// 创建单页
|
||||
$result = OnePage::insertGetId($onePageData);
|
||||
|
||||
if ($result === false) {
|
||||
return json([
|
||||
'code' => 500,
|
||||
'msg' => '创建失败'
|
||||
]);
|
||||
}
|
||||
|
||||
// 记录操作日志
|
||||
$this->logSuccess('单页管理', '创建单页', ['id' => $result]);
|
||||
return json([
|
||||
'code' => 200,
|
||||
'msg' => '创建成功',
|
||||
'data' => ['id' => $result]
|
||||
]);
|
||||
} catch (ValidateException $e) {
|
||||
// 记录失败日志
|
||||
$this->logFail('单页管理', '创建单页', $e->getMessage());
|
||||
return json([
|
||||
'code' => 400,
|
||||
'msg' => $e->getError()
|
||||
]);
|
||||
} catch (DbException $e) {
|
||||
// 记录失败日志
|
||||
$this->logFail('单页管理', '创建单页', $e->getMessage());
|
||||
return json([
|
||||
'code' => 500,
|
||||
'msg' => '创建失败:' . $e->getMessage()
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑单页
|
||||
* @param int $id 单页ID
|
||||
* @return Json
|
||||
*/
|
||||
public function editOnePage(int $id)
|
||||
{
|
||||
try {
|
||||
// 获取请求参数
|
||||
$data = $this->request->param();
|
||||
|
||||
// 验证参数
|
||||
$this->validate($data, [
|
||||
'title|标题' => 'require|max:255',
|
||||
'content|内容' => 'require',
|
||||
'path|路由' => 'require|max:200',
|
||||
'sort|排序号' => 'integer',
|
||||
'status|状态' => 'integer|in:0,1',
|
||||
]);
|
||||
|
||||
// 验证路由格式(必须以 / 开头)
|
||||
if (!preg_match('/^\/[a-zA-Z0-9\/_-]*$/', $data['path'])) {
|
||||
return json([
|
||||
'code' => 400,
|
||||
'msg' => '路由必须以 / 开头,只能包含字母、数字、下划线、横线和斜线'
|
||||
]);
|
||||
}
|
||||
|
||||
// 检查单页是否存在
|
||||
$onePage = OnePage::where('id', $id)
|
||||
->where('delete_time', null)
|
||||
->find();
|
||||
|
||||
if (!$onePage) {
|
||||
return json([
|
||||
'code' => 404,
|
||||
'msg' => '单页不存在'
|
||||
]);
|
||||
}
|
||||
|
||||
// 检查路由是否被其他单页使用
|
||||
$exists = OnePage::where('path', $data['path'])
|
||||
->where('id', '<>', $id)
|
||||
->where('delete_time', null)
|
||||
->find();
|
||||
if ($exists) {
|
||||
return json([
|
||||
'code' => 400,
|
||||
'msg' => '路由已被其他单页使用,请使用其他路由'
|
||||
]);
|
||||
}
|
||||
|
||||
// 准备更新数据
|
||||
$updateData = [
|
||||
'title' => $data['title'],
|
||||
'content' => $data['content'],
|
||||
'path' => $data['path'],
|
||||
'sort' => $data['sort'] ?? 0,
|
||||
'status' => $data['status'] ?? 1,
|
||||
'update_time' => date('Y-m-d H:i:s'),
|
||||
];
|
||||
|
||||
// 执行更新
|
||||
OnePage::where('id', $id)->update($updateData);
|
||||
|
||||
// 获取更新后的单页信息
|
||||
$updatedOnePage = OnePage::where('id', $id)->find();
|
||||
|
||||
// 记录操作日志
|
||||
$this->logSuccess('单页管理', '更新单页', ['id' => $id]);
|
||||
return json([
|
||||
'code' => 200,
|
||||
'msg' => '更新成功',
|
||||
'data' => $updatedOnePage ? $updatedOnePage->toArray() : []
|
||||
]);
|
||||
} catch (ValidateException $e) {
|
||||
// 记录失败日志
|
||||
$this->logFail('单页管理', '更新单页', $e->getMessage());
|
||||
return json([
|
||||
'code' => 400,
|
||||
'msg' => $e->getError()
|
||||
]);
|
||||
} catch (DbException $e) {
|
||||
// 记录失败日志
|
||||
$this->logFail('单页管理', '更新单页', $e->getMessage());
|
||||
return json([
|
||||
'code' => 500,
|
||||
'msg' => '更新失败:' . $e->getMessage()
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除单页
|
||||
* @param int $id 单页ID
|
||||
* @return Json
|
||||
*/
|
||||
public function deleteOnePage(int $id)
|
||||
{
|
||||
try {
|
||||
// 检查单页是否存在
|
||||
$onePage = OnePage::where('id', $id)
|
||||
->where('delete_time', null)
|
||||
->find();
|
||||
|
||||
if (!$onePage) {
|
||||
return json([
|
||||
'code' => 404,
|
||||
'msg' => '单页不存在'
|
||||
]);
|
||||
}
|
||||
|
||||
// 逻辑删除单页
|
||||
$result = OnePage::where('id', $id)
|
||||
->where('delete_time', null)
|
||||
->update([
|
||||
'delete_time' => date('Y-m-d H:i:s'),
|
||||
'update_time' => date('Y-m-d H:i:s')
|
||||
]);
|
||||
|
||||
if ($result === false) {
|
||||
return json([
|
||||
'code' => 500,
|
||||
'msg' => '删除失败'
|
||||
]);
|
||||
}
|
||||
|
||||
// 记录操作日志
|
||||
$this->logSuccess('单页管理', '删除单页', ['id' => $id]);
|
||||
return json([
|
||||
'code' => 200,
|
||||
'msg' => '删除成功'
|
||||
]);
|
||||
} catch (DbException $e) {
|
||||
// 记录失败日志
|
||||
$this->logFail('单页管理', '删除单页', $e->getMessage());
|
||||
return json([
|
||||
'code' => 500,
|
||||
'msg' => '删除失败:' . $e->getMessage()
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,256 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\admin\controller\OperationLog;
|
||||
|
||||
use app\admin\BaseController;
|
||||
use think\facade\Request;
|
||||
use think\response\Json;
|
||||
use app\model\OperationLog;
|
||||
use app\model\AdminUser;
|
||||
|
||||
class OperationLogController extends BaseController
|
||||
{
|
||||
/**
|
||||
* 获取操作日志列表
|
||||
* @return Json
|
||||
*/
|
||||
public function getOperationLogs()
|
||||
{
|
||||
try {
|
||||
$page = Request::param('page/d', 1);
|
||||
$pageSize = Request::param('pageSize/d', 20);
|
||||
$keyword = Request::param('keyword/s', '');
|
||||
$module = Request::param('module/s', '');
|
||||
$action = Request::param('action/s', '');
|
||||
$status = Request::param('status/s', '');
|
||||
$startTime = Request::param('startTime/s', '');
|
||||
$endTime = Request::param('endTime/s', '');
|
||||
|
||||
$query = OperationLog::where('delete_time', null);
|
||||
|
||||
// 关键词搜索(用户姓名、URL)
|
||||
if ($keyword) {
|
||||
$query->where(function ($q) use ($keyword) {
|
||||
$q
|
||||
->whereOr('user_name', 'like', "%{$keyword}%")
|
||||
->whereOr('url', 'like', "%{$keyword}%");
|
||||
});
|
||||
}
|
||||
|
||||
// 模块筛选
|
||||
if ($module) {
|
||||
$query->where('module', $module);
|
||||
}
|
||||
|
||||
// 操作动作筛选
|
||||
if ($action) {
|
||||
$query->where('action', $action);
|
||||
}
|
||||
|
||||
// 状态筛选
|
||||
if ($status !== '') {
|
||||
$query->where('status', $status);
|
||||
}
|
||||
|
||||
// 时间范围筛选
|
||||
if ($startTime) {
|
||||
$query->where('create_time', '>=', $startTime);
|
||||
}
|
||||
if ($endTime) {
|
||||
$query->where('create_time', '<=', $endTime);
|
||||
}
|
||||
|
||||
// 获取总数
|
||||
$total = $query->count();
|
||||
|
||||
// 分页查询
|
||||
$list = $query->order('id', 'desc')
|
||||
->page($page, $pageSize)
|
||||
->select()
|
||||
->toArray();
|
||||
|
||||
// 获取所有唯一的 user_id
|
||||
$userIds = array_unique(array_column($list, 'user_id'));
|
||||
$userIds = array_filter($userIds); // 过滤掉0和null
|
||||
|
||||
// 批量查询用户信息
|
||||
$users = [];
|
||||
if (!empty($userIds)) {
|
||||
$userList = AdminUser::whereIn('id', $userIds)
|
||||
->where('delete_time', null)
|
||||
->field('id, name')
|
||||
->select()
|
||||
->toArray();
|
||||
|
||||
// 转换为以 id 为键的数组,方便查找
|
||||
foreach ($userList as $user) {
|
||||
$users[$user['id']] = [
|
||||
'name' => $user['name']
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
// 将用户信息合并到日志列表中
|
||||
foreach ($list as &$item) {
|
||||
if (isset($users[$item['user_id']])) {
|
||||
$item['user_name'] = $users[$item['user_id']]['name'];
|
||||
} else {
|
||||
$item['user_name'] = $item['user_name'] ?? '';
|
||||
}
|
||||
}
|
||||
unset($item); // 释放引用
|
||||
|
||||
return json([
|
||||
'code' => 200,
|
||||
'msg' => '获取成功',
|
||||
'data' => [
|
||||
'list' => $list,
|
||||
'total' => $total,
|
||||
'page' => $page,
|
||||
'pageSize' => $pageSize
|
||||
]
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
return json([
|
||||
'code' => 500,
|
||||
'msg' => '获取操作日志失败: ' . $e->getMessage()
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取操作日志详情
|
||||
* @param int $id
|
||||
* @return Json
|
||||
*/
|
||||
public function getOperationLogDetail(int $id)
|
||||
{
|
||||
try {
|
||||
$log = OperationLog::where('id', $id)
|
||||
->where('delete_time', null)
|
||||
->find();
|
||||
|
||||
if (!$log) {
|
||||
return json([
|
||||
'code' => 404,
|
||||
'msg' => '操作日志不存在'
|
||||
]);
|
||||
}
|
||||
|
||||
$logData = $log->toArray();
|
||||
$this->logSuccess('操作日志', '查看操作日志详情', $logData);
|
||||
return json([
|
||||
'code' => 200,
|
||||
'msg' => '获取成功',
|
||||
'data' => $logData
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
$this->logFail('操作日志', '查看操作日志详情', $e->getMessage());
|
||||
return json([
|
||||
'code' => 500,
|
||||
'msg' => '获取操作日志详情失败: ' . $e->getMessage()
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除操作日志
|
||||
* @param int $id
|
||||
* @return Json
|
||||
*/
|
||||
public function deleteOperationLog(int $id)
|
||||
{
|
||||
try {
|
||||
$log = OperationLog::where('id', $id)
|
||||
->where('delete_time', null)
|
||||
->find();
|
||||
|
||||
if (!$log) {
|
||||
return json([
|
||||
'code' => 404,
|
||||
'msg' => '操作日志不存在'
|
||||
]);
|
||||
}
|
||||
|
||||
// 软删除
|
||||
$log->delete();
|
||||
|
||||
return json([
|
||||
'code' => 200,
|
||||
'msg' => '删除成功'
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
return json([
|
||||
'code' => 500,
|
||||
'msg' => '删除失败: ' . $e->getMessage()
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量删除操作日志
|
||||
* @return Json
|
||||
*/
|
||||
public function batchDeleteOperationLogs()
|
||||
{
|
||||
try {
|
||||
$ids = Request::param('ids/a', []);
|
||||
|
||||
if (empty($ids)) {
|
||||
return json([
|
||||
'code' => 400,
|
||||
'msg' => '请选择要删除的操作日志'
|
||||
]);
|
||||
}
|
||||
|
||||
OperationLog::whereIn('id', $ids)
|
||||
->where('delete_time', null)
|
||||
->delete();
|
||||
|
||||
return json([
|
||||
'code' => 200,
|
||||
'msg' => '批量删除成功'
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
return json([
|
||||
'code' => 500,
|
||||
'msg' => '批量删除失败: ' . $e->getMessage()
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取操作统计信息(模块、动作等)
|
||||
* @return Json
|
||||
*/
|
||||
public function getOperationStatistics()
|
||||
{
|
||||
try {
|
||||
// 获取模块列表
|
||||
$modules = OperationLog::where('delete_time', null)
|
||||
->group('module')
|
||||
->column('module');
|
||||
|
||||
// 获取动作列表
|
||||
$actions = OperationLog::where('delete_time', null)
|
||||
->group('action')
|
||||
->column('action');
|
||||
|
||||
return json([
|
||||
'code' => 200,
|
||||
'msg' => '获取成功',
|
||||
'data' => [
|
||||
'modules' => $modules,
|
||||
'actions' => $actions
|
||||
]
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
return json([
|
||||
'code' => 500,
|
||||
'msg' => '获取统计信息失败: ' . $e->getMessage()
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
<?php
|
||||
|
||||
namespace app\admin\controller\OperationLog;
|
||||
|
||||
use app\model\OperationLog;
|
||||
use think\facade\Request;
|
||||
use think\facade\Session;
|
||||
|
||||
/**
|
||||
* 操作日志记录助手类
|
||||
*/
|
||||
class OperationLogHelper
|
||||
{
|
||||
/**
|
||||
* 记录操作日志
|
||||
* @param string $module 操作模块
|
||||
* @param string $action 操作动作
|
||||
* @param array $requestData 请求数据
|
||||
* @param array $responseData 响应数据
|
||||
* @param int $status 操作状态:1-成功,0-失败
|
||||
* @param string $errorMessage 错误信息
|
||||
* @param float $executionTime 执行时间(秒)
|
||||
* @return bool
|
||||
*/
|
||||
public static function log(
|
||||
string $module,
|
||||
string $action,
|
||||
array $requestData = [],
|
||||
array $responseData = [],
|
||||
int $status = 1,
|
||||
string $errorMessage = '',
|
||||
float $executionTime = 0.0
|
||||
): bool {
|
||||
try {
|
||||
// 获取用户信息
|
||||
$userInfo = Session::get('user');
|
||||
$userId = $userInfo['id'] ?? 0;
|
||||
$userAccount = $userInfo['account'] ?? '';
|
||||
$userName = $userInfo['name'] ?? '';
|
||||
|
||||
// 获取请求信息
|
||||
$method = Request::method();
|
||||
$url = Request::url(true);
|
||||
$ip = Request::ip();
|
||||
$userAgent = Request::header('user-agent', '');
|
||||
|
||||
// 过滤敏感信息(如密码)
|
||||
$filteredRequestData = self::filterSensitiveData($requestData);
|
||||
|
||||
// 记录日志
|
||||
OperationLog::create([
|
||||
'user_id' => $userId,
|
||||
'user_account' => $userAccount,
|
||||
'user_name' => $userName,
|
||||
'module' => $module,
|
||||
'action' => $action,
|
||||
'method' => $method,
|
||||
'url' => $url,
|
||||
'ip' => $ip,
|
||||
'user_agent' => $userAgent,
|
||||
'request_data' => !empty($filteredRequestData) ? json_encode($filteredRequestData, JSON_UNESCAPED_UNICODE) : null,
|
||||
'response_data' => !empty($responseData) ? json_encode($responseData, JSON_UNESCAPED_UNICODE) : null,
|
||||
'status' => $status,
|
||||
'error_message' => $errorMessage,
|
||||
'execution_time' => $executionTime,
|
||||
]);
|
||||
|
||||
return true;
|
||||
} catch (\Exception $e) {
|
||||
// 记录日志失败不应该影响主流程,只记录错误
|
||||
error_log('操作日志记录失败: ' . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 过滤敏感数据
|
||||
* @param array $data
|
||||
* @return array
|
||||
*/
|
||||
private static function filterSensitiveData(array $data): array
|
||||
{
|
||||
$sensitiveKeys = ['password', 'pwd', 'token', 'api_key', 'secret'];
|
||||
|
||||
foreach ($data as $key => $value) {
|
||||
if (in_array(strtolower($key), $sensitiveKeys)) {
|
||||
$data[$key] = '***';
|
||||
} elseif (is_array($value)) {
|
||||
$data[$key] = self::filterSensitiveData($value);
|
||||
}
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
<?php
|
||||
|
||||
namespace app\admin\controller\OperationLog;
|
||||
|
||||
use think\facade\Request;
|
||||
use app\model\OperationLog;
|
||||
use app\service\JwtService;
|
||||
|
||||
/**
|
||||
* 操作日志记录器(JWT版)
|
||||
*/
|
||||
class OperationLogger
|
||||
{
|
||||
private static function getUserFromToken(): array
|
||||
{
|
||||
return JwtService::getUserFromHeader(Request::header('Authorization', ''));
|
||||
}
|
||||
|
||||
public static function record(
|
||||
string $module,
|
||||
string $action,
|
||||
array $requestData = [],
|
||||
array $responseData = [],
|
||||
int $status = 1,
|
||||
string $errorMessage = '',
|
||||
array $userInfo = []
|
||||
): bool {
|
||||
try {
|
||||
if (empty($userInfo)) {
|
||||
$userInfo = self::getUserFromToken();
|
||||
}
|
||||
|
||||
$userId = $userInfo['id'] ?? 0;
|
||||
$userAccount = $userInfo['account'] ?? '';
|
||||
$userName = $userInfo['name'] ?? '';
|
||||
|
||||
if (empty($requestData)) {
|
||||
$requestData = Request::param();
|
||||
}
|
||||
|
||||
$method = Request::method();
|
||||
$url = Request::url(true);
|
||||
$ip = Request::ip();
|
||||
$userAgent = Request::header('user-agent', '');
|
||||
|
||||
$filteredRequestData = self::filterSensitiveData($requestData);
|
||||
|
||||
OperationLog::create([
|
||||
'user_id' => $userId,
|
||||
'user_account' => $userAccount,
|
||||
'user_name' => $userName,
|
||||
'module' => $module,
|
||||
'action' => $action,
|
||||
'method' => $method,
|
||||
'url' => $url,
|
||||
'ip' => $ip,
|
||||
'user_agent' => $userAgent,
|
||||
'request_data' => !empty($filteredRequestData) ? json_encode($filteredRequestData, JSON_UNESCAPED_UNICODE) : null,
|
||||
'response_data' => !empty($responseData) ? json_encode($responseData, JSON_UNESCAPED_UNICODE) : null,
|
||||
'status' => $status,
|
||||
'error_message' => $errorMessage,
|
||||
'execution_time' => 0.0,
|
||||
]);
|
||||
|
||||
return true;
|
||||
} catch (\Exception $e) {
|
||||
error_log('操作日志记录失败: ' . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static function success(string $module, string $action, array $responseData = [], array $userInfo = []): bool
|
||||
{
|
||||
return self::record($module, $action, [], $responseData, 1, '', $userInfo);
|
||||
}
|
||||
|
||||
public static function fail(string $module, string $action, string $errorMessage, array $userInfo = []): bool
|
||||
{
|
||||
return self::record($module, $action, [], [], 0, $errorMessage, $userInfo);
|
||||
}
|
||||
|
||||
private static function filterSensitiveData(array $data): array
|
||||
{
|
||||
$sensitiveKeys = ['password', 'pwd', 'token', 'api_key', 'secret'];
|
||||
|
||||
foreach ($data as $key => $value) {
|
||||
if (in_array(strtolower($key), $sensitiveKeys)) {
|
||||
$data[$key] = '***';
|
||||
} elseif (is_array($value)) {
|
||||
$data[$key] = self::filterSensitiveData($value);
|
||||
}
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,249 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\admin\controller;
|
||||
|
||||
use app\admin\BaseController;
|
||||
use think\exception\ValidateException;
|
||||
use think\facade\Db;
|
||||
use think\facade\Session;
|
||||
use think\response\Json;
|
||||
|
||||
use app\model\AdminUser;
|
||||
use app\model\AdminUserGroup;
|
||||
|
||||
class RoleController extends BaseController
|
||||
{
|
||||
/**
|
||||
* 获取所有角色列表
|
||||
* @return Json
|
||||
*/
|
||||
public function getAllRoles()
|
||||
{
|
||||
$roles = AdminUserGroup::where('delete_time', null)
|
||||
->order('id', 'asc')
|
||||
->select();
|
||||
return json([
|
||||
'code' => 200,
|
||||
'msg' => '获取成功',
|
||||
'data' => $roles->toArray()
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取角色详情
|
||||
* @param int $id
|
||||
* @return Json
|
||||
*/
|
||||
public function getRoleById(int $id)
|
||||
{
|
||||
$role = AdminUserGroup::where('id', $id)
|
||||
->where('delete_time', null)
|
||||
->find();
|
||||
if (!$role) {
|
||||
return json([
|
||||
'code' => 404,
|
||||
'msg' => '角色不存在',
|
||||
'data' => null
|
||||
]);
|
||||
}
|
||||
return json([
|
||||
'code' => 200,
|
||||
'msg' => '获取成功',
|
||||
'data' => $role->toArray()
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建角色
|
||||
* @return Json
|
||||
*/
|
||||
public function createRole()
|
||||
{
|
||||
try {
|
||||
$data = $this->request->param();
|
||||
|
||||
// 验证参数
|
||||
$this->validate($data, [
|
||||
'name|角色名称' => 'require|max:50',
|
||||
'status|状态' => 'in:0,1',
|
||||
'rights|权限' => 'array'
|
||||
]);
|
||||
|
||||
// 检查角色名称是否已存在
|
||||
$exists = AdminUserGroup::where('name', $data['name'])
|
||||
->where('delete_time', null)
|
||||
->find();
|
||||
if ($exists) {
|
||||
return json([
|
||||
'code' => 400,
|
||||
'msg' => '角色名称已存在'
|
||||
]);
|
||||
}
|
||||
|
||||
// 准备数据
|
||||
$roleData = [
|
||||
'name' => $data['name'],
|
||||
'status' => $data['status'] ?? 1,
|
||||
'rights' => !empty($data['rights']) ? json_encode($data['rights']) : null,
|
||||
'create_time' => date('Y-m-d H:i:s'),
|
||||
'update_time' => date('Y-m-d H:i:s')
|
||||
];
|
||||
|
||||
// 创建角色
|
||||
$roleModel = new AdminUserGroup();
|
||||
$roleModel->save($roleData);
|
||||
|
||||
// 记录操作日志
|
||||
$this->logSuccess('角色管理', '创建角色', ['id' => $roleModel->id]);
|
||||
return json([
|
||||
'code' => 200,
|
||||
'msg' => '创建成功',
|
||||
'data' => $roleModel->toArray()
|
||||
]);
|
||||
} catch (ValidateException $e) {
|
||||
// 记录失败日志
|
||||
$this->logFail('角色管理', '创建角色', $e->getMessage());
|
||||
return json([
|
||||
'code' => 400,
|
||||
'msg' => $e->getError()
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
// 记录失败日志
|
||||
$this->logFail('角色管理', '创建角色', $e->getMessage());
|
||||
return json([
|
||||
'code' => 500,
|
||||
'msg' => '创建失败:' . $e->getMessage()
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新角色
|
||||
* @param int $id
|
||||
* @return Json
|
||||
*/
|
||||
public function updateRole(int $id)
|
||||
{
|
||||
try {
|
||||
$data = $this->request->param();
|
||||
|
||||
// 验证参数
|
||||
$this->validate($data, [
|
||||
'name|角色名称' => 'require|max:50',
|
||||
'status|状态' => 'in:0,1',
|
||||
'rights|权限' => 'array'
|
||||
]);
|
||||
|
||||
// 查找角色
|
||||
$role = AdminUserGroup::where('id', $id)
|
||||
->where('delete_time', null)
|
||||
->find();
|
||||
if (!$role) {
|
||||
return json([
|
||||
'code' => 404,
|
||||
'msg' => '角色不存在'
|
||||
]);
|
||||
}
|
||||
|
||||
// 检查角色名称是否已被其他角色使用
|
||||
$exists = AdminUserGroup::where('name', $data['name'])
|
||||
->where('id', '<>', $id)
|
||||
->where('delete_time', null)
|
||||
->find();
|
||||
if ($exists) {
|
||||
return json([
|
||||
'code' => 400,
|
||||
'msg' => '角色名称已存在'
|
||||
]);
|
||||
}
|
||||
|
||||
// 更新数据
|
||||
$role->name = $data['name'];
|
||||
$role->status = $data['status'] ?? 1;
|
||||
$role->rights = !empty($data['rights']) ? json_encode($data['rights']) : null;
|
||||
$role->update_time = date('Y-m-d H:i:s');
|
||||
$role->save();
|
||||
|
||||
// 记录操作日志
|
||||
$this->logSuccess('角色管理', '更新角色', ['id' => $id]);
|
||||
return json([
|
||||
'code' => 200,
|
||||
'msg' => '更新成功',
|
||||
'data' => $role->toArray()
|
||||
]);
|
||||
} catch (ValidateException $e) {
|
||||
// 记录失败日志
|
||||
$this->logFail('角色管理', '更新角色', $e->getMessage());
|
||||
return json([
|
||||
'code' => 400,
|
||||
'msg' => $e->getError()
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
// 记录失败日志
|
||||
$this->logFail('角色管理', '更新角色', $e->getMessage());
|
||||
return json([
|
||||
'code' => 500,
|
||||
'msg' => '更新失败:' . $e->getMessage()
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除角色
|
||||
* @param int $id
|
||||
* @return Json
|
||||
*/
|
||||
public function deleteRole(int $id)
|
||||
{
|
||||
try {
|
||||
// 不允许删除ID为1的超级管理员角色
|
||||
if ($id == 1) {
|
||||
return json([
|
||||
'code' => 400,
|
||||
'msg' => '不允许删除超级管理员角色'
|
||||
]);
|
||||
}
|
||||
|
||||
// 查找角色
|
||||
$role = AdminUserGroup::where('id', $id)
|
||||
->where('delete_time', null)
|
||||
->find();
|
||||
if (!$role) {
|
||||
return json([
|
||||
'code' => 404,
|
||||
'msg' => '角色不存在'
|
||||
]);
|
||||
}
|
||||
|
||||
// 检查是否有用户正在使用该角色
|
||||
$userCount = AdminUser::where('group_id', $id)
|
||||
->where('delete_time', null)
|
||||
->count();
|
||||
if ($userCount > 0) {
|
||||
return json([
|
||||
'code' => 400,
|
||||
'msg' => "该角色下还有 {$userCount} 个用户,无法删除"
|
||||
]);
|
||||
}
|
||||
|
||||
// 软删除
|
||||
$role->delete();
|
||||
|
||||
// 记录操作日志
|
||||
$this->logSuccess('角色管理', '删除角色', ['id' => $id]);
|
||||
return json([
|
||||
'code' => 200,
|
||||
'msg' => '删除成功'
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
// 记录失败日志
|
||||
$this->logFail('角色管理', '删除角色', $e->getMessage());
|
||||
return json([
|
||||
'code' => 500,
|
||||
'msg' => '删除失败:' . $e->getMessage()
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\admin\controller;
|
||||
|
||||
use app\admin\BaseController;
|
||||
use think\exception\ValidateException;
|
||||
use think\facade\Db;
|
||||
use think\response\Json;
|
||||
use app\model\SystemSiteSettings;
|
||||
|
||||
class SiteSettingsController extends BaseController
|
||||
{
|
||||
/**
|
||||
* 获取所有站点设置列表
|
||||
* @return Json
|
||||
*/
|
||||
public function getNormalInfos()
|
||||
{
|
||||
$siteSettings = SystemSiteSettings::where('delete_time', null)
|
||||
->field('id, label, value, sort')
|
||||
->order('id', 'asc')
|
||||
->select();
|
||||
return json([
|
||||
'code' => 200,
|
||||
'msg' => '获取成功',
|
||||
'data' => $siteSettings->toArray()
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存基本信息
|
||||
* @return Json
|
||||
*/
|
||||
public function saveNormalInfos()
|
||||
{
|
||||
$params = $this->request->param();
|
||||
|
||||
if (!isset($params['data']) || empty($params['data'])) {
|
||||
return json(['code' => 400, 'msg' => '数据不能为空', 'data' => null]);
|
||||
}
|
||||
|
||||
$data = json_decode($params['data'], true);
|
||||
if (!is_array($data)) {
|
||||
return json(['code' => 400, 'msg' => '数据格式错误', 'data' => null]);
|
||||
}
|
||||
|
||||
foreach ($data as $item) {
|
||||
if (!isset($item['label']) || !isset($item['value'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$label = $item['label'];
|
||||
$value = $item['value'] ?? '';
|
||||
|
||||
$setting = SystemSiteSettings::where('label', $label)->find();
|
||||
if ($setting) {
|
||||
$setting->value = $value;
|
||||
$setting->save();
|
||||
} else {
|
||||
SystemSiteSettings::create([
|
||||
'label' => $label,
|
||||
'value' => $value,
|
||||
'create_time' => date('Y-m-d H:i:s')
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
$allSettings = SystemSiteSettings::column('value', 'label');
|
||||
$allSettings['type'] = 'normal';
|
||||
|
||||
$labels = array_column($data, 'label');
|
||||
$userInfo = $this->getAdminUserInfo();
|
||||
$this->logSuccess('站点设置管理', '保存基本信息', ['labels' => $labels], $userInfo);
|
||||
|
||||
return json([
|
||||
'code' => 200,
|
||||
'msg' => '保存成功',
|
||||
'data' => $allSettings
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有站点设置列表
|
||||
* @return Json
|
||||
*/
|
||||
public function getLegalInfos()
|
||||
{
|
||||
$siteSettings = SystemSiteSettings::where('delete_time', null)
|
||||
->field('id, label, value, sort')
|
||||
->order('id', 'asc')
|
||||
->select();
|
||||
return json([
|
||||
'code' => 200,
|
||||
'msg' => '获取成功',
|
||||
'data' => $siteSettings->toArray()
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存法律声明和隐私条款
|
||||
* @return Json
|
||||
*/
|
||||
public function saveLegalInfos()
|
||||
{
|
||||
$params = $this->request->param();
|
||||
|
||||
if (!isset($params['data']) || empty($params['data'])) {
|
||||
return json(['code' => 400, 'msg' => '数据不能为空', 'data' => null]);
|
||||
}
|
||||
|
||||
$data = json_decode($params['data'], true);
|
||||
if (!is_array($data)) {
|
||||
return json(['code' => 400, 'msg' => '数据格式错误', 'data' => null]);
|
||||
}
|
||||
|
||||
foreach ($data as $item) {
|
||||
if (!isset($item['label']) || !isset($item['value'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$label = $item['label'];
|
||||
$value = $item['value'] ?? '';
|
||||
|
||||
$setting = SystemSiteSettings::where('label', $label)->find();
|
||||
if ($setting) {
|
||||
$setting->value = $value;
|
||||
$setting->save();
|
||||
} else {
|
||||
SystemSiteSettings::create([
|
||||
'label' => $label,
|
||||
'value' => $value,
|
||||
'create_time' => date('Y-m-d H:i:s')
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$allSettings = SystemSiteSettings::column('value', 'label');
|
||||
$allSettings['type'] = 'legal_notice';
|
||||
|
||||
$labels = array_column($data, 'label');
|
||||
|
||||
$userId = Session::get('user_id', 0);
|
||||
$userFromSession = Session::get('user', []);
|
||||
$userFromCache = Cache::get('userInfo_' . $userId, []);
|
||||
$account = Session::get('account', '');
|
||||
$name = Session::get('name', '');
|
||||
|
||||
\think\facade\Log::record('SiteSettings Debug - user_id: ' . $userId . ', userFromSession: ' . json_encode($userFromSession) . ', userFromCache: ' . json_encode($userFromCache) . ', account: ' . $account . ', name: ' . $name);
|
||||
|
||||
$this->logSuccess('站点设置管理', '保存法律声明和隐私条款', ['labels' => $labels]);
|
||||
|
||||
return json([
|
||||
'code' => 200,
|
||||
'msg' => '保存成功',
|
||||
'data' => $allSettings
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
namespace app\admin\controller;
|
||||
|
||||
use app\admin\BaseController;
|
||||
use think\facade\Request;
|
||||
use think\Response;
|
||||
|
||||
class StorageController extends BaseController
|
||||
{
|
||||
/**
|
||||
* 获取存储文件
|
||||
* 用于访问 /storage/ 路径下的静态文件
|
||||
* @param string $path 文件路径
|
||||
*/
|
||||
public function index($path = '')
|
||||
{
|
||||
// 如果没有传递路径参数,从pathinfo获取
|
||||
if (empty($path)) {
|
||||
$pathinfo = Request::pathinfo();
|
||||
$path = str_replace('storage/', '', $pathinfo);
|
||||
}
|
||||
|
||||
// 防止路径遍历攻击
|
||||
$path = str_replace('..', '', $path);
|
||||
$path = ltrim($path, '/');
|
||||
|
||||
// 构建文件完整路径
|
||||
$filePath = public_path() . 'storage/' . $path;
|
||||
|
||||
// 检查文件是否存在
|
||||
if (!file_exists($filePath) || !is_file($filePath)) {
|
||||
return json([
|
||||
'code' => 404,
|
||||
'msg' => '文件不存在'
|
||||
]);
|
||||
}
|
||||
|
||||
// 获取文件MIME类型
|
||||
$mimeType = mime_content_type($filePath);
|
||||
if (!$mimeType) {
|
||||
// 根据文件扩展名判断MIME类型
|
||||
$ext = strtolower(pathinfo($filePath, PATHINFO_EXTENSION));
|
||||
$mimeTypes = [
|
||||
'jpg' => 'image/jpeg',
|
||||
'jpeg' => 'image/jpeg',
|
||||
'png' => 'image/png',
|
||||
'gif' => 'image/gif',
|
||||
'webp' => 'image/webp',
|
||||
'mp4' => 'video/mp4',
|
||||
'mp3' => 'audio/mpeg',
|
||||
'pdf' => 'application/pdf',
|
||||
];
|
||||
$mimeType = $mimeTypes[$ext] ?? 'application/octet-stream';
|
||||
}
|
||||
|
||||
// 读取文件内容
|
||||
$content = file_get_contents($filePath);
|
||||
|
||||
// 返回文件响应
|
||||
return Response::create($content, 'file', 200)
|
||||
->header([
|
||||
'Content-Type' => $mimeType,
|
||||
'Content-Length' => filesize($filePath),
|
||||
'Cache-Control' => 'public, max-age=31536000',
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\admin\controller;
|
||||
|
||||
use app\admin\BaseController;
|
||||
use think\exception\ValidateException;
|
||||
use think\facade\Db;
|
||||
use think\facade\Session;
|
||||
use think\response\Json;
|
||||
|
||||
use app\model\AdminUser;
|
||||
|
||||
class UserController extends BaseController
|
||||
{
|
||||
/**
|
||||
* 获取所有用户信息
|
||||
* @return Json
|
||||
*/
|
||||
public function getAllUsers()
|
||||
{
|
||||
$users = AdminUser::where('delete_time', null)->field('id, account, name, phone,email, qq, sex, group_id, status, last_login_ip, login_count, create_time, update_time')->select()->toArray();
|
||||
return json([
|
||||
'code' => 200,
|
||||
'msg' => '获取成功',
|
||||
'data' => [
|
||||
'list' => $users,
|
||||
'total' => count($users)
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户信息
|
||||
* @return Json
|
||||
*/
|
||||
public function getUserInfo(int $id)
|
||||
{
|
||||
$user = AdminUser::where('id', $id)
|
||||
->field('id, account, name, phone,email, qq, sex, group_id, status')
|
||||
->find();
|
||||
|
||||
// 记录操作日志
|
||||
$this->logSuccess('用户管理', '获取用户信息', ['id' => $id]);
|
||||
|
||||
return json([
|
||||
'code' => 200,
|
||||
'msg' => '获取成功',
|
||||
'data' => $user
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改密码
|
||||
* @return Json
|
||||
*/
|
||||
public function changePassword(int $id, string $password)
|
||||
{
|
||||
try {
|
||||
AdminUser::where('id', $id)->update([
|
||||
'password' => md5($password),
|
||||
'update_time' => date('Y-m-d H:i:s')
|
||||
]);
|
||||
// 记录操作日志
|
||||
$this->logSuccess('用户管理', '修改密码', ['id' => $id]);
|
||||
return json([
|
||||
'code' => 200,
|
||||
'msg' => '修改成功'
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
// 记录失败日志
|
||||
$this->logFail('用户管理', '修改密码', $e->getMessage());
|
||||
return json([
|
||||
'code' => 500,
|
||||
'msg' => '修改失败'
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加用户
|
||||
* @return Json
|
||||
*/
|
||||
public function addUser()
|
||||
{
|
||||
$data = request()->param();
|
||||
$data['password'] = md5($data['password']);
|
||||
$data['create_time'] = date('Y-m-d H:i:s');
|
||||
$data['update_time'] = $data['create_time'];
|
||||
$data['group_id'] = 2;
|
||||
$id = AdminUser::insertGetId($data);
|
||||
// 记录操作日志
|
||||
$this->logSuccess('用户管理', '添加用户', ['data' => $data]);
|
||||
return json([
|
||||
'code' => 200,
|
||||
'msg' => '添加成功',
|
||||
'data' => ['id' => $id]
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑用户
|
||||
* @return Json
|
||||
*/
|
||||
public function editUser(int $id)
|
||||
{
|
||||
$data = request()->param();
|
||||
$data['update_time'] = date('Y-m-d H:i:s');
|
||||
AdminUser::where('id', $id)->update($data);
|
||||
// 记录操作日志
|
||||
$this->logSuccess('用户管理', '编辑用户', ['data' => $data]);
|
||||
return json([
|
||||
'code' => 200,
|
||||
'msg' => '编辑成功'
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除用户
|
||||
* @return Json
|
||||
*/
|
||||
public function deleteUser(int $id)
|
||||
{
|
||||
AdminUser::where('id', $id)->delete();
|
||||
// 记录操作日志
|
||||
$this->logSuccess('用户管理', '删除用户', ['data' => $id]);
|
||||
return json([
|
||||
'code' => 200,
|
||||
'msg' => '删除成功'
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
<?php
|
||||
// 这是系统自动生成的event定义文件
|
||||
return [
|
||||
|
||||
];
|
||||
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
// 全局中间件定义文件
|
||||
return [
|
||||
// 全局请求缓存
|
||||
// \think\middleware\CheckRequestCache::class,
|
||||
// 多语言加载
|
||||
// \think\middleware\LoadLangPack::class,
|
||||
// Session初始化
|
||||
\think\middleware\SessionInit::class,
|
||||
];
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
namespace app\admin\middleware;
|
||||
|
||||
use think\Response;
|
||||
use think\Request;
|
||||
|
||||
/**
|
||||
* 自定义跨域中间件
|
||||
* 解决 AJAX 请求 Cookie 传递问题
|
||||
*/
|
||||
class CustomCors
|
||||
{
|
||||
public function handle(Request $request, \Closure $next): Response
|
||||
{
|
||||
$origin = $request->header('origin', '');
|
||||
|
||||
// 允许的域名列表(根据实际情况修改)
|
||||
$allowedDomains = [
|
||||
'http://localhost:3000',
|
||||
'http://localhost:8000',
|
||||
'http://127.0.0.1:3000',
|
||||
'http://127.0.0.1:8000',
|
||||
'http://backapi.yunzer.cn',
|
||||
'http://localhost:5173',
|
||||
'http://www.yunzer.cn',
|
||||
'https://www.yunzer.cn',
|
||||
'http://yunzer.cn',
|
||||
'https://yunzer.cn',
|
||||
];
|
||||
|
||||
// 检查是否为允许的域名
|
||||
if (in_array($origin, $allowedDomains)) {
|
||||
$header['Access-Control-Allow-Origin'] = $origin;
|
||||
} else {
|
||||
// 对于同源请求,允许当前域名
|
||||
if (!empty($origin)) {
|
||||
$header['Access-Control-Allow-Origin'] = $origin;
|
||||
} else {
|
||||
$header['Access-Control-Allow-Origin'] = '*';
|
||||
}
|
||||
}
|
||||
|
||||
// 处理 OPTIONS 预检请求,直接返回
|
||||
if ($request->method() === 'OPTIONS') {
|
||||
$header['Access-Control-Allow-Credentials'] = 'true';
|
||||
$header['Access-Control-Max-Age'] = 1800;
|
||||
$header['Access-Control-Allow-Methods'] = 'GET, POST, PATCH, PUT, DELETE, OPTIONS';
|
||||
$header['Access-Control-Allow-Headers'] = 'Authorization, Content-Type, If-Match, If-Modified-Since, If-None-Match, If-Unmodified-Since, X-CSRF-TOKEN, X-Requested-With';
|
||||
\think\facade\Log::record('CORS OPTIONS preflight for: ' . $origin);
|
||||
return response('', 200, $header);
|
||||
}
|
||||
|
||||
$header['Access-Control-Allow-Credentials'] = 'true';
|
||||
$header['Access-Control-Max-Age'] = 1800;
|
||||
$header['Access-Control-Allow-Methods'] = 'GET, POST, PATCH, PUT, DELETE, OPTIONS';
|
||||
$header['Access-Control-Allow-Headers'] = 'Authorization, Content-Type, If-Match, If-Modified-Since, If-None-Match, If-Unmodified-Since, X-CSRF-TOKEN, X-Requested-With';
|
||||
|
||||
return $next($request)->header($header);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
<?php
|
||||
use think\facade\Route;
|
||||
|
||||
// 首页路由
|
||||
Route::get('/', 'app\\admin\\controller\\Index@index');
|
||||
Route::get('index/index', 'app\\admin\\controller\\Index@index');
|
||||
|
||||
|
||||
// 静态文件路由 - 必须在其他路由之前定义,使用通配符匹配所有storage路径
|
||||
Route::get('storage/:path', 'app\\admin\\controller\\StorageController@index')->pattern(['path' => '.*']);
|
||||
|
||||
// 登录路由
|
||||
Route::post('login', 'app\\admin\\controller\\LoginController@login');
|
||||
Route::post('logout', 'app\\admin\\controller\\LoginController@logout');
|
||||
Route::get('user/info', 'app\\admin\\controller\\LoginController@userInfo');
|
||||
|
||||
// 菜单路由
|
||||
Route::get('allmenu', 'app\\admin\\controller\\MenuController@getAllMenus');
|
||||
Route::get('menu/:id', 'app\\admin\\controller\\MenuController@getMenus');
|
||||
Route::post('createMenu', 'app\\admin\\controller\\MenuController@createMenu');
|
||||
Route::put('updateMenu/:id', 'app\\admin\\controller\\MenuController@updateMenu');
|
||||
Route::delete('deleteMenu/:id', 'app\\admin\\controller\\MenuController@deleteMenu');
|
||||
Route::patch('menu/status/:id', 'app\\admin\\controller\\MenuController@updateMenuStatus');
|
||||
|
||||
// 前端导航路由
|
||||
Route::get('frontmenus', 'app\\admin\\controller\\FrontMenuController@getFrontMenus');
|
||||
Route::post('createfrontmenu', 'app\\admin\\controller\\FrontMenuController@createFrontMenu');
|
||||
Route::post('editfrontmenu/:id', 'app\\admin\\controller\\FrontMenuController@editFrontMenu');
|
||||
Route::delete('deletefrontmenu/:id', 'app\\admin\\controller\\FrontMenuController@deleteFrontMenu');
|
||||
|
||||
// Banner路由
|
||||
Route::get('allbanners', 'app\\admin\\controller\\BannerController@getAllBanners');
|
||||
Route::post('createbanner', 'app\\admin\\controller\\BannerController@createBanner');
|
||||
Route::post('editbanner/:id', 'app\\admin\\controller\\BannerController@editBanner');
|
||||
Route::delete('deletebanner/:id', 'app\\admin\\controller\\BannerController@deleteBanner');
|
||||
|
||||
// 单页路由
|
||||
Route::get('allonepages', 'app\\admin\\controller\\OnePageController@getAllOnePages');
|
||||
Route::post('createonepage', 'app\\admin\\controller\\OnePageController@createOnePage');
|
||||
Route::post('editonepage/:id', 'app\\admin\\controller\\OnePageController@editOnePage');
|
||||
Route::delete('deleteonepage/:id', 'app\\admin\\controller\\OnePageController@deleteOnePage');
|
||||
|
||||
// 文章路由
|
||||
Route::get('articlesList', 'app\\admin\\controller\\Article\\ArticleController@getArticlesList');
|
||||
Route::get('allarticles', 'app\\admin\\controller\\Article\\ArticleController@getAllArticles');
|
||||
Route::get('articles/:id', 'app\\admin\\controller\\Article\\ArticleController@getArticle');
|
||||
Route::post('createarticle', 'app\\admin\\controller\\Article\\ArticleController@createArticle');
|
||||
Route::post('editarticle/:id', 'app\\admin\\controller\\Article\\ArticleController@editArticle');
|
||||
Route::delete('deletearticle/:id', 'app\\admin\\controller\\Article\\ArticleController@deleteArticle');
|
||||
Route::post('publisharticle/:id', 'app\\admin\\controller\\Article\\ArticleController@publishArticle');
|
||||
Route::post('unPublisharticle/:id', 'app\\admin\\controller\\Article\\ArticleController@unPublishArticle');
|
||||
Route::post('articleRecommend/:id', 'app\\admin\\controller\\Article\\ArticleController@articleRecommend');
|
||||
Route::post('unArticleRecommend/:id', 'app\\admin\\controller\\Article\\ArticleController@unArticleRecommend');
|
||||
Route::post('articleTop/:id', 'app\\admin\\controller\\Article\\ArticleController@articleTop');
|
||||
Route::post('unArticleTop/:id', 'app\\admin\\controller\\Article\\ArticleController@unArticleTop');
|
||||
|
||||
// 文章分类路由
|
||||
Route::get('allcategories', 'app\\admin\\controller\\Article\\ArticleCategoryController@getAllArticleCategories');
|
||||
Route::get('categories', 'app\\admin\\controller\\Article\\ArticleCategoryController@getArticleCategories');
|
||||
Route::delete('categories/:id', 'app\\admin\\controller\\Article\\ArticleCategoryController@deleteCategory');
|
||||
Route::post('createCategory', 'app\\admin\\controller\\Article\\ArticleCategoryController@createCategory');
|
||||
Route::post('editCategory/:id', 'app\\admin\\controller\\Article\\ArticleCategoryController@editCategory');
|
||||
|
||||
// 文件路由
|
||||
Route::get('usercate', 'app\\admin\\controller\\FileController@getUserCate');
|
||||
Route::get('allfiles', 'app\\admin\\controller\\FileController@getAllFiles');
|
||||
Route::get('catefiles/:id', 'app\\admin\\controller\\FileController@getCateFiles');
|
||||
Route::post('uploadfile', 'app\\admin\\controller\\FileController@uploadFile');
|
||||
Route::post('updatefile/:id', 'app\\admin\\controller\\FileController@updateFile');
|
||||
Route::delete('deletefile/:id', 'app\\admin\\controller\\FileController@deleteFile');
|
||||
Route::get('movefile/:id', 'app\\admin\\controller\\FileController@moveFile');
|
||||
Route::post('createfilecate', 'app\\admin\\controller\\FileController@createFileCate');
|
||||
Route::post('renamefilecate/:id', 'app\\admin\\controller\\FileController@renameFileCate');
|
||||
Route::delete('deletefilecate/:id', 'app\\admin\\controller\\FileController@deleteFileCate');
|
||||
|
||||
// 用户路由
|
||||
Route::get('getAllUsers', 'app\\admin\\controller\\UserController@getAllUsers');
|
||||
Route::get('getUserInfo/:id', 'app\\admin\\controller\\UserController@getUserInfo');
|
||||
Route::post('addUser', 'app\\admin\\controller\\UserController@addUser');
|
||||
Route::post('editUser/:id', 'app\\admin\\controller\\UserController@editUser');
|
||||
Route::delete('deleteUser/:id', 'app\\admin\\controller\\UserController@deleteUser');
|
||||
Route::post('changePassword', 'app\\admin\\controller\\UserController@changePassword');
|
||||
|
||||
// 角色路由
|
||||
Route::get('allRoles', 'app\\admin\\controller\\RoleController@getAllRoles');
|
||||
Route::get('roles/:id', 'app\\admin\\controller\\RoleController@getRoleById');
|
||||
Route::post('roles', 'app\\admin\\controller\\RoleController@createRole');
|
||||
Route::put('roles/:id', 'app\\admin\\controller\\RoleController@updateRole');
|
||||
Route::delete('roles/:id', 'app\\admin\\controller\\RoleController@deleteRole');
|
||||
|
||||
// 操作日志路由(注意:具体路径要在动态路径之前)
|
||||
Route::get('operationLogs/statistics', 'app\\admin\\controller\\OperationLog\\OperationLogController@getOperationStatistics');
|
||||
Route::get('operationLogs/:id', 'app\\admin\\controller\\OperationLog\\OperationLogController@getOperationLogDetail');
|
||||
Route::get('operationLogs', 'app\\admin\\controller\\OperationLog\\OperationLogController@getOperationLogs');
|
||||
|
||||
// 站点设置路由
|
||||
Route::get('normalInfos', 'app\\admin\\controller\\SiteSettingsController@getNormalInfos');
|
||||
Route::post('saveNormalInfos', 'app\\admin\\controller\\SiteSettingsController@saveNormalInfos');
|
||||
Route::get('legalInfos', 'app\\admin\\controller\\SiteSettingsController@getLegalInfos');
|
||||
Route::post('saveLegalInfos', 'app\\admin\\controller\\SiteSettingsController@saveLegalInfos');
|
||||
@@ -0,0 +1,100 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
// 关键修正:命名空间对应控制器目录
|
||||
namespace app\index;
|
||||
|
||||
use think\App;
|
||||
use think\exception\ValidateException;
|
||||
use think\Validate;
|
||||
use think\Request;
|
||||
|
||||
// 在控制器方法中添加
|
||||
header('Access-Control-Allow-Origin: *');
|
||||
header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE');
|
||||
header('Access-Control-Allow-Headers: Content-Type, Authorization');
|
||||
|
||||
/**
|
||||
* 控制器基础类
|
||||
*/
|
||||
abstract class BaseController
|
||||
{
|
||||
/**
|
||||
* Request实例
|
||||
* @var Request
|
||||
*/
|
||||
protected $request;
|
||||
|
||||
/**
|
||||
* 应用实例
|
||||
* @var App
|
||||
*/
|
||||
protected $app;
|
||||
|
||||
/**
|
||||
* 是否批量验证
|
||||
* @var bool
|
||||
*/
|
||||
protected $batchValidate = false;
|
||||
|
||||
/**
|
||||
* 控制器中间件
|
||||
* @var array
|
||||
*/
|
||||
protected $middleware = [];
|
||||
|
||||
/**
|
||||
* 构造方法
|
||||
* @access public
|
||||
* @param App $app 应用对象
|
||||
*/
|
||||
public function __construct(App $app)
|
||||
{
|
||||
$this->app = $app;
|
||||
$this->request = $this->app->request;
|
||||
|
||||
// 控制器初始化
|
||||
$this->initialize();
|
||||
}
|
||||
|
||||
// 初始化
|
||||
protected function initialize() {}
|
||||
|
||||
/**
|
||||
* 验证数据
|
||||
* @access protected
|
||||
* @param array $data 数据
|
||||
* @param string|array $validate 验证器名或者验证规则数组
|
||||
* @param array $message 提示信息
|
||||
* @param bool $batch 是否批量验证
|
||||
* @return array|string|true
|
||||
* @throws ValidateException
|
||||
*/
|
||||
protected function validate(array $data, string|array $validate, array $message = [], bool $batch = false)
|
||||
{
|
||||
if (is_array($validate)) {
|
||||
$v = new Validate();
|
||||
$v->rule($validate);
|
||||
} else {
|
||||
if (strpos($validate, '.')) {
|
||||
// 支持场景
|
||||
[$validate, $scene] = explode('.', $validate);
|
||||
}
|
||||
$class = false !== strpos($validate, '\\') ? $validate : $this->app->parseClass('validate', $validate);
|
||||
$v = new $class();
|
||||
if (!empty($scene)) {
|
||||
$v->scene($scene);
|
||||
}
|
||||
}
|
||||
|
||||
$v->message($message);
|
||||
|
||||
// 是否批量验证
|
||||
if ($batch || $this->batchValidate) {
|
||||
$v->batch(true);
|
||||
}
|
||||
|
||||
return $v->failException(true)->check($data);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
<?php
|
||||
// 这是系统自动生成的公共文件
|
||||
@@ -0,0 +1,249 @@
|
||||
<?php
|
||||
declare (strict_types = 1);
|
||||
|
||||
namespace app\index\controller\Article;
|
||||
|
||||
use app\index\BaseController;
|
||||
use app\model\Articles;
|
||||
use app\model\ArticlesCategory;
|
||||
use Symfony\Component\VarDumper\VarDumper;
|
||||
use think\exception\ValidateException;
|
||||
use think\facade\Request;
|
||||
use think\facade\Session;
|
||||
use think\response\Json;
|
||||
use think\db\exception\DbException;
|
||||
|
||||
class ArticleController extends BaseController
|
||||
{
|
||||
/**
|
||||
* 获取新闻中心顶部4篇文章
|
||||
* @return Json
|
||||
*/
|
||||
public function getNewsCenterTop4(): Json
|
||||
{
|
||||
try {
|
||||
// 1. 查询新闻中心主分类
|
||||
$newsCenterCategory = ArticlesCategory::where('name', '新闻中心')
|
||||
->where('delete_time', null)
|
||||
->find();
|
||||
|
||||
if (!$newsCenterCategory) {
|
||||
return json([
|
||||
'code' => 200,
|
||||
'msg' => 'success',
|
||||
'list' => [],
|
||||
]);
|
||||
}
|
||||
|
||||
// 2. 获取新闻中心及其子分类的ID列表
|
||||
$mainCategoryId = $newsCenterCategory['id'];
|
||||
|
||||
// 查询子分类
|
||||
$subCategories = ArticlesCategory::where('cid', $mainCategoryId)
|
||||
->where('delete_time', null)
|
||||
->column('id');
|
||||
|
||||
// 合并主分类和子分类ID
|
||||
$categoryIds = array_merge([$mainCategoryId], $subCategories);
|
||||
|
||||
if (empty($categoryIds)) {
|
||||
return json([
|
||||
'code' => 200,
|
||||
'msg' => 'success',
|
||||
'list' => [],
|
||||
]);
|
||||
}
|
||||
|
||||
// 3. 查询符合条件的文章,按指定优先级排序,取最新4篇
|
||||
$articles = Articles::published()
|
||||
->whereIn('cate', $categoryIds)
|
||||
->order([
|
||||
'recommend' => 'desc',
|
||||
'publishdate' => 'desc',
|
||||
'sort' => 'desc',
|
||||
'id' => 'desc'
|
||||
])
|
||||
->field([
|
||||
'id',
|
||||
'title',
|
||||
'content',
|
||||
'publishdate',
|
||||
'DAY(publishdate) AS `date`',
|
||||
'DATE_FORMAT(publishdate, "%Y-%m") AS `month`',
|
||||
'image',
|
||||
'recommend'
|
||||
])
|
||||
->limit(4)
|
||||
->select();
|
||||
|
||||
// 4. 处理文章数据,去除HTML标签并生成desc字段
|
||||
foreach ($articles as &$article) {
|
||||
// 使用PHP的strip_tags函数去除HTML标签,然后截取前100个字符
|
||||
$article['desc'] = mb_substr(trim(strip_tags($article['content'])), 0, 100, 'UTF-8');
|
||||
// 移除原始content字段,减少返回数据大小
|
||||
unset($article['content']);
|
||||
}
|
||||
|
||||
return json([
|
||||
'code' => 200,
|
||||
'msg' => 'success',
|
||||
'list' => $articles,
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
// 打印完整错误信息到日志
|
||||
$errorMsg = '错误信息:' . $e->getMessage() . ' | 错误行号:' . $e->getLine() . ' | 执行SQL:' . Articles::getLastSql();
|
||||
trace('新闻中心文章查询失败: ' . $errorMsg, 'error');
|
||||
return json([
|
||||
'code' => 500,
|
||||
'msg' => '新闻中心文章查询失败,请稍后重试',
|
||||
'list' => []
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 游客文章阅读量函数
|
||||
* @return Json
|
||||
*/
|
||||
public function articleViews(): Json
|
||||
{
|
||||
$id = Request::param('id');
|
||||
if (!$id) {
|
||||
return json([
|
||||
'code' => 400,
|
||||
'msg' => '没有文章id',
|
||||
'list' => []
|
||||
]);
|
||||
}
|
||||
|
||||
try {
|
||||
$article = Articles::where('id', $id)->where('delete_time', null)->find();
|
||||
|
||||
if (!$article) {
|
||||
return json([
|
||||
'code' => 404,
|
||||
'msg' => '文章不存在',
|
||||
'list' => []
|
||||
]);
|
||||
}
|
||||
|
||||
$article->inc('views');
|
||||
$article->save();
|
||||
|
||||
return json([
|
||||
'code' => 200,
|
||||
'msg' => '阅读量更新成功',
|
||||
'data' => [
|
||||
'views' => $article->views
|
||||
]
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
trace('阅读量更新失败: ' . $e->getMessage(), 'error');
|
||||
return json([
|
||||
'code' => 500,
|
||||
'msg' => '阅读量更新失败,请稍后重试',
|
||||
'list' => []
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 游客文章点赞函数
|
||||
* @return Json
|
||||
*/
|
||||
public function articleLikes(): Json
|
||||
{
|
||||
$id = Request::param('id');
|
||||
if (!$id) {
|
||||
return json([
|
||||
'code' => 400,
|
||||
'msg' => '没有文章id',
|
||||
'list' => []
|
||||
]);
|
||||
}
|
||||
|
||||
try {
|
||||
$article = Articles::where('id', $id)->where('delete_time', null)->find();
|
||||
|
||||
if (!$article) {
|
||||
return json([
|
||||
'code' => 404,
|
||||
'msg' => '文章不存在',
|
||||
'list' => []
|
||||
]);
|
||||
}
|
||||
|
||||
$article->inc('likes');
|
||||
$article->save();
|
||||
|
||||
return json([
|
||||
'code' => 200,
|
||||
'msg' => '点赞成功',
|
||||
'data' => [
|
||||
'likes' => $article->likes
|
||||
]
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
trace('点赞失败: ' . $e->getMessage(), 'error');
|
||||
return json([
|
||||
'code' => 500,
|
||||
'msg' => '点赞失败,请稍后重试',
|
||||
'list' => []
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 游客文章取消点赞函数
|
||||
* @return Json
|
||||
*/
|
||||
public function articleUnlikes(): Json
|
||||
{
|
||||
$id = Request::param('id');
|
||||
if (!$id) {
|
||||
return json([
|
||||
'code' => 400,
|
||||
'msg' => '没有文章id',
|
||||
'list' => []
|
||||
]);
|
||||
}
|
||||
|
||||
try {
|
||||
$article = Articles::where('id', $id)->where('delete_time', null)->find();
|
||||
|
||||
if (!$article) {
|
||||
return json([
|
||||
'code' => 404,
|
||||
'msg' => '文章不存在',
|
||||
'list' => []
|
||||
]);
|
||||
}
|
||||
|
||||
if ($article->likes <= 0) {
|
||||
return json([
|
||||
'code' => 400,
|
||||
'msg' => '文章点赞数为0,不能取消点赞',
|
||||
'list' => []
|
||||
]);
|
||||
}
|
||||
|
||||
$article->dec('likes');
|
||||
$article->save();
|
||||
|
||||
return json([
|
||||
'code' => 200,
|
||||
'msg' => '取消点赞成功',
|
||||
'data' => [
|
||||
'likes' => $article->likes
|
||||
]
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
trace('取消点赞失败: ' . $e->getMessage(), 'error');
|
||||
return json([
|
||||
'code' => 500,
|
||||
'msg' => '取消点赞失败,请稍后重试',
|
||||
'list' => []
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\index\controller;
|
||||
|
||||
use app\model\Banner;
|
||||
use app\index\BaseController;
|
||||
use app\model\FrontMenu;
|
||||
use app\model\OnePage;
|
||||
use think\db\exception\DbException;
|
||||
|
||||
class Index extends BaseController
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
return '您好!这是一个[index]示例应用';
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取前端导航接口
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function getHeadMenu()
|
||||
{
|
||||
try {
|
||||
// 获取所有未删除的菜单
|
||||
$frontMenus = FrontMenu::where('delete_time', null)
|
||||
->field('id,pid,title,type,image,path,component_path,sort,desc')
|
||||
->order('sort', 'desc')
|
||||
->select()
|
||||
->toArray(); // 转换为数组
|
||||
|
||||
// 使用 buildFrontMenuTree 构建树形结构
|
||||
$treeFrontMenus = $this->buildFrontMenuTree($frontMenus);
|
||||
|
||||
return json([
|
||||
'code' => 200,
|
||||
'msg' => 'success',
|
||||
'data' => $treeFrontMenus
|
||||
]);
|
||||
} catch (DbException $e) {
|
||||
return json([
|
||||
'code' => 500,
|
||||
'msg' => 'fail:' . $e->getMessage(),
|
||||
'data' => $e->getTraceAsString()
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建前端导航树形结构
|
||||
* @param array $frontMenus 前端导航列表
|
||||
* @param int $pid 父前端导航ID
|
||||
* @return array
|
||||
*/
|
||||
private function buildFrontMenuTree(array $frontMenus, int $pid = 0): array
|
||||
{
|
||||
$tree = [];
|
||||
|
||||
foreach ($frontMenus as $frontMenu) {
|
||||
// 将null的pid视为0处理
|
||||
$menuPid = is_null($frontMenu['pid']) ? 0 : $frontMenu['pid'];
|
||||
if ($menuPid == $pid) {
|
||||
$children = $this->buildFrontMenuTree($frontMenus, $frontMenu['id']);
|
||||
if (!empty($children)) {
|
||||
$frontMenu['children'] = $children;
|
||||
}
|
||||
$tree[] = $frontMenu;
|
||||
}
|
||||
}
|
||||
|
||||
// 按 sort 字段降序排序
|
||||
usort($tree, function ($a, $b) {
|
||||
$sortA = $a['sort'] ?? 0;
|
||||
$sortB = $b['sort'] ?? 0;
|
||||
return $sortB - $sortA;
|
||||
});
|
||||
|
||||
return $tree;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据路径获取单页内容
|
||||
* @param string $path 路由路径
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function getOnePageByPath($path = '')
|
||||
{
|
||||
try {
|
||||
// 从路由参数获取路径
|
||||
if (empty($path)) {
|
||||
// 如果没有路由参数,尝试从 pathinfo 获取
|
||||
$requestPath = $this->request->pathinfo();
|
||||
$path = str_replace('onepage/', '', $requestPath);
|
||||
}
|
||||
|
||||
// 确保路径以 / 开头
|
||||
if (empty($path) || $path[0] !== '/') {
|
||||
$path = '/' . $path;
|
||||
}
|
||||
|
||||
// 解码路径
|
||||
$path = urldecode($path);
|
||||
|
||||
// 查找对应路径的单页
|
||||
$onePage = OnePage::where('path', $path)
|
||||
->where('status', 1)
|
||||
->where('delete_time', null)
|
||||
->find();
|
||||
|
||||
if (!$onePage) {
|
||||
return json([
|
||||
'code' => 404,
|
||||
'msg' => '单页不存在或已禁用',
|
||||
'data' => null
|
||||
]);
|
||||
}
|
||||
|
||||
return json([
|
||||
'code' => 200,
|
||||
'msg' => 'success',
|
||||
'data' => $onePage->toArray()
|
||||
]);
|
||||
} catch (DbException $e) {
|
||||
return json([
|
||||
'code' => 500,
|
||||
'msg' => 'fail:' . $e->getMessage(),
|
||||
'data' => null
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,434 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\index\controller;
|
||||
|
||||
use app\index\BaseController;
|
||||
use Symfony\Component\VarDumper\VarDumper;
|
||||
use think\exception\ValidateException;
|
||||
use think\facade\Request;
|
||||
use think\facade\Session;
|
||||
use think\response\Json;
|
||||
use think\db\exception\DbException;
|
||||
|
||||
use app\model\Articles;
|
||||
use app\model\ArticlesCategory;
|
||||
|
||||
|
||||
class NewsCenterController extends BaseController
|
||||
{
|
||||
/**
|
||||
* 根据分类获取文章
|
||||
* @param string $category 分类名称
|
||||
* @return Json
|
||||
*/
|
||||
public function getNewsByCategory(string $category): Json
|
||||
{
|
||||
// 查询分类
|
||||
$categoryInfo = ArticlesCategory::where('name', $category)
|
||||
->where('delete_time', null)
|
||||
->find();
|
||||
|
||||
if (!$categoryInfo) {
|
||||
return json([
|
||||
'code' => 200,
|
||||
'msg' => 'success',
|
||||
'list' => [],
|
||||
]);
|
||||
}
|
||||
|
||||
// 查询文章
|
||||
$articles = Articles::published()
|
||||
->where('cate', $categoryInfo['id'])
|
||||
->order('publishdate', 'desc')
|
||||
->limit(4)
|
||||
->select();
|
||||
|
||||
return json([
|
||||
'code' => 200,
|
||||
'msg' => 'success',
|
||||
'list' => $articles,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取企业新闻
|
||||
* @return Json
|
||||
*/
|
||||
public function getCompanyNews(): Json
|
||||
{
|
||||
// 获取分页参数
|
||||
$page = Request::param('page', 1);
|
||||
$limit = Request::param('limit', 10);
|
||||
$page = max(1, intval($page));
|
||||
$limit = max(1, min(50, intval($limit))); // 限制每页最多50条
|
||||
|
||||
// 查询分类
|
||||
$categoryInfo = ArticlesCategory::where('name', '企业新闻')
|
||||
->where('delete_time', null)
|
||||
->find();
|
||||
|
||||
if (!$categoryInfo) {
|
||||
return json([
|
||||
'code' => 200,
|
||||
'msg' => 'success',
|
||||
'list' => [],
|
||||
'total' => 0,
|
||||
'page' => $page,
|
||||
'limit' => $limit,
|
||||
]);
|
||||
}
|
||||
|
||||
// 查询总数
|
||||
$total = Articles::where('cate', $categoryInfo['id'])
|
||||
->where('delete_time', null)
|
||||
->where('status', 2)
|
||||
->count();
|
||||
|
||||
// 查询文章
|
||||
$articles = Articles::where('cate', $categoryInfo['id'])
|
||||
->where('delete_time', null)
|
||||
->where('status', 2)
|
||||
->order('top', 'desc')
|
||||
->order('recommend', 'desc')
|
||||
->order('sort', 'desc')
|
||||
->order('publishdate', 'desc')
|
||||
->page($page, $limit)
|
||||
->select();
|
||||
|
||||
return json([
|
||||
'code' => 200,
|
||||
'msg' => 'success',
|
||||
'list' => $articles,
|
||||
'total' => $total,
|
||||
'page' => $page,
|
||||
'limit' => $limit,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取金蝶新闻
|
||||
* @return Json
|
||||
*/
|
||||
public function getKingdeeNews(): Json
|
||||
{
|
||||
// 获取分页参数
|
||||
$page = Request::param('page', 1);
|
||||
$limit = Request::param('limit', 10);
|
||||
$page = max(1, intval($page));
|
||||
$limit = max(1, min(50, intval($limit))); // 限制每页最多50条
|
||||
|
||||
// 查询分类
|
||||
$categoryInfo = ArticlesCategory::where('name', '金蝶新闻')
|
||||
->where('delete_time', null)
|
||||
->find();
|
||||
|
||||
if (!$categoryInfo) {
|
||||
return json([
|
||||
'code' => 200,
|
||||
'msg' => 'success',
|
||||
'list' => [],
|
||||
'total' => 0,
|
||||
'page' => $page,
|
||||
'limit' => $limit,
|
||||
]);
|
||||
}
|
||||
|
||||
// 查询总数
|
||||
$total = Articles::where('cate', $categoryInfo['id'])
|
||||
->where('delete_time', null)
|
||||
->where('status', 2)
|
||||
->count();
|
||||
|
||||
// 查询文章
|
||||
$articles = Articles::where('cate', $categoryInfo['id'])
|
||||
->where('delete_time', null)
|
||||
->where('status', 2)
|
||||
->order('top', 'desc')
|
||||
->order('recommend', 'desc')
|
||||
->order('sort', 'desc')
|
||||
->order('publishdate', 'desc')
|
||||
->page($page, $limit)
|
||||
->select();
|
||||
|
||||
return json([
|
||||
'code' => 200,
|
||||
'msg' => 'success',
|
||||
'list' => $articles,
|
||||
'total' => $total,
|
||||
'page' => $page,
|
||||
'limit' => $limit,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取技术中心子分类
|
||||
* @return Json
|
||||
*/
|
||||
public function getTechnologyCategories(): Json
|
||||
{
|
||||
// 获取"技术中心"主分类
|
||||
$parentCategory = ArticlesCategory::where('name', '技术中心')
|
||||
->where('delete_time', null)
|
||||
->find();
|
||||
|
||||
if (!$parentCategory) {
|
||||
return json([
|
||||
'code' => 200,
|
||||
'msg' => 'success',
|
||||
'data' => []
|
||||
]);
|
||||
}
|
||||
|
||||
// 查找所有子分类
|
||||
$subCategories = ArticlesCategory::where('cid', $parentCategory['id'])
|
||||
->where('delete_time', null)
|
||||
->field('id,name,desc,sort,image')
|
||||
->order('sort', 'desc')
|
||||
->select();
|
||||
|
||||
return json([
|
||||
'code' => 200,
|
||||
'msg' => 'success',
|
||||
'data' => $subCategories
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取技术中心
|
||||
* @return Json
|
||||
*/
|
||||
public function getTechnologyCenter(): Json
|
||||
{
|
||||
// 1. 分页参数规范化
|
||||
$page = max(1, intval(Request::param('page', 1)));
|
||||
$limit = max(1, min(50, intval(Request::param('limit', 10))));
|
||||
|
||||
// 获取分类ID参数(可选)
|
||||
$categoryId = Request::param('category_id', null);
|
||||
|
||||
// 2. 第一步:获取“技术中心”主分类的 id
|
||||
$parentCategory = ArticlesCategory::where('name', '技术中心')
|
||||
->where('delete_time', null)
|
||||
->find();
|
||||
|
||||
// print_r($parentCategory['id']);
|
||||
|
||||
if (!$parentCategory) {
|
||||
return $this->emptyResponse($page, $limit);
|
||||
}
|
||||
|
||||
// 3. 第二步:查找 cid 等于主分类 id 的所有子分类 id
|
||||
$subCategoryQuery = ArticlesCategory::where('cid', $parentCategory['id'])
|
||||
->where('delete_time', null);
|
||||
|
||||
// print_r($subCategoryQuery->select());
|
||||
|
||||
// 如果指定了分类ID,则只查询该分类
|
||||
if ($categoryId) {
|
||||
$subCategoryQuery->where('id', $categoryId);
|
||||
}
|
||||
|
||||
$subCategoryIds = $subCategoryQuery->column('id');
|
||||
|
||||
// print_r($subCategoryIds);
|
||||
|
||||
if (empty($subCategoryIds)) {
|
||||
return $this->emptyResponse($page, $limit);
|
||||
}
|
||||
|
||||
// 5. 第三步:查询 Articles 表,并限定字段
|
||||
$articleQuery = Articles::whereIn('cate', $subCategoryIds)
|
||||
->where('delete_time', null)
|
||||
->where('status', 2);
|
||||
|
||||
// 获取总数
|
||||
$total = (clone $articleQuery)->count();
|
||||
|
||||
// 获取列表:限定返回 id, title, desc, publishdate, image, likes, views
|
||||
$articles = $articleQuery->field('id,title,desc,publishdate,image,likes,views,cate')
|
||||
->order([
|
||||
'top' => 'desc',
|
||||
'recommend' => 'desc',
|
||||
'sort' => 'desc',
|
||||
'publishdate' => 'desc'
|
||||
])
|
||||
->page($page, $limit)
|
||||
->select();
|
||||
|
||||
// 如果文章没有image,查询对应分类的image
|
||||
$categoryImages = ArticlesCategory::whereIn('id', $subCategoryIds)->column('image', 'id');
|
||||
|
||||
foreach ($articles as &$article) {
|
||||
if (empty($article['image']) && isset($categoryImages[$article['cate']])) {
|
||||
$article['image'] = $categoryImages[$article['cate']];
|
||||
}
|
||||
}
|
||||
|
||||
return json([
|
||||
'code' => 200,
|
||||
'msg' => 'success',
|
||||
'list' => $articles,
|
||||
'total' => $total,
|
||||
'page' => $page,
|
||||
'limit' => $limit,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 统一空返回
|
||||
*/
|
||||
private function emptyResponse($page, $limit): Json
|
||||
{
|
||||
return json([
|
||||
'code' => 200,
|
||||
'msg' => 'success',
|
||||
'list' => [],
|
||||
'total' => 0,
|
||||
'page' => $page,
|
||||
'limit' => $limit,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取金蝶新闻详情
|
||||
*/
|
||||
public function getKingdeeNewsDetail(int $id): Json
|
||||
{
|
||||
$article = Articles::where('id', $id)
|
||||
->where('delete_time', null)
|
||||
->where('status', 2)
|
||||
->field('id,title,cate,image,desc,author,content,publisher,publishdate,views,likes,is_trans,transurl')
|
||||
->find();
|
||||
|
||||
if (!$article) {
|
||||
return json(['code' => 404, 'msg' => '文章不存在', 'data' => null]);
|
||||
}
|
||||
|
||||
$cate = (int) $article['cate'];
|
||||
|
||||
// 转换为数组再处理
|
||||
$articleData = $article->toArray();
|
||||
// $articleData['catename'] = $this->getCategoryName($cate);
|
||||
|
||||
// 获取相关文章
|
||||
$articleData['relatedArticles'] = $this->getRelatedArticles($id, $cate);
|
||||
|
||||
// 获取上一篇下一篇
|
||||
$articleData['nextPreviousArticles'] = $this->getNextPreviousArticles($id, $cate);
|
||||
|
||||
// 增加浏览量
|
||||
Articles::where('id', $id)->inc('views')->update();
|
||||
|
||||
return json(['code' => 200, 'msg' => 'success', 'data' => $articleData]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取企业新闻详情
|
||||
*/
|
||||
public function getCompanyNewsDetail(int $id): Json
|
||||
{
|
||||
$article = Articles::where('id', $id)
|
||||
->where('delete_time', null)
|
||||
->where('status', 2)
|
||||
->find();
|
||||
|
||||
if (!$article) {
|
||||
return json(['code' => 404, 'msg' => '文章不存在', 'data' => null]);
|
||||
}
|
||||
|
||||
$cate = (int) $article['cate'];
|
||||
|
||||
// 转换为数组再处理
|
||||
$articleData = $article->toArray();
|
||||
$articleData['catename'] = $this->getCategoryName($cate);
|
||||
|
||||
// 获取相关文章
|
||||
$articleData['relatedArticles'] = $this->getRelatedArticles($id, $cate);
|
||||
|
||||
// 获取上一篇下一篇
|
||||
$articleData['nextPreviousArticles'] = $this->getNextPreviousArticles($id, $cate);
|
||||
|
||||
// 增加浏览量
|
||||
Articles::where('id', $id)->inc('views')->update();
|
||||
|
||||
return json(['code' => 200, 'msg' => 'success', 'data' => $articleData]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取上一篇下一篇
|
||||
* @param int $id 文章ID
|
||||
* @param int $cate 分类ID
|
||||
* @return array
|
||||
*/
|
||||
private function getNextPreviousArticles(int $id, int $cate): array
|
||||
{
|
||||
$nextArticle = Articles::where('id', '<', $id)
|
||||
->where('cate', $cate)
|
||||
->where('delete_time', null)
|
||||
->where('status', 2)
|
||||
->field('id,title')
|
||||
->find();
|
||||
|
||||
$previousArticle = Articles::where('id', '>', $id)
|
||||
->where('cate', $cate)
|
||||
->where('delete_time', null)
|
||||
->where('status', 2)
|
||||
->field('id,title')
|
||||
->find();
|
||||
|
||||
return [
|
||||
'code' => 200,
|
||||
'msg' => 'success',
|
||||
'next' => $nextArticle,
|
||||
'previous' => $previousArticle,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过分类 ID 获取分类名称
|
||||
* @param int $cateId 分类ID
|
||||
* @return string
|
||||
*/
|
||||
private function getCategoryName(int $id): string
|
||||
{
|
||||
$categoryInfo = ArticlesCategory::where('id', $id)
|
||||
->where('delete_time', null)
|
||||
->find();
|
||||
|
||||
if (!$categoryInfo) {
|
||||
return '未分类';
|
||||
}
|
||||
|
||||
return $categoryInfo['name'];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取相关文章
|
||||
*/
|
||||
private function getRelatedArticles(int $id, int $cate): array
|
||||
{
|
||||
$articles = Articles::where('id', '<>', $id)
|
||||
->where('cate', $cate)
|
||||
->where('delete_time', null)
|
||||
->where('status', 2)
|
||||
->order('top', 'desc')
|
||||
->order('recommend', 'desc')
|
||||
->order('sort', 'desc')
|
||||
->order('id', 'desc')
|
||||
->limit(5)
|
||||
->select();
|
||||
|
||||
foreach ($articles as &$article) {
|
||||
$article['cate'] = $this->getCategoryName($article['cate']);
|
||||
}
|
||||
|
||||
return [
|
||||
'code' => 200,
|
||||
'msg' => 'success',
|
||||
'list' => $articles,
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
<?php
|
||||
// 这是系统自动生成的event定义文件
|
||||
return [
|
||||
|
||||
];
|
||||
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
// 全局中间件定义文件
|
||||
return [
|
||||
// 全局跨域中间件
|
||||
\think\middleware\AllowCrossDomain::class,
|
||||
// 全局请求缓存
|
||||
// \think\middleware\CheckRequestCache::class,
|
||||
// 多语言加载
|
||||
// \think\middleware\LoadLangPack::class,
|
||||
// Session初始化
|
||||
\think\middleware\SessionInit::class,
|
||||
];
|
||||
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
use think\facade\Route;
|
||||
|
||||
// 首页路由
|
||||
Route::get('/', 'app\index\controller\Index@index');
|
||||
Route::get('index/index', 'app\index\controller\Index@index');
|
||||
|
||||
// --- 文章详情路由 ---
|
||||
Route::get('kingdeenews/detail/:id', 'app\index\controller\NewsCenterController@getKingdeeNewsDetail');
|
||||
Route::get('companynews/detail/:id', 'app\index\controller\NewsCenterController@getCompanyNewsDetail');
|
||||
|
||||
// --- 文章列表路由 ---
|
||||
Route::get('kingdeenews$', 'app\index\controller\NewsCenterController@getKingdeeNews');
|
||||
Route::get('companynews$', 'app\index\controller\NewsCenterController@getCompanyNews');
|
||||
Route::get('technologyCenter$', 'app\index\controller\NewsCenterController@getTechnologyCenter');
|
||||
Route::get('technologyCategories$', 'app\index\controller\NewsCenterController@getTechnologyCategories');
|
||||
Route::get('newscentertop4', 'app\index\controller\Article\ArticleController@getNewsCenterTop4');
|
||||
Route::get('newsbycategory/:category', 'app\index\controller\NewsCenterController@getNewsByCategory');
|
||||
|
||||
// --- 文章互动路由 ---
|
||||
Route::post('articleViews/:id', 'app\index\controller\Article\ArticleController@articleViews');
|
||||
Route::post('articleLikes/:id', 'app\index\controller\Article\ArticleController@articleLikes');
|
||||
Route::post('articleUnlikes/:id', 'app\index\controller\Article\ArticleController@articleUnlikes');
|
||||
|
||||
// --- 前端导航与单页路由 ---
|
||||
Route::get('headmenu', 'app\index\controller\Index@getHeadMenu');
|
||||
Route::rule('onepage/:path', 'app\index\controller\Index@getOnePageByPath', 'GET')->pattern(['path' => '.*']);
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006-2018 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: Liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace app\model;
|
||||
|
||||
use think\Model;
|
||||
use think\model\concern\SoftDelete;
|
||||
|
||||
/**
|
||||
* 文章分类模型
|
||||
*/
|
||||
class AdminUser extends Model
|
||||
{
|
||||
// 启用软删除
|
||||
use SoftDelete;
|
||||
|
||||
// 数据库表名
|
||||
protected $name = 'mete_admin_user';
|
||||
|
||||
// 字段类型转换
|
||||
protected $type = [
|
||||
'id' => 'integer',
|
||||
'sex' => 'integer',
|
||||
'group_id' => 'integer',
|
||||
'login_count' => 'integer',
|
||||
'status' => 'integer',
|
||||
'api_key_status' => 'integer',
|
||||
'create_time' => 'datetime',
|
||||
'update_time' => 'datetime',
|
||||
'delete_time' => 'datetime',
|
||||
];
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006-2018 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: Liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace app\model;
|
||||
|
||||
use think\Model;
|
||||
use think\model\concern\SoftDelete;
|
||||
|
||||
/**
|
||||
* 用户组模型
|
||||
*/
|
||||
class AdminUserGroup extends Model
|
||||
{
|
||||
// 启用软删除
|
||||
use SoftDelete;
|
||||
|
||||
// 数据库表名
|
||||
protected $name = 'mete_admin_user_group';
|
||||
|
||||
// 字段类型转换
|
||||
protected $type = [
|
||||
'id' => 'integer',
|
||||
'name' => 'string',
|
||||
'status' => 'integer',
|
||||
'rights' => 'string',
|
||||
'create_time' => 'datetime',
|
||||
'update_time' => 'datetime',
|
||||
'delete_time' => 'datetime',
|
||||
];
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006-2018 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: Liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace app\model;
|
||||
|
||||
use think\Model;
|
||||
use think\model\concern\SoftDelete;
|
||||
|
||||
/**
|
||||
* 文章模型
|
||||
*/
|
||||
class Articles extends Model
|
||||
{
|
||||
// 启用软删除
|
||||
use SoftDelete;
|
||||
|
||||
// 数据库表名
|
||||
protected $name = 'mete_articles';
|
||||
|
||||
// 自动写入时间戳
|
||||
protected $autoWriteTimestamp = true;
|
||||
|
||||
// 时间戳字段名
|
||||
protected $createTime = 'create_time';
|
||||
protected $updateTime = 'update_time';
|
||||
protected $deleteTime = 'delete_time';
|
||||
|
||||
// 字段类型转换
|
||||
protected $type = [
|
||||
'id' => 'integer',
|
||||
'cate' => 'integer',
|
||||
'sort' => 'integer',
|
||||
'status' => 'integer',
|
||||
'is_trans' => 'integer',
|
||||
'views' => 'integer',
|
||||
'likes' => 'integer',
|
||||
'recommend' => 'integer',
|
||||
'top' => 'integer',
|
||||
'publisher' => 'integer',
|
||||
'create_time' => 'datetime',
|
||||
'update_time' => 'datetime',
|
||||
'publishdate' => 'datetime',
|
||||
'delete_time' => 'datetime',
|
||||
];
|
||||
|
||||
/**
|
||||
* 关联文章分类
|
||||
*/
|
||||
public function category()
|
||||
{
|
||||
return $this->belongsTo('app\index\model\ArticleCategory', 'cate', 'id', [], 'LEFT')->bind([
|
||||
'cate_name' => 'name',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 关联发布者
|
||||
*/
|
||||
public function publisher()
|
||||
{
|
||||
return $this->belongsTo('app\index\model\AdminUser', 'publisher', 'id', [], 'LEFT')->bind([
|
||||
'publisher_name' => 'name',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取已发布的文章
|
||||
*/
|
||||
public static function published()
|
||||
{
|
||||
return self::where('status', 2)->where('delete_time', null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取推荐文章
|
||||
*/
|
||||
public static function recommended()
|
||||
{
|
||||
return self::published()->where('recommend', 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取置顶文章
|
||||
*/
|
||||
public static function top()
|
||||
{
|
||||
return self::published()->where('top', 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 按分类获取文章
|
||||
*/
|
||||
public static function byCategory($cateId)
|
||||
{
|
||||
return self::published()->where('cate', $cateId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 按关键词搜索文章
|
||||
*/
|
||||
public static function search($keyword)
|
||||
{
|
||||
return self::published()->where('title', 'like', '%' . $keyword . '%');
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新阅读量
|
||||
*/
|
||||
public function incrementViews()
|
||||
{
|
||||
return $this->where('id', $this->id)->inc('views')->update();
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新点赞量
|
||||
*/
|
||||
public function incrementLikes()
|
||||
{
|
||||
return $this->where('id', $this->id)->inc('likes')->update();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006-2018 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: Liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace app\model;
|
||||
|
||||
use think\Model;
|
||||
use think\model\concern\SoftDelete;
|
||||
|
||||
/**
|
||||
* 文章分类模型
|
||||
*/
|
||||
class ArticlesCategory extends Model
|
||||
{
|
||||
// 启用软删除
|
||||
use SoftDelete;
|
||||
|
||||
// 数据库表名
|
||||
protected $name = 'mete_articles_category';
|
||||
|
||||
// 自动写入时间戳
|
||||
protected $autoWriteTimestamp = true;
|
||||
|
||||
// 时间戳字段名
|
||||
protected $createTime = 'create_time';
|
||||
protected $updateTime = 'update_time';
|
||||
protected $deleteTime = 'delete_time';
|
||||
|
||||
// 字段类型转换
|
||||
protected $type = [
|
||||
'id' => 'integer',
|
||||
'sort' => 'integer',
|
||||
'status' => 'integer',
|
||||
'create_time' => 'datetime',
|
||||
'update_time' => 'datetime',
|
||||
'delete_time' => 'datetime',
|
||||
];
|
||||
|
||||
/**
|
||||
* 关联文章
|
||||
*/
|
||||
public function articles()
|
||||
{
|
||||
return $this->hasMany('app\index\model\Articles', 'cate', 'id');
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有分类
|
||||
*/
|
||||
public static function allCategories()
|
||||
{
|
||||
return self::where('delete_time', null)->order('sort asc, id asc');
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取启用的分类
|
||||
*/
|
||||
public static function enabled()
|
||||
{
|
||||
return self::where('status', 1)->where('delete_time', null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取分类下的文章数量
|
||||
*/
|
||||
public function articleCount()
|
||||
{
|
||||
return $this->hasMany('app\index\model\Articles', 'cate', 'id')
|
||||
->where('status', 1)
|
||||
->where('delete_time', null)
|
||||
->count();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006-2018 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: Liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace app\model;
|
||||
|
||||
use think\Model;
|
||||
use think\model\concern\SoftDelete;
|
||||
|
||||
/**
|
||||
* 文章分类模型
|
||||
*/
|
||||
class Banner extends Model
|
||||
{
|
||||
// 启用软删除
|
||||
use SoftDelete;
|
||||
|
||||
// 数据库表名
|
||||
protected $name = 'mete_banner';
|
||||
|
||||
// 字段类型转换
|
||||
protected $type = [
|
||||
'id' => 'integer',
|
||||
'title' => 'string',
|
||||
'desc' => 'string',
|
||||
'url' => 'string',
|
||||
'image' => 'string',
|
||||
'sort' => 'integer',
|
||||
'create_time' => 'datetime',
|
||||
'update_time' => 'datetime',
|
||||
'delete_time' => 'datetime',
|
||||
];
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006-2018 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: Liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace app\model;
|
||||
|
||||
use think\Model;
|
||||
use think\model\concern\SoftDelete;
|
||||
|
||||
/**
|
||||
* 文章分类模型
|
||||
*/
|
||||
class Files extends Model
|
||||
{
|
||||
// 启用软删除
|
||||
use SoftDelete;
|
||||
|
||||
// 数据库表名
|
||||
protected $name = 'mete_files';
|
||||
|
||||
// 字段类型转换
|
||||
protected $type = [
|
||||
'id' => 'integer',
|
||||
'name' => 'string',
|
||||
'type' => 'integer',
|
||||
'cate' => 'integer',
|
||||
'size' => 'integer',
|
||||
'src' => 'string',
|
||||
'uploader' => 'integer',
|
||||
'md5' => 'string',
|
||||
'create_time' => 'datetime',
|
||||
'update_time' => 'datetime',
|
||||
'delete_time' => 'datetime',
|
||||
];
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006-2018 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: Liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace app\model;
|
||||
|
||||
use think\Model;
|
||||
use think\model\concern\SoftDelete;
|
||||
|
||||
/**
|
||||
* 文章分类模型
|
||||
*/
|
||||
class FilesCategory extends Model
|
||||
{
|
||||
// 启用软删除
|
||||
use SoftDelete;
|
||||
|
||||
// 数据库表名
|
||||
protected $name = 'mete_files_category';
|
||||
|
||||
// 字段类型转换
|
||||
protected $type = [
|
||||
'id' => 'integer',
|
||||
'name' => 'string',
|
||||
'create_time' => 'datetime',
|
||||
'update_time' => 'datetime',
|
||||
'delete_time' => 'datetime',
|
||||
];
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006-2018 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: Liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace app\model;
|
||||
|
||||
use think\Model;
|
||||
use think\model\concern\SoftDelete;
|
||||
|
||||
/**
|
||||
* 文章分类模型
|
||||
*/
|
||||
class FrontMenu extends Model
|
||||
{
|
||||
// 启用软删除
|
||||
use SoftDelete;
|
||||
|
||||
// 数据库表名
|
||||
protected $name = 'mete_front_menu';
|
||||
|
||||
// 字段类型转换
|
||||
protected $type = [
|
||||
'id' => 'integer',
|
||||
'pid' => 'integer',
|
||||
'title' => 'string',
|
||||
'type' => 'integer',
|
||||
'path' => 'string',
|
||||
'component_path' => 'string',
|
||||
'desc' => 'string',
|
||||
'create_time' => 'datetime',
|
||||
'update_time' => 'datetime',
|
||||
'delete_time' => 'datetime',
|
||||
];
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006-2018 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: Liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace app\model;
|
||||
|
||||
use think\Model;
|
||||
use think\model\concern\SoftDelete;
|
||||
|
||||
/**
|
||||
* 单页模型
|
||||
*/
|
||||
class OnePage extends Model
|
||||
{
|
||||
// 启用软删除
|
||||
use SoftDelete;
|
||||
|
||||
// 数据库表名
|
||||
protected $name = 'mete_onepage';
|
||||
|
||||
// 字段类型转换
|
||||
protected $type = [
|
||||
'id' => 'integer',
|
||||
'title' => 'string',
|
||||
'content' => 'string',
|
||||
'path' => 'string',
|
||||
'sort' => 'integer',
|
||||
'status' => 'integer',
|
||||
'create_time' => 'datetime',
|
||||
'update_time' => 'datetime',
|
||||
'delete_time' => 'datetime',
|
||||
];
|
||||
}
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006-2018 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: Liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace app\model;
|
||||
|
||||
use think\Model;
|
||||
use think\model\concern\SoftDelete;
|
||||
|
||||
/**
|
||||
* 操作日志模型
|
||||
*/
|
||||
class OperationLog extends Model
|
||||
{
|
||||
// 启用软删除
|
||||
use SoftDelete;
|
||||
|
||||
// 数据库表名
|
||||
protected $name = 'mete_operation_log';
|
||||
|
||||
// 字段类型转换
|
||||
protected $type = [
|
||||
'id' => 'integer',
|
||||
'user_id' => 'integer',
|
||||
'status' => 'integer',
|
||||
'execution_time' => 'float',
|
||||
'create_time' => 'datetime',
|
||||
'update_time' => 'datetime',
|
||||
'delete_time' => 'datetime',
|
||||
];
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006-2018 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: Liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace app\model;
|
||||
|
||||
use think\Model;
|
||||
use think\model\concern\SoftDelete;
|
||||
|
||||
/**
|
||||
* 系统菜单模型
|
||||
*/
|
||||
class SystemMenu extends Model
|
||||
{
|
||||
// 启用软删除
|
||||
use SoftDelete;
|
||||
|
||||
// 数据库表名
|
||||
protected $name = 'mete_system_menu';
|
||||
|
||||
// 字段类型转换
|
||||
protected $type = [
|
||||
'id' => 'integer',
|
||||
'pid' => 'integer',
|
||||
'title' => 'string',
|
||||
'path' => 'string',
|
||||
'component_path' => 'string',
|
||||
'icon' => 'string',
|
||||
'sort' => 'integer',
|
||||
'status' => 'integer',
|
||||
'type' => 'integer',
|
||||
'permission' => 'string',
|
||||
'remark' => 'string',
|
||||
'creater' => 'string',
|
||||
'create_time' => 'datetime',
|
||||
'update_time' => 'datetime',
|
||||
'delete_time' => 'datetime',
|
||||
];
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006-2018 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: Liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace app\model;
|
||||
|
||||
use think\Model;
|
||||
use think\model\concern\SoftDelete;
|
||||
|
||||
/**
|
||||
* 文章分类模型
|
||||
*/
|
||||
class SystemSiteSettings extends Model
|
||||
{
|
||||
// 启用软删除
|
||||
use SoftDelete;
|
||||
|
||||
// 数据库表名
|
||||
protected $name = 'mete_system_site_settings';
|
||||
|
||||
// 字段类型转换
|
||||
protected $type = [
|
||||
'id' => 'integer',
|
||||
'label' => 'string',
|
||||
'value' => 'string',
|
||||
'sort' => 'integer',
|
||||
'remark' => 'string',
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\service;
|
||||
|
||||
use Firebase\JWT\JWT;
|
||||
use Firebase\JWT\Key;
|
||||
|
||||
class JwtService
|
||||
{
|
||||
private static string $secret = 'meitian@#!';
|
||||
private static int $expire = 86400;
|
||||
|
||||
public static function generateToken(array $userInfo): string
|
||||
{
|
||||
$payload = [
|
||||
'iss' => 'backapi.yunzer.cn',
|
||||
'sub' => $userInfo['id'],
|
||||
'iat' => time(),
|
||||
'exp' => time() + self::$expire,
|
||||
'user' => $userInfo
|
||||
];
|
||||
return JWT::encode($payload, self::$secret, 'HS256');
|
||||
}
|
||||
|
||||
public static function verifyToken(string $token): ?array
|
||||
{
|
||||
try {
|
||||
$decoded = JWT::decode($token, new Key(self::$secret, 'HS256'));
|
||||
return (array)$decoded;
|
||||
} catch (\Exception $e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static function getUserFromHeader(string $authHeader): array
|
||||
{
|
||||
if (!preg_match('/Bearer\s+(.+)/i', $authHeader, $matches)) {
|
||||
return ['id' => 0, 'account' => '', 'name' => ''];
|
||||
}
|
||||
|
||||
$tokenData = self::verifyToken($matches[1]);
|
||||
if (!$tokenData || !isset($tokenData['user'])) {
|
||||
return ['id' => 0, 'account' => '', 'name' => ''];
|
||||
}
|
||||
|
||||
return (array)$tokenData['user'];
|
||||
}
|
||||
|
||||
public static function getSecret(): string
|
||||
{
|
||||
return self::$secret;
|
||||
}
|
||||
|
||||
public static function setSecret(string $secret): void
|
||||
{
|
||||
self::$secret = $secret;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user