diff --git a/app/command/DataMigration.php b/app/command/DataMigration.php index 1b48d496..969d791c 100644 --- a/app/command/DataMigration.php +++ b/app/command/DataMigration.php @@ -45,7 +45,10 @@ class DataMigration extends Command // $this->migrateProductAttr(); // 迁移产品 - $this->migrateProduct(); + // $this->migrateProduct(); + + // 迁移产品关联产品数据 + $this->migrateProductRelated(); // 迁移文章 // $this->migrateArticle([ @@ -245,15 +248,27 @@ class DataMigration extends Command $old_db = Db::connect('old'); + $success_map = []; + $success_arr = include_once(runtime_path() . 'product_success.php'); + foreach ($success_arr as $so) { + $success_map['p_' . $so['cod_product_id']] = $so; + } + $products = $old_db->name('product') - ->where('id', '=', 128) ->where('country_code', 'in', ['ZH', 'US']) + ->where('id', '>', 0) ->order(['id' => 'asc']) ->cursor(); + $total = 0; $uploadMgr = new UploadMannager(); foreach ($products as $v) { + $start = microtime(true); + if (isset($success_map['p_' . $v['id']])) { + continue; + } + Db::startTrans(); try { // 处理封面图片 @@ -262,7 +277,9 @@ class DataMigration extends Command if ($image_ret['code'] == 0) { $image = $image_ret['data']['path']; } else { - $image = $image_ret['msg']; + if ($image_ret['code'] != 400) { + $image = $image_ret['msg']; + } } // 处理视频 @@ -271,7 +288,9 @@ class DataMigration extends Command if ($video_ret['code'] == 0) { $video = $video_ret['data']['path']; } else { - $video = $video_ret['msg']; + if ($video_ret['code'] != 400) { + $video = $video_ret['msg']; + } } // 处理详情中图片 @@ -287,11 +306,11 @@ class DataMigration extends Command return ''; } return ''; - }, $v['ld_md_content']); + }, $v['ld_md_content']??''); $item = [ 'language_id' => $v['country_code'] == 'ZH' ? 1 : 2, - 'category_id' => '', + 'category_id' => $v['cid'], 'spu' => $v['brand_id'], 'name' => $v['name'], 'short_name' => $v['shortname'], @@ -302,31 +321,33 @@ class DataMigration extends Command 'is_sale' => $v['is_onsale'], 'is_new' => $v['isnew'], 'is_hot' => $v['ishot'], - 'is_show' => $v['is_show'], - 'sort' => $v['sort'], + 'is_show' => $v['is_show'] == 0 ? 1 : 0, + 'sort' => $v['sort'] == 9999 ? 0 : $v['sort'], 'detail' => $content, 'status' => $v['stat'] == -1 ? -1 : 1, 'seo_title' => $v['seo_title'], 'seo_keywords' => $v['seo_keyword'], 'seo_desc' => $v['seo_description'], 'created_at' => date('Y-m-d H:i:s', $v['createtime']), - 'updated_at' => date('Y-m-d H:i:s', $v['updatetime']), + 'updated_at' => $v['updatetime'] == 0 ? date('Y-m-d H:i:s', $v['createtime']) : date('Y-m-d H:i:s', $v['updatetime']), 'deleted_at' => $v['stat'] == -1 ? date('Y-m-d H:i:s') : null, ]; // 保存产品数据 $id = Db::name('product')->insertGetId($item); // 保存产品参数数据 - $prarms = []; - $views = unserialize($v['product_view']); - foreach ($views as $p) { - $prarms[] = [ - 'product_id' => $id, - 'name' => $p['desc_title'], - 'value' => $p['desc_desc'] - ]; + if (!empty($v['product_view'])) { + $prarms = []; + $views = unserialize($v['product_view']); + foreach ($views as $p) { + $prarms[] = [ + 'product_id' => $id, + 'name' => $p['desc_title'], + 'value' => $p['desc_desc'] + ]; + } + Db::name('product_params')->insertAll($prarms); } - Db::name('product_params')->insertAll($prarms); // 保存sku数据 $skus = []; @@ -350,6 +371,7 @@ class DataMigration extends Command if (!isset($images[$pkey])) { $images[$pkey] = []; $images[$pkey]['sku'] = $im['sku']; + $images[$pkey]['attrs'] = []; } // 处理图册 @@ -365,6 +387,10 @@ class DataMigration extends Command $photo_album[$pkey] = []; } $photo_album[$pkey][] = $photos_ret['data']['path']; + } else { + if ($photos_ret['code'] != 400) { + $photo_album[$pkey][] = json_encode($photos_ret); + } } } } @@ -372,17 +398,19 @@ class DataMigration extends Command // 处理属性 $attrs = json_decode($im['image_color'], true); if (!empty($attrs)) { - foreach ($attrs as $k => $at) { - if ($k != 'sort') { - $attr_value = $at; - if (in_array($k, ['颜色', 'Color'])) { - if ($k == 'Color') $k = '颜色'; - $attr_value = '/static/common/images/colors/' . $at . '.png'; + foreach ($attrs as $attr) { + foreach ($attr as $k => $at) { + if ($k != 'sort') { + $attr_value = $at; + if (in_array($k, ['颜色', 'Color'])) { + if ($k == 'Color') $k = '颜色'; + $attr_value = '/static/common/images/colors/' . $at . '.png'; + } + $images[$pkey]['attrs'][] = [ + 'attr_id' => $attrs_map[$v['country_code']][$k], + 'attr_value' => $attr_value, + ]; } - $images[$pkey]['attrs'] = [ - 'attr_id' => $attrs_map[$v['country_code']][$k], - 'attr_value' => $attr_value, - ]; } } } else { @@ -402,20 +430,21 @@ class DataMigration extends Command 'attr_value' => $attr_value, ]; if ( - empty($images[$pkey]['attrs']) || - (!empty($images[$pkey]['attrs']) && empty(array_intersect($images[$pkey]['attrs'], $attr_arr))) + empty($images[$pkey]['attrs']) || !array_some($images[$pkey]['attrs'], function($k, $v) use($attr_arr) { + return $v == $attr_arr; + }) ) { - $images[$pkey]['attrs'][] = $attr_arr; - file_put_contents(runtime_path() . 'attrs.txt', json_encode($attr_arr)); } } } - foreach ($images as $key => $image) { - $images[$key]['photo_album'] = $photo_album[$key]; + if (!empty($photo_album)) { + foreach ($images as $key => $image) { + if (isset($photo_album[$key])) { + $images[$key]['photo_album'] = $photo_album[$key]; + } + } } - file_put_contents(runtime_path() . 'images.txt', json_encode($images, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE)); - file_put_contents(runtime_path() . 'photo_album.txt', json_encode($photo_album, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE)); $two_images = $old_db->name('product_two_img') ->where('product_id', '=', $v['id']) @@ -428,56 +457,74 @@ class DataMigration extends Command if (!empty($ti['image_color'])) { $tpkey = md5($ti['image_color']); } - if (!empty($ti['image_url'])) { + if (!empty($ti['image_url']) && $ti['image_url'] != '[]') { $main_image_ret = $uploadMgr->upload($uploadMgr->download($ti['image_url']), 'image', 'product'); if ($main_image_ret['code'] == 0) { $ti['image_url'] = $main_image_ret['data']['path']; } } - $skus[] = ['main_image' => $ti['image_url'], 'image_color' => $ti['image_color'], 'pkey' => $tpkey]; + if (\think\helper\Str::endsWith($ti['image_color'], ['.png', '.jpg', 'jpeg', '.gif'])) { + $arrt_ret = $uploadMgr->upload($uploadMgr->download($ti['image_color']), 'image', 'product'); + if ($arrt_ret['code'] == 0) { + $ti['image_color'] = $arrt_ret['data']['path']; + } + } + $skus[] = [ + 'main_image' => $ti['image_url'], + 'attrs' => [[ + 'attr_id' => $attrs_map[$v['country_code']]['颜色'], + 'attr_value' => $ti['image_color'], + ]], + 'pkey' => $tpkey + ]; } + if (!empty($skus)) { - $temp = []; + $temp = []; + $temp_images = []; foreach ($skus as $idx => $sku) { + $skus[$idx]['product_id'] = $id; if (isset($images[$sku['pkey']])) { - $skus[$idx]['product_id'] = $id; - $skus[$idx]['sku'] = $images[$sku['pkey']]['sku']; - $skus[$idx]['photo_album'] = $images[$sku['pkey']]['photo_album']; - $skus[$idx]['attrs'] = $images[$sku['pkey']]['attrs']; + $skus[$idx]['sku'] = $images[$sku['pkey']]['sku']??null; + $skus[$idx]['photo_album'] = $images[$sku['pkey']]['photo_album']??null; + if (!empty($images[$sku['pkey']]['attrs'])) { + $skus[$idx]['attrs'] = $images[$sku['pkey']]['attrs']; + } } else { - if (!empty($images[$idx])) { - $temp = $images[$idx]; + if (empty($temp_images)) { + $temp_images = array_values($images); + } + if (!empty($temp_images[$idx])) { + $temp = $temp_images[$idx]; } if (!empty($temp)) { - $skus[$idx]['product_id'] = $id; - $skus[$idx]['sku'] = $temp['sku']; - $skus[$idx]['photo_album'] = $temp['photo_album']; - $skus[$idx]['attrs'] = $temp['attrs']; + $skus[$idx]['sku'] = $temp['sku']??null; + $skus[$idx]['photo_album'] = $temp['photo_album']??null; + if (!empty($temp['attrs'])) { + $skus[$idx]['attrs'] = $temp['attrs']; + } } } unset($skus[$idx]['pkey']); - unset($skus[$idx]['image_color']); } } else { foreach ($images as $image) { $skus[] = [ 'product_id' => $id, - 'sku' => $image['sku'], + 'sku' => $image['sku']??null, 'main_image' => '', - 'photo_album' => $image['photo_album'], + 'photo_album' => $image['photo_album']??null, 'skus' => $image['attrs'] ]; } } - file_put_contents(runtime_path() . 'skus.txt', json_encode($skus, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE)); - // throw new \Exception("exit;"); foreach ($skus as $sku) { $sku_id = Db::name('product_sku')->insertGetId([ 'product_id' => $sku['product_id'], - 'sku' => $sku['sku'], - 'main_image' => $sku['main_image'], - 'photo_album' => json_encode($sku['photo_album']), + 'sku' => $sku['sku']??'', + 'main_image' => $sku['main_image']??'', + 'photo_album' => !empty($sku['photo_album']) ? json_encode($sku['photo_album']) : null, ]); if (!empty($sku['attrs'])) { foreach ($sku['attrs'] as $attr) { @@ -491,13 +538,21 @@ class DataMigration extends Command } } } + Db::commit(); - $this->println(sprintf('迁移产品ID:%s => %s', $v['id'], $id)); + $total += 1; + file_put_contents( + runtime_path() . 'product_success.txt', + sprintf('["ow_product_id" => %d, "cod_product_id" => %d]'.PHP_EOL, $id, $v['id']), + FILE_APPEND + ); + $this->println(sprintf('迁移产品ID:%s => %s 【耗时:%s】', $v['id'], 0, round(microtime(true) - $start, 2) . 's')); } catch (\Throwable $th) { Db::rollback(); file_put_contents( runtime_path() . 'product_throwable.txt', - sprintf('【%s】 产品【%s】,迁移失败,错误【%s:%d】' . PHP_EOL, date('Y-h-d H:i:s'), $v['id'], $th->getMessage(), $th->getLine()) + sprintf('【%s】 产品【%s】,迁移失败,错误【%s:%d】' . PHP_EOL, date('Y-h-d H:i:s'), $v['id'], $th->getMessage(), $th->getLine()), + FILE_APPEND ); file_put_contents( runtime_path() . 'product_throwable_details.txt', @@ -505,6 +560,38 @@ class DataMigration extends Command ); } } + + $this->println(sprintf('迁移产品完成,共迁移 %s 条数据', $total)); + } + + // 迁移产品关联产品数据 + private function migrateProductRelated() + { + $sources = include_once(runtime_path() . 'product_success.php'); + $maps = []; + foreach ($sources as $so) { + $maps[$so['cod_product_id']] = $so; + } + + $old_db = Db::connect('old'); + $related = $old_db->name('product_related') + ->where('country_code', 'in', ['ZH', 'US']) + ->where('stat', '=', 0) + ->cursor(); + + $data = []; + foreach ($related as $rl) { + if (empty($maps[$rl['product_id']]) || empty($maps[$rl['related_product_id']])) { + continue; + } + $data[] = [ + 'product_id' => $maps[$rl['product_id']]['ow_product_id'], + 'related_product_id' => $maps[$rl['related_product_id']]['ow_product_id'], + 'desc' => empty($rl['related_desc']) ? null : $rl['related_desc'], + 'sort' => $rl['related_sort'] == 9999 ? 0 : $rl['related_sort'] + ]; + } + Db::name('product_related')->insertAll($data); } // 迁移文章 @@ -758,11 +845,13 @@ class UploadMannager private $password = 'Aa-1221'; private $token = ''; private $retrys = []; + private $maps = []; public function __construct() { // 登录获取token $this->token = $this->getAuthorization(); + $this->maps = include_once(runtime_path() . 'fiber_product_image_mapping.php'); } // 下载图片 @@ -805,21 +894,14 @@ class UploadMannager $file_path = self::DOWNLOAD_TEMP_PATH . $file_name; if (file_exists($file_path)) { + if (!is_valid_image($file_path)) { + return ''; + } return $file_path; } - // 使用file_get_contents下载 - // $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); - // 使用curl下载 - $file = $this->file_get_withcurl($url); + $file = file_get_withcurl($url); $dir = dirname($file_path); if (!is_dir($dir)) { mkdir($dir, 0777, true); @@ -828,34 +910,17 @@ class UploadMannager return $file_path; } - // 使用curl下载图片 - private function file_get_withcurl($url) - { - $ch = curl_init($url); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10); // 连接超时 10 秒 - curl_setopt($ch, CURLOPT_TIMEOUT, 30); // 传输超时 30 秒 - curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); // 跟随重定向 - curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0'); // 模拟浏览器 UA - - $data = curl_exec($ch); - if ($data === false) { - error_log(sprintf('cURL Error: %s; URL: %s', curl_error($ch), $url)); - return false; - } - curl_close($ch); - - return $data; - } - // 上传图片 public function upload($file_path, $field_name, $module = 'unknown') { if (empty($file_path)) { - return ['code' => 0, 'msg' => 'file_path为空', 'data' => ['path' => '']]; + return ['code' => 400, 'msg' => 'file_path为空', 'data' => ['path' => '']]; } if (\think\helper\Str::startsWith($file_path, 'http')) { return ['code' => 0, 'msg' => 'file_path为http', 'data' => ['path' => $file_path]]; } + if (isset($this->maps[$file_path])) { + return ['code' => 0, 'msg' => '成功', 'data' => ['path' => $this->maps[$file_path]]]; + } switch($field_name){ case 'image': @@ -939,3 +1004,48 @@ class UploadMannager return $result['data']['token']; } } + +// 使用curl下载图片 +function file_get_withcurl($url) +{ + $ch = curl_init($url); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10); // 连接超时 10 秒 + curl_setopt($ch, CURLOPT_TIMEOUT, 60); // 传输超时 30 秒 + curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); // 跟随重定向 + curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0'); // 模拟浏览器 UA + + $data = curl_exec($ch); + if ($data === false) { + file_put_contents(runtime_path() . 'file_get_withcurl.txt', sprintf('cURL Error: %s; URL: %s' . PHP_EOL, curl_error($ch), $url), FILE_APPEND); + return false; + } + curl_close($ch); + + return $data; +} + +// 检查图片是否有效 +function is_valid_image($filepath) +{ + try { + $image_size = getimagesize($filepath); + if ($image_size === false) { + return false; + } + [$width, $height] = $image_size; + return $width > 0 && $height > 0; + } catch (\Throwable $e) { + return false; + } +} + +// 根据自定义函数检查数组 +function array_some($array, callable $ca): bool +{ + foreach ($array as $k => $v) { + if ($ca($k, $v)) return true; + } + + return false; +} diff --git a/public/migrate_temp_images/.gitignore b/public/migrate_temp_images/.gitignore new file mode 100644 index 00000000..c96a04f0 --- /dev/null +++ b/public/migrate_temp_images/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore \ No newline at end of file