| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655 |
- <?php
- namespace addons\StarChain\common\services;
- use addons\StarChain\common\enums\GoodsStatusEnum;
- use addons\StarChain\common\enums\SkuStatusEnum;
- use addons\StarChain\common\models\GoodsAttr;
- use addons\StarChain\common\models\MallGoods;
- use addons\StarChain\common\models\Setting;
- use addons\StarChain\common\models\Storage;
- use Exception;
- use Yii;
- use yii\helpers\Json;
- /**
- * 商品服务
- */
- class GoodsService extends BaseService
- {
- /**
- * 标识
- *
- * @return string
- */
- private $goods_source = "StarChain";
- /**
- * 最大库存
- *
- * @var integer
- */
- private $max_stock_num = 999999;
- /**
- * 最大库存
- *
- * @var integer
- */
- private $is_update = false;
- /**
- * 是否上架
- *
- * @var boolean
- */
- private $goods_status = false;
- /**
- * 当前商城商品ID
- *
- * @var integer
- */
- private $goods_id;
- /**
- * 当前供应链商品ID
- *
- * @var string
- */
- private $spu_id;
- /**
- * 商城设置
- *
- * @var array
- */
- private $setting;
- /**
- * 设置价格
- *
- * @var array
- */
- private $price;
- /**
- * 初始化
- *
- * @param array $config
- */
- public function __construct($config)
- {
- parent::__construct($config);
- // 获取设置
- $this->setting = Setting::getImportSetting($this->mall_id);
- }
- /**
- * 添加商品
- *
- * @param string $spu_id
- * @return boolean|MallGoods
- */
- public function addGoods($spu_id)
- {
- try {
- // spu详情
- $spu_info = $this->api->getSpuBySpuIds([$spu_id]);
- // sku详情
- $sku_list_detail = $this->api->listSkuBySpuId($spu_id);
- } catch (Exception $e) {
- throw new Exception("API接口获取商品失败:" . $e->getMessage());
- // return $this->error("API接口获取商品失败:" . $e->getMessage());
- }
- // 设置当前供应链商品ID
- $this->spu_id = $spu_id;
- try {
- // 是否更新
- if ($goods_id = Storage::find()->where(['mall_id' => $this->mall_id, 'is_delete' => 0, 'spu_id' => $spu_id])->select('mall_goods_id')->scalar()) {
- $this->goods_id = $goods_id;
- $this->is_update = true;
- }
- // 获取商品属性
- $goods_attr = $this->getGoodsAttr($spu_info[0], $sku_list_detail);
- // 获取规格组
- $attr_groups = $this->getAttrGroups($sku_list_detail);
- // $attr_groups = $this->removeAttrGroups($attr_groups, $goods_attr['attr']);
- // 匹配规格ID
- $goods_attr = $this->matchingAttrId($goods_attr, $attr_groups);
- $goods_attr['attr_groups'] = $attr_groups;
- // 保存商品
- $res = MallGoods::saveGoods($goods_attr);
- if ($res === false) {
- throw new Exception('商品添加到商城失败');
- }
- } catch (Exception $e) {
- throw new Exception($e->getMessage());
- // return $this->error($e->getMessage());
- }
- return $res;
- }
- /**
- * 下架商品
- *
- * @param array $spu_ids
- * @return void
- */
- public function goodsPulled($spu_ids)
- {
- $storage_list = Storage::find()->where(['spu_id' => $spu_ids])->with('goods')->all();
- try {
- foreach ($storage_list as $storage) {
- $storage->goods->goodsPulled();
- // 下架
- MallGoods::setData([
- 'id' => $storage->mall_goods_id,
- 'mall_id' => $storage->mall_id,
- 'is_on_sale' => 0,
- ]);
- }
- } catch (Exception $e) {
- throw new Exception($e->getMessage());
- }
- }
-
- /**
- * 添加商品
- *
- * @param string $spu_id
- * @return boolean|MallGoods
- */
- public function editGoods($spu_id)
- {
- // 是否更新
- $goods_id = Storage::getMallGoodsIdBySpuId($this->mall_id, $spu_id);
- if (empty($goods_id)) {
- return false;
- }
- $this->goods_id = $goods_id;
- $this->is_update = true;
- try {
- // spu详情
- $spu_info = $this->api->getSpuBySpuIds([$spu_id]);
- // sku详情
- $sku_list_detail = $this->api->listSkuBySpuId($spu_id);
- } catch (Exception $e) {
- throw new Exception("API接口获取商品失败:" . $e->getMessage());
- }
- // 设置当前供应链商品ID
- $this->spu_id = $spu_id;
- try {
- // 获取商品属性
- $goods_attr = $this->getGoodsAttr($spu_info[0], $sku_list_detail);
- // 获取规格组
- $attr_groups = $this->getAttrGroups($sku_list_detail);
- // $attr_groups = $this->removeAttrGroups($attr_groups, $goods_attr['attr']);
- // 匹配规格ID
- $goods_attr = $this->matchingAttrId($goods_attr, $attr_groups);
- $goods_attr['attr_groups'] = $attr_groups;
- // 保存商品
- $res = MallGoods::saveGoods($goods_attr);
- if ($res === false) {
- throw new Exception('商品添加到商城失败');
- }
- } catch (Exception $e) {
- throw new Exception($e->getMessage());
- }
- return $res;
- }
- /**
- * 获取添加商品属性
- *
- * @param array $goods_info
- * @return array
- */
- private function getGoodsAttr($goods_info, $sku_list_detail)
- {
- // 获取匹配的Attr
- $specs_attr = $this->getSpecsForAttr($sku_list_detail, $goods_info['status']);
- $is_attr = count($specs_attr) > 1 ? 1 : 0;
- $stock = $this->getStock($specs_attr);
- $attr = [
- 'attr' => $specs_attr, // 商品规格属性
- 'bannar_pic' => $this->getBannarPic($goods_info['carouselImgList']),
- 'cover_pic' => $goods_info['carouselImgList'][0], // 主图
- 'price' => $this->price['sell'], // 销售价
- 'original_price' => $this->price['original'], // 原价
- 'cost_price' => $this->price['cost'], // 成本价
- 'detail' => $this->getDetailImg($goods_info['detailImgList']), // 商品详情
- 'stock' => $stock > $this->max_stock_num ? $this->max_stock_num : $stock, // 总库存
- 'goods_name' => $goods_info['name'], // 商品名称
- 'goods_source' => $this->goods_source,
- 'is_attr' => $is_attr,
- 'goods_no' => '',
- ];
- // 商品规格为空 不添加
- if (empty($attr['attr'])) {
- throw new Exception("可售规格均已下架, 不能导入");
- }
- // 当商品为上架时,还需要判断库存是否支持上架
- if ($goods_info['status'] == GoodsStatusEnum::STATUS_0) {
- $goods_info['status'] = $this->goods_status ? 0 : 1;
- }
- // 上下架设置
- $attr['is_on_sale'] = $goods_info['status'] == GoodsStatusEnum::STATUS_0 ? 1 : 0;
- if ($this->is_update) {
- // 当商品上架时 不需要改变系统商品状态1
- if ($goods_info['status'] == 1) {
- unset($goods_info['status']);
- }
- $detail = MallGoods::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['categoryId1'],
- $goods_info['categoryId2'],
- $goods_info['categoryId3']
- );
- return array_merge($attr, [
- 'goods_type' => 1, // 默认1
- 'subtitle' => '', //
- 'cats' => [$cats], // 分类
- 'labels' => [], // 商品标签
- 'mchCats' => [], // 多商户分类
- 'services' => [], // 商品服务
- '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,
- ]);
- }
- }
- /**
- * 获取商品规格属性
- *
- * @param array $specs
- * @return array
- */
- private function getSpecsForAttr($sku_list, $status)
- {
- if (empty($sku_list)) {
- throw new Exception('');
- }
- $attr = [];
- foreach ($sku_list as $sku) {
- // 仅需要上架的
- if ($sku['status'] == SkuStatusEnum::STATUS_0 || $status == SkuStatusEnum::STATUS_1) {
- $temp = [
- 'attr_list' => $this->getAttrList($sku['skuPropertyList']), // 规格
- 'stock' => $sku['stock'] > $this->max_stock_num ? $this->max_stock_num : $sku['stock'], // 库存
- 'price' => $this->getPrice($sku, 'sell'), // 价格
- 'original_price' => $this->getPrice($sku, 'original'), // 价格
- 'cost_price' => $this->getPrice($sku, 'cost'), // 价格
- 'goods_no' => '',
- 'weight' => 0,
- 'pic_url' => $sku['skuPicUrl'],
- 'distributionLevelList' => [],
- 'member_price' => [],
- 'levelPrice' => [],
- 'sku_id' => $sku['skuId'],
- 'chain_goods_id' => $this->spu_id,
- ];
- // 如果是更新
- if ($this->is_update) {
- $params = [
- 'sku_id' => $temp['sku_id'],
- 'mall_id' => $this->mall_id,
- 'mall_goods_id' => $this->goods_id,
- ];
- $sku_model = GoodsAttr::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;
- }
- $attr[] = $temp;
- }
- }
- // 剔除没有规格的商品
- foreach ($attr as $k => $v) {
- if (empty($v['attr_list'])) {
- unset($attr[$k]);
- }
- }
- return $attr;
- }
- /**
- * 获取规格属性
- *
- * @param array $skus
- * @return array
- */
- private function getAttrGroups($skus)
- {
- $first_key = array_key_first($skus);
- $sku_property_list = $skus[$first_key]['skuPropertyList'];
- $attr_groups = [];
- $attr_group_id = 1;
- foreach ($sku_property_list as $option) {
- $attr_groups[] = [
- 'attr_group_id' => $attr_group_id,
- 'attr_group_name' => $option['specName'],
- ];
- $attr_group_id++;
- }
- $temp = 0;
- $attr_name = [];
- $attr_id = 1;
- foreach ($skus as $sku) {
- if ($sku['skuPropertyList']) {
- foreach ($attr_groups as $k => $group) {
- foreach ($sku['skuPropertyList'] as $option) {
- if (
- $option['specName'] == $group['attr_group_name'] &&
- !in_array($option['specValueName'], $attr_name)
- ) {
- $attr_name[] = $option['specValueName'];
- $attr_groups[$k]['attr_list'][] = [
- 'attr_id' => $attr_id,
- 'attr_name' => $option['specValueName'],
- // 图片仅第一个添加
- 'pic_url' => $sku['skuPicUrl'],
- ];
- $attr_id++;
- }
- }
- $temp++;
- }
- }
- }
- $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);
- }
- /**
- * 匹配规格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'];
- }
- }
- }
- /**
- * 获取商品规格属性
- *
- * @param array $options
- * [
- * [
- * 'spec_name' => '颜色'
- * 'spec_item_name' => '纯白'
- * ]
- * [
- * 'spec_name' => '尺码'
- * 'spec_item_name' => '39'
- * ]
- * ]
- * @return array
- */
- private function getAttrList($list)
- {
- $attr = [];
- foreach ($list as $option) {
- !empty($option['specName']) && !empty($option['specValueName']) && $attr[] = [
- 'attr_group_name' => $option['specName'],
- 'attr_name' => $option['specValueName'],
- ];
- }
- return $attr;
- }
- /**
- * 获取商品轮播图
- *
- * @param array $gallery
- * @return array
- */
- private function getBannarPic($list)
- {
- $pic = [];
- foreach ($list as $v) {
- $pic[] = [
- 'id' => 0,
- 'pic_url' => $v,
- ];
- }
- return $pic;
- }
- /**
- * 获取库存
- *
- * @param array $attr
- * @return integer
- */
- private function getStock($attr)
- {
- $stock = 0;
- foreach ($attr as $v) {
- $stock = bcadd($stock, $v['stock']);
- }
- return $stock;
- }
- /**
- * 融合属性详情
- *
- * @param array $detail
- * @param array $attr
- * @return array
- */
- private function mergeDetail($detail, $attr)
- {
- // 全局设置
- $setting = $this->setting;
- // 更新价格 自动更新为1 不更新为0 不更新的时候进入方法进行价格删除
- if (!$setting['auto_present_price']) {
- // 删除更新获取回来的价格
- unset($attr['price']);
- unset($attr['original_price']);
- unset($attr['cost_price']);
- unset($attr['stock']);
- // 查询旧属性,重组获取旧价格
- $old_attr = [];
- // 查询所有循环重组 减少sql查询
- $model = GoodsAttr::find();
- $sku_id_list = $model->select('attr_id, sku_id')->where([
- 'mall_goods_id' => $detail['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'];
- }
- }
- }
- // 更新属性 自动更新为1 不更新为0 不更新的时候进入方法进行价格删除
- if (!$setting['auto_base_info']) {
- unset($attr['detail']);
- unset($attr['name']);
- unset($attr['goods_num']);
- }
- return array_merge($detail, $attr);
- }
- /**
- * 获取详情
- *
- * @param array $imgs
- * @return string
- */
- private function getDetailImg($imgs)
- {
- $str = '';
- foreach ($imgs as $v) {
- $str .= '<p><img src="' . $v . '" /></p>';
- }
- return $str;
- }
- /**
- * 获取价格
- *
- * @param array $info
- * @param array $type
- * @return float
- */
- private function getPrice($info, $type)
- {
- $setting = $this->setting;
- $price_field = $setting[$type . '_name'];
- $price_value = $setting[$type . '_value'];
- if (empty($price_field) || empty($price_value)) {
- throw new Exception('自动设置价格字段有误');
- }
- $price = $info[$price_field];
- if (empty($price)) {
- throw new Exception('自动设置价格不存在');
- }
-
- $last_price = bcdiv(bcmul($price, $price_value, 2), 100, 2);
- // 商品详情不存在价格 存储规格中对应最低价格作为商品详情的价格
- empty($this->price[$type])
- ? $this->price[$type] = $last_price
- : ($last_price > $this->price[$type]
- ?: $this->price[$type] = $last_price);
- return $last_price;
- }
- }
|