443 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			443 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
| <?php
 | ||
| /**
 | ||
|  *	后台管理系统-首页
 | ||
|  */
 | ||
| namespace app\admin\controller;
 | ||
| use app\admin\controller\Base;
 | ||
| use think\facade\Db;
 | ||
| use think\facade\View;
 | ||
| use think\facade\Env;
 | ||
| use think\facade\Config;
 | ||
| 
 | ||
| class Index extends Base{
 | ||
| 	# 首页
 | ||
| 	public function index(){
 | ||
| 		$menus = [];
 | ||
| 		$menu = [];
 | ||
| 		$where = ['group_id'=>$this->aUser['group_id']];
 | ||
| 		$role = Db::name('admin_user_group')->where($where)->find();
 | ||
| 		if($role){
 | ||
| 			$role['rights'] = (isset($role['rights']) && $role['rights']) ? json_decode($role['rights'],true) : [];
 | ||
| 		}
 | ||
| 		if($role['rights']){
 | ||
| 			$where = [
 | ||
| 				['smid','in',implode(',',$role['rights']) ],
 | ||
| 				['status','=',1]
 | ||
| 			];
 | ||
| 			// 获取所有菜单
 | ||
| 			$menus = Db::name('admin_sys_menu')->order('type,sort desc')->where($where)->select()->toArray();
 | ||
| 			
 | ||
| 			// 构建树形结构菜单
 | ||
| 			$menuTree = [];
 | ||
| 			$menuMap = [];
 | ||
| 			
 | ||
| 			// 先将所有菜单项映射到一个关联数组中
 | ||
| 			foreach($menus as $item){
 | ||
| 				$item['children'] = [];
 | ||
| 				$menuMap[$item['smid']] = $item;
 | ||
| 			}
 | ||
| 			
 | ||
| 			// 构建树形结构
 | ||
| 			foreach($menus as $item){
 | ||
| 				if($item['parent_id'] == 0){
 | ||
| 					// 顶级菜单
 | ||
| 					$menuTree[$item['smid']] = &$menuMap[$item['smid']];
 | ||
| 				}else{
 | ||
| 					// 子菜单,添加到父菜单的children数组中
 | ||
| 					if(isset($menuMap[$item['parent_id']])){
 | ||
| 						$menuMap[$item['parent_id']]['children'][] = &$menuMap[$item['smid']];
 | ||
| 					}
 | ||
| 				}
 | ||
| 			}
 | ||
| 			
 | ||
| 			$menu = $menuTree;
 | ||
| 		}
 | ||
| 		
 | ||
| 		View::assign([
 | ||
| 			'role' => $role,
 | ||
| 			'menu' => $menu
 | ||
| 		]);
 | ||
| 		return View::fetch();
 | ||
| 	}
 | ||
| 	# 欢迎页面
 | ||
| 	public function welcome(){
 | ||
| 		// 获取今日统计数据
 | ||
| 		$today = date('Y-m-d');
 | ||
| 		$todayStats = Db::name('daily_stats')
 | ||
| 			->where('date', $today)
 | ||
| 			->find();
 | ||
| 
 | ||
| 		// 获取最近7天的访问趋势
 | ||
| 		$last7Days = Db::name('daily_stats')
 | ||
| 			->where('date', '>=', date('Y-m-d', strtotime('-7 days')))
 | ||
| 			->where('date', '<=', $today)
 | ||
| 			->order('date', 'asc')
 | ||
| 			->select()
 | ||
| 			->toArray();
 | ||
| 
 | ||
| 		// 获取用户增长趋势
 | ||
| 		$userGrowth = Db::name('daily_stats')
 | ||
| 			->where('date', '>=', date('Y-m-d', strtotime('-30 days')))
 | ||
| 			->where('date', '<=', $today)
 | ||
| 			->field('date, new_users, total_users')
 | ||
| 			->order('date', 'asc')
 | ||
| 			->select()
 | ||
| 			->toArray();
 | ||
| 
 | ||
| 		// 获取资源下载统计
 | ||
| 		$resourceStats = Db::name('daily_stats')
 | ||
| 			->where('date', '>=', date('Y-m-d', strtotime('-7 days')))
 | ||
| 			->where('date', '<=', $today)
 | ||
| 			->field('date, daily_resources, resource_downloads')
 | ||
| 			->order('date', 'asc')
 | ||
| 			->select()
 | ||
| 			->toArray();
 | ||
| 
 | ||
| 		// 获取文章访问统计
 | ||
| 		$articleStats = Db::name('daily_stats')
 | ||
| 			->where('date', '>=', date('Y-m-d', strtotime('-7 days')))
 | ||
| 			->where('date', '<=', $today)
 | ||
| 			->field('date, daily_articles, article_views')
 | ||
| 			->order('date', 'asc')
 | ||
| 			->select()
 | ||
| 			->toArray();
 | ||
| 
 | ||
| 		// 获取最近的活动记录
 | ||
| 		$recentActivities = $this->getRecentActivities();
 | ||
| 
 | ||
| 		// 准备图表数据
 | ||
| 		$chartData = [
 | ||
| 			'visitTrend' => $this->formatVisitTrendData($last7Days),
 | ||
| 			'userGrowth' => $this->formatUserGrowthData($userGrowth),
 | ||
| 			'resourceStats' => $this->formatResourceStatsData($resourceStats),
 | ||
| 			'articleStats' => $this->formatArticleStatsData($articleStats)
 | ||
| 		];
 | ||
| 
 | ||
| 		// 准备统计数据
 | ||
| 		$stats = [
 | ||
| 			'total_users' => $todayStats['total_users'] ?? 0,
 | ||
| 			'daily_visits' => $todayStats['daily_visits'] ?? 0,
 | ||
| 			'total_articles' => $todayStats['total_articles'] ?? 0,
 | ||
| 			'total_resources' => $todayStats['total_resources'] ?? 0,
 | ||
| 		];
 | ||
| 
 | ||
| 		return View::fetch('', [
 | ||
| 			'stats' => $stats,
 | ||
| 			'chartData' => $chartData,
 | ||
| 			'recentActivities' => $recentActivities
 | ||
| 		]);
 | ||
| 	}
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * 获取最近的活动记录
 | ||
| 	 */
 | ||
| 	private function getRecentActivities()
 | ||
| 	{
 | ||
| 		$today = date('Y-m-d');
 | ||
| 		$activities = [];
 | ||
| 
 | ||
| 		// 获取今日新用户
 | ||
| 		$newUsers = Db::name('daily_stats')
 | ||
| 			->where('date', $today)
 | ||
| 			->value('new_users');
 | ||
| 		if ($newUsers > 0) {
 | ||
| 			$activities[] = [
 | ||
| 				'icon' => '👥',
 | ||
| 				'title' => '新增用户 ' . $newUsers . ' 人',
 | ||
| 				'time' => '今日'
 | ||
| 			];
 | ||
| 		}
 | ||
| 
 | ||
| 		// 获取今日文章
 | ||
| 		$newArticles = Db::name('daily_stats')
 | ||
| 			->where('date', $today)
 | ||
| 			->value('daily_articles');
 | ||
| 		if ($newArticles > 0) {
 | ||
| 			$activities[] = [
 | ||
| 				'icon' => '📝',
 | ||
| 				'title' => '发布文章 ' . $newArticles . ' 篇',
 | ||
| 				'time' => '今日'
 | ||
| 			];
 | ||
| 		}
 | ||
| 
 | ||
| 		// 获取今日资源
 | ||
| 		$newResources = Db::name('daily_stats')
 | ||
| 			->where('date', $today)
 | ||
| 			->value('daily_resources');
 | ||
| 		if ($newResources > 0) {
 | ||
| 			$activities[] = [
 | ||
| 				'icon' => '📦',
 | ||
| 				'title' => '上传资源 ' . $newResources . ' 个',
 | ||
| 				'time' => '今日'
 | ||
| 			];
 | ||
| 		}
 | ||
| 
 | ||
| 		return $activities;
 | ||
| 	}
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * 格式化访问趋势数据
 | ||
| 	 */
 | ||
| 	private function formatVisitTrendData($data)
 | ||
| 	{
 | ||
| 		$dates = [];
 | ||
| 		$visits = [];
 | ||
| 		$uvs = [];
 | ||
| 
 | ||
| 		foreach ($data as $item) {
 | ||
| 			$dates[] = date('m-d', strtotime($item['date']));
 | ||
| 			$visits[] = $item['daily_visits'];
 | ||
| 			$uvs[] = $item['unique_visitors'];
 | ||
| 		}
 | ||
| 
 | ||
| 		return [
 | ||
| 			'dates' => $dates,
 | ||
| 			'visits' => $visits,
 | ||
| 			'uvs' => $uvs
 | ||
| 		];
 | ||
| 	}
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * 格式化用户增长数据
 | ||
| 	 */
 | ||
| 	private function formatUserGrowthData($data)
 | ||
| 	{
 | ||
| 		$dates = [];
 | ||
| 		$newUsers = [];
 | ||
| 		$totalUsers = [];
 | ||
| 
 | ||
| 		foreach ($data as $item) {
 | ||
| 			$dates[] = date('m-d', strtotime($item['date']));
 | ||
| 			$newUsers[] = $item['new_users'];
 | ||
| 			$totalUsers[] = $item['total_users'];
 | ||
| 		}
 | ||
| 
 | ||
| 		return [
 | ||
| 			'dates' => $dates,
 | ||
| 			'newUsers' => $newUsers,
 | ||
| 			'totalUsers' => $totalUsers
 | ||
| 		];
 | ||
| 	}
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * 格式化资源统计数据
 | ||
| 	 */
 | ||
| 	private function formatResourceStatsData($data)
 | ||
| 	{
 | ||
| 		$dates = [];
 | ||
| 		$resources = [];
 | ||
| 		$downloads = [];
 | ||
| 
 | ||
| 		foreach ($data as $item) {
 | ||
| 			$dates[] = date('m-d', strtotime($item['date']));
 | ||
| 			$resources[] = $item['daily_resources'];
 | ||
| 			$downloads[] = $item['resource_downloads'];
 | ||
| 		}
 | ||
| 
 | ||
| 		return [
 | ||
| 			'dates' => $dates,
 | ||
| 			'resources' => $resources,
 | ||
| 			'downloads' => $downloads
 | ||
| 		];
 | ||
| 	}
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * 格式化文章统计数据
 | ||
| 	 */
 | ||
| 	private function formatArticleStatsData($data)
 | ||
| 	{
 | ||
| 		$dates = [];
 | ||
| 		$articles = [];
 | ||
| 		$views = [];
 | ||
| 
 | ||
| 		foreach ($data as $item) {
 | ||
| 			$dates[] = date('m-d', strtotime($item['date']));
 | ||
| 			$articles[] = $item['daily_articles'];
 | ||
| 			$views[] = $item['article_views'];
 | ||
| 		}
 | ||
| 
 | ||
| 		return [
 | ||
| 			'dates' => $dates,
 | ||
| 			'articles' => $articles,
 | ||
| 			'views' => $views
 | ||
| 		];
 | ||
| 	}
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * 保存附件信息到数据库
 | ||
| 	 * @param string $name 文件名
 | ||
| 	 * @param int $type 附件类型
 | ||
| 	 * @param int $size 文件大小
 | ||
| 	 * @param string $src 文件路径
 | ||
| 	 * @return int 附件ID
 | ||
| 	 */
 | ||
| 	private function saveAttachment($name, $type, $size, $src) {
 | ||
| 		$data = [
 | ||
| 			'name' => $name,
 | ||
| 			'type' => $type,
 | ||
| 			'size' => $size,
 | ||
| 			'src' => $src,
 | ||
| 			'create_time' => time(),
 | ||
| 			'update_time' => time()
 | ||
| 		];
 | ||
| 		return Db::name('attachments')->insertGetId($data);
 | ||
| 	}
 | ||
| 
 | ||
| 	# 图片上传
 | ||
| 	public function upload_img(){
 | ||
| 		// 获取上传的文件
 | ||
| 		$file = request()->file();
 | ||
| 		$files = request()->file('file');
 | ||
| 		
 | ||
| 		// 检查是否有文件上传
 | ||
| 		if(empty($file)){
 | ||
| 			return json(['code'=>1, 'msg'=>'没有文件上传'])->send();
 | ||
| 		}
 | ||
| 		
 | ||
| 		try {
 | ||
| 			// 验证上传的文件
 | ||
| 			validate([
 | ||
| 				'image'=>'filesize:10240|fileExt:jpg,png,gif,jpeg'
 | ||
| 			])->check($file);
 | ||
| 			
 | ||
| 			// 存储文件到public磁盘的uploads目录
 | ||
| 			$info = \think\facade\Filesystem::disk('public')->putFile('uploads', $files);
 | ||
| 			
 | ||
| 			// 处理文件路径,统一使用正斜杠
 | ||
| 			$info = str_replace("\\", "/", $info);
 | ||
| 			$img = '/storage/'.$info;
 | ||
| 			
 | ||
| 			// 保存附件信息
 | ||
| 			$fileName = $files->getOriginalName();
 | ||
| 			$fileSize = $files->getSize();
 | ||
| 			$attachmentId = $this->saveAttachment($fileName, 1, $fileSize, $img); // 1: 图片
 | ||
| 			
 | ||
| 			// 返回成功信息
 | ||
| 			return json([
 | ||
| 				'code' => 0, 
 | ||
| 				'data' => $img, 
 | ||
| 				'url' => $this->config['admin_domain'].$img,
 | ||
| 				'attachment_id' => $attachmentId
 | ||
| 			])->send();
 | ||
| 			
 | ||
| 		} catch (\think\exception\ValidateException $e) {
 | ||
| 			// 捕获验证异常并返回错误信息
 | ||
| 			return json(['code'=>1, 'msg'=>$e->getMessage()])->send();
 | ||
| 		} catch (\Exception $e) {
 | ||
| 			// 捕获其他异常
 | ||
| 			return json(['code'=>1, 'msg'=>'上传失败:'.$e->getMessage()])->send();
 | ||
| 		}
 | ||
| 	}
 | ||
| 	# 清除缓存
 | ||
| 	public function clear(){
 | ||
| 		$a = delete_dir_file(Env::get('runtime_path').'cache/');
 | ||
| 		$b = delete_dir_file(Env::get('runtime_path').'temp/');
 | ||
| 		if ($a || $b) {
 | ||
| 			$this->returnCode(0, '清除缓存成功');
 | ||
| 		} else {
 | ||
| 			$this->returnCode(1, '清除缓存失败');
 | ||
| 		}
 | ||
| 	}
 | ||
| 	# 文件上传
 | ||
| 	public function upload_file(){
 | ||
| 		$file = request()->file();
 | ||
| 		$files = request()->file('file');
 | ||
| 		if(empty($file)){
 | ||
| 			return json(['code'=>1, 'msg'=>'没有文件上传'])->send();
 | ||
| 		}
 | ||
| 		try {
 | ||
| 			// 只验证文件扩展名,不验证大小
 | ||
| 			validate([
 | ||
| 				'file'=>'fileExt:doc,docx,xls,xlsx,ppt,pptx,pdf,txt,zip,rar,7z'
 | ||
| 			])->check($file);
 | ||
| 			
 | ||
| 			$info = \think\facade\Filesystem::disk('public')->putFile('uploads/files', $files);
 | ||
| 			
 | ||
| 			// 处理文件路径
 | ||
| 			$info = str_replace("\\", "/", $info);
 | ||
| 			$filePath = '/storage/'.$info;
 | ||
| 			
 | ||
| 			// 保存附件信息
 | ||
| 			$fileName = $files->getOriginalName();
 | ||
| 			$fileSize = $files->getSize();
 | ||
| 			$attachmentId = $this->saveAttachment($fileName, 2, $fileSize, $filePath); // 2: 文件
 | ||
| 			
 | ||
| 			return json([
 | ||
| 				'code' => 0,
 | ||
| 				'data' => [
 | ||
| 					'src' => $filePath,
 | ||
| 					'attachment_id' => $attachmentId
 | ||
| 				]
 | ||
| 			])->send();
 | ||
| 		} catch (\think\exception\ValidateException $e) {
 | ||
| 			return json(['code'=>1, 'msg'=>$e->getMessage()])->send();
 | ||
| 		} catch (\Exception $e) {
 | ||
| 			return json(['code'=>1, 'msg'=>'上传失败:'.$e->getMessage()])->send();
 | ||
| 		}
 | ||
| 	}
 | ||
| 
 | ||
| 	# 视频上传
 | ||
| 	public function upload_video(){
 | ||
| 		$file = request()->file();
 | ||
| 		$files = request()->file('file');
 | ||
| 		if(empty($file)){
 | ||
| 			return json(['code'=>1, 'msg'=>'没有文件上传'])->send();
 | ||
| 		}
 | ||
| 		try {
 | ||
| 			validate(['video'=>'filesize:102400|fileExt:mp4,avi,mov,wmv,flv'])->check($file);
 | ||
| 			$info = \think\facade\Filesystem::disk('public')->putFile('uploads/videos', $files);
 | ||
| 			
 | ||
| 			// 处理文件路径
 | ||
| 			$info = str_replace("\\", "/", $info);
 | ||
| 			$videoPath = '/storage/'.$info;
 | ||
| 			
 | ||
| 			// 保存附件信息
 | ||
| 			$fileName = $files->getOriginalName();
 | ||
| 			$fileSize = $files->getSize();
 | ||
| 			$attachmentId = $this->saveAttachment($fileName, 3, $fileSize, $videoPath); // 3: 视频
 | ||
| 			
 | ||
| 			return json([
 | ||
| 				'code' => 0,
 | ||
| 				'data' => [
 | ||
| 					'src' => $videoPath,
 | ||
| 					'attachment_id' => $attachmentId
 | ||
| 				]
 | ||
| 			])->send();
 | ||
| 		} catch (\think\exception\ValidateException $e) {
 | ||
| 			return json(['code'=>1, 'msg'=>$e->getMessage()])->send();
 | ||
| 		}
 | ||
| 	}
 | ||
| 
 | ||
| 	# 音频上传
 | ||
| 	public function upload_audio(){
 | ||
| 		$file = request()->file();
 | ||
| 		$files = request()->file('file');
 | ||
| 		if(empty($file)){
 | ||
| 			return json(['code'=>1, 'msg'=>'没有文件上传'])->send();
 | ||
| 		}
 | ||
| 		try {
 | ||
| 			validate(['audio'=>'filesize:51200|fileExt:mp3,wav,ogg,m4a'])->check($file);
 | ||
| 			$info = \think\facade\Filesystem::disk('public')->putFile('uploads/audios', $files);
 | ||
| 			
 | ||
| 			// 处理文件路径
 | ||
| 			$info = str_replace("\\", "/", $info);
 | ||
| 			$audioPath = '/storage/'.$info;
 | ||
| 			
 | ||
| 			// 保存附件信息
 | ||
| 			$fileName = $files->getOriginalName();
 | ||
| 			$fileSize = $files->getSize();
 | ||
| 			$attachmentId = $this->saveAttachment($fileName, 4, $fileSize, $audioPath); // 4: 音频
 | ||
| 			
 | ||
| 			return json([
 | ||
| 				'code' => 0,
 | ||
| 				'data' => [
 | ||
| 					'src' => $audioPath,
 | ||
| 					'attachment_id' => $attachmentId
 | ||
| 				]
 | ||
| 			])->send();
 | ||
| 		} catch (\think\exception\ValidateException $e) {
 | ||
| 			return json(['code'=>1, 'msg'=>$e->getMessage()])->send();
 | ||
| 		}
 | ||
| 	}
 | ||
| 
 | ||
| } |