From f67455e37a61ff4f44f5cef8f2aea9da72f93b2b Mon Sep 17 00:00:00 2001 From: feibisi Date: Tue, 21 Oct 2025 14:48:11 +0800 Subject: [PATCH] =?UTF-8?q?=E8=84=9A=E6=9C=AC=E5=A4=87=E4=BB=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- migrate-script.php | 68 +++++++++ rollback-script.php | 64 ++++++++ test-plugin.php | 39 +++++ verify-data.php | 47 ++++++ wp-cli-migrate-brands.php | 301 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 519 insertions(+) create mode 100644 migrate-script.php create mode 100644 rollback-script.php create mode 100644 test-plugin.php create mode 100644 verify-data.php create mode 100644 wp-cli-migrate-brands.php diff --git a/migrate-script.php b/migrate-script.php new file mode 100644 index 0000000..8732357 --- /dev/null +++ b/migrate-script.php @@ -0,0 +1,68 @@ +get_var("SELECT COUNT(*) FROM {$wpdb->terms} t + INNER JOIN {$wpdb->term_taxonomy} tt ON t.term_id = tt.term_id + WHERE tt.taxonomy = 'yith_product_brand'"); + +$native_brands_before = $wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->terms} t + INNER JOIN {$wpdb->term_taxonomy} tt ON t.term_id = tt.term_id + WHERE tt.taxonomy = 'product_brand'"); + +echo " YITH 品牌数量: " . ($yith_brands_before ?: 0) . "\n"; +echo " 原生品牌数量: " . ($native_brands_before ?: 0) . "\n\n"; + +if ($yith_brands_before > 0) { + echo "🔄 开始迁移 YITH 品牌到原生品牌...\n"; + + if ($native_brands_before > 0) { + echo "⚠️ 警告:已存在 " . $native_brands_before . " 个原生品牌\n"; + } + + $wpdb->query('START TRANSACTION'); + + try { + $result = $wpdb->update( + $wpdb->term_taxonomy, + array('taxonomy' => 'product_brand'), + array('taxonomy' => 'yith_product_brand') + ); + + if ($result !== false) { + echo "✅ 成功迁移 " . $result . " 个品牌\n"; + $wpdb->query('COMMIT'); + + wp_cache_flush(); + if (function_exists('wc_delete_product_transients')) { + wc_delete_product_transients(); + } + + echo "✅ 缓存已清理\n"; + + $yith_brands_after = $wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->term_taxonomy} WHERE taxonomy = 'yith_product_brand'"); + $native_brands_after = $wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->term_taxonomy} WHERE taxonomy = 'product_brand'"); + + echo "\n📊 迁移后数据统计:\n"; + echo " YITH 品牌数量: " . ($yith_brands_after ?: 0) . "\n"; + echo " 原生品牌数量: " . ($native_brands_after ?: 0) . "\n"; + + } else { + $wpdb->query('ROLLBACK'); + echo "❌ 迁移失败\n"; + } + } catch (Exception $e) { + $wpdb->query('ROLLBACK'); + echo "❌ 迁移出错: " . $e->getMessage() . "\n"; + } + +} else { + echo "ℹ️ 未检测到 YITH 品牌数据,无需迁移\n"; +} + +echo "\n=== 迁移完成 ===\n"; \ No newline at end of file diff --git a/rollback-script.php b/rollback-script.php new file mode 100644 index 0000000..da11221 --- /dev/null +++ b/rollback-script.php @@ -0,0 +1,64 @@ +get_var("SELECT COUNT(*) FROM {$wpdb->terms} t + INNER JOIN {$wpdb->term_taxonomy} tt ON t.term_id = tt.term_id + WHERE tt.taxonomy = 'yith_product_brand'"); + +$native_brands_before = $wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->terms} t + INNER JOIN {$wpdb->term_taxonomy} tt ON t.term_id = tt.term_id + WHERE tt.taxonomy = 'product_brand'"); + +echo " YITH 品牌数量: " . ($yith_brands_before ?: 0) . "\n"; +echo " 原生品牌数量: " . ($native_brands_before ?: 0) . "\n\n"; + +if ($native_brands_before > 0) { + echo "🔄 开始回滚原生品牌到 YITH 品牌...\n"; + + $wpdb->query('START TRANSACTION'); + + try { + $result = $wpdb->update( + $wpdb->term_taxonomy, + array('taxonomy' => 'yith_product_brand'), + array('taxonomy' => 'product_brand') + ); + + if ($result !== false) { + echo "✅ 成功回滚 " . $result . " 个品牌\n"; + $wpdb->query('COMMIT'); + + wp_cache_flush(); + if (function_exists('wc_delete_product_transients')) { + wc_delete_product_transients(); + } + + echo "✅ 缓存已清理\n"; + + $yith_brands_after = $wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->term_taxonomy} WHERE taxonomy = 'yith_product_brand'"); + $native_brands_after = $wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->term_taxonomy} WHERE taxonomy = 'product_brand'"); + + echo "\n📊 回滚后数据统计:\n"; + echo " YITH 品牌数量: " . ($yith_brands_after ?: 0) . "\n"; + echo " 原生品牌数量: " . ($native_brands_after ?: 0) . "\n"; + + } else { + $wpdb->query('ROLLBACK'); + echo "❌ 回滚失败\n"; + } + } catch (Exception $e) { + $wpdb->query('ROLLBACK'); + echo "❌ 回滚出错: " . $e->getMessage() . "\n"; + } + +} else { + echo "ℹ️ 未检测到原生品牌数据,无需回滚\n"; +} + +echo "\n=== 回滚完成 ===\n"; \ No newline at end of file diff --git a/test-plugin.php b/test-plugin.php new file mode 100644 index 0000000..9048d92 --- /dev/null +++ b/test-plugin.php @@ -0,0 +1,39 @@ +last_error) { + echo "❌ 连接失败: " . $wpdb->last_error . "\n"; + exit(1); +} else { + echo "✅ 连接正常\n"; +} + +$yith_brands = $wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->terms} t + INNER JOIN {$wpdb->term_taxonomy} tt ON t.term_id = tt.term_id + WHERE tt.taxonomy = 'yith_product_brand'"); + +$native_brands = $wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->terms} t + INNER JOIN {$wpdb->term_taxonomy} tt ON t.term_id = tt.term_id + WHERE tt.taxonomy = 'product_brand'"); + +echo "\n📊 当前数据统计:\n"; +echo " YITH 品牌数量: " . ($yith_brands ?: 0) . "\n"; +echo " 原生品牌数量: " . ($native_brands ?: 0) . "\n\n"; + +if ($yith_brands > 0) { + echo "🔄 检测到 YITH 品牌数据,可以执行迁移\n"; + echo " 运行命令: php migrate-script.php\n"; +} elseif ($native_brands > 0) { + echo "✅ 检测到原生品牌数据,迁移已完成\n"; + echo " 如需回滚: php rollback-script.php\n"; +} else { + echo "ℹ️ 未检测到任何品牌数据\n"; +} + +echo "\n=== 检查完成 ===\n"; \ No newline at end of file diff --git a/verify-data.php b/verify-data.php new file mode 100644 index 0000000..f608512 --- /dev/null +++ b/verify-data.php @@ -0,0 +1,47 @@ +get_var("SELECT COUNT(*) FROM {$wpdb->term_taxonomy} WHERE taxonomy = 'yith_product_brand'"); +$native_count = $wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->term_taxonomy} WHERE taxonomy = 'product_brand'"); + +echo "📊 当前数据统计:\n"; +echo " YITH 品牌数量: " . ($yith_count ?: 0) . "\n"; +echo " 原生品牌数量: " . ($native_count ?: 0) . "\n\n"; + +if ($native_count > 0) { + $brands = $wpdb->get_results(" + SELECT t.name, t.slug, tt.count + FROM {$wpdb->terms} t + INNER JOIN {$wpdb->term_taxonomy} tt ON t.term_id = tt.term_id + WHERE tt.taxonomy = 'product_brand' + ORDER BY tt.count DESC + LIMIT 10 + "); + + echo "🏷️ 前10个原生品牌:\n"; + foreach ($brands as $brand) { + echo " - " . $brand->name . " (" . $brand->count . "个产品)\n"; + } + + $relationships = $wpdb->get_var(" + SELECT COUNT(*) FROM {$wpdb->term_relationships} tr + INNER JOIN {$wpdb->term_taxonomy} tt ON tr.term_taxonomy_id = tt.term_taxonomy_id + WHERE tt.taxonomy = 'product_brand' + "); + + $meta_count = $wpdb->get_var(" + SELECT COUNT(*) FROM {$wpdb->termmeta} tm + INNER JOIN {$wpdb->term_taxonomy} tt ON tm.term_id = tt.term_id + WHERE tt.taxonomy = 'product_brand' + "); + + echo "\n🔗 产品-品牌关联数量: " . $relationships . "\n"; + echo "📝 品牌元数据记录: " . $meta_count . "\n"; +} + +echo "\n=== 验证完成 ===\n"; \ No newline at end of file diff --git a/wp-cli-migrate-brands.php b/wp-cli-migrate-brands.php new file mode 100644 index 0000000..f8b0bd3 --- /dev/null +++ b/wp-cli-migrate-brands.php @@ -0,0 +1,301 @@ +dry_run = in_array('--dry-run=true', $argv); + $this->cleanup = in_array('--cleanup=true', $argv); + + if ($this->dry_run) { + WP_CLI::line('🔍 预览模式:将显示迁移计划但不执行实际操作'); + } + + $this->run(); + } + + private function run() { + WP_CLI::line('开始 YITH 品牌迁移...'); + + if (!$this->check_requirements()) { + return; + } + + $brands = $this->get_yith_brands(); + + if (empty($brands)) { + WP_CLI::warning('未找到 YITH 品牌数据'); + return; + } + + WP_CLI::line(sprintf('找到 %d 个品牌需要迁移', count($brands))); + + if ($this->dry_run) { + $this->preview_migration($brands); + } else { + $this->execute_migration($brands); + } + } + + private function check_requirements() { + if (!class_exists('WooCommerce')) { + WP_CLI::error('WooCommerce 插件未激活'); + return false; + } + + if (!taxonomy_exists($this->old_taxonomy)) { + WP_CLI::error('YITH 品牌分类法不存在'); + return false; + } + + if (!taxonomy_exists($this->new_taxonomy)) { + WP_CLI::line('注册 WooCommerce 原生品牌分类法...'); + $this->register_product_brand_taxonomy(); + } + + return true; + } + + private function register_product_brand_taxonomy() { + $labels = [ + 'name' => '品牌', + 'singular_name' => '品牌', + 'menu_name' => '品牌', + 'all_items' => '所有品牌', + 'edit_item' => '编辑品牌', + 'view_item' => '查看品牌', + 'update_item' => '更新品牌', + 'add_new_item' => '添加新品牌', + 'new_item_name' => '新品牌名称', + 'search_items' => '搜索品牌', + 'popular_items' => '热门品牌', + 'not_found' => '未找到品牌', + ]; + + $args = [ + 'labels' => $labels, + 'public' => true, + 'show_ui' => true, + 'show_in_menu' => true, + 'show_in_nav_menus' => true, + 'show_tagcloud' => true, + 'show_in_quick_edit' => true, + 'show_admin_column' => true, + 'hierarchical' => true, + 'query_var' => true, + 'rewrite' => ['slug' => 'product-brand', 'with_front' => false], + 'show_in_rest' => true, + ]; + + register_taxonomy($this->new_taxonomy, ['product'], $args); + WP_CLI::success('品牌分类法注册成功'); + } + + private function get_yith_brands() { + return get_terms([ + 'taxonomy' => $this->old_taxonomy, + 'hide_empty' => false, + 'number' => 0, + ]); + } + + private function preview_migration($brands) { + WP_CLI::line(''); + WP_CLI::line('📋 迁移预览:'); + WP_CLI::line(''); + + $table_data = []; + + foreach ($brands as $brand) { + $product_count = $this->get_brand_product_count($brand->term_id); + $existing_brand = get_term_by('slug', $brand->slug, $this->new_taxonomy); + $action = $existing_brand ? '更新' : '创建'; + + $table_data[] = [ + 'ID' => $brand->term_id, + '品牌名称' => $brand->name, + '别名' => $brand->slug, + '产品数量' => $product_count, + '操作' => $action, + ]; + } + + WP_CLI\Utils\format_items('table', $table_data, ['ID', '品牌名称', '别名', '产品数量', '操作']); + + WP_CLI::line(''); + WP_CLI::line('要执行实际迁移,请运行:'); + WP_CLI::line('wp eval-file wp-cli-migrate-brands.php'); + } + + private function execute_migration($brands) { + $progress = WP_CLI\Utils\make_progress_bar('迁移品牌', count($brands)); + $migrated_count = 0; + $errors = []; + + foreach ($brands as $brand) { + try { + $this->migrate_single_brand($brand); + $migrated_count++; + } catch (Exception $e) { + $errors[] = sprintf('品牌 "%s" 迁移失败:%s', $brand->name, $e->getMessage()); + } + + $progress->tick(); + } + + $progress->finish(); + + WP_CLI::line(''); + WP_CLI::success(sprintf('成功迁移 %d 个品牌', $migrated_count)); + + if (!empty($errors)) { + WP_CLI::warning('以下品牌迁移时出现错误:'); + foreach ($errors as $error) { + WP_CLI::line(' - ' . $error); + } + } + + if ($this->cleanup) { + $this->cleanup_old_data(); + } else { + WP_CLI::line(''); + WP_CLI::line('要清理旧数据,请运行:'); + WP_CLI::line('wp eval-file wp-cli-migrate-brands.php --cleanup=true'); + } + + wp_cache_flush(); + } + + private function migrate_single_brand($old_term) { + $existing_term = get_term_by('slug', $old_term->slug, $this->new_taxonomy); + + if ($existing_term) { + $new_term_id = $existing_term->term_id; + + wp_update_term($new_term_id, $this->new_taxonomy, [ + 'name' => $old_term->name, + 'description' => $old_term->description, + ]); + } else { + $result = wp_insert_term( + $old_term->name, + $this->new_taxonomy, + [ + 'slug' => $old_term->slug, + 'description' => $old_term->description, + ] + ); + + if (is_wp_error($result)) { + throw new Exception($result->get_error_message()); + } + + $new_term_id = $result['term_id']; + } + + $this->migrate_term_meta($old_term->term_id, $new_term_id); + $this->migrate_product_relationships($old_term->term_id, $new_term_id); + } + + private function migrate_term_meta($old_term_id, $new_term_id) { + $meta_keys = [ + 'thumbnail_id', + 'brand_logo', + 'brand_banner', + 'brand_url', + 'brand_email', + 'brand_description', + 'yith_wcbr_brand_logo', + 'yith_wcbr_brand_banner', + 'yith_wcbr_brand_url', + 'yith_wcbr_brand_email', + ]; + + foreach ($meta_keys as $meta_key) { + $meta_value = get_term_meta($old_term_id, $meta_key, true); + if (!empty($meta_value)) { + update_term_meta($new_term_id, $meta_key, $meta_value); + } + } + } + + private function migrate_product_relationships($old_term_id, $new_term_id) { + global $wpdb; + + $products = $wpdb->get_col($wpdb->prepare(" + SELECT object_id + FROM {$wpdb->term_relationships} tr + INNER JOIN {$wpdb->term_taxonomy} tt ON tr.term_taxonomy_id = tt.term_taxonomy_id + WHERE tt.term_id = %d AND tt.taxonomy = %s + ", $old_term_id, $this->old_taxonomy)); + + foreach ($products as $product_id) { + wp_set_object_terms($product_id, [$new_term_id], $this->new_taxonomy, false); + } + } + + private function get_brand_product_count($term_id) { + global $wpdb; + + return $wpdb->get_var($wpdb->prepare(" + SELECT COUNT(object_id) + FROM {$wpdb->term_relationships} tr + INNER JOIN {$wpdb->term_taxonomy} tt ON tr.term_taxonomy_id = tt.term_taxonomy_id + WHERE tt.term_id = %d AND tt.taxonomy = %s + ", $term_id, $this->old_taxonomy)); + } + + private function cleanup_old_data() { + WP_CLI::line(''); + WP_CLI::confirm('确定要删除 YITH 品牌的旧数据吗?此操作不可逆!'); + + global $wpdb; + + $term_taxonomy_ids = $wpdb->get_col($wpdb->prepare(" + SELECT term_taxonomy_id + FROM {$wpdb->term_taxonomy} + WHERE taxonomy = %s + ", $this->old_taxonomy)); + + if (!empty($term_taxonomy_ids)) { + $placeholders = implode(',', array_fill(0, count($term_taxonomy_ids), '%d')); + + $relationships_deleted = $wpdb->query($wpdb->prepare(" + DELETE FROM {$wpdb->term_relationships} + WHERE term_taxonomy_id IN ($placeholders) + ", ...$term_taxonomy_ids)); + + $taxonomy_deleted = $wpdb->query($wpdb->prepare(" + DELETE FROM {$wpdb->term_taxonomy} + WHERE taxonomy = %s + ", $this->old_taxonomy)); + + WP_CLI::success(sprintf('清理完成:删除了 %d 个关系记录和 %d 个分类法记录', + $relationships_deleted, $taxonomy_deleted)); + } + + wp_cache_flush(); + } +} + +new WPCLIBrandsMigrator(); \ No newline at end of file