StoreGoods.php 70 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479
  1. <?php
  2. namespace addons\Store\common\models;
  3. use addons\Store\common\enums\StoreEnum;
  4. use addons\Store\common\events\goods\GoodsEditEvent;
  5. use common\components\export\CsvTool;
  6. use common\enums\AddonsEnum;
  7. use common\enums\DownloadEnum;
  8. use common\enums\GoodsTypeEnum;
  9. use common\enums\import\GoodsImportEnum;
  10. use common\enums\RabbitMqEnum;
  11. use common\enums\StatusEnum;
  12. use common\enums\WhetherEnum;
  13. use common\globals\GlobalInvoke;
  14. use common\helpers\ArrayHelper;
  15. use common\helpers\csv\CsvImportForm;
  16. use common\models\base\BaseActiveRecord;
  17. use common\models\common\Download;
  18. use common\models\common\ImportFailLog;
  19. use common\models\common\ImportLog;
  20. use common\models\goods\GoodsAttr;
  21. use common\models\goods\GoodsCateRelate;
  22. use common\models\goods\GoodsExtra;
  23. use common\models\goods\GoodsAssociation;
  24. use common\models\goods\GoodsReserveSku;
  25. use common\models\mall\MallAddons;
  26. use Exception;
  27. use Yii;
  28. use yii\db\Expression;
  29. /**
  30. * This is the model class for table "qimall_addons_store_goods".
  31. *
  32. * @property int $id
  33. * @property int $mall_id 商城id
  34. * @property int $mall_goods_id 主商城goods_id
  35. * @property int $store_id 门店id
  36. * @property string $goods_name 商品名称
  37. * @property string $subtitle 商品副标题
  38. * @property float $price 售价
  39. * @property float $original_price 原价(划线价仅展示)
  40. * @property float $cost_price 成本价
  41. * @property string $unit 单位
  42. * @property string $cover_pic 商品缩略图
  43. * @property string $bannar_pic 商品展示bannar图
  44. * @property string $video_url 商品视频
  45. * @property string $video_cover_pic 商品视频封面
  46. * @property int $is_on_sale 上架状态【1是 0否】
  47. * @property int $is_attr 是否有规格【1是 0否】
  48. * @property string|null $attr_groups 规格组
  49. * @property string|null $attr_groups_format 格式化规格组
  50. * @property int $status 状态[1正常 -1删除]
  51. * @property int $stock 商品库存
  52. * @property int $stock_warning 商品库存预警
  53. * @property int $is_show_stock 是否显示库存
  54. * @property int $is_show_sales 是否显示销量【1是 0否】
  55. * @property int $virtual_sales 虚拟销量
  56. * @property int $sales_num 累计销量
  57. * @property int $buy_num_limit 购买数量限制
  58. * @property string $freight_type 物流方式【1快递发货 2上门自提】逗号分隔
  59. * @property int $freight_rules_type 运费规则 【1运费模板 2自定义运费】
  60. * @property int $freight_id 运费模板ID
  61. * @property float $shipping_fee 自定义运费金额
  62. * @property int $free_shipping_num 商品满件包邮
  63. * @property float $free_shipping_money 商品满额包邮
  64. * @property int $goods_type 商品类型
  65. * @property string|null $detail 商品详情,图文
  66. * @property int $is_area_limit 是否开启限购区域
  67. * @property string|null $area_limit 限购区域
  68. * @property string $services 商品服务
  69. * @property string $labels 商品标签
  70. * @property string|null $goods_source 商品来源
  71. * @property int $supplier_type 供应者类型:0:自身;1:商城;2:o2o;3:多商户
  72. * @property int $supplier_id 供应者id
  73. * @property int $supplier_goods_id 供应品id
  74. * @property int $sort 排序
  75. * @property int $check_status 审核状态 0:待审核;1;:已通过;2:未通过
  76. * @property string $check_remark 审核备注
  77. * @property int $created_at 创建时间
  78. * @property int $updated_at 修改时间
  79. * @property int $show_style 商品展示风格
  80. * @property int $is_recommend 店铺推荐
  81. * @property float $credit_rate 赊账比例
  82. * @property string $package_data 套餐json数据
  83. */
  84. class StoreGoods extends BaseActiveRecord
  85. {
  86. /**
  87. * {@inheritdoc}
  88. */
  89. public static function tableName()
  90. {
  91. return '{{%addons_store_goods}}';
  92. }
  93. /**
  94. * {@inheritdoc}
  95. */
  96. public function rules()
  97. {
  98. return [
  99. [['mall_id', 'mall_goods_id', 'store_id', 'is_on_sale', 'is_attr', 'status', 'stock', 'stock_warning', 'is_show_stock', 'is_show_sales', 'virtual_sales', 'sales_num', 'buy_num_limit', 'freight_rules_type', 'freight_id', 'free_shipping_num', 'goods_type', 'is_area_limit', 'supplier_type', 'supplier_id', 'supplier_goods_id', 'sort', 'check_status', 'created_at', 'updated_at',
  100. 'is_recommend','show_style'], 'integer'],
  101. [['price', 'original_price', 'cost_price', 'shipping_fee', 'free_shipping_money','credit_rate'], 'number'],
  102. [['attr_groups', 'attr_groups_format', 'detail', 'area_limit', 'package_data'], 'string'],
  103. [['goods_name'], 'string', 'max' => 120],
  104. [['subtitle'], 'string', 'max' => 150],
  105. [['unit', 'cover_pic', 'video_url', 'freight_type', 'goods_no'], 'string', 'max' => 255],
  106. [['check_remark'], 'safe'],
  107. [['bannar_pic'], 'string', 'max' => 1500],
  108. [['video_cover_pic', 'services', 'labels'], 'string', 'max' => 500],
  109. [['goods_source'], 'string', 'max' => 32],
  110. ];
  111. }
  112. /**
  113. * {@inheritdoc}
  114. */
  115. public function attributeLabels()
  116. {
  117. return [
  118. 'id' => 'ID',
  119. 'mall_id' => '商城id',
  120. 'mall_goods_id' => '主商城goods_id',
  121. 'store_id' => '门店id',
  122. 'goods_name' => '商品名称',
  123. 'subtitle' => '商品副标题',
  124. 'price' => '售价',
  125. 'original_price' => '原价(划线价仅展示)',
  126. 'cost_price' => '成本价',
  127. 'unit' => '单位',
  128. 'cover_pic' => '商品缩略图',
  129. 'bannar_pic' => '商品展示bannar图',
  130. 'video_url' => '商品视频',
  131. 'video_cover_pic' => '商品视频封面',
  132. 'is_on_sale' => '上架状态【1是 0否】',
  133. 'is_attr' => '是否有规格【1是 0否】',
  134. 'attr_groups' => '规格组',
  135. 'attr_groups_format' => '格式化规格组',
  136. 'status' => '状态[1正常 -1删除]',
  137. 'stock' => '商品库存',
  138. 'stock_warning' => '商品库存预警',
  139. 'is_show_stock' => '是否显示库存',
  140. 'is_show_sales' => '是否显示销量【1是 0否】',
  141. 'virtual_sales' => '虚拟销量',
  142. 'sales_num' => '累计销量',
  143. 'buy_num_limit' => '购买数量限制',
  144. 'freight_type' => '物流方式【1快递发货 2上门自提】逗号分隔',
  145. 'freight_rules_type' => '运费规则 【1运费模板 2自定义运费】',
  146. 'freight_id' => '运费模板ID',
  147. 'shipping_fee' => '自定义运费金额',
  148. 'free_shipping_num' => '商品满件包邮',
  149. 'free_shipping_money' => '商品满额包邮',
  150. 'goods_type' => '商品类型',
  151. 'detail' => '商品详情,图文',
  152. 'is_area_limit' => '是否开启限购区域',
  153. 'area_limit' => '限购区域',
  154. 'services' => '商品服务',
  155. 'labels' => '商品标签',
  156. 'goods_source' => '商品来源',
  157. 'supplier_type' => '供应者类型:0:自身;1:商城;2:o2o;3:多商户',
  158. 'supplier_id' => '供应者id',
  159. 'supplier_goods_id' => '供应品id',
  160. 'sort' => '排序',
  161. 'check_status' => '审核状态 0:待审核;1;:已通过;2:未通过',
  162. 'check_remark' => '审核备注',
  163. 'created_at' => '创建时间',
  164. 'updated_at' => '修改时间',
  165. 'show_style' => '商品展示风格',
  166. 'is_recommend' => '店铺推荐',
  167. 'package_data' => '套餐json数据',
  168. 'goods_no' => '商品货号',
  169. ];
  170. }
  171. // 分类关系
  172. public function getStoreGoodsCateRelate()
  173. {
  174. return $this->hasMany(StoreGoodsCateRelate::class, ['goods_id' => 'id']);
  175. }
  176. // 分类
  177. public function getCats()
  178. {
  179. return $this->hasMany(StoreGoodsCate::class, ['id' => 'cate_id'])->via('storeGoodsCateRelate');
  180. }
  181. // 商品额外设置
  182. public function getExtra()
  183. {
  184. return $this->hasOne(StoreGoodsExtra::class, ['goods_id' => 'id'])->andWhere(['status' => [StatusEnum::ENABLED]]);
  185. }
  186. // sku
  187. public function getAttr()
  188. {
  189. return $this->hasMany(StoreGoodsAttr::class, ['goods_id' => 'id'])->andWhere(['status' => [StatusEnum::ENABLED]]);
  190. }
  191. // 预约商品信息
  192. public function getReserve()
  193. {
  194. return $this->hasOne(StoreGoodsReserve::class, ['goods_id' => 'id']);
  195. }
  196. public function getStore()
  197. {
  198. return $this->hasOne(Store::class, ['id' => 'store_id']);
  199. }
  200. /**
  201. * 保存数据
  202. * @Author bing
  203. * @DateTime 2021-08-24 15:34:48
  204. * @param array $params
  205. * @param string $addons 来自插件
  206. * @param callback $call_back 插件回调函数
  207. * @return object|boolean
  208. * @copyright: Copyright (c) 2020 广东七件事集团
  209. */
  210. public static function setData(array $params)
  211. {
  212. $store_id = $params['store_id'];
  213. $mall_id = $params['mall_id'];
  214. try {
  215. $wait_del_attr_id_arr = [];
  216. if (!empty($params['id'])) {
  217. $model = self::findOne(['mall_id' => $params['mall_id'], 'id' => $params['id'], 'status' => [StatusEnum::ENABLED]]);
  218. if (empty($model)) throw new \Exception('商品不存在或已删除');
  219. } else {
  220. $model = new self();
  221. $model->loadDefaultValues();
  222. }
  223. // 如果是预约商品
  224. if ($params['goods_type'] == GoodsTypeEnum::GoodsTypeReserved) {
  225. // 取出最低价
  226. if (!empty($params['reserve']['skus'])) {
  227. $params['stock'] = array_sum(array_column($params['reserve']['skus'], 'reserve_max_buy'));
  228. } else {
  229. $params['stock'] = $params['reserve']['reserve_max_buy'];
  230. }
  231. }
  232. self::priceLimit($mall_id,$params['price']);
  233. isset($params['services']) && $params['services'] = implode(',', $params['services']);
  234. isset($params['labels']) && $params['labels'] = implode(',', $params['labels']);
  235. isset($params['freight_type']) && $params['freight_type'] = implode(',', $params['freight_type']);
  236. isset($params['freight_id']) && empty($params['freight_id']) && $params['freight_id'] = 0;
  237. if (isset($params['is_attr']) && $params['is_attr'] == 1) {
  238. if (isset($params['attr_groups']) && !empty($params['attr_groups'])) {
  239. $attr_groups = $params['attr_groups'];
  240. $params['attr_groups'] = json_encode($params['attr_groups'], JSON_UNESCAPED_UNICODE);
  241. $params['attr_groups_format'] = json_encode(self::formatGoodsAttrGroup($attr_groups, JSON_UNESCAPED_UNICODE));
  242. }
  243. } else {
  244. $params['attr_groups'] = '';
  245. $params['attr_groups_format'] = '';
  246. }
  247. isset($params['bannar_pic']) && $params['bannar_pic'] = json_encode($params['bannar_pic'], JSON_UNESCAPED_UNICODE);
  248. isset($params['area_limit']) && $params['area_limit'] = json_encode($params['area_limit'], JSON_UNESCAPED_UNICODE);
  249. isset($params['package_data']) && $params['package_data'] = json_encode($params['package_data'], JSON_UNESCAPED_UNICODE);
  250. $trans = Yii::$app->db->beginTransaction();
  251. // 分类判断当条件不满足时不导入商品
  252. if (isset($params['cats']) && !empty($params['cats'])) {
  253. $params['cats'] = self::goodsCategorizeValidate($params);
  254. }
  255. if (!$model->load($params, '') || !$model->save()) {
  256. throw new \Exception($model->getErrorMessage());
  257. }
  258. //商品分类处理
  259. if (isset($params['cats']) && !empty($params['cats'])) {
  260. $res = StoreGoodsCateRelate::setData($model->id, $params['cats']);
  261. if ($res === false) throw new \Exception('分类处理失败:' . GoodsCateRelate::getStaticError());
  262. }
  263. $goods = ArrayHelper::toArray($model);
  264. if ($goods['goods_type'] === GoodsTypeEnum::GoodsTypeReserved) {
  265. // 预约商品
  266. $reserve = $params['reserve'];
  267. $res = StoreGoodsReserve::setData($goods, $reserve);
  268. if ($res === false) throw new Exception('预约商品SKU处理失败:' . StoreGoodsAttr::getStaticError());
  269. } else {
  270. // 商品规格处理
  271. if ($model->is_attr == 1) {
  272. $attrs = $params['attr'] ?? [];
  273. $res = StoreGoodsAttr::setData($model->id, $attrs, $attr_groups, $mall_id, $wait_del_attr_id_arr);
  274. if ($res === false) throw new \Exception('规格处理失败:' . GoodsAttr::getStaticError());
  275. } else {
  276. //无规格商品处理
  277. $goods = ArrayHelper::toArray($model);
  278. $goods['pic_url'] = $goods['cover_pic'];
  279. $goods['weight'] = $params['goods_weight'] ?? 0;
  280. if ($goods['supplier_type'] == StoreEnum::SELF_MALL_WUZHOU && $goods['mall_goods_id']) {
  281. // 五洲供应链商品,记录规格id
  282. $mall_goods_attr = GoodsAttr::findOne(['goods_id' => $goods['mall_goods_id'], 'status' => StatusEnum::ENABLED]);
  283. $goods['mall_attr_id'] = $mall_goods_attr->id ?? 0;
  284. }
  285. $res = StoreGoodsAttr::setAttr($model->id, $goods);
  286. if ($res === false) throw new \Exception('设置货品失败:' . GoodsAttr::getStaticError());
  287. }
  288. }
  289. // 商品额外信息处理
  290. if (isset($params['extra']) && !empty($params['extra'])) {
  291. if (isset($params['extra']['platform_score_give_setting'])) unset($params['extra']['platform_score_give_setting']);
  292. $params['extra']['store_id'] = $store_id;
  293. $params['extra']['mall_id'] = $mall_id;
  294. $res = StoreGoodsExtra::setData($model->id, $params['extra']);
  295. if ($res === false) throw new \Exception('商品额外信息处理失败:' . GoodsExtra::getStaticError());
  296. }
  297. if (!empty($params['addons'])) {
  298. GlobalInvoke::exec(GlobalInvoke::GOODS_DETAIL_EDIT, $params['mall_id'], [
  299. 'type' => 'save',
  300. 'mall_id' => $params['mall_id'],
  301. 'goods_id' => $model->id,
  302. 'setting' => $params['addons'],
  303. ]);
  304. // $res = CouponGiveItemSetting::setGoodsSetting($params['mall_id'], $model->id, $params['coupon_selection']);
  305. // if ($res === false) throw new Exception('商品优惠券处理失败:' . CouponGiveItemSetting::getStaticError());
  306. }
  307. $is_install = MallAddons::isInstall($mall_id, AddonsEnum::ADDONS_NAME_COUPON);
  308. if($is_install){
  309. $res = StoreCouponGiveItemSetting::setGoodsSetting($params['mall_id'], $params['store_id'],$model['id'], $params);
  310. if ($res === false) throw new \Exception('商品设置赠送优惠券信息处理失败:' . StoreCouponGiveItemSetting::getStaticError());
  311. }
  312. $res = GoodsAssociation::setData($model->id, $params);
  313. $trans->commit();
  314. $event = new GoodsEditEvent($model['mall_id']);
  315. $data['goods_id'] = $model['id'];
  316. $data['mall_id'] = $model['mall_id'];
  317. Yii::$app->services->events->dispatch($event, $data, $event->listeners);
  318. return $model;
  319. } catch (\Exception $e) {
  320. if (isset($trans)) $trans->rollback();
  321. self::$static_error = $e->getMessage();
  322. return false;
  323. }
  324. }
  325. /**
  326. * 商品分类过滤验证
  327. * @param array $params
  328. * @return array
  329. * @throws Exception
  330. */
  331. public static function goodsCategorizeValidate(array $params): array
  332. {
  333. // 获取商品分类ID合集
  334. $caseIds = StoreGoodsCate::find()->where(['mall_id' => $params['mall_id'], 'status' => StatusEnum::ENABLED])
  335. ->select(['id'])->asArray()->all();
  336. $caseIds = array_column($caseIds, "id");
  337. // 判断输入ID是否是数字
  338. $is_numeric = array_filter($params['cats'], function ($val) {
  339. return !is_numeric($val);
  340. });
  341. if (!empty($is_numeric)) throw new Exception('商品分类必须为数字,不能包含其它字符');
  342. // 判断输入ID 是否在商品分类ID中 获取符合的ID
  343. $caseIds = array_filter($params['cats'], function ($val) use ($caseIds) {
  344. return in_array($val, $caseIds);
  345. });
  346. if (empty($caseIds)) throw new Exception('商品分类不存在或被删除');
  347. return $caseIds;
  348. }
  349. public static function priceLimit($mall_id,$price){
  350. $goodsStore = \Yii::$app->services->setting->addons->get($mall_id, StoreEnum::ADDONS_NAME, 'goodsStore');
  351. if(!empty($goodsStore['min_price'])){
  352. if($price<$goodsStore['min_price']){
  353. throw new \Exception('最低价格不能低于'.$goodsStore['min_price']);
  354. }
  355. }
  356. if(isset($goodsStore['is_limit_max_price'])&&$goodsStore['is_limit_max_price']=='false'){
  357. if(!empty($goodsStore['min_price'])) {
  358. if ($price > $goodsStore['max_price']) {
  359. throw new \Exception('最高价格不能高于' . $goodsStore['max_price']);
  360. }
  361. }
  362. }
  363. }
  364. /**
  365. * 格式化商品规格组数据
  366. * @Author bing
  367. * @DateTime 2021-09-03 11:06:31
  368. * @param array $attr_group 规格组
  369. * @return array
  370. * @copyright: Copyright (c) 2020 广东七件事集团
  371. */
  372. public static function formatGoodsAttrGroup(array $attr_group)
  373. {
  374. $format_attr_group = [];
  375. $group_attr_ids = [];
  376. $all_attr_groups = [];
  377. $group_attr_list = array_column($attr_group, 'attr_list', 'attr_id');
  378. //按规格项生成全部规格组
  379. foreach ($group_attr_list as $key => $group_attrs) {
  380. array_push($group_attr_ids, array_column($group_attrs, 'attr_id'));
  381. $attr_group_id = $attr_group[$key]['attr_group_id'];
  382. $attr_group_name = $attr_group[$key]['attr_group_name'];
  383. foreach ($group_attrs as $k => &$group_attr) {
  384. $group_attr['attr_group_id'] = $attr_group_id;
  385. $group_attr['attr_group_name'] = $attr_group_name;
  386. }
  387. $all_attr_groups = array_merge($all_attr_groups, $group_attrs);
  388. }
  389. $all_attr_groups = array_column($all_attr_groups, null, 'attr_id');
  390. // 规格组之间的笛卡尔集组合
  391. $attr_dika = ArrayHelper::combineDika(array_values($group_attr_ids));
  392. // 拼装以规格id列表为key的规格组格式,格式为attr_id1:attr_id2 => [attr_id1的规格组信息, attr_id的规格组信息]
  393. foreach ($attr_dika as $key => $attr_id_arr) {
  394. asort($attr_id_arr);
  395. $sign_id = implode(':', $attr_id_arr);
  396. $format_attr_group[$sign_id] = array_map(function ($id) use ($all_attr_groups) {
  397. return $all_attr_groups[$id];
  398. }, $attr_id_arr);
  399. }
  400. return $format_attr_group;
  401. }
  402. /**
  403. * 商品处理
  404. * @Author: lun
  405. * @DateTime: 2022/1/19 0019 18:06
  406. * @Copyright: copyright (c) 2021 广东七件事集团
  407. * @param $params
  408. */
  409. public static function goodsHandle($goods_id, $params)
  410. {
  411. try {
  412. $model = self::findOne(['id' => $goods_id, 'mall_id' => $params['mall_id'], 'status' => [StatusEnum::ENABLED]]);
  413. if (empty($model)) throw new \Exception('商品不存在或已删除');
  414. if (!$model->load($params, '') || !$model->save()) throw new \yii\db\Exception($model->getErrorMessage());
  415. return true;
  416. } catch (\Exception $e) {
  417. self::setStaticError($e->getMessage());
  418. return false;
  419. }
  420. }
  421. /**
  422. * 商品详情
  423. * @param $params
  424. * @return mixed|void
  425. */
  426. public static function getGoodsDetail($params)
  427. {
  428. $goods = StoreGoods::getOne($params['where'], $params['is_array'], $params['with'], $params['order'], $params['select']);
  429. if (empty($goods)) return $goods;
  430. $goods['is_member_price'] = '0';
  431. $goods['price_type'] = '新品';
  432. $goods['sales_num'] += $goods['virtual_sales'];
  433. unset($goods['virtual_sales']);
  434. return $goods;
  435. }
  436. public static function getGoodListInDiy(int $mall_id, array $params)
  437. {
  438. $wheres[] = ['sg.mall_id' => $mall_id, 'sg.status' => StatusEnum::ENABLED, 'sg.check_status' => StoreEnum::CHECK_ADOPT, 'sg.is_on_sale' => 1, 's.status' => StatusEnum::ENABLED, 's.check_status' => StoreEnum::CHECK_ADOPT];
  439. $wheres[] = ['or', ['>', 'expire_time', time()], ['expire_time' => 0]];
  440. if (!empty($params['keyword'])) {
  441. $wheres[] = ['like', 'sg.goods_name', $params['keyword']];
  442. }
  443. if (!empty($params['store_id'])) {
  444. $wheres[] = ['sg.store_id' => $params['store_id']];
  445. }
  446. $params = [
  447. 'alias' => 'sg',
  448. 'select' => 'sg.*,s.store_name',
  449. 'join' => [['INNER JOIN', Store::tableName() . ' as s', 'sg.store_id = s.id']],
  450. 'where' => $wheres,
  451. 'order' => 'sg.sort asc,sg.created_at desc',
  452. 'page' => $params['page'] ?? 1,
  453. 'limit' => $params['limit'] ?? 15,
  454. ];
  455. $list = self::listPage($params);
  456. if (false === $list) {
  457. return self::$static_error;
  458. }
  459. foreach ($list['list'] as &$lv) {
  460. $lv['title'] = $lv['goods_name'];
  461. $lv['img'] = $lv['cover_pic'];
  462. }
  463. return $list;
  464. }
  465. public static function getRecommendGoodListInDiy(int $mall_id, array $params)
  466. {
  467. $wheres[] = [
  468. 'sg.mall_id' => $mall_id,
  469. 'sg.is_recommend' => 1,
  470. 'sg.status' => StatusEnum::ENABLED,
  471. 'sg.check_status' => StoreEnum::CHECK_ADOPT,
  472. 'sg.is_on_sale' => 1, 's.status' => StatusEnum::ENABLED,
  473. 's.check_status' => StoreEnum::CHECK_ADOPT
  474. ];
  475. $wheres[] = ['or', ['>', 'expire_time', time()], ['expire_time' => 0]];
  476. if (!empty($params['keyword'])) {
  477. $wheres[] = ['like', 'sg.goods_name', $params['keyword']];
  478. }
  479. if (!empty($params['store_id'])) $wheres[] = ['sg.store_id' => $params['store_id']];
  480. $params = [
  481. 'alias' => 'sg',
  482. 'select' => 'sg.id,sg.goods_name,sg.cover_pic,sg.price,sg.original_price,sg.show_style,(sg.virtual_sales+sg.sales_num) as sales_num,sg.created_at,s.store_name ',
  483. 'join' => [
  484. ['INNER JOIN', Store::tableName() . ' as s', 'sg.store_id = s.id'],
  485. ],
  486. 'where' => $wheres,
  487. 'order' => 'sg.sort asc,sg.created_at desc',
  488. 'page' => $params['page'] ?? 1,
  489. 'limit' => $params['limit'] ?? 15,
  490. ];
  491. $list = StoreGoods::listPage($params);
  492. if (false === $list) {
  493. return self::$static_error;
  494. }
  495. foreach ($list['list'] as &$val) {
  496. $val['title'] = $val['goods_name'];
  497. $val['img'] = $val['cover_pic'];
  498. $val['discount'] = bcsub($val['original_price'],$val['price'],2);
  499. if($val['discount']<0) $val['discount'] = 0;
  500. }
  501. return $list;
  502. }
  503. public static function getRecommendList($mall_id,$store_id){
  504. $goods_list = StoreGoods::lists([
  505. 'where'=>[
  506. ['mall_id'=>$mall_id,'store_id'=>$store_id,'status'=>StatusEnum::ENABLED,'is_recommend'=>1,'check_status'=>1,'is_on_sale'=>1]
  507. ],
  508. 'select'=>'id,goods_name,price,original_price,cover_pic'
  509. ]);
  510. return $goods_list;
  511. }
  512. public static function syncMallGoods($params){
  513. try{
  514. $mall_id = $params['mall_id'];
  515. $selected_goods_ids = $params['selected_goods_ids'];
  516. $selected_store_ids = $params['selected_store_ids'];
  517. $sync_type = $params['sync_type'];
  518. // 同步商品到全部门店
  519. if($sync_type == 1){
  520. $store_where[] = ['mall_id' => $mall_id, 'check_status' => StoreEnum::CHECK_ADOPT, 'status' => StatusEnum::ENABLED];
  521. $store_ids = Store::lists([
  522. 'where' => $store_where,
  523. 'select' => 'id'
  524. ]);
  525. $selected_store_ids = array_column($store_ids, 'id') ?? [];
  526. }
  527. $store_goods_cate = self::getPlatformGoodsCate($mall_id);
  528. if(empty($store_goods_cate)){
  529. $cate = ['mall_id' => $mall_id, 'name' => '平台', 'store_id' => 0, 'cate_id' => 0,'parent_id' => 0];
  530. if(!$store_goods_cate = StoreGoodsCate::setData($cate)) throw new \Exception(StoreGoodsCate::getStaticError());
  531. }
  532. /**
  533. * @var $goods_list \common\models\goods\Goods[]
  534. */
  535. $goods_list = \common\models\goods\Goods::findAll(['mall_id' => $mall_id, 'id' => $selected_goods_ids]);
  536. foreach ($goods_list as $goods) {
  537. foreach ($selected_store_ids as $store_id) {
  538. try{
  539. $store_goods = self::findOne(['mall_id' => $mall_id, 'store_id' => $store_id, 'mall_goods_id' => $goods['id'], 'status' => StatusEnum::ENABLED]);
  540. $base_params = [
  541. 'mall_id' => $mall_id,
  542. 'store_id' => $store_id,
  543. 'supplier_id' => $store_id,
  544. 'cats' => [$store_goods_cate['id']],
  545. 'mall_goods_id' => $goods['id'],
  546. 'id' => $store_goods['id'] ?? 0,
  547. 'coupon_selection' => [],
  548. 'check_status' => StatusEnum::ENABLED
  549. ];
  550. $mall_goods = $goods['attributes'];
  551. $base_params['extra'] = !empty($goods->extra) ? ArrayHelper::toArray($goods->extra) : [];
  552. if(isset($base_params['extra']['goods_id'])) unset($base_params['extra']['goods_id']);
  553. if(empty($mall_goods['services'])){
  554. $mall_goods['services'] = [];
  555. }else{
  556. $mall_goods['services'] = explode(',', $mall_goods['services']);
  557. }
  558. if(empty($mall_goods['labels'])){
  559. $mall_goods['labels'] = [];
  560. }else{
  561. $mall_goods['labels'] = explode(',', $mall_goods['labels']);
  562. }
  563. if(empty($mall_goods['freight_type'])){
  564. $mall_goods['freight_type'] = [];
  565. }else{
  566. $mall_goods['freight_type'] = explode(',', $mall_goods['freight_type']);
  567. }
  568. !empty($mall_goods['attr_groups']) && $mall_goods['attr_groups'] = json_decode($mall_goods['attr_groups'], true);
  569. !empty($mall_goods['bannar_pic']) && $mall_goods['bannar_pic'] = json_decode($mall_goods['bannar_pic'], true);
  570. !empty($mall_goods['area_limit']) && $mall_goods['area_limit'] = json_decode($mall_goods['area_limit'], true);
  571. // 预约商品
  572. if ($mall_goods['goods_type'] == GoodsTypeEnum::GoodsTypeReserved) {
  573. $reserve = ArrayHelper::toArray($goods->reserve);
  574. unset($reserve['id'], $reserve['goods_id']);
  575. $skus = GoodsReserveSku::lists([
  576. 'where' => [['goods_id' => $goods['id']]],
  577. 'with' => ['times' => function($query) {
  578. $query->with(['timeSpec']);
  579. }]
  580. ]);
  581. foreach ($skus as &$sku) {
  582. $signId = $sku['sign_id'];
  583. $attr_groups_format = json_decode($goods['attr_groups_format'], true) ?? [];
  584. $sku['attr_list'] = $attr_groups_format[$signId] ?? [];
  585. unset($sku['id'], $sku['goods_id']);
  586. foreach ($sku['times'] as &$time) {
  587. unset($time['id'], $time['reserve_sku_id']);
  588. if (!empty($time['timeSpec'])) {
  589. foreach ($time['timeSpec'] as &$time_spec) {
  590. unset($time_spec['id'], $time_spec['reserve_time_id'], $time_spec['reserve_sku_id']);
  591. }
  592. $time['time_spec'] = $time['timeSpec'];
  593. unset($time_spec, $time['timeSpec']);
  594. }
  595. }
  596. unset($time);
  597. }
  598. unset($sku);
  599. $reserve['skus'] = $skus;
  600. $mall_goods['reserve'] = $reserve;
  601. } else {
  602. if ($mall_goods['is_attr'] == 1) {
  603. $temp_attrs = ArrayHelper::toArray($goods->attr);
  604. $attr_groups = $mall_goods['attr_groups'];
  605. $attr = [];
  606. foreach ($temp_attrs as $val) {
  607. unset($val['id']);
  608. unset($val['goods_id']);
  609. $sku_group_name = explode(',', $val['name']);
  610. $attr_list = [];
  611. foreach($sku_group_name as $name){
  612. $name_arr = explode(':', $name);
  613. foreach($attr_groups as $group){
  614. if($group['attr_group_name'] == $name_arr[0]){
  615. foreach($group['attr_list'] as $v){
  616. if($v['attr_name'] == $name_arr[1]){
  617. $attr_list[] = [
  618. 'attr_id' => $v['attr_id'],
  619. 'attr_name' => $v['attr_name'],
  620. 'pic_url' => '',
  621. 'attr_group_id' => $group['attr_group_id'],
  622. 'attr_group_name' => $group['attr_group_name'],
  623. ];
  624. break;
  625. }
  626. }
  627. break;
  628. }
  629. }
  630. }
  631. $val['attr_list'] = $attr_list;
  632. $attr[] = $val;
  633. }
  634. $base_params['attr'] = $attr;
  635. }
  636. }
  637. unset($mall_goods['id']);
  638. if(!self::setData(array_merge($mall_goods, $base_params))) throw new \Exception(self::getStaticError());
  639. }catch(\Exception $e){
  640. \Yii::error(self::class.'->'.__FUNCTION__.'//errMsg2:'.$e->getMessage().'//params:'.json_encode(array_merge($mall_goods, $base_params)));
  641. }
  642. }
  643. }
  644. }catch(\Exception $e){
  645. \Yii::error(self::class.'->'.__FUNCTION__.'//errMsg1:'.$e->getMessage());
  646. }finally{
  647. return true;
  648. }
  649. }
  650. public static function getPlatformGoodsCate($mall_id)
  651. {
  652. return StoreGoodsCate::findOne(['mall_id' => $mall_id, 'store_id' => 0, 'name' => '平台']);
  653. }
  654. public static function computeMemberPrice($price, $sign_id, $user_level, $is_attr, $is_member_price, $member_price_setting, $discount)
  655. {
  656. $after_price = $price;
  657. switch ($is_member_price) {
  658. case 0:
  659. $after_price = bcdiv($price * $discount, 10, 2);
  660. break;
  661. case 1:
  662. //单独设置会员价
  663. if (!empty($is_attr)) {
  664. //多规格
  665. if (isset($member_price_setting['level' . $user_level][$sign_id])) $member_price_discount = $member_price_setting['level' . $user_level][$sign_id];
  666. } else {
  667. //默认规格
  668. if (isset($member_price_setting[$user_level])) $member_price_discount = $member_price_setting[$user_level];
  669. }
  670. if (!empty($member_price_discount)) {
  671. $after_price = 0;
  672. if ($price >= $member_price_discount) $after_price = bcmul($member_price_discount, 1, 2);
  673. }
  674. break;
  675. }
  676. return $after_price;
  677. }
  678. public static function syncMchGoods($params)
  679. {
  680. try {
  681. $selected_goods_ids = $params['selected_goods_ids'];
  682. $store_id = $params['store_id'];
  683. $mall_id = $params['mall_id'];
  684. $goods_list = \common\models\goods\Goods::findAll(['id' => $selected_goods_ids]);
  685. /** @var \common\models\goods\Goods $goods */
  686. foreach ($goods_list as $goods) {
  687. try {
  688. $cats = ArrayHelper::toArray($goods->cats);
  689. $goods_cats_name = array_column($cats, 'name') ?? [];
  690. if(empty($goods_cats_name)) continue;
  691. $store_goods_cates = StoreGoodsCate::lists([
  692. 'where' => [
  693. ['mall_id' => $mall_id], ['name' => $goods_cats_name], ['status' => StatusEnum::ENABLED],
  694. ],
  695. 'select' => 'id, name, parent_id'
  696. ]);
  697. $idx_name = array_column($store_goods_cates, 'id', 'name') ?? [];
  698. $need_insert = $idx_name ? array_diff($idx_name, $goods_cats_name) : $goods_cats_name;
  699. if(!empty($need_insert)){
  700. $need_insert_cate = \common\models\goods\GoodsCate::lists([
  701. 'where' => [
  702. ['mall_id' => $goods->mall_id],
  703. ['name' => $need_insert]
  704. ],
  705. 'select' => 'id,mall_id, name, mch_id, parent_id, pic, big_pic, advert_pic, advert_url, advert_params, is_show',
  706. 'with' => ['parent.parent']
  707. ]);
  708. self::loopSaveGoodsCate($need_insert_cate, $store_id, $mall_id, $idx_name);
  709. }
  710. $store_goods = self::findOne(['mall_id' => $mall_id, 'store_id' => $store_id, 'mall_goods_id' => $goods['id'], 'status' => StatusEnum::ENABLED]);
  711. $base_params = [
  712. 'mall_id' => $mall_id,
  713. 'store_id' => $store_id,
  714. 'cats' => array_values($idx_name),
  715. 'mall_goods_id' => $goods['id'],
  716. 'id' => $store_goods['id'] ?? 0,
  717. 'coupon_selection' => [],
  718. 'check_status' => StatusEnum::ENABLED,
  719. 'is_on_sale' => StatusEnum::DISABLED
  720. ];
  721. $mall_goods = $goods['attributes'];
  722. $base_params['extra'] = !empty($goods->extra) ? ArrayHelper::toArray($goods->extra) : [];
  723. if (isset($base_params['extra']['goods_id'])) unset($base_params['extra']['goods_id']);
  724. if (empty($mall_goods['services'])) {
  725. $mall_goods['services'] = [];
  726. } else {
  727. $mall_goods['services'] = explode(',', $mall_goods['services']);
  728. }
  729. if (empty($mall_goods['labels'])) {
  730. $mall_goods['labels'] = [];
  731. } else {
  732. $mall_goods['labels'] = explode(',', $mall_goods['labels']);
  733. }
  734. if (empty($mall_goods['freight_type'])) {
  735. $mall_goods['freight_type'] = [];
  736. } else {
  737. $mall_goods['freight_type'] = explode(',', $mall_goods['freight_type']);
  738. }
  739. !empty($mall_goods['attr_groups']) && $mall_goods['attr_groups'] = json_decode($mall_goods['attr_groups'], true);
  740. !empty($mall_goods['bannar_pic']) && $mall_goods['bannar_pic'] = json_decode($mall_goods['bannar_pic'], true);
  741. !empty($mall_goods['area_limit']) && $mall_goods['area_limit'] = json_decode($mall_goods['area_limit'], true);
  742. if ($mall_goods['is_attr'] == 1) {
  743. $temp_attrs = ArrayHelper::toArray($goods->attr);
  744. $attr_groups = $mall_goods['attr_groups'];
  745. $attr = [];
  746. foreach ($temp_attrs as $val) {
  747. unset($val['id']);
  748. unset($val['goods_id']);
  749. $sku_group_name = explode(',', $val['name']);
  750. $attr_list = [];
  751. foreach ($sku_group_name as $name) {
  752. $name_arr = explode(':', $name);
  753. foreach ($attr_groups as $group) {
  754. if ($group['attr_group_name'] == $name_arr[0]) {
  755. foreach ($group['attr_list'] as $v) {
  756. if ($v['attr_name'] == $name_arr[1]) {
  757. $attr_list[] = [
  758. 'attr_id' => $v['attr_id'],
  759. 'attr_name' => $v['attr_name'],
  760. 'pic_url' => '',
  761. 'attr_group_id' => $group['attr_group_id'],
  762. 'attr_group_name' => $group['attr_group_name'],
  763. ];
  764. }
  765. }
  766. break;
  767. }
  768. }
  769. }
  770. $val['attr_list'] = $attr_list;
  771. $attr[] = $val;
  772. }
  773. $base_params['attr'] = $attr;
  774. }
  775. unset($mall_goods['id']);
  776. if (!self::setData(array_merge($mall_goods, $base_params))) throw new \Exception(self::getStaticError());
  777. } catch (\Exception $e) {
  778. dd($e->getMessage());
  779. \Yii::error(__METHOD__ . '//errMsg2:' . $e->getMessage() . '//params:' . json_encode(array_merge($mall_goods, $base_params)));
  780. }
  781. }
  782. } catch (\Exception $e) {
  783. dd($e->getMessage());
  784. \Yii::error(__METHOD__ . '//errMsg1:' . $e->getMessage());
  785. } finally {
  786. return true;
  787. }
  788. }
  789. public static function loopSaveGoodsCate($cats, $store_id, $mall_id, &$idx_name)
  790. {
  791. foreach ($cats as $item) {
  792. $item['store_id'] = $store_id;
  793. $item['mall_id'] = $mall_id;
  794. unset($item['id'], $item['mch_id']);
  795. if ($item['parent']) {
  796. self::loopSaveGoodsCate([$item['parent']], $store_id, $mall_id, $idx_name);
  797. } else {
  798. $saveRes = StoreGoodsCate::setData($item);
  799. if ($saveRes === false) throw new \Exception(StoreGoodsCate::getStaticError());
  800. $idx_name['name'] = $saveRes->id;
  801. }
  802. }
  803. }
  804. //商品导出整理数据
  805. public static function exportHandle(array $data)
  806. {
  807. ini_set('memory_limit','512M');
  808. $download_id = $data['download_id'] ?? 0;
  809. $download = Download::findOne(['id' => $download_id]);
  810. if ($download) {
  811. try {
  812. $params = json_decode($download->params, true);
  813. $filename = $download->name;
  814. $type = $download->type;
  815. $goods_detail_model = StoreGoods::find();
  816. $goods_detail_model->where(['mall_id' => $params['mall_id'],'store_id' => $params['store_id']]);
  817. $goods_detail_model->andWhere(['>', 'status', StatusEnum::DISABLED]);
  818. !empty($params['start']) && $goods_detail_model->andWhere(['>=', 'g.created_at', strtotime($params['start'])]);
  819. !empty($params['end']) && $goods_detail_model->andWhere(['<=', 'g.created_at', strtotime($params['end'])]);
  820. !empty($params['goods_name']) && $goods_detail_model->andWhere(['like', 'g.goods_name', trim($params['goods_name']) . '%', false]);
  821. // !empty($params['cats']) && $goods_detail_model->andWhere(['in', 'gcr.cate_id', $params['cats']]);
  822. if(!empty($params['cats'])){
  823. $goods_cate_relate_list = StoreGoodsCateRelate::lists(['where'=>[['cate_id'=>$params['cats']]]]);
  824. $goods_ids = array_column($goods_cate_relate_list,'goods_id');
  825. $goods_detail_model->andWhere(['in', 'g.id', $goods_ids]);
  826. }
  827. !empty($params['goods_id']) && $goods_detail_model->andWhere(['=', 'g.id', $params['goods_id']]);
  828. !empty($params['choose_list']) && $goods_detail_model->andWhere(['in', 'g.id', $params['choose_list']]);
  829. !empty($params['goods_type']) && $goods_detail_model->andWhere(['=', 'g.goods_type', $params['goods_type']]);
  830. if (!empty($params['type'])) {
  831. switch ($params['type']) {
  832. case 'checking':
  833. $goods_detail_model->andWhere(['=', 'g.check_status', 0]);
  834. break;
  835. case 'onsale':
  836. $goods_detail_model->andWhere(['=', 'g.is_on_sale', WhetherEnum::ENABLED]);
  837. $goods_detail_model->andWhere(['=', 'g.check_status', 1]);
  838. $goods_detail_model->andWhere(['>', 'g.stock', 0]);
  839. break;
  840. case 'offline':
  841. $goods_detail_model->andWhere(['=', 'g.is_on_sale', WhetherEnum::DISABLED]);
  842. break;
  843. case 'sell-out':
  844. $goods_detail_model->andWhere(['=', 'g.stock', 0]);
  845. break;
  846. case 'stock-lack':
  847. $goods_detail_model->andWhere(new Expression('g.stock <= g.stock_warning'));
  848. break;
  849. }
  850. }
  851. //连表
  852. // $goods_detail_model->join('LEFT JOIN', '{{%goods_cate_relate}} gcr', 'gcr.goods_id=g.id');
  853. $goods_detail_model->alias('g');
  854. $goods_detail_model->with(['cats']);
  855. $goods_detail_model->orderBy('g.id desc');
  856. $fields = $params['fields'];
  857. $fields_arr = self::fieldsList(); //var_dump($fields);
  858. $batchSize = 5000; // 设置订单批量导出数量
  859. $i = 0; //循环次数
  860. $goodsCount = $goods_detail_model->count(); // 查询商品总条数
  861. $is_goods_page = $goodsCount > $batchSize; // 判断是否需要分页
  862. foreach ($goods_detail_model->asArray()->batch($batchSize) as $goods_details) {
  863. $goods_groups_max = 0;
  864. $goods_collective_list = [];
  865. foreach ($goods_details as $goods_detail) {
  866. $processed_goods_detail = self::processGoodsDetail($goods_detail);
  867. $goods_collective_list[] = $processed_goods_detail;
  868. $goods_groups_max = max($goods_groups_max, $processed_goods_detail['goods_group_num']);
  869. }
  870. // 处理商品规格
  871. $goods_export_list = [];
  872. foreach ($goods_collective_list as $goods_detail) {
  873. $goods_item = [];
  874. foreach ($fields as $field) {
  875. if ($field == 'attr_groups') {
  876. // 以最大的规格数量为标准,不足的规格数量用空格补齐
  877. foreach (range(1, $goods_groups_max) as $index) {
  878. $goods_item[] = $goods_detail['attr_groups'][$index - 1] ?? '';
  879. }
  880. } else {
  881. $goods_item[] = $goods_detail[$field] ?? '';
  882. }
  883. }
  884. $goods_export_list[] = $goods_item;
  885. }
  886. if (!empty($goods_export_list)) {
  887. $csv = new CsvTool();
  888. $csv->filename = $is_goods_page ? $filename . "_1" : $filename;;
  889. $csv->file_dirname = DownloadEnum::getTypeStorageDirMap($type);
  890. $csv->export_mode = CsvTool::EXPORT_MODE_ASYNC;
  891. $have_select_field_arr = [];
  892. foreach ($fields as $value) {
  893. if ($value == 'attr_groups' && $goods_groups_max > 0) {
  894. foreach (range(1, $goods_groups_max) as $index) {
  895. $have_select_field_arr[] = '规格' . $index;
  896. }
  897. } else {
  898. $have_select_field_arr[] = $fields_arr[$value];
  899. }
  900. }
  901. $csv->header = $have_select_field_arr;
  902. $csv->data = $goods_export_list;
  903. if ($is_goods_page && $i == 0) { // 因为订单页需要分页,所以需要设置文件名
  904. $download->name = $filename . '_1';
  905. $download->save();
  906. }
  907. if ($i >= 1) {
  908. $newFileName = $filename . '_' . ($i + 1);
  909. $csv->filename = $newFileName;
  910. $downloadData = array(
  911. 'mall_id' => $download->mall_id,
  912. 'mch_id' => $download->mch_id,
  913. 'store_id' => $download->store_id,
  914. 'name' => $newFileName,
  915. 'type' => $download->type,
  916. 'handler_class' => $download->handler_class,
  917. 'method' => $download->method,
  918. 'params' => $download->params,
  919. 'data' => $goods_export_list
  920. );
  921. $downloadModel = Download::setData($downloadData);
  922. if ($downloadModel === false) {
  923. Yii::$app->custom->logs('创建下载任务失败:' . $downloadData['name'], Download::getStaticError());
  924. throw new Exception('创建新下载任务失败');
  925. };
  926. $download = $downloadModel;
  927. }
  928. $csv->export();
  929. $csv->updateDown($download, $params['mall_id']);
  930. $i++;
  931. }
  932. }
  933. return true;
  934. } catch (\Exception $e) {
  935. $download->status = 3;
  936. $res = $download->save();
  937. self::setStaticError($e->getMessage());
  938. return $e->getMessage();
  939. }
  940. }
  941. }
  942. public static function processGoodsDetail(array $goods_detail = [])
  943. {
  944. if ($goods_detail['is_attr'] == 1 && !empty($goods_detail['attr_groups'])) {
  945. $attr = json_decode($goods_detail['attr_groups'], true);
  946. if ($attr === null) {
  947. // 如果解码失败,返回默认值
  948. $attr_arr = [];
  949. } else {
  950. $attr_arr = array_map(function ($item) {
  951. $item_attr_names = array_map(function ($item) {
  952. return htmlspecialchars($item['attr_name']); // 防止 XSS 攻击
  953. }, $item['attr_list']);
  954. return htmlspecialchars($item['attr_group_name']) . ':' . implode('、', $item_attr_names);
  955. }, $attr);
  956. }
  957. $goods_detail['goods_group_num'] = count($attr_arr);
  958. $goods_detail['attr_groups'] = $attr_arr;
  959. } else {
  960. $goods_detail['attr_groups'] = '-';
  961. $goods_detail['goods_group_num'] = 0;
  962. }
  963. if (!empty($goods_detail['cats'])) {
  964. $goods_detail['cats'] = implode('|', array_column($goods_detail['cats'], 'name')) ?? '';
  965. }
  966. if (!empty($goods_detail['created_at'])) {
  967. $goods_detail['created_at'] = date("Y-m-d H:i:s", $goods_detail['created_at']) ?? '';
  968. }
  969. if (in_array($goods_detail['is_on_sale'], [0, 1])) {
  970. $goods_detail['is_on_sale'] = $goods_detail['is_on_sale'] == 1 ? '上架' : '下架';
  971. }
  972. return $goods_detail;
  973. }
  974. public static function fieldsList($field = null)
  975. {
  976. $fields = [
  977. 'id' => '商品ID',
  978. 'goods_name' => '商品名',
  979. 'price' => '售价',
  980. 'cost_price' => '成本价',
  981. 'goods_no' => '商品货号',
  982. 'original_price' => '划线价',
  983. 'sales_num' => '销量',
  984. 'virtual_sales' => '虚拟销量',
  985. 'is_on_sale' => '状态',
  986. 'created_at' => '商品添加时间',
  987. 'unit' => '商品单位',
  988. 'stock' => '库存',
  989. 'cats' => '商品分类',
  990. 'attr_groups' => '规格',
  991. ];
  992. if ($field === null) return $fields;
  993. $temp = [];
  994. foreach ($field as $val) {
  995. if (isset($fields[$val])) $temp[$val] = $fields[$val];
  996. }
  997. return $temp;
  998. }
  999. /**
  1000. * 批量导入商品
  1001. * @param $params
  1002. * @return array|bool
  1003. */
  1004. public static function importGoods($params)
  1005. {
  1006. $t = \Yii::$app->db->beginTransaction();
  1007. try {
  1008. $csvImport = new CsvImportForm();
  1009. $data = $csvImport->setDataForm($params)
  1010. ->setMaxCount(GoodsImportEnum::MAX_COUNT)
  1011. ->setImportFieldConfig(GoodsImportEnum::HEAD_LIST)
  1012. ->setColumnNum(GoodsImportEnum::COLUMN_NUM)
  1013. ->readCsvData();
  1014. if (!$data) throw new \Exception(CsvImportForm::getStaticError());
  1015. $temp_data = array_column($data, null, 'serial_number');
  1016. if (count($temp_data) != count($data)) throw new \Exception('存在重复序号');
  1017. unset($temp_data);
  1018. $result = self::handleImportData($params, $data, 1);
  1019. $t->commit();
  1020. return ['counts' => $result['counts'], 'success_counts' => $result['success_counts'], 'fail_counts' => $result['fail_counts']];
  1021. } catch (\Exception $e) {
  1022. self::$static_error = $e->getMessage();
  1023. $t->rollBack();
  1024. return false;
  1025. }
  1026. }
  1027. /**
  1028. * 商品数据入库
  1029. * @param $params
  1030. * @param $data
  1031. * @param $sync
  1032. * @return array|void
  1033. * @throws Exception
  1034. */
  1035. public static function handleImportData($params, $data, $sync = 1)
  1036. {
  1037. $success_counts = $fail_counts = 0;
  1038. $counts = count($data);
  1039. $fail_arr = [];
  1040. $temp_data = [];
  1041. $attr_suffix_arr = ['a', 'b', 'c', 'd', 'e'];
  1042. $bannar_pic_suffix_arr = ['a', 'b', 'c', 'd', 'e', 'f', 'h', 'i'];
  1043. $goods_detail_suffix_arr = ['a', 'b', 'c', 'd', 'e', 'f', 'h', 'i', 'j'];
  1044. // 方便记录错误
  1045. $data = array_column($data, null, 'serial_number');
  1046. foreach ($data as $val) {
  1047. $val['data_row_id'] = $val['serial_number'];
  1048. $val['mall_id'] = $params['mall_id'];
  1049. if (empty($val['serial_number'])) {
  1050. $val['reason'] = '序号为空';
  1051. list($fail_arr, $fail_counts) = ImportFailLog::packFailArr($val, $fail_arr, $fail_counts);
  1052. continue;
  1053. }
  1054. if (empty($val['goods_type'])) {
  1055. $val['reason'] = '商品类型为空';
  1056. list($fail_arr, $fail_counts) = ImportFailLog::packFailArr($val, $fail_arr, $fail_counts);
  1057. continue;
  1058. }
  1059. if (empty($val['goods_no'])) {
  1060. $val['reason'] = '商品编号为空';
  1061. list($fail_arr, $fail_counts) = ImportFailLog::packFailArr($val, $fail_arr, $fail_counts);
  1062. continue;
  1063. }
  1064. if (empty($val['goods_name'])) {
  1065. $val['reason'] = '商品名称为空';
  1066. list($fail_arr, $fail_counts) = ImportFailLog::packFailArr($val, $fail_arr, $fail_counts);
  1067. continue;
  1068. }
  1069. if (empty($val['cate'])) {
  1070. $val['reason'] = '商品分类为空';
  1071. list($fail_arr, $fail_counts) = ImportFailLog::packFailArr($val, $fail_arr, $fail_counts);
  1072. continue;
  1073. }
  1074. if (empty($val['price'])) {
  1075. $val['reason'] = '商品售价为空';
  1076. list($fail_arr, $fail_counts) = ImportFailLog::packFailArr($val, $fail_arr, $fail_counts);
  1077. continue;
  1078. }
  1079. if (empty($val['stock'])) {
  1080. $val['reason'] = '商品库存为空';
  1081. list($fail_arr, $fail_counts) = ImportFailLog::packFailArr($val, $fail_arr, $fail_counts);
  1082. continue;
  1083. }
  1084. if (empty($val['cover_pic'])) {
  1085. $val['reason'] = '商品主图为空';
  1086. list($fail_arr, $fail_counts) = ImportFailLog::packFailArr($val, $fail_arr, $fail_counts);
  1087. continue;
  1088. }
  1089. if (!isset($temp_data[$val['goods_no'] . $val['goods_name']])) {
  1090. $temp_data[$val['goods_no'] . $val['goods_name']] = [
  1091. // 存储每一行的数据
  1092. 'list' => [],
  1093. // 存储规格名称与规格值
  1094. 'attr_groups' => [],
  1095. // 第一个规格名的规格值图片
  1096. 'attr_value_pic' => [],
  1097. ];
  1098. }
  1099. $name = '';
  1100. $temp_attr_list = [];
  1101. $exist_attr = 0;
  1102. // 判断门店是否需要审核商品
  1103. $isGoodsAuth = Yii::$app->services->setting->addons->get($params['mall_id'], StoreEnum::ADDONS_NAME, 'basic.goods_auth');
  1104. // 处理商品规格
  1105. foreach ($attr_suffix_arr as $v) {
  1106. $attr_column_name = 'attr_name_' . $v;
  1107. $attr_column_value = 'attr_value_' . $v;
  1108. if (!isset($val[$attr_column_name])) continue;
  1109. if (empty($val[$attr_column_name])) continue;
  1110. if (!isset($val[$attr_column_value])) continue;
  1111. if (empty($val[$attr_column_value])) continue;
  1112. if (!isset($attr_column_name, $temp_data[$val['goods_no'] . $val['goods_name']]['attr_groups'])) {
  1113. $temp_data[$val['goods_no'] . $val['goods_name']]['attr_groups'][$val[$attr_column_name]] = [];
  1114. }
  1115. $temp_data[$val['goods_no'] . $val['goods_name']]['attr_groups'][$val[$attr_column_name]][] = $val[$attr_column_value];
  1116. $temp_data[$val['goods_no'] . $val['goods_name']]['attr_groups'][$val[$attr_column_name]] = array_unique($temp_data[$val['goods_no'] . $val['goods_name']]['attr_groups'][$val[$attr_column_name]]);
  1117. if ($v == 'a' && !empty($val['pic_url'])) {
  1118. $temp_data[$val['goods_no'] . $val['goods_name']]['attr_value_pic'][$val[$attr_column_value]] = $val['pic_url'];
  1119. }
  1120. $name .= $val[$attr_column_name] . ':' . $val[$attr_column_value] . ':';
  1121. $temp_attr_list[] = [
  1122. 'attr_id' => 0,
  1123. 'attr_name' => $val[$attr_column_value],
  1124. 'attr_group_id' => 0,
  1125. 'attr_group_name' => $val[$attr_column_name],
  1126. 'pic_url' => ''
  1127. ];
  1128. $exist_attr++;
  1129. }
  1130. if ($exist_attr <= 0) {
  1131. $val['reason'] = '商品规格全为空';
  1132. list($fail_arr, $fail_counts) = ImportFailLog::packFailArr($val, $fail_arr, $fail_counts);
  1133. unset($temp_data[$val['goods_no'] . $val['goods_name']]);
  1134. continue;
  1135. }
  1136. $val['name'] = trim($name, ':');
  1137. $val['temp_attr_list'] = $temp_attr_list;
  1138. // 处理商品轮播图
  1139. foreach ($bannar_pic_suffix_arr as $k => $bannar_item) {
  1140. $bannar_pic_column = 'bannar_pic_' . $bannar_item;
  1141. if (!isset($val[$bannar_pic_column])) continue;
  1142. if (empty($val[$bannar_pic_column])) continue;
  1143. $val['bannar_pic'][] = ['id' => $k, 'pic_url' => $val[$bannar_pic_column]];
  1144. }
  1145. // 处理商品详情图片
  1146. $goods_detail_str = '<p>';
  1147. foreach ($goods_detail_suffix_arr as $detail_item) {
  1148. $goods_detail_column = 'goods_detail_' . $detail_item;
  1149. if (!isset($val[$goods_detail_column])) continue;
  1150. if (empty($val[$goods_detail_column])) continue;
  1151. $goods_detail_str .= '<img src="' . $val[$goods_detail_column] . '" />';
  1152. }
  1153. $goods_detail_str .= '</p>';
  1154. $val['goods_detail'] = $goods_detail_str;
  1155. if (!empty($val['cate'])) {
  1156. $val['cate'] = explode(',', $val['cate']);
  1157. // 查询是否存在分类
  1158. $caste_ids=StoreGoodsCate::find()->select(['id'])
  1159. ->where(['mall_id' => $params['mall_id'], 'store_id' => $params['store_id']
  1160. , 'name' => $val['cate'], 'status' => StatusEnum::ENABLED])->all();
  1161. if ($caste_ids) {
  1162. $val['cats'] = array_column($caste_ids, "id");
  1163. }
  1164. }
  1165. $temp_data[$val['goods_no'] . $val['goods_name']]['list'][] = $val;
  1166. }
  1167. $extra = [
  1168. 'full_reduce_price_limit' => 0,
  1169. 'full_reduce_money' => 0,
  1170. 'is_member_price' => 0,
  1171. 'is_show_price' => 0,
  1172. 'member_price_switch' => 0,
  1173. 'score_give_setting' => [
  1174. 'code' => 'score',
  1175. 'name' => '积分',
  1176. 'give_num' => 0,
  1177. 'give_type' => 1,
  1178. ],
  1179. 'member_buy_limit_setting' => [
  1180. 'member_level' => [],
  1181. 'every_num' => 0,
  1182. 'every_day_num' => 0,
  1183. 'every_week_num' => 0,
  1184. 'every_month_num' => 0,
  1185. 'nums' => 0,
  1186. 'total_num' => 0,
  1187. ],
  1188. 'currency_deduct_setting' => [],
  1189. 'score_deduct_setting' => [
  1190. 'code'=>'score',
  1191. 'name'=> '积分',
  1192. 'is_enable'=> 0,
  1193. 'deduct_num'=> 0,
  1194. 'deduct_type' => 1,
  1195. ],
  1196. ];
  1197. foreach ($temp_data as $goods_no => $list) {
  1198. $sku = $list['list'];
  1199. $attr_groups = $list['attr_groups'];
  1200. $attr_value_pic = $list['attr_value_pic'];
  1201. $is_attr = count($sku) > 1 ? 1 : 0;
  1202. $goods_type_res = array_search($sku[0]['goods_type'], GoodsImportEnum::GOODS_TYPE);
  1203. $temp = [
  1204. 'goods_name' => $sku[0]['goods_name'],
  1205. 'subtitle' => $sku[0]['subtitle'] ?? '',
  1206. 'cats' => $sku[0]['cate'],
  1207. 'is_attr' => $is_attr,
  1208. 'check_status' => empty($isGoodsAuth) ? 1 : 0,
  1209. 'price' => !empty($sku[0]['price']) ? $sku[0]['price'] : 0,
  1210. 'cost_price' => !empty($sku[0]['cost_price']) ? $sku[0]['cost_price'] : 0,
  1211. 'original_price' => !empty($sku[0]['original_price']) ? $sku[0]['original_price'] : 0,
  1212. 'sales_num' => !empty($sku['sales_num']) ? $sku['sales_num'] : 0,
  1213. 'virtual_sales' => !empty($sku[0]['virtual_sales']) ? $sku[0]['virtual_sales'] : 0,
  1214. 'unit' => $sku[0]['unit'] ?? '',
  1215. 'stock' => !empty($sku[0]['stock']) ? $sku[0]['stock'] : 0,
  1216. 'cover_pic' => $sku[0]['cover_pic'] ?? '',
  1217. 'bannar_pic' => $sku[0]['bannar_pic'] ? $sku[0]['bannar_pic'] : [],
  1218. 'freight_type' => ['1'],
  1219. 'freight_rules_type' => 2,
  1220. 'freight_id' => 3,
  1221. 'shipping_fee' => 0,
  1222. 'free_shipping_num' => 0,
  1223. 'free_shipping_money' => 0,
  1224. 'is_on_sale' => 1, //上架状态
  1225. 'goods_type' => $goods_type_res ? $goods_type_res : 1, //商品类型
  1226. 'mall_id' => $params['mall_id'],
  1227. 'store_id' => $params['store_id'],
  1228. 'detail' => $sku[0]['goods_detail'],
  1229. 'extra' => $extra,
  1230. 'serial_number' => $sku[0]['serial_number']
  1231. ];
  1232. // 处理多规格
  1233. if ($is_attr == 1) {
  1234. $attr = [];
  1235. $temp_attr_groups = [];
  1236. $attr_groups_index = 1;
  1237. $attr_list_index = 2;
  1238. $temporary_attr_list = [];
  1239. $temporary_attr_group_list = [];
  1240. foreach ($attr_groups as $group_name => $group) {
  1241. $attr_list = [];
  1242. foreach ($group as $attr_item) {
  1243. $attr_list[] = [
  1244. 'attr_id' => $attr_list_index,
  1245. 'attr_name' => $attr_item,
  1246. 'pic_url' => $attr_value_pic[$attr_item] ?? '',
  1247. ];
  1248. $temporary_attr_list[$attr_item] = $attr_list_index;
  1249. $attr_list_index++;
  1250. }
  1251. $temp_attr_groups[] = [
  1252. 'attr_group_id' => $attr_groups_index,
  1253. 'attr_group_name' => $group_name,
  1254. 'attr_list' => $attr_list
  1255. ];
  1256. $temporary_attr_group_list[$group_name] = $attr_groups_index;
  1257. $attr_groups_index++;
  1258. }
  1259. $temp['attr_groups'] = $temp_attr_groups;
  1260. foreach ($sku as $ku) {
  1261. $temp_sku = [];
  1262. $temp_sku['price'] = !empty($ku['price']) ? $ku['price'] : 0;
  1263. $temp_sku['stock'] = !empty($ku['stock']) ? $ku['stock'] : 0;
  1264. $temp_sku['original_price'] = !empty($ku['original_price']) ? $ku['original_price'] : 0;
  1265. $temp_sku['cost_price'] = !empty($ku['cost_price']) ? $ku['cost_price'] : 0;
  1266. $temp_sku['name'] = $ku['name'];
  1267. $temp_sku['weight'] = 0;
  1268. $temp_sku['goods_no'] = $goods_no;
  1269. $temp_sku['attr_list'] = $ku['temp_attr_list'];
  1270. $temp_sku['data_row_id'] = $ku['data_row_id'];
  1271. foreach ($temp_sku['attr_list'] as &$sku_attr) {
  1272. $sku_attr['attr_id'] = $temporary_attr_list[$sku_attr['attr_name']];
  1273. $sku_attr['attr_group_id'] = $temporary_attr_group_list[$sku_attr['attr_group_name']];
  1274. }
  1275. unset($sku_attr);
  1276. $temp_sku['sign_id'] = implode(':', array_column($temp_sku['attr_list'], 'attr_id'));
  1277. $attr[] = $temp_sku;
  1278. }
  1279. $temp['attr'] = $attr;
  1280. }
  1281. $res = self::setData($temp);
  1282. if ($res) {
  1283. $success_counts += $is_attr == 1 ? count($temp['attr']) : 1;
  1284. } else {
  1285. $reason = self::getStaticError();
  1286. if ($is_attr == 1) {
  1287. foreach ($temp['attr'] as $temp_attr) {
  1288. $temp_params = $data[$temp_attr['data_row_id']];
  1289. $temp_params['reason'] = $reason;
  1290. list($fail_arr, $fail_counts) = ImportFailLog::packFailArr($temp_params, $fail_arr, $fail_counts);
  1291. }
  1292. } else {
  1293. $temp_params = $data[$temp['serial_number']];
  1294. $temp_params['reason'] = $reason;
  1295. list($fail_arr, $fail_counts) = ImportFailLog::packFailArr($temp_params, $fail_arr, $fail_counts);
  1296. }
  1297. }
  1298. }
  1299. $param['counts'] = $counts;
  1300. $param['success_counts'] = $success_counts;
  1301. $param['fail_counts'] = $fail_counts;
  1302. $param['source'] = self::class;
  1303. $param['mall_id'] = $params['mall_id'];
  1304. $param['store_id'] = $params['store_id'];
  1305. if ($sync == 0) {
  1306. $param['fail_arr'] = $fail_arr;
  1307. // 异步返回数据
  1308. return $param;
  1309. } else {
  1310. $import_log_model = ImportLog::setData($param);
  1311. if (!$import_log_model) throw new \Exception(ImportLog::getStaticError());
  1312. if (!empty($fail_arr)) {
  1313. $res = ImportFailLog::setData($fail_arr, $import_log_model->id);
  1314. if (!$res) throw new \Exception(ImportFailLog::getStaticError());
  1315. }
  1316. }
  1317. }
  1318. /**
  1319. * 异步批量导入商品
  1320. * @param $params
  1321. * @return false|int[]
  1322. */
  1323. public static function importGoodsSync($params)
  1324. {
  1325. $t = \Yii::$app->db->beginTransaction();
  1326. try {
  1327. $basePath = \Yii::$app->basePath;
  1328. if (empty($_FILES) || !isset($_FILES['file'])) throw new \Exception('请上传csv文件');
  1329. $fileName = $_FILES['file']['name'];
  1330. $tmpName = $_FILES['file']['tmp_name'];
  1331. $basePath = $basePath . '/web/uploads/goods/';
  1332. if (!is_dir($basePath)) mkdir($basePath, 0775, true);
  1333. $ext = strtolower(pathinfo($fileName, PATHINFO_EXTENSION));
  1334. if ($ext != 'csv') throw new \Exception('请上传csv文件');
  1335. $uploadFile = $basePath . 'ADMIN-' . date('Y-m-d His') . '.' . $ext;
  1336. //上传
  1337. move_uploaded_file($tmpName, $uploadFile);
  1338. $success_counts = $fail_counts = 0;
  1339. $counts = 0;
  1340. $param['counts'] = $counts;
  1341. $param['success_counts'] = $success_counts;
  1342. $param['fail_counts'] = $fail_counts;
  1343. $param['upload_file'] = $uploadFile;
  1344. $param['mall_id'] = $params['mall_id'];
  1345. $param['store_id'] = $params['store_id'];
  1346. $param['source'] = self::class;
  1347. $import_log_model = ImportLog::setData($param);
  1348. if (!$import_log_model) throw new \Exception(ImportLog::getStaticError());
  1349. $t->commit();
  1350. $params = ['id' => $import_log_model['id']];
  1351. Yii::$app->services->rabbitMq->push(RabbitMqEnum::EXCHANGE_TASK_IMPORT_USER, RabbitMqEnum::IMPORT_USER, $params, self::class, 'syncImportGoods');
  1352. return ['counts' => $counts, 'success_counts' => $success_counts, 'fail_counts' => $fail_counts];
  1353. } catch (\Exception $e) {
  1354. self::$static_error = $e->getMessage();
  1355. $t->rollBack();
  1356. return false;
  1357. }
  1358. }
  1359. /**
  1360. * 异步执行
  1361. * @param $params
  1362. * @return void
  1363. */
  1364. public static function syncImportGoods($params)
  1365. {
  1366. try {
  1367. if (empty($params['id'])) throw new Exception('缺少参数id');
  1368. $model = ImportLog::findOne(['id' => $params['id']]);
  1369. if (empty($model['upload_file'])) throw new Exception('缺少参数upload_file');
  1370. $upload_file = $model['upload_file'];
  1371. $mall_id = $model['mall_id'];
  1372. $store_id = $model['store_id'];
  1373. $cvsFile = fopen($upload_file, 'r'); //开始读取csv文件数据
  1374. $i = 0;
  1375. $head_list = GoodsImportEnum::HEAD_LIST;
  1376. $head_list_fields = array_column($head_list, 'field');
  1377. $rows = [];
  1378. while ($fileData = fgetcsv($cvsFile)) {
  1379. $i++;
  1380. if ($i == 1) continue; //过滤表头
  1381. $temp_row = [];
  1382. foreach ($fileData as $k => $val) {
  1383. $val = mb_convert_encoding(trim($val), "UTF-8", "GBK");
  1384. foreach ($head_list_fields as $key => $field) {
  1385. if ($k == $key) {
  1386. $temp_row[$field] = $val;
  1387. continue;
  1388. }
  1389. }
  1390. }
  1391. $rows[] = $temp_row;
  1392. }
  1393. $temp_rows = array_column($rows, null, 'serial_number');
  1394. if (count($temp_rows) == count($rows)) {
  1395. $result = self::handleImportData(['mall_id' => $mall_id,'store_id' => $store_id], $rows, 0);
  1396. $model->counts = $result['counts'];
  1397. $model->success_counts = $result['success_counts'];
  1398. $model->fail_counts = $result['fail_counts'];
  1399. } else {
  1400. $model->counts = count($rows);
  1401. $model->success_counts = 0;
  1402. $model->fail_counts = $model->counts;
  1403. $result = [];
  1404. }
  1405. if (!$model->save()) throw new \Exception($model->getErrorMessage());
  1406. $fail_arr = $result['fail_arr'] ?? [];
  1407. if (!empty($fail_arr)) {
  1408. $res = ImportFailLog::setData($fail_arr, $model->id);
  1409. if (!$res) throw new \Exception(ImportFailLog::getStaticError());
  1410. }
  1411. } catch (\Exception $e) {
  1412. $msg = __METHOD__ . ' msg: ' . $e->getMessage();
  1413. \Yii::error($msg);
  1414. }
  1415. }
  1416. }