println)($msg); } protected function configure() { // 指令配置 $this->setName('migrate') ->setDescription('执行数据迁移'); } protected function execute(Input $input, Output $output) { // 指令输出 $this->println = function ($msg) use ($output) { $output->writeln($msg); }; try { // 迁移tock产品分类 // $this->productTcoCategory(); // 迁移产品分类 // $this->productCategory(); // 迁移文章 // $this->migrateArticle([ // 1 => 2, // 2 => 3, // 33 => 4, // 36 => 5, // 16 => 7, // 31 => 8, // 32 => 9 // ]); // 迁移faq // $this->migrateFaq(); // 迁移附件下载 // $this->migrateAttachment([ // 1 => 1, // 2 => 2, // 3 => 3, // 4 => 4, // 5 => 5, // 43 => 6, // 22 => 8, // 24 => 11, // 25 => 12, // 26 => 13, // 44 => 9, // 45 => 10 // ]); // 迁移视频分类 // $this->migrateVideoCategory(); // 迁移视频 $this->migrateVideo([ 0 => 0, 1 => 1, 2 => 2, 3 => 3, 4 => 4, 5 => 5, 6 => 6, 7 => 7, 8 => 8, 11 => 0, 36 => 9, 37 => 10, 38 => 11, 39 => 12, 40 => 13, 41 => 14, 42 => 15, 43 => 16, 60 => 17, 62 => 18, 63 => 19 ]); $output->writeln('success'); } catch(\Throwable $th) { $output->writeln($th->getMessage() .':' . $th->getLine()); } } // 迁移tco产品分类 private function productTcoCategory() { $category = Db::connect('old') ->name('product_tco_category') ->select(); foreach ($category as $val) { $item = [ 'id' => $val['id'], 'language_id' => $val['country_code'] == 'ZH' ? 1 : 2, 'name' => $val['name'], 'tco_id' => $val['tco_id'], 'tco_pid' => $val['tco_pid'], 'tco_path' => $val['tco_path'], 'erp_id' => $val['erp_id'], 'erp_pid' => $val['erp_pid'], 'erp_path' => $val['erp_path'], 'erp_code' => $val['erp_code'], 'disabled' => $val['disabled'], 'sync_time' => $val['sync_time'], ]; Db::name('product_tco_category')->insert($item); } } // 迁移产品分类 private function productCategory() { $tco_category = Db::connect('old') ->name('product_tco_category') ->select(); $tco_category_map = []; foreach ($tco_category as $val) { $key = sprintf("%s_%s", $val['category_id'], $val['country_code']); if (isset($tco_category_map[$key])) { $tco_category_map[$key] = []; } $tco_category_map[$key][] = $val['id']; } $category = Db::connect('old') ->name('product_category') ->select() ->toArray(); // 处理数据 $this->handlerProductCategory(array_to_tree($category, 0, 'pid', 1), $tco_category_map); } private function handlerProductCategory($category, $map) { foreach ($category as $val) { $key = sprintf("%s_%s", $val['id'], $val['country_code']); $item = [ 'id' => $val['id'], 'language_id' => $val['country_code'] == 'ZH' ? 1 : 2, 'unique_id' => uniqid('PRO_CATE_'), 'pid' => $val['pid'], 'name' => $val['name'], 'icon' => $val['icon'], 'desc' => $val['description'], 'related_tco_category' => isset($map[$key]) ? implode(',', $map[$key]) : '', 'sort' => $val['sort'] == 9999 ? 0 : $val['sort'], 'level' => $val['level'], 'is_show' => $val['isshow'], ]; Db::name('product_category')->insert($item); if (isset($val['children'])) { $this->handlerProductCategory($val['children'], $map); } } } // 迁移文章 private function migrateArticle($category_map = []) { $this->println('开始迁移文章......'); if (empty($category_map)) { throw new \Exception('请确认分类ID'); } $article = Db::connect('old') ->name('article') ->where('cid', 'in', array_keys($category_map)) ->order(['id' => 'asc']) ->cursor(); $uploadMgr = new UploadMannager(); foreach ($article as $v) { // 处理封面图片 $image = ''; $ret = $uploadMgr->upload($uploadMgr->download($v['picture']), 'image', 'article'); if ($ret['code'] == 0) { $image = $ret['data']['path']; } else { $image = $ret['msg']; } // 处理详情中图片 $content = $v['content']; preg_match_all('//i', $content, $matches); $content_images = []; foreach ($matches[1] as $val) { try { $ret = $uploadMgr->upload($uploadMgr->download($val), 'image'); if ($ret['code'] == 0) { $content_images[$val] = $ret['data']['path']; } } catch (\Throwable $th) { continue; } } foreach ($content_images as $key => $val) { $content = str_replace($key, $val, $content); } $item = [ 'language_id' => $v['country_code'] == 'ZH' ? 1 : 2, 'category_id' => $category_map[$v['cid']], 'title' => $v['name'], 'author' => $v['writer'], 'source' => $v['source'], 'image' => $image, 'desc' => $v['description'], 'recommend' => $v['recommend'], 'sort' => $v['sort'] == 9999 ? 0 : $v['sort'], 'link' => $v['jump_link'], 'content' => $content, 'view_count' => $v['viewcount'], 'praise_count' => $v['zancount'], 'seo_title' => $v['seo_title'], 'seo_keywords' => $v['seo_keyword'], 'seo_desc' => $v['seo_description'], 'enabled' => $v['stat'], 'release_time' => date('Y-m-d H:i:s', $v['createtime']) ]; Db::name('article')->insert($item); $this->println('迁移文章ID:' . $v['id']); } } // 迁移FAQ private function migrateFaq() { $faq = Db::connect('old') ->name('fq') ->where('id', '>', 0) ->where('stat', '>=', 0) ->select(); $uploadMgr = new UploadMannager(); foreach ($faq as $key => $val) { $image = ''; $ret = $uploadMgr->upload($uploadMgr->download($val['picture']), 'image', 'faq'); if ($ret['code'] == 0) { $image = $ret['data']['path']; } else { $image = $ret['msg']; } $content = explode("\n", $val['content']); $content = '

' . implode("

", $content) . '

'; $item = [ 'language_id' => $val['country_code'] == 'ZH' ? 1 : 2, 'image' => $image, 'question' => $val['name'], 'answer' => $content, 'recommend' => $val['is_home'], 'sort' => $val['sort'] == 9999 ? 0 : $val['sort'] ]; Db::name('faq')->insert($item); } } // 迁移附件 private function migrateAttachment($category_map = []) { $attachment = Db::connect('old') ->name('download') ->where('id', '>', 0) ->where('country_code', 'in', ['ZH', 'US']) ->order(['id' => 'asc']) ->cursor(); $uploadMgr = new UploadMannager(); foreach ($attachment as $key => $val) { $image = ''; $image_ret = $uploadMgr->upload($uploadMgr->download($val['picture']), 'image'); if ($image_ret['code'] == 0) { $image = $image_ret['data']['path']; } else { $image = $image_ret['msg']; } $paths = explode(',', $val['downloadpath']); $btns = explode(',', $val['downloadpath64']); $attach = []; foreach ($paths as $k => $path) { $file_path = ''; if (\think\helper\Str::contains($path, "oos.oricogroup.com")) { $file_path = $path; } else { $attach_ret = $uploadMgr->upload($uploadMgr->download($path), 'attachment'); if ($attach_ret['code'] == 0) { $file_path = $attach_ret['data']['path']; } else { $file_path = $attach_ret['msg']; } } $file_ext = ''; $last_sper_idx = strrpos($path, '.'); if ($last_sper_idx !== false) { $file_ext = substr($path, $last_sper_idx + 1); if (substr($path, $last_sper_idx - 3, $last_sper_idx) == 'tar') { $file_ext = 'tar.' . $file_ext; } } $attach[] = [ 'file_path' => $file_path, 'file_ext' => $file_ext, 'btn_name' => $btns[$k], ]; } $item = [ 'language_id' => $val['country_code'] == 'ZH' ? 1 : 2, 'category_id' => $category_map[$val['cid']], 'name' => $val['name'], 'desc' => $val['description'], 'image' => $image, 'applicable_to' => $val['app_model'], 'support_platform' => $val['support_os'], 'attach' => json_encode($attach), 'sort' => $val['sort'] == 9999 ? 0 : $val['sort'], 'recommend' => $val['recommend'], 'seo_title' => $val['seo_title'], 'seo_keywords' => $val['seo_keyword'], 'seo_desc' => $val['seo_description'], 'deleted_at' => $val['stat'] == -1 ? date('Y-m-d H:i:s') : null ]; Db::name('attachment')->insert($item); $this->println('迁移附件ID:' . $val['id']); } } // 迁移视频 private function migrateVideoCategory() { $categorys = Db::connect('old') ->name('video_category') ->where('id', '>', 0) ->where('country_code', 'in', ['ZH', 'US']) ->order(['id' => 'asc']) ->cursor(); foreach ($categorys as $val) { $item = [ 'language_id' => $val['country_code'] == 'ZH' ? 1 : 2, 'name' => $val['name'], 'sort' => $val['sort'] == 9999 ? 0 : $val['sort'], 'is_show' => $val['isshow'] ]; Db::name('video_category')->insert($item); $this->println('迁移视频分类ID:' . $val['id']); } } // 迁移视频 private function migrateVideo($category_map = []) { $videos = Db::connect('old') ->name('video') ->where('id', '>', 844) ->where('country_code', 'in', ['ZH', 'US']) ->order(['id' => 'asc']) ->cursor(); $uploadMgr = new UploadMannager(); foreach ($videos as $val) { $image = ''; $image_ret = $uploadMgr->upload($uploadMgr->download($val['picture']), 'image', 'video'); if ($image_ret['code'] == 0) { $image = $image_ret['data']['path']; } else { $image = $image_ret['msg']; } $video = ''; if (!empty($val['videourl'])) { $video = $val['videourl']; } else { $video_ret = $uploadMgr->upload($uploadMgr->download($val['videopath']), 'video', 'video'); if ($video_ret['code'] == 0) { $video = $video_ret['data']['path']; } else { $video = $video_ret['msg']; } } $item = [ 'language_id' => $val['country_code'] == 'ZH' ? 1 : 2, 'category_id' => $category_map[$val['cid']], 'name' => $val['name'], 'desc' => $val['description'], 'image' => $image, 'video' => $video, 'sort' => $val['sort'] == 9999 ? 0 : $val['sort'], 'recommend' => $val['recommend'], 'seo_title' => $val['seo_title'], 'seo_keywords' => $val['seo_keyword'], 'seo_desc' => $val['seo_description'], 'deleted_at' => $val['stat'] == -1 ? date('Y-m-d H:i:s') : null ]; Db::name('video')->insert($item); $this->println('迁移视频ID:' . $val['id']); } } } class UploadMannager { const UPLOAD_BASE_API = 'http://dev.ow.f2b211.com'; const DOWNLOAD_BASE_API = 'https://www.orico.com.cn'; const DOWNLOAD_TEMP_PATH = '/var/www/html/orico-official-website/public/migrate_temp_images'; private $username = 'admin'; private $password = 'Aa-1221'; private $token = ''; private $retrys = []; public function __construct() { // 登录获取token $this->token = $this->getAuthorization(); } // 下载图片 public function download($file_name) { if (empty($file_name)) { return ''; } $url = $file_name; if (\think\helper\Str::startsWith($file_name, 'http')) { $need = 'orico.com.cn'; if (!\think\helper\Str::contains($file_name, $need)) { if (\think\helper\Str::contains($file_name, 'https')) { // 避免https过期情况 $url = 'http://' . \think\helper\Str::substr($file_name, mb_strlen('https://')); } } else { $url = self::DOWNLOAD_BASE_API . \think\helper\Str::substr($file_name, mb_strpos($file_name, $need) + mb_strlen($need)); } $file_name = '/' . \think\helper\Str::substr($url, mb_strpos($url, '://') + 3); } else { $url = self::DOWNLOAD_BASE_API . str_replace(" ", "%20", $file_name); } $file_path = self::DOWNLOAD_TEMP_PATH . $file_name; $opts = [ 'http' => [ 'method' => 'GET', 'header' => 'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)' ] ]; $context = stream_context_create($opts); $file = file_get_contents($url, false, $context); $dir = dirname($file_path); if (!is_dir($dir)) { mkdir($dir, 0777, true); } file_put_contents($file_path, $file, FILE_USE_INCLUDE_PATH); return $file_path; } // 上传图片 public function upload($file_path, $field_name, $module = 'unknown') { if (empty($file_path)) { return ['code' => 0, 'msg' => 'file_path为空', 'data' => ['path' => '']]; } if (\think\helper\Str::startsWith($file_path, 'http')) { return ['code' => 0, 'msg' => 'file_path为http', 'data' => ['path' => $file_path]]; } switch($field_name){ case 'image': $url_path = "/admapi/v1/images/$module/upload"; break; case 'attachment': $url_path = '/admapi/v1/attachment/upload'; break; case 'video': $url_path = "/admapi/v1/video/$module/upload"; break; default: $url_path = "/admapi/v1/images/$module/upload"; break; } $ch = curl_init(self::UPLOAD_BASE_API . $url_path); $post_data = [ $field_name => new \CURLFile($file_path) ]; curl_setopt($ch, CURLOPT_HTTPHEADER, [ 'Authorization: Bearer ' . $this->token ]); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); $response = curl_exec($ch); $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); // 登录失效 if ($http_code == 401) { $this->token = $this->getAuthorization(); if (!isset($this->retrys[$file_path])) { $this->retrys[$file_path] = 0; } if ($this->retrys[$file_path] > 0) { throw new \Exception('[' . $file_path . ']上传重试失败'); } $this->retrys[$file_path] += 1; return $this->upload($file_path, $field_name); } if ($http_code != 200 && $http_code != 401) { print($file_path . PHP_EOL); print($field_name . PHP_EOL); print(self::UPLOAD_BASE_API . $url_path . PHP_EOL); print($response . PHP_EOL); print($http_code . PHP_EOL); } $ret = json_decode($response, true); if (empty($ret)) { throw new \Exception($response); } return $ret; } // 登录获取token private function getAuthorization() { $ch = curl_init(self::UPLOAD_BASE_API . '/admapi/v1/user/login'); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, [ 'username' => $this->username, 'password' => md5($this->password) ]); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); $response = curl_exec($ch); $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($http_code != 200) { throw new \Exception('获取token失败'); } $result = json_decode($response, true); if ($result['code'] != 0) { throw new \Exception($result['msg']); } return $result['data']['token']; } }