从OC导入产品数据脚本

This commit is contained in:
Edward Yang 2023-01-10 16:51:27 +08:00
parent 1f15bbda6f
commit 9f3946253a
3 changed files with 412 additions and 1 deletions

View File

@ -24,6 +24,7 @@ use Beike\Admin\View\Components\NoData;
use Beike\Admin\View\Components\Sidebar;
use Beike\Console\Commands\GenerateDatabaseDict;
use Beike\Console\Commands\MakeRootAdminUser;
use Beike\Console\Commands\MigrateFromOpenCart;
use Beike\Console\Commands\Sitemap;
use Beike\Models\AdminUser;
use Illuminate\Support\Facades\Config;
@ -83,8 +84,9 @@ class AdminServiceProvider extends ServiceProvider
if ($this->app->runningInConsole()) {
$this->commands([
MakeRootAdminUser::class,
Sitemap::class,
MigrateFromOpenCart::class,
GenerateDatabaseDict::class,
Sitemap::class,
]);
}
}

View File

@ -0,0 +1,390 @@
<?php
/**
* MigrateFromOpenCart.php
*
* @copyright 2023 beikeshop.com - All Rights Reserved
* @link https://beikeshop.com
* @author Edward Yang <yangjin@guangda.work>
* @created 2023-01-03 18:57:53
* @modified 2023-01-03 18:57:53
*/
namespace Beike\Console\Commands;
use Beike\Admin\Services\ProductService;
use Beike\Models\Brand;
use Beike\Models\Product;
use Beike\Models\ProductDescription;
use Beike\Models\ProductSku;
use Illuminate\Console\Command;
use Illuminate\Database\ConnectionInterface;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
class MigrateFromOpenCart extends Command
{
public const PER_PAGE = 1000;
public const LANG_MAPPING = [
'zh_cn' => 11,
'es' => 6,
];
protected $signature = 'migrate:oc';
protected $description = '从 OpenCart 迁移数据';
protected ConnectionInterface $ocdb;
private $ocProductVariants;
private $ocVariantDescriptions;
private $ocVariantValues;
private $ocVariantValueDescriptions;
private $ocProductImages;
private int $page = 1;
public function __construct()
{
parent::__construct();
$this->ocdb = DB::connection('opencart');
}
/**
* 导入OC产品数据
*/
public function handle()
{
// $this->importBrands();
// $this->importProducts();
}
/**
* 导入品牌数据
*/
private function importBrands()
{
Brand::query()->truncate();
$this->ocdb->table('manufacturer')
->orderBy('manufacturer_id')
->chunk(self::PER_PAGE, function ($ocBrands) {
$bkBrands = [];
foreach ($ocBrands as $ocBrand) {
$bkBrands[] = [
'id' => $ocBrand->manufacturer_id,
'name' => $ocBrand->name,
'first' => mb_strtoupper(mb_substr($ocBrand->name, 0, 1)),
'logo' => $ocBrand->image,
'sort_order' => $ocBrand->sort_order,
'status' => $ocBrand->status,
'created_at' => now(),
'updated_at' => now(),
];
}
Brand::query()->insert($bkBrands);
});
}
/**
* 导入产品
*/
private function importProducts()
{
$this->ocProductVariants = $this->ocdb->table('product_variant')->get()->groupBy('product_id');
$this->ocVariantDescriptions = $this->ocdb->table('variant_description')->get()->groupBy('variant_id');
$this->ocVariantValues = $this->ocdb->table('variant_value')->get()->keyBy('variant_value_id');
$this->ocVariantValueDescriptions = $this->ocdb->table('variant_value_description')->get()->groupBy('variant_value_id');
$this->ocProductImages = $this->ocdb->table('product_image')->get()->groupBy('product_id');
$this->clearData();
$this->ocdb->table('product')
->where('parent_id', 0)
// ->where('product_id', 1894)
// ->where('sku', '10012378')
->orderBy('product_id')
->chunk(self::PER_PAGE, function ($ocProducts) {
$this->importProduct($ocProducts);
});
}
/**
* 导入单个产品
* @param Collection $ocProducts
*/
private function importProduct(Collection $ocProducts)
{
$total = $ocProducts->count();
$ocProductIds = $ocProducts->pluck('product_id');
$childProducts = $this->ocdb->table('product')->whereIn('parent_id', $ocProductIds)->get()->groupBy('parent_id');
foreach ($ocProducts as $index => $ocProduct) {
dump("Start handle Page: $this->page - {$index}/{$total}");
$ocProductId = $ocProduct->product_id;
$productVariants = $this->ocProductVariants[$ocProductId] ?? [];
$childProducts = $childProducts[$ocProductId] ?? [];
$bkProduct = $this->generateBeikeProduct($ocProduct, $productVariants, $childProducts);
(new ProductService)->create($bkProduct);
}
$this->page++;
}
/**
* 构造 beike 产品
*/
private function generateBeikeProduct($ocProduct, $productVariants, $childProducts)
{
$variables = $this->generateVariables($ocProduct, $childProducts);
$bkProduct = [
'active' => $ocProduct->status,
'brand_id' => $ocProduct->manufacturer_id,
'position' => $ocProduct->sort_order,
'tax_class_id' => $ocProduct->tax_class_id,
'variables' => json_encode($variables),
];
$bkProduct['descriptions'] = $this->generateDescriptions($ocProduct);
$bkProduct['images'] = [$ocProduct->image];
$bkProduct['skus'] = $this->generateSkus($ocProduct, $productVariants, $childProducts, $variables);
return $bkProduct;
}
/**
* @return array[]
*/
private function generateVariables($ocProduct, $childProducts): array
{
$productIds = [$ocProduct->product_id];
foreach ($childProducts as $childProduct) {
$productIds[] = $childProduct->product_id;
}
$locales = locales();
$items = $values = [];
foreach ($productIds as $productId) {
$productVariants = $this->ocProductVariants[$productId] ?? [];
foreach ($productVariants as $productVariant) {
$productVariantId = $productVariant->variant_id;
$productVariantValueId = $productVariant->variant_value_id;
$variants = $this->ocVariantDescriptions[$productVariantId];
$variantValue = $this->ocVariantValues[$productVariantValueId];
$variantValueDescriptions = $this->ocVariantValueDescriptions[$productVariantValueId];
$variants = $variants->keyBy('language_id');
$names = [];
foreach ($locales as $locale) {
$variant = $variants[self::LANG_MAPPING[$locale['code']]];
$names[$locale['code']] = $variant->name;
}
$valueNames = [];
$variantValueDescriptions = $variantValueDescriptions->keyBy('language_id');
foreach ($locales as $locale) {
$variantValueDescription = $variantValueDescriptions[self::LANG_MAPPING[$locale['code']]];
$valueNames[$locale['code']] = $variantValueDescription->name;
}
$values[$productVariantValueId] = [
'variant_value_id' => $productVariantValueId,
'name' => $valueNames,
'image' => $variantValue->image,
];
$items[$productVariantId] = [
'variant_id' => $productVariantId,
'name' => $names,
'values' => $values,
'isImage' => (bool) $variantValue->image,
];
}
}
$items = array_values($items);
foreach ($items as $index => $item) {
$items[$index]['values'] = array_values($item['values']);
}
return $items;
}
/**
* 生成 beike 产品描述
* @param $ocProduct
* @return array
*/
private function generateDescriptions($ocProduct): array
{
$descriptions = [];
$locales = locales();
$ocDescriptions = $this->ocdb->table('product_description')
->where('product_id', $ocProduct->product_id)
->get()
->keyBy('language_id')
->toArray();
foreach ($locales as $locale) {
$ocDescription = $ocDescriptions[self::LANG_MAPPING[$locale['code']]];
$descriptions[$locale['code']] = [
'name' => $ocDescription->name,
'content' => html_entity_decode($ocDescription->description),
];
}
return $descriptions;
}
/**
* 生成 beike 产品 SKU
*
* @param $ocProduct
* @param $productVariants
* @param $childProducts
* @param array $variables
* @return array
*/
private function generateSkus($ocProduct, $productVariants, $childProducts, array $variables): array
{
// 简单商品
if (count($productVariants) == 0) {
return $this->generateSimpleSku($ocProduct);
}
$masterSku = $childSkus = [];
// 有主商品
if (count($productVariants) > 0) {
$masterSku = $this->generateMasterSku($ocProduct, $productVariants, $variables);
}
// 有子商品
if (count($childProducts) > 0) {
$childSkus = $this->generateChildSkus($childProducts, $variables);
}
if ($masterSku) {
array_push($childSkus, $masterSku);
}
return $childSkus;
}
/**
* 获取简单商品 SKU
* @param $ocProduct
* @return array[]
*/
private function generateSimpleSku($ocProduct): array
{
$images = $this->ocProductImages[$ocProduct->product_id] ?? [];
return [
[
'image' => $images,
'model' => $ocProduct->model,
'sku' => $ocProduct->sku,
'price' => $ocProduct->price,
'origin_price' => $ocProduct->price,
'cost_price' => $ocProduct->cost_price,
'quantity' => $ocProduct->quantity,
'variants' => null,
'position' => $ocProduct->sort_order,
'is_default' => 1,
],
];
}
/**
* 获取主商品 SKU
* @param $ocProduct
* @param $productVariants
* @param $variables
* @return array
*/
private function generateMasterSku($ocProduct, $productVariants, $variables): array
{
$variants = $this->generateVariants($productVariants, $variables);
$images = $this->ocProductImages[$ocProduct->product_id] ?? [];
return [
'image' => $images,
'model' => $ocProduct->model,
'sku' => $ocProduct->sku,
'price' => $ocProduct->price,
'origin_price' => $ocProduct->price,
'cost_price' => $ocProduct->cost_price,
'quantity' => $ocProduct->quantity,
'variants' => $variants,
'position' => $ocProduct->sort_order,
'is_default' => 1,
];
}
/**
* 获取子商品SKU
* @param $childProducts
* @param $variables
* @return array
*/
private function generateChildSkus($childProducts, $variables): array
{
$items = [];
foreach ($childProducts as $ocProduct) {
$ocProductId = $ocProduct->product_id;
$productVariants = $this->ocProductVariants[$ocProductId] ?? [];
$images = $this->ocProductImages[$ocProductId] ?? [];
$variants = $this->generateVariants($productVariants, $variables);
$items[] = [
'image' => $images,
'model' => $ocProduct->model,
'sku' => $ocProduct->sku,
'price' => $ocProduct->price,
'origin_price' => $ocProduct->price,
'cost_price' => $ocProduct->cost_price,
'quantity' => $ocProduct->quantity,
'variants' => $variants,
'position' => $ocProduct->sort_order,
'is_default' => 0,
];
}
return $items;
}
/**
* 生成商品 SKU 多规格数据
*
* @param $productVariants
* @param $variables
* @return array
*/
private function generateVariants($productVariants, $variables): array
{
$items = [];
foreach ($productVariants as $productVariant) {
$variantId = $productVariant->variant_id;
$variantValueId = $productVariant->variant_value_id;
foreach ($variables as $index1 => $variable) {
if ($variantId == $variable['variant_id']) {
foreach ($variable['values'] as $index2 => $value) {
if ($variantValueId == $value['variant_value_id']) {
$items[$index1] = (string) $index2;
}
}
}
}
}
return $items;
}
/**
* 清空产品相关数据
*/
private function clearData()
{
Product::query()->truncate();
ProductSku::query()->truncate();
ProductDescription::query()->truncate();
}
}

View File

@ -63,6 +63,25 @@ return [
]) : [],
],
'opencart' => [
'driver' => 'mysql',
'host' => env('DB_HOST', '127.0.0.1'),
'port' => env('DB_PORT', '3306'),
'database' => env('OC_DATABASE', 'forge'),
'username' => env('DB_USERNAME', 'forge'),
'password' => env('DB_PASSWORD', ''),
'unix_socket' => env('DB_SOCKET', ''),
'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',
'prefix' => env('OC_PREFIX', ''),
'prefix_indexes' => true,
'strict' => true,
'engine' => 'InnoDB',
'options' => extension_loaded('pdo_mysql') ? array_filter([
PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),
]) : [],
],
'pgsql' => [
'driver' => 'pgsql',
'url' => env('DATABASE_URL'),