From bf1e5b8692c45aa6b38fc59c295e7882be130058 Mon Sep 17 00:00:00 2001 From: jsasg <735273025@qq.com> Date: Fri, 11 Apr 2025 18:15:12 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=95=B0=E6=8D=AE=E8=BF=81=E7=A7=BB?= =?UTF-8?q?=E5=91=BD=E4=BB=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 4 +- app/command/DataMigration.php | 334 ++++++++++++++++++++++++++++++ app/index/controller/Article.php | 26 +++ app/index/route/route.php | 13 +- app/index/view/article/index.html | 73 +++++++ config/console.php | 1 + runtime/.gitignore | 0 7 files changed, 447 insertions(+), 4 deletions(-) create mode 100644 app/command/DataMigration.php create mode 100644 app/index/controller/Article.php create mode 100644 app/index/view/article/index.html mode change 100644 => 100755 runtime/.gitignore diff --git a/.gitignore b/.gitignore index 07a87f35..fb5700d2 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,4 @@ Thumbs.db /vendor /.settings /.buildpath -/.project - -app/index/controller/DataMigration.php \ No newline at end of file +/.project \ No newline at end of file diff --git a/app/command/DataMigration.php b/app/command/DataMigration.php new file mode 100644 index 00000000..8fc74105 --- /dev/null +++ b/app/command/DataMigration.php @@ -0,0 +1,334 @@ +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); + }; + + // 迁移tock产品分类 + // $this->productTcoCategory(); + + // 迁移产品分类 + // $this->productCategory(); + + // 迁移文章 + $this->migrateArticle([ + 1 => 2, + 2 => 3, + 33 => 4, + 36 => 5, + 16 => 7, + 31 => 8, + 32 => 9 + ]); + + // 迁移faq + // $this->migrateFaq(); + + $output->writeln('success'); + } + + // 迁移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'); + 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', '>=', 10) + ->where('stat', '>=', 0) + ->select(); + + $uploadMgr = new UploadMannager(); + foreach ($faq as $key => $val) { + $image = ''; + $ret = $uploadMgr->upload($uploadMgr->download($val['picture']), 'image'); + 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); + } + } +} + +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)) { + return $url; + } + $url = self::DOWNLOAD_BASE_API . \think\helper\Str::substr($file_name, mb_strpos($file_name, $need) + mb_strlen($need)); + } else { + $url = self::DOWNLOAD_BASE_API . $file_name; + } + + $file_path = self::DOWNLOAD_TEMP_PATH . $file_name; + $file = file_get_contents($url); + $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) { + if (empty($file_path)) { + return ['code' => 0, 'msg' => 'file_path为空', 'data' => ['path' => '']]; + } + $ch = curl_init(self::UPLOAD_BASE_API . '/admapi/v1/images/faq/upload'); + $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); + } + + return json_decode($response, true); + } + + // 登录获取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']; + } +} diff --git a/app/index/controller/Article.php b/app/index/controller/Article.php new file mode 100644 index 00000000..e080e8db --- /dev/null +++ b/app/index/controller/Article.php @@ -0,0 +1,26 @@ +name('product_index'); // 产品详情页 Route::get('detail/:id', 'Product/detail')->name('product_detail'); // 产品搜索页 - Route::get('search', 'Product/search')->name('product_search'); + Route::get('search', 'Product/search'); +}); + +// 文章相关路由 +Route::group('article', function() { + // 文章列表页 + Route::get('index/:id', 'Article/index'); + // 文章详情页 + Route::get('detail/:id', 'Article/detail'); + // 文章搜索页 + Route::get('search', 'Article/search'); }); // 数据迁移 diff --git a/app/index/view/article/index.html b/app/index/view/article/index.html new file mode 100644 index 00000000..5a5122dd --- /dev/null +++ b/app/index/view/article/index.html @@ -0,0 +1,73 @@ +{extend name="public/base" /} +{block name="style"} + +{/block} +{block name="main"} +
+ + +
+{/block} +{block name="script"} + +{/block} \ No newline at end of file diff --git a/config/console.php b/config/console.php index a818a980..22916796 100644 --- a/config/console.php +++ b/config/console.php @@ -5,5 +5,6 @@ return [ // 指令定义 'commands' => [ + 'data:migrate' => \app\command\DataMigration::class, ], ]; diff --git a/runtime/.gitignore b/runtime/.gitignore old mode 100644 new mode 100755