501 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			PHP
		
	
	
		
		
			
		
	
	
			501 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			PHP
		
	
	
|  | <?php | |||
|  | 
 | |||
|  | 
 | |||
|  | namespace app\controller\manager; | |||
|  | 
 | |||
|  | use app\model\Log; | |||
|  | use app\service\AliOss; | |||
|  | use think\facade\Config; | |||
|  | use Exception; | |||
|  | use app\model\Attachment as AttachmentModel; | |||
|  | use think\db\exception\DataNotFoundException; | |||
|  | use think\db\exception\DbException; | |||
|  | use think\db\exception\ModelNotFoundException; | |||
|  | use think\exception\ValidateException; | |||
|  | use think\response\Json; | |||
|  | use think\response\View; | |||
|  | 
 | |||
|  | /** | |||
|  |  * 附件管理 - 素材管理 | |||
|  |  * Class Attachment | |||
|  |  * @package app\controller\manager | |||
|  |  */ | |||
|  | class Attachment extends Base | |||
|  | { | |||
|  |     protected $noNeedLogin = ['file', 'getSize', 'md5List', 'pathDirHandle', 'toOss', 'delLostFile', 'test']; | |||
|  |     protected $DIRECTORY_SEPARATOR = "/"; | |||
|  | 
 | |||
|  |     protected function initialize() | |||
|  |     { | |||
|  |         parent::initialize(); // TODO: Change the autogenerated stub
 | |||
|  |         $this->DIRECTORY_SEPARATOR = DIRECTORY_SEPARATOR == "\\" ? "/" : DIRECTORY_SEPARATOR; | |||
|  |     } | |||
|  | 
 | |||
|  |     /** | |||
|  |      * 删除 | |||
|  |      * | |||
|  |      * @return Json | |||
|  |      * @throws DataNotFoundException | |||
|  |      * @throws DbException | |||
|  |      * @throws ModelNotFoundException | |||
|  |      */ | |||
|  |     public function del(): Json | |||
|  |     { | |||
|  |         if ($this->request->isPost()) { | |||
|  |             $ids = input('post.ids/a', []); | |||
|  |             if (empty($ids)) { | |||
|  |                 $ids[] = input('post.id/d'); | |||
|  |             } | |||
|  | 
 | |||
|  |             $items = AttachmentModel::whereIn('id', $ids)->where('is_dir', AttachmentModel::COMMON_ON)->select(); | |||
|  |             if ($items->where('is_dir', AttachmentModel::COMMON_ON)->count()) { | |||
|  |                 $dirPaths = []; | |||
|  |                 foreach ($items->toArray() as $item) { | |||
|  |                     $dirPaths[] = $item['path'].$item['name'].$this->DIRECTORY_SEPARATOR; | |||
|  |                 } | |||
|  |                 if (AttachmentModel::where('path', 'in', $dirPaths)->count()) { | |||
|  |                     return $this->json(4001, '待删除目录下存在内容!'); | |||
|  |                 } | |||
|  |             } | |||
|  |             AttachmentModel::deleteByIds($ids); | |||
|  | 
 | |||
|  |             //            Log::write(get_class().'Del', 'del', '涉及到的ID为:'.implode(',', $ids));
 | |||
|  |             return $this->json(); | |||
|  |         } | |||
|  |         return $this->json(4001, '非法请求!'); | |||
|  |     } | |||
|  | 
 | |||
|  |     /** | |||
|  |      * 单个字段编辑 | |||
|  |      * | |||
|  |      * @return Json | |||
|  |      * @throws Exception | |||
|  |      */ | |||
|  |     public function modify(): Json | |||
|  |     { | |||
|  |         if ($this->request->isPost()) { | |||
|  |             $item     = input('post.'); | |||
|  |             $validate = $this->validateByApi($item, [ | |||
|  |                 'field' => 'require', | |||
|  |                 'value' => 'require', | |||
|  |             ]); | |||
|  | 
 | |||
|  |             if ($validate !== true) { | |||
|  |                 return $validate; | |||
|  |             } | |||
|  | 
 | |||
|  |             if (!$info = AttachmentModel::findById($item['id'])) { | |||
|  |                 return $this->json(4001, '记录不存在'); | |||
|  |             } | |||
|  | 
 | |||
|  |             if ($item['field'] == 'name' && $info['is_dir'] == AttachmentModel::COMMON_ON) { | |||
|  |                 return $this->json(4002, '目录名称不能修改'); | |||
|  | 
 | |||
|  |             } | |||
|  | 
 | |||
|  |             $update = [$item['field'] => $item['value']]; | |||
|  | 
 | |||
|  |             try { | |||
|  |                 $info->save($update); | |||
|  |                 return $this->json(); | |||
|  |             } catch (ValidateException $e) { | |||
|  |                 return $this->json(4001, $e->getError()); | |||
|  |             } | |||
|  |         } | |||
|  |         return $this->json(4000, '非法请求'); | |||
|  |     } | |||
|  | 
 | |||
|  |     /** | |||
|  |      * 添加 | |||
|  |      * | |||
|  |      * @return Json|View | |||
|  |      * @throws Exception | |||
|  |      */ | |||
|  |     public function add() | |||
|  |     { | |||
|  |         if ($this->request->isPost()) { | |||
|  |             $item = input('post.'); | |||
|  | 
 | |||
|  |             $validate = $this->validateByApi($item, [ | |||
|  |                 'title|合集标题'     => 'require', | |||
|  |                 'user|虚拟用户'      => 'require', | |||
|  |                 'headimg|虚拟用户头像' => 'require', | |||
|  |             ]); | |||
|  | 
 | |||
|  |             if ($validate !== true) { | |||
|  |                 return $validate; | |||
|  |             } | |||
|  | 
 | |||
|  |             try { | |||
|  |                 $now                = date('Y-m-d H:i:s'); | |||
|  |                 $item['created_at'] = $now; | |||
|  |                 $item['created_by'] = $this->auth['user_id']; | |||
|  |                 AttachmentModel::create($item); | |||
|  |                 return $this->json(); | |||
|  |             } catch (ValidateException $e) { | |||
|  |                 return $this->json(4001, $e->getError()); | |||
|  |             } | |||
|  |         } | |||
|  | 
 | |||
|  |         return $this->view(); | |||
|  |     } | |||
|  | 
 | |||
|  |     /** | |||
|  |      * 添加文件夹 | |||
|  |      * | |||
|  |      * @return Json|View | |||
|  |      * @throws Exception | |||
|  |      */ | |||
|  |     public function addFolder() | |||
|  |     { | |||
|  |         if ($this->request->isPost()) { | |||
|  |             $item = input('post.'); | |||
|  | 
 | |||
|  |             $validate = $this->validateByApi($item, [ | |||
|  |                 'name|文件夹名称' => 'require|alphaDash|max:20', | |||
|  |                 'path|文件路径'  => 'require', | |||
|  |             ]); | |||
|  | 
 | |||
|  |             // 例 name=dir4 path=/storage/dir1/dir2/dir3
 | |||
|  | 
 | |||
|  |             // 去首尾/
 | |||
|  |             $path = trim($item['path'], $this->DIRECTORY_SEPARATOR); | |||
|  |             // 全路径 如 /storage/dir1/dir2/dir3/dir4/  注意前后都有/
 | |||
|  |             $fullPath = $this->DIRECTORY_SEPARATOR.$path.$this->DIRECTORY_SEPARATOR.$item['name'].$this->DIRECTORY_SEPARATOR; | |||
|  | 
 | |||
|  |             if ($validate !== true) { | |||
|  |                 return $validate; | |||
|  |             } | |||
|  | 
 | |||
|  |             AttachmentModel::pathDirHandle($fullPath); | |||
|  |             return $this->json(); | |||
|  |         } | |||
|  | 
 | |||
|  |         $path = input('path/s', AttachmentModel::ROOT_PATH); | |||
|  | 
 | |||
|  |         $this->data['path'] = $path; | |||
|  |         return $this->view(); | |||
|  |     } | |||
|  | 
 | |||
|  |     /** | |||
|  |      * 图片列表 | |||
|  |      * | |||
|  |      * @return View|Json | |||
|  |      * @throws Exception | |||
|  |      */ | |||
|  |     public function image() | |||
|  |     { | |||
|  |         $path     = input('post.path', AttachmentModel::ROOT_PATH); | |||
|  |         $path     = $this->DIRECTORY_SEPARATOR.trim($path, $this->DIRECTORY_SEPARATOR).$this->DIRECTORY_SEPARATOR; | |||
|  |         $path     = str_replace("\\", "/", $path); | |||
|  |         $type     = input('type/s', 'image'); | |||
|  |         $selected = input('selected', false); | |||
|  |         $multiple = input('multiple', false); | |||
|  | 
 | |||
|  |         Config::load('extra/alioss', 'alioss'); | |||
|  |         Config::load('extra/base', 'base'); | |||
|  |         $config     = config('alioss'); | |||
|  |         $baseConfig = config('base'); | |||
|  | 
 | |||
|  |         $oss = $baseConfig['oss'] == 'true' ? $config['customDomain'] : ''; | |||
|  | 
 | |||
|  |         if ($this->request->isPost()) { | |||
|  |             $items = $this->list($path, ['image']); | |||
|  | 
 | |||
|  |             return $this->json(0, '操作成功', $items); | |||
|  |         } | |||
|  | 
 | |||
|  |         $this->data['path']     = $path; | |||
|  |         $this->data['oss']      = $oss; | |||
|  |         $this->data['type']     = $type; | |||
|  |         $this->data['multiple'] = $multiple; | |||
|  |         $this->data['selected'] = $selected; | |||
|  |         return $this->view(); | |||
|  |     } | |||
|  | 
 | |||
|  |     /** | |||
|  |      * 视频列表 | |||
|  |      * | |||
|  |      * @return View|Json | |||
|  |      * @throws Exception | |||
|  |      */ | |||
|  |     public function video() | |||
|  |     { | |||
|  |         $path     = input('post.path', AttachmentModel::ROOT_PATH); | |||
|  |         $path     = $this->DIRECTORY_SEPARATOR.trim($path, $this->DIRECTORY_SEPARATOR).$this->DIRECTORY_SEPARATOR; | |||
|  |         $path     = str_replace("\\", "/", $path); | |||
|  |         $type     = input('type/s', 'video'); | |||
|  |         $selected = input('selected', false); | |||
|  |         $multiple = input('multiple', false); | |||
|  | 
 | |||
|  |         Config::load('extra/alioss', 'alioss'); | |||
|  |         Config::load('extra/base', 'base'); | |||
|  |         $config     = config('alioss'); | |||
|  |         $baseConfig = config('base'); | |||
|  |         $oss        = $baseConfig['oss'] == 'true' ? $config['customDomain'] : ''; | |||
|  |         if ($this->request->isPost()) { | |||
|  |             $items = $this->list($path, ['video']); | |||
|  | 
 | |||
|  |             return $this->json(0, '操作成功', $items); | |||
|  |         } | |||
|  | 
 | |||
|  |         $this->data['path']     = $path; | |||
|  |         $this->data['oss']      = $oss; | |||
|  |         $this->data['type']     = $type; | |||
|  |         $this->data['multiple'] = $multiple; | |||
|  |         $this->data['selected'] = $selected; | |||
|  |         return $this->view(); | |||
|  |     } | |||
|  | 
 | |||
|  |     /** | |||
|  |      * 文件列表 | |||
|  |      * | |||
|  |      * @return View|Json | |||
|  |      * @throws Exception | |||
|  |      */ | |||
|  |     public function file() | |||
|  |     { | |||
|  |         Config::load('extra/alioss', 'alioss'); | |||
|  |         $config     = config('alioss'); | |||
|  |         $baseConfig = config('base'); | |||
|  |         $oss        = $baseConfig['oss'] == 'true' ? $config['customDomain'] : ''; | |||
|  |         $type       = input('type/s', 'all'); | |||
|  |         $selected   = input('selected', false); | |||
|  |         $multiple   = input('multiple', false); | |||
|  | 
 | |||
|  |         if ($this->request->isPost()) { | |||
|  |             $page         = input('post.page', 1); | |||
|  |             $size         = input('post.size', 20); | |||
|  |             $searchParams = input('searchParams'); | |||
|  |             $where        = []; | |||
|  | 
 | |||
|  |             if ($searchParams) { | |||
|  |                 foreach ($searchParams as $key => $param) { | |||
|  |                     if (!empty($param)) { | |||
|  |                         if (is_string($param)) { | |||
|  |                             if ($key == 'is_oss') { | |||
|  |                                 if ($param >= 0) { | |||
|  |                                     $where[] = ['is_oss', '=', $param]; | |||
|  |                                 } | |||
|  |                             } else { | |||
|  |                                 $where[] = [$key, 'like', '%'.$param.'%']; | |||
|  |                             } | |||
|  |                         } elseif (is_array($param)) { | |||
|  |                             //数组空元素去除
 | |||
|  |                             foreach ($param as $k => $val) { | |||
|  |                                 if (empty($val)) { | |||
|  |                                     unset($param[$k]); | |||
|  |                                 } | |||
|  |                             } | |||
|  |                             if (!empty($param)) { | |||
|  |                                 $where[] = [$key, 'in', $param]; | |||
|  |                             } | |||
|  |                         } | |||
|  |                     } | |||
|  |                 } | |||
|  |             } | |||
|  | 
 | |||
|  | 
 | |||
|  |             if ($type !== 'all') { | |||
|  |                 $where[] = ['type', '=', $type]; | |||
|  |             } | |||
|  | 
 | |||
|  |             $items = AttachmentModel::findList($where, [], $page, $size, function ($q) { | |||
|  |                 return $q->where('type', '<>', 'dir')->order('updated_at', 'desc'); | |||
|  |             }); | |||
|  | 
 | |||
|  |             $items['list']->each(function ($item) { | |||
|  |                 $item->size_text = getFilesize($item['size'] ?? 0); | |||
|  |             }); | |||
|  | 
 | |||
|  |             return $this->json(0, '操作成功', $items); | |||
|  |         } | |||
|  | 
 | |||
|  |         $this->data['oss']      = $oss; | |||
|  |         $this->data['type']     = $type; | |||
|  |         $this->data['multiple'] = $multiple; | |||
|  |         $this->data['selected'] = $selected; | |||
|  |         return $this->view(); | |||
|  |     } | |||
|  | 
 | |||
|  |     /** | |||
|  |      * 一键删除失效记录 即oss不存在&&本地不存在 | |||
|  |      * | |||
|  |      * @return Json | |||
|  |      */ | |||
|  |     public function delLostFile(): Json | |||
|  |     { | |||
|  |         if ($this->request->isPost()) { | |||
|  |             $total = AttachmentModel::where('type', '<>', AttachmentModel::TYPE_DIR) | |||
|  |                 ->where('is_dir', AttachmentModel::COMMON_OFF) | |||
|  |                 ->where('is_oss', AttachmentModel::COMMON_OFF) | |||
|  |                 ->where('has_local', AttachmentModel::COMMON_OFF) | |||
|  |                 ->count(); | |||
|  |             if ($total === 0) { | |||
|  |                 return $this->json(0, 'success', ['total' => $total]); | |||
|  |             } | |||
|  | 
 | |||
|  |             if (AttachmentModel::where('type', '<>', AttachmentModel::TYPE_DIR) | |||
|  |                 ->where('is_dir', AttachmentModel::COMMON_OFF) | |||
|  |                 ->where('is_oss', AttachmentModel::COMMON_OFF) | |||
|  |                 ->where('has_local', AttachmentModel::COMMON_OFF) | |||
|  |                 ->delete()) { | |||
|  |                 return $this->json(0, 'success', ['total' => $total]); | |||
|  |             } | |||
|  |             return $this->json(4004, '删除失败'); | |||
|  |         } | |||
|  |         return $this->json(4000, '请求错误'); | |||
|  |     } | |||
|  | 
 | |||
|  |     /** | |||
|  |      * 一键上传本地文件到OSS | |||
|  |      * | |||
|  |      * @return Json | |||
|  |      */ | |||
|  |     public function toOss(): Json | |||
|  |     { | |||
|  |         if ($this->request->isPost()) { | |||
|  |             Config::load('extra/alioss', 'alioss'); | |||
|  |             Config::load('extra/base', 'base'); | |||
|  |             $config     = config('alioss'); | |||
|  |             $baseConfig = config('base'); | |||
|  |             if ($baseConfig['oss'] != 'true') { | |||
|  |                 return $this->json('4000', '配置未开启OSS上传'); | |||
|  |             } | |||
|  | 
 | |||
|  |             $ossObject = AliOss::instance(); | |||
|  |             $bucket    = $config['bucket']; | |||
|  | 
 | |||
|  |             $total = AttachmentModel::where('type', '<>', AttachmentModel::TYPE_DIR) | |||
|  |                 ->where('is_dir', AttachmentModel::COMMON_OFF) | |||
|  |                 ->where('is_oss', AttachmentModel::COMMON_OFF) | |||
|  |                 ->field('id') | |||
|  |                 ->count(); | |||
|  |             $done  = 0; | |||
|  |             $none  = 0; | |||
|  |             if ($total === 0) { | |||
|  |                 return $this->json(0, 'success', ['total' => $total, 'done' => $done, 'none' => $none]); | |||
|  |             } | |||
|  |             try { | |||
|  |                 AttachmentModel::where('type', '<>', AttachmentModel::TYPE_DIR) | |||
|  |                     ->where('is_dir', AttachmentModel::COMMON_OFF) | |||
|  |                     ->where('is_oss', AttachmentModel::COMMON_OFF) | |||
|  |                     ->field('id,src') | |||
|  |                     ->chunk(3, function ($items) use ($ossObject, $bucket, &$done, &$none) { | |||
|  |                         $doneIds = []; | |||
|  |                         $noneIds = []; | |||
|  |                         foreach ($items as $item) { | |||
|  |                             if ($item['src']) { | |||
|  |                                 $realPath = public_path().ltrim($item['src'], $this->DIRECTORY_SEPARATOR); | |||
|  |                                 if (!file_exists($realPath)) { | |||
|  |                                     $none++; | |||
|  |                                     $noneIds[] = $item['id']; | |||
|  |                                     continue; | |||
|  |                                 } | |||
|  |                                 $pathInfo = pathinfo($item['src']); | |||
|  |                                 $object   = ltrim($item['src'], $this->DIRECTORY_SEPARATOR); | |||
|  |                                 //是否存在
 | |||
|  |                                 if (!$ossObject->doesObjectExist($bucket, $object)) { | |||
|  |                                     //创建目录
 | |||
|  |                                     $ossObject->createObjectDir($bucket, ltrim($pathInfo['dirname'], $this->DIRECTORY_SEPARATOR)); | |||
|  | 
 | |||
|  |                                     $ossObject->uploadFile($bucket, $object, $realPath); | |||
|  |                                 } | |||
|  |                                 $doneIds[] = $item['id']; | |||
|  |                                 $done++; | |||
|  |                             } | |||
|  |                         } | |||
|  | 
 | |||
|  |                         // 失效标记
 | |||
|  |                         if ($noneIds) { | |||
|  |                             $update = ['is_oss' => AttachmentModel::COMMON_OFF, 'has_local' => AttachmentModel::COMMON_OFF]; | |||
|  |                             (new AttachmentModel())->where('id', 'in', $noneIds)->update($update); | |||
|  |                         } | |||
|  | 
 | |||
|  |                         // 完成标记
 | |||
|  |                         if ($doneIds) { | |||
|  |                             $update = ['is_oss' => AttachmentModel::COMMON_ON]; | |||
|  |                             (new AttachmentModel())->where('id', 'in', $doneIds)->update($update); | |||
|  |                         } | |||
|  |                     }); | |||
|  | 
 | |||
|  |                 return $this->json(0, 'success', ['total' => $total, 'done' => $done, 'none' => $none]); | |||
|  |             } catch (Exception $e) { | |||
|  |                 \think\facade\Log::error('本地文件一键上传OSS失败 '.$e->getMessage()); | |||
|  |                 return $this->json(0, 'success', ['total' => $total, 'done' => $done, 'none' => $none]); | |||
|  |             } | |||
|  |         } | |||
|  |     } | |||
|  | 
 | |||
|  |     /** | |||
|  |      * 指定类型附件列表 | |||
|  |      * | |||
|  |      * @param  array  $type | |||
|  |      * @param  string  $path | |||
|  |      * @return array | |||
|  |      * @throws Exception | |||
|  |      */ | |||
|  |     protected function list(string $path, array $type): array | |||
|  |     { | |||
|  |         $type[]  = 'dir'; | |||
|  |         $where[] = ['path', '=', $path]; | |||
|  |         $where[] = ['type', 'in', $type]; | |||
|  |         $items   = AttachmentModel::findList($where, [], 1, 0, function ($q) { | |||
|  |             return $q->order('is_dir', 'desc')->order('updated_at', 'desc'); | |||
|  |         }); | |||
|  |         $items['list']->each(function ($item) { | |||
|  |             $item->size_text = getFilesize($item['size'] ?? 0); | |||
|  |         }); | |||
|  |         $items['path'] = $path; | |||
|  | 
 | |||
|  |         return $items; | |||
|  |     } | |||
|  | 
 | |||
|  |     /** | |||
|  |      * 获取文件大小 | |||
|  |      * | |||
|  |      * @return Json | |||
|  |      */ | |||
|  |     public function getSize(): Json | |||
|  |     { | |||
|  |         $path  = input('post.path', ''); | |||
|  |         $types = input('post.type/a', []); | |||
|  | 
 | |||
|  |         $size = ''; | |||
|  |         if (empty($path)) { | |||
|  |             return $this->json(0, '操作成功', $size); | |||
|  |         } | |||
|  |         $path  = str_replace("\\", "/", $path); | |||
|  |         $total = AttachmentModel::where('path', 'like', $path.'%') | |||
|  |             ->when(!empty($types), function ($q) use ($types) { | |||
|  |                 $q->where('type', 'in', $types); | |||
|  |             }) | |||
|  |             ->sum('size'); | |||
|  | 
 | |||
|  |         return $this->json(0, '操作成功', getFilesize($total)); | |||
|  |     } | |||
|  | 
 | |||
|  |     // 将没有md5的文件 更新md5  仅针对本地文件
 | |||
|  |     public function md5List() | |||
|  |     { | |||
|  |         $noMd5List = AttachmentModel::whereNull('md5')->select(); | |||
|  |         $update    = []; | |||
|  |         foreach ($noMd5List as $item) { | |||
|  |             try { | |||
|  |                 if (!empty($item['src'])) { | |||
|  |                     $arr        = []; | |||
|  |                     $path       = public_path().ltrim($item['src'], $this->DIRECTORY_SEPARATOR); | |||
|  |                     $file       = new \think\File($path); | |||
|  |                     $arr['md5'] = $file->md5(); | |||
|  |                     $arr['id']  = $item['id']; | |||
|  | 
 | |||
|  |                     $update[] = $arr; | |||
|  |                 } | |||
|  |             } catch (Exception $e) { | |||
|  |                 continue; | |||
|  |             } | |||
|  | 
 | |||
|  |         } | |||
|  |         (new AttachmentModel())->saveAll($update); | |||
|  |     } | |||
|  | } |