refactor: 同步程序

This commit is contained in:
2025-05-14 14:02:00 +08:00
parent f727bcae70
commit a66f4ca6dc
2 changed files with 203 additions and 91 deletions

View File

@@ -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 '<img src="解析替换图片失败,请手动处理" alt="" />';
}
return '<img src="' . $file_path . '" alt="" />';
}, $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;
}

2
public/migrate_temp_images/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
*
!.gitignore