$goods_juhe_id], ] * * @var array */ private $result_data = []; /** * 是否是更新数据 * * @var boolean */ private $is_update = true; /** * 商城ID * * @var int */ public $mall_id; /** * 当前商品ID * * @var int */ private $goods_id; /** * 默认排序 * * @var integer */ protected $sort = 100; /** * 商品上架状态 * * @var boolean */ protected $goods_status = false; /** * 支持最大库存 * * @var integer */ protected $max_stock_num = 999999; /** * 聚合商品model * * @var array * [ * 'goods_id' => GoodsModel, * 'goods_id' => GoodsModel * ] */ protected $storage_models; /** * settings * * @var array */ public $settings; public function __construct($config = []) { parent::__construct($config); if (empty($this->mall_id)) { throw new Exception('mall-id不能为空'); } } /** * 初始化值 * * @return void */ private function initValues() { // $this->result_data = []; $this->is_update = false; $this->goods_id = null; } /** * 添加商品 * * @param array|integer $ids * @return array|boolean */ public static function add($ids, $mall_id = null) { try { // 查询商品ID $where = ['id' => $ids, 'mall_id' => $mall_id]; // $goods_list = GoodsModel::find()->where($where)->select('id, supply_id, goods_id')->all(); $goods_list = StorageModel::find()->where($where)->select('id, supply_id, goods_id, middle_goods_id, mall_goods_id')->all(); if (empty($goods_list)) { throw new Exception('选品不存在'); } // 以供应链作为区分添加商品 $class = new self(['mall_id' => $mall_id]); $class->addGoodsOfSupply($goods_list); return true; } catch (Exception $e) { self::$error = $e->getMessage(); return false; } } /** * 执行队列 * * @param array $params * @return void */ public static function handleSync($params) { // static::addLog('当前Ids: ' . json_encode($params['ids'])); if (self::add($params['ids'], $params['mall_id']) === false) { // 记录错误日志 // self::$error } } /** * 队列任务形式执行添加商品 * * @param array|integer $ids * @param integer $mall_id * @return void */ public static function queueAdd($ids, $mall_id = null, $is_notice = false) { // 5一组 $num = ceil(count($ids) / 2) - 1; for ($i = 0; $i <= $num; $i++) { $id = array_slice($ids, $i * 2, 2); if ($is_notice) { self::handleSync([ 'ids' => $id, 'mall_id' => $mall_id, ]); } else { Yii::$app->services->rabbitMq->push(RabbitMqEnum::EXCHANGE_TASK, RabbitMqEnum::TASK_QUEUE, [ 'ids' => $id, 'mall_id' => $mall_id, ], self::class, 'handleSync'); } } // static::handleSync([ // 'ids' => $ids, // 'mall_id' => $mall_id, // ]); } /** * 储存model关系 * * @param array $goods * @return void */ private function setGoodsModel($goods_list) { foreach ($goods_list as $goods) { $this->storage_models[$goods->middle_goods_id] = $goods; } } /** * 添加平台商品 * * @param array $ids * @return void */ private function addGoodsOfSupply($goods_list) { // 储存model关系 $this->setGoodsModel($goods_list); // 设置API foreach ($goods_list as $goods) { $this->setApi($goods->supply_id); $this->setSettings($goods->supply_id); $ids_list[$goods->supply_id][] = $goods->middle_goods_id; } // 添加商品 foreach ($ids_list as $supply_id => $ids) { $this->addGoods($ids, $supply_id); } } /** * 设置API * * @return void */ private function setApi($supply_id) { if (empty($this->apis[$supply_id])) { $this->apis[$supply_id] = ApiService::getConnect($this->mall_id, $supply_id); } } /** * 获取并供应链设置 * * @param integer $supply_id * @return void */ public function setSettings($supply_id) { if (empty($this->settings[$supply_id])) { $this->settings[$supply_id] = SupplysService::getSet($this->mall_id, $supply_id); } } /** * 添加商品 * * @param integer|array $id * @return void */ private function addGoods($id, $supply_id) { // static::addLog('当前执行SupplyId: ' . $supply_id); // static::addLog('执行添加Ids: ' . json_encode($id)); // 当前导入商品供应链ID $this->setSupplyId($supply_id); // API获取属性 $goods_list = $this->apis[$supply_id]->getGoodsInfo($id); if (empty($goods_list['list'])) { $this->handNotGoods($supply_id, $id); throw new Exception('商品不存在'); } $db = Yii::$app->db->beginTransaction(); try { // static::addLog('接口获取的详情ID: ' . json_encode(array_column($goods_list['list'], 'id') ?? [])); // 查询的商品数量和实际返回数量不一致 if (count($goods_list['list']) != count($id)) { $goods_ids = array_column($goods_list['list'], 'id'); $not_info_ids = array_diff($id, $goods_ids); $this->handNotGoods($supply_id, $not_info_ids); } foreach ($goods_list['list'] as $goods_info) { // static::addLog('第一步: ' . $goods_info['id']); // 初始 $this->initValues(); // 中台商品ID $this->supply_goods_id = $goods_info['id']; // 是否更新 if ($this->goods_id = $this->storage_models[$goods_info['id']]->mall_goods_id) { $this->is_update = true; // 当前商品ID } // static::addLog('第二步: ' . $goods_info['id']); // 获取商城商品字段 try { $goods_attr = $this->getGoodsAttr($goods_info); // 获取规格属性 $attr_groups = $this->getAttrGroups($goods_info['skus']); } catch (Exception $e) { $this->storage_models[$goods_info['id']]->import_msg = $e->getMessage(); !$this->is_update && $this->storage_models[$goods_info['id']]->import_at = time(); $this->is_update && $this->storage_models[$goods_info['id']]->updated_at = time(); $this->storage_models[$goods_info['id']]->is_import = 0; $this->storage_models[$goods_info['id']]->save(); continue; } // $attr_groups = $this->removeAttrGroups($attr_groups, $goods_attr['attr']); // 匹配规格ID $goods_attr = $this->matchingAttrId($goods_attr, $attr_groups); $goods_attr['attr_groups'] = $attr_groups; // static::addLog('第三步: ' . $goods_info['id']); // 商品 $mall_goods = new MallGoodsModel(); // 如果是已保存的商品加上ID isset($this->goods_id) && $goods_attr['id'] = $this->goods_id; // 保存商品 try { $res = $mall_goods->saveGoods($goods_attr); if ($res === false) { $this->storage_models[$goods_info['id']]->import_msg = '添加失败'; !$this->is_update && $this->storage_models[$goods_info['id']]->import_at = time(); $this->is_update && $this->storage_models[$goods_info['id']]->updated_at = time(); $this->storage_models[$goods_info['id']]->is_import = 0; $this->storage_models[$goods_info['id']]->save(); // 跳过当前商品 continue; } } catch (Exception $e) { $msg = Json::encode([ 'line' => $e->getLine(), 'msg' => $e->getMessage(), ]); $this->storage_models[$goods_info['id']]->import_msg = $msg; !$this->is_update && $this->storage_models[$goods_info['id']]->import_at = time(); $this->is_update && $this->storage_models[$goods_info['id']]->updated_at = time(); $this->storage_models[$goods_info['id']]->is_import = 0; $this->storage_models[$goods_info['id']]->save(); // 跳过当前商品 continue; } $this->storage_models[$goods_info['id']]->mall_goods_id = $res->id; !$this->is_update && $this->storage_models[$goods_info['id']]->import_at = time(); $this->is_update && $this->storage_models[$goods_info['id']]->updated_at = time(); $this->storage_models[$goods_info['id']]->is_import = 2; $this->storage_models[$goods_info['id']]->save(); // static::addLog('执行添加完成ID: ' . $goods_info['id']); } // 提交 $db->commit(); // static::addLog('全部ID执行完成'); return true; } catch (Exception $e) { // static::addLog('执行添加失败ID: ' . $goods_info['id']); $db->rollback(); $msg = Json::encode([ 'line' => $e->getLine(), 'msg' => $e->getMessage(), ]); if (!empty($this->storage_models[$this->supply_goods_id]) && $model = $this->storage_models[$this->supply_goods_id]) { $res = StorageModel::updateAll([ 'is_import' => 0, 'import_msg' => $msg, 'updated_at' => time(), ], ['id' => $model->id]); } } } /** * 去除未使用的规格 * * @param array $attr_groups * @param array $attr * @return array */ protected function removeAttrGroups($attr_groups, $attrs) { $groups_ids = []; foreach ($attr_groups as $group) { foreach ($group['attr_list'] as $group_item) { $groups_ids[] = $group_item['attr_id']; } } $attr_group_ids = []; foreach ($attrs as $attr) { foreach ($attr['attr_list'] as $attr_item) { $attr_group_ids[] = $attr_item['attr_id']; } } $diff = array_diff($groups_ids, $attr_group_ids); if ($diff) { foreach ($attr_groups as $k1 => $group) { foreach ($group['attr_list'] as $k2 => $group_item) { foreach ($diff as $v) { if ($v == $group_item['attr_id']) { unset($attr_groups[$k1]['attr_list'][$k2]); } } } } } foreach ($attr_groups as $k1 => $group) { $attr_groups[$k1]['attr_list'] = array_values($group['attr_list']); } return $attr_groups; } /** * 匹配规格ID * * @param array $goods_attr * @param array $attr_groups * @return array */ private function matchingAttrId($goods_attr, $attr_groups) { foreach ($goods_attr['attr'] as $k => $sku) { $sign = []; foreach ($sku['attr_list'] as $k1 => $attr) { $attr_group_id = $this->getAttrGroupId($attr_groups, $attr['attr_group_name']); $attr_id = $this->getAttrId($attr_groups, $attr['attr_name']); $sign[] = $attr_id; $goods_attr['attr'][$k]['attr_list'][$k1]['attr_id'] = $attr_id; $goods_attr['attr'][$k]['attr_list'][$k1]['attr_group_id'] = $attr_group_id; } // 拼接sign_id // asort($sign); count($sign) > 1 && $goods_attr['attr'][$k]['sign_id'] = implode(':', $sign); } return $goods_attr; } /** * 获取规格属性ID * * @param array $attr_groups 规格组 * @param string $name 规格名称 * @return integer|null */ private function getAttrId($attr_groups, $name) { foreach ($attr_groups as $group) { foreach ($group['attr_list'] as $attr) { if ($attr['attr_name'] == $name) { return $attr['attr_id']; } } } } /** * 获取规格属性组ID * * @param array $attr_groups 规格组 * @param string $name 属性组名称 * @return integer|null */ public function getAttrGroupId($attr_groups, $name) { foreach ($attr_groups as $group) { if ($group['attr_group_name'] == $name) { return $group['attr_group_id']; } } } /** * 设置平台ID * * @param integer $id * @return void */ public function setSupplyId($id) { $this->supply_id = $id; } /** * 获取规格属性 * * @param array $skus * @return array */ private function getAttrGroups($skus) { $first_key = array_key_first($skus); $options = $skus[$first_key]['options']; $attr_groups = []; $attr_group_id = 1; foreach ($options as $option) { $attr_groups[] = [ 'attr_group_id' => $attr_group_id, 'attr_group_name' => $option['spec_name'], ]; $attr_group_id++; } $attr_name = []; foreach ($skus as $sku) { if ($sku['options']) { foreach ($attr_groups as $k => $group) { foreach ($sku['options'] as $option) { if ( $option['spec_name'] == $group['attr_group_name'] && !in_array($option['spec_item_name'], $attr_name) ) { $attr_name[] = $option['spec_item_name']; $attr_groups[$k]['attr_list'][] = [ 'attr_name' => $option['spec_item_name'], 'pic_url'=>$sku['image_url'], ]; } } } } } $attr_id = 0; return array_map(function ($item) use (&$attr_id) { return array_merge($item, ['attr_list' => array_map(function ($attr) use (&$attr_id) { $attr_id++; return array_merge($attr, [ 'attr_id' => $attr_id, ]); }, $item['attr_list'])]); }, $attr_groups); } /** * 获取规格属性图片 * * @return void */ private function getPicUrl($id, $options) { try { foreach ($options as $value) { if (strstr($value['spec_value_ids'], strval($id)) !== false) { return $value['image']; } } } catch (Exception $e) { return ''; } return ''; } /** * 获取添加商品属性 * * @param array $goods_info * @return array */ private function getGoodsAttr($goods_info) { // 获取匹配的Attr $specs_attr = $this->getSpecsForAttr($goods_info['skus']); // $is_attr = count($specs_attr) > 1 ? 1 : 0; $is_attr = 1; // 默认多规格 $attr = [ // 'cats' => [$this->getCats($goods_info)], // 分类 'attr' => $specs_attr, // 商品规格属性 'bannar_pic' => $this->getUrlForGallery($goods_info['gallery']), 'cover_pic' => $goods_info['image_url'], // 主图 'price' => $this->getPrice($goods_info, 'sell'), // 销售价 'original_price' => $this->getPrice($goods_info, 'original'), // 原价 'cost_price' => $this->getPrice($goods_info, 'cost'), // 成本价 'detail' => $this->_formatDetail(empty($goods_info['detail_images']) ? $goods_info['title'] : $goods_info['detail_images']), // 商品详情 'stock' => $goods_info['stock'] > $this->max_stock_num ? $this->max_stock_num : $goods_info['stock'], // 总库存 // 'is_on_sale' => 0, // $goods_info['is_display'], // 状态 相同 'goods_name' => $goods_info['title'], // 商品名称 'goods_source' => $this->goods_source, 'goods_no' => '', 'is_attr' => $is_attr, ]; // 商品规格为空 不添加 if (empty($attr['attr'])) { throw new Exception("商品规格为空, 不能导入"); } // 当商品为上架时,还需要判断库存是否支持上架 if ($goods_info['is_display'] == 1) { $goods_info['is_display'] = $this->goods_status ? 1 : 0; } // 全局设置 $setting = $this->settings[$this->supply_id]; $attr['is_on_sale'] = $goods_info['is_display']; if ($this->is_update) { // 当商品上架时 不需要改变系统商品状态1 if ((!isset($setting['auto_display']) || !$setting['auto_display']) && $goods_info['is_display'] == 1) { unset($attr['is_on_sale']); } $detail = MallGoodsModel::getDetail($this->goods_id); return $this->mergeDetail($detail, $attr); } else { // 获取分类 仅新增时添加分类 $mall_cate_service = new MallCateService(['mall_id' => $this->mall_id]); $cats = $mall_cate_service->getCats( $goods_info['category_1'], $goods_info['category_2'], $goods_info['category_3'] ); return array_merge($attr, [ 'goods_type' => 1, // 默认1 'subtitle' => '', // 'cats' => [$cats], // 分类 'labels' => [], // 商品标签 'mchCats' => [], // 多商户分类 'services' => [], // 商品服务 'unit' => $goods_info['unit'], // 单位 'virtual_sales' => 0, // 虚拟销量 'stock_warning' => 0, 'goods_weight' => 0, // 商品权重 'goods_no' => '', // 商品货号 'is_show_stock' => 1, 'is_show_sales' => 0, // 是否显示销量 'freight_type' => [], 'freight_rules_type' => 1, 'freight_id' => '', 'shipping_fee' => 0, 'free_shipping_num' => 0, 'free_shipping_money' => 0, 'is_cross_border' => 0, 'is_id_card' => 0, 'extra' => '', // 额外属性 'coupon_selection' => [], 'goods_setting' => [], 'is_area_limit' => 0, 'area_limit' => [], 'attr_groups' => [], 'mch_id' => 0, 'mall_id' => $this->mall_id, 'is_show' => 1, ]); } } /** * 获取商品规格属性 * * @param array $specs * @return array */ private function getSpecsForAttr($skus) { if (empty($skus)) { throw new Exception('供应链未返回商品规格信息skus'); } $attr = []; foreach ($skus as $sku) { // 仅需要上架的 // if ($sku['is_display'] == 1) { // 规格为空 if (empty($sku['options'])) throw new Exception('商品规格参数有误'); $temp = [ 'attr_list' => $this->getAttrList($sku['options']), // 规格 'stock' => $sku['stock'] > $this->max_stock_num ? $this->max_stock_num : $sku['stock'], // 库存 'price' => $this->getPrice($sku, 'sell'), // 价格 sale_price cost_price market_price guide_price 'original_price' => $this->getPrice($sku, 'original'), // 价格 sale_price cost_price market_price guide_price 'cost_price' => $this->getPrice($sku, 'cost'), // 价格 sale_price cost_price market_price guide_price 'no' => '', 'weight' => 0, 'pic_url' => $sku['image_url'], 'distributionLevelList' => [], 'member_price' => [], 'levelPrice' => [], // 'sku_id' => $sku['original_sku_id'], 'sku_id' => $sku['id'], ]; // 如果是更新 if ($this->is_update) { $params = [ 'sku_id' => $temp['sku_id'], 'mall_id' => $this->mall_id, 'goods_id' => $this->goods_id, ]; $sku_model = GoodsSkuModel::find()->where($params)->select('attr_id')->one(); if ($sku_model) { $temp['id'] = $sku_model->attr_id; } } // 如果库存大于0 可以上架 if ($sku['stock'] > 0) { $this->goods_status = true; } // 中台ID if (isset($this->supply_id)) { $temp['supply_id'] = $this->supply_id; } // 供应链ID if (isset($this->supply_goods_id)) { $temp['supply_goods_id'] = $this->supply_goods_id; } $attr[] = $temp; // } } // 剔除没有规格的商品 foreach ($attr as $k => $v) { if (empty($v['attr_list'])) { unset($attr[$k]); } } return $attr; } /** * 获取商品规格属性 * * @param array $options * [ * [ * 'spec_name' => '颜色' * 'spec_item_name' => '纯白' * ] * [ * 'spec_name' => '尺码' * 'spec_item_name' => '39' * ] * ] * @return array */ private function getAttrList($options) { $attr = []; foreach ($options as $option) { !empty($option['spec_name']) && !empty($option['spec_item_name']) && $attr[] = [ 'attr_group_name' => $option['spec_name'], 'attr_name' => $option['spec_item_name'], ]; } return $attr; } /** * 获取产品价格 * * @param array $info * @param string $type * @return float */ public function getPrice($info, $type) { $set = $this->settings[$this->supply_id]; if ($type == 'sell') { $price_type = $set['sell_strategy']; $radio = $set['sell_value']; } if ($type == 'original') { $price_type = $set['original_strategy']; $radio = $set['original_value']; } if ($type == 'cost') { $price_type = $set['cost_strategy']; $radio = $set['cost_value']; } // 指导价 guide_price 协议价 agreement_price 市场价 activity_price switch ($price_type) { case '1': $price_key = 'guide_price'; break; case '2': $price_key = 'agreement_price'; break; case '3': $price_key = 'activity_price'; break; } // 供应链端返回无agreement_price 字段 使用price 字段 if ($price_key == 'agreement_price' && empty($info[$price_key])) { $price_key = 'price'; } return bcdiv(bcmul($info[$price_key], bcdiv($radio, 100, 2)), 100, 2); } /** * 获取商品轮播图 * * @param array $gallery * @return array */ private function getUrlForGallery($gallery) { $pic = []; foreach ($gallery as $v) { $pic[] = [ 'id' => 0, 'pic_url' => $v['src'], ]; } return $pic; } /** * 融合属性详情 * * @param array $detail * @param array $attr * @return array */ private function mergeDetail($detail, $attr) { // 全局设置 $setting = $this->settings[$this->supply_id]; // 获取单独设置 商品独立设置 /* $setting_model = ShopGoodsConfig::find(); $independent_setting = $setting_model->select('setting')->where(['mall_id' => Yii::$app->mall->id, 'goods_id' => $this->goods_id])->scalar(); if (!empty($independent_setting)) { $independent_setting = json_decode($independent_setting, true); } else { $independent_setting = []; } // 合并设置 $setting = array_merge($setting, $independent_setting); */ // 更新价格 自动更新为1 不更新为0 不更新的时候进入方法进行价格删除 if (!$setting['auto_present_price'] && $this->is_update) { // 删除更新获取回来的价格 unset($attr['price']); unset($attr['original_price']); unset($attr['cost_price']); // 查询旧属性,重组获取旧价格 $old_attr = []; // 查询所有循环重组 减少sql查询 // $model = GoodsSkuModel::find(); // $sku_id_list = $model->select('attr_id, sku_id')->where([ // 'goods_id' => $detail['id'], // 'supply_id' => $this->supply_id, // 'is_delete' => 0, // ])->asArray()->indexBy('attr_id')->all(); // foreach ($detail['attr'] as $k => $v) { // // 按sku_id重组旧规格 // if (isset($sku_id_list[$v['id']])) { // $sku_id = $sku_id_list[$v['id']]['sku_id']; // $old_attr[$sku_id] = $v; // } // } // 旧价格写入新属性 // foreach ($attr['attr'] as $k => $v) { // // 旧属性存在 // if (isset($old_attr[$v['sku_id']])) { // // 替换新属性价格为旧属性价格 // $temp = $old_attr[$v['sku_id']]; // $attr['attr'][$k]['price'] = $temp['price']; // $attr['attr'][$k]['original_price'] = $temp['original_price']; // $attr['attr'][$k]['cost_price'] = $temp['cost_price']; // } // } // 旧价格写入新属性 foreach ($attr['attr'] as $k => $v) { if (isset($v['id'])) { $item = current(array_filter($detail['attr'], function ($item) use ($v) { return $item['id'] == $v['id']; })); if ($item) { $attr['attr'][$k]['price'] = $item['price']; $attr['attr'][$k]['original_price'] = $item['original_price']; $attr['attr'][$k]['cost_price'] = $item['cost_price']; } } } } // 更新属性 自动更新为1 不更新为0 不更新的时候进入方法进行价格删除 if (!$setting['auto_base_info']) { unset($attr['detail']); unset($attr['name']); unset($attr['goods_num']); } return array_merge($detail, $attr); } /** * 处理商品信息为空的商品 * * @param int $supply_id * @param array $not_info_ids * @return void */ public function handNotGoods($supply_id, $not_info_ids) { StorageModel::updateAll( [ 'is_import' => 0, 'import_msg' => '商品不存在', 'updated_at' => time(), ], [ 'middle_goods_id' => $not_info_ids, 'supply_id' => $supply_id ] ); // 下架不存在的商品 $goods = StorageModel::find()->where(['middle_goods_id' => $not_info_ids])->all(); foreach ($goods as $item) { if ($item->mall_goods_id > 0) { MallGoodsModel::updateDown($item->mall_id, $item->mall_goods_id); } } } /** * @param string $html * @return string */ private function _formatDetail(string $html = '') { return str_replace('