GoodsService.php 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566
  1. <?php
  2. namespace addons\QiDingDong\common\service;
  3. use addons\QiDingDong\common\enums\QiDingDongEnums;
  4. use addons\QiDingDong\common\expand\sdk\qidingdong\src\model\DeliveryMethodModel;
  5. use addons\QiDingDong\common\expand\sdk\qidingdong\src\model\GoodsDetailV2Model;
  6. use addons\QiDingDong\common\expand\sdk\qidingdong\src\model\GoodsListV2Model;
  7. use addons\QiDingDong\common\expand\sdk\qidingdong\src\request\DeliveryMethodRequest;
  8. use addons\QiDingDong\common\expand\sdk\qidingdong\src\request\GoodsDetailV2Request;
  9. use addons\QiDingDong\common\expand\sdk\qidingdong\src\request\GoodsListV2Request;
  10. use addons\QiDingDong\common\helper\RequestHelper;
  11. use addons\QiDingDong\common\logic\GoodsLogic;
  12. use addons\QiDingDong\common\models\QiDingDongEvent;
  13. use addons\QiDingDong\common\models\QiDingDongGoods;
  14. use addons\QiDingDong\common\models\QiDingDongGoodsAttr;
  15. use common\enums\RabbitMqEnum;
  16. use common\enums\StatusEnum;
  17. use common\helpers\FormatHelper;
  18. use common\models\goods\Goods;
  19. use common\models\goods\GoodsAttr;
  20. use common\models\mall\Mall;
  21. use yii\helpers\Json;
  22. class GoodsService
  23. {
  24. public function page(int $mall_id, array $params)
  25. {
  26. $where = [
  27. ['mall_id' => $mall_id],
  28. ['status' => [StatusEnum::ENABLED, StatusEnum::DISABLED]]
  29. ];
  30. if (!empty($params['goods_name'])) {
  31. $where[] = ['like', 'goods_name', $params['goods_name']];
  32. }
  33. if (!empty($params['cate_id'])) {
  34. $where[] = ['cate_one' => $params['cate_id']];
  35. }
  36. if (isset($params['imported']) && $params['imported'] !== '') {
  37. switch ($params['imported']) {
  38. case 1:
  39. $where[] = ['>', 'goods_id' , 0];
  40. break;
  41. case 0:
  42. $where[] = ['=', 'goods_id' , 0];
  43. break;
  44. }
  45. }
  46. $ret = QiDingDongGoods::listPage([
  47. 'where' => $where,
  48. 'limit' => $params['limit'] ?? 10,
  49. 'order' => 'id desc',
  50. 'with' => ['cateOne', 'cateTwo']
  51. ]);
  52. if (!empty(QiDingDongGoods::getStaticError())) return QiDingDongGoods::getStaticError();
  53. if (!empty($ret['list'])) {
  54. $rate = MallService::rate($mall_id);
  55. foreach ($ret['list'] as &$item) {
  56. $item['content'] = Json::decode($item['content']);
  57. $item['price'] = bcmul($rate, $item['price'], 2);
  58. $item['retail_price'] = bcmul($rate, $item['retail_price'], 2);
  59. $item['lowest_price'] = bcmul($rate, $item['lowest_price'], 2);
  60. }
  61. }
  62. return $ret;
  63. }
  64. public function thirdPage(int $mall_id, array $params)
  65. {
  66. try {
  67. $contentModel = RequestHelper::contentModelInstance($mall_id);
  68. $contentModel->content->data = new GoodsListV2Model();
  69. if (!empty($params['page'])) $contentModel->content->data->page = $params['page'];
  70. if (!empty($params['limit'])) $contentModel->content->data->page_size = $params['limit'];
  71. if (!empty($params['goods_id'])) $contentModel->content->data->goods_id = $params['goods_id'];
  72. if (!empty($params['sort'])) $contentModel->content->data->sort = $params['sort'];
  73. if (!empty($params['sort_type'])) $contentModel->content->data->sort_type = $params['sort_type'];
  74. if (!empty($params['goods_name'])) $contentModel->content->data->goods_name = $params['goods_name'];
  75. if (!empty($params['cate_id'])) $contentModel->content->data->cate_id = $params['cate_id'];
  76. if (!empty($params['discount'])) $contentModel->content->data->discount = $params['discount'];
  77. if (!empty($params['goods_type'])) $contentModel->content->data->goods_type = $params['goods_type'];
  78. $contentModel->content->data->device = QiDingDongEnums::REQUEST_PARAMS_DEVICE; // 固定值
  79. if (!empty($params['sort_type']) && !empty($params['sort'])) {
  80. // 默认最新 1、价格高到低 2、价格低到高 3、最新上架 4、修改时间 5、商品最后上下时间
  81. $contentModel->content->data->sort = $params['sort_type'];
  82. $contentModel->content->data->sort_type = $params['sort'];
  83. }
  84. return RequestHelper::send(new GoodsListV2Request(), RequestHelper::enCrypto($contentModel, $mall_id), $mall_id);
  85. } catch (\Exception $e) {
  86. return $e->getMessage();
  87. }
  88. }
  89. public function localPage(int $mall_id, array $params)
  90. {
  91. $where = [
  92. ['mall_id' => $mall_id],
  93. ['status' => StatusEnum::ENABLED]
  94. ];
  95. if (!empty($params['goods_name'])) {
  96. $where[] = ['like', 'goods_name', $params['goods_name']];
  97. }
  98. if (!empty($params['cate_id'])) {
  99. $where[] = ['cate_one' => $params['third_cate_id']];
  100. }
  101. if(!empty($params['discount'])) {
  102. $where[] = ['discount' => $params['discount']];
  103. }
  104. if(!empty($params['goods_id'])) {
  105. $where[] = ['third_goods_id' => $params['goods_id']];
  106. }
  107. if (!empty($params['goods_type'])) {
  108. $where[] = ['goods_type' => $params['goods_type']];
  109. }
  110. $order = 'id desc';
  111. if (!empty($params['sort']) && !empty($params['sort_type'])) {
  112. switch ($params['sort_type']) {
  113. case 'time':
  114. $order = "created_at {$params['sort']}";
  115. break;
  116. case 'price':
  117. $order = "price {$params['sort']}";
  118. break;
  119. }
  120. }
  121. $ret = QiDingDongGoods::listPage([
  122. 'where' => $where,
  123. 'order' => $order,
  124. 'limit' => $params['limit'] ?? 18
  125. ]);
  126. if (!empty(QiDingDongGoods::getStaticError())) return QiDingDongGoods::getStaticError();
  127. if (!empty($ret['list'])) {
  128. foreach ($ret['list'] as &$item) {
  129. // 供货价,建议零售价,最低售价
  130. // $price = $item['cost_price'];
  131. // $retail_price = $item['price'];
  132. // $item['retail_price'] = $retail_price;
  133. // $item['price'] = $price;
  134. $item['goods_id'] = $item['third_goods_id'];
  135. }
  136. }
  137. return $ret;
  138. }
  139. /**
  140. * 批量导入
  141. * @param int $mall_id
  142. * @param array $goods
  143. * @return string|true
  144. */
  145. public function batchImport(int $mall_id, array $goods)
  146. {
  147. $list = [];
  148. $ids = [];
  149. $t_ids = array_column($goods, 'goods_id');
  150. $t_ids = QiDingDongGoods::find()->where(['mall_id' => $mall_id])
  151. ->andWhere(['status' => [StatusEnum::ENABLED, StatusEnum::DISABLED]])
  152. ->andWhere(['third_goods_id' => $t_ids])
  153. ->select('third_goods_id')
  154. ->column();
  155. foreach ($goods as $good) {
  156. if (in_array($good['goods_id'], $t_ids)) continue;
  157. $list[] = [
  158. 'created_at' => time(),
  159. 'updated_at' => time(),
  160. 'mall_id' => $mall_id,
  161. 'status' => StatusEnum::ENABLED,
  162. 'third_goods_id'=> $good['goods_id'],
  163. 'goods_name' => $good['goods_name'],
  164. 'retail_price' => $good['retail_price'], // 建议零售价
  165. 'price' => $good['price'], // 供货价
  166. 'discount' => ceil(bcdiv($good['price'] * 10, $good['retail_price'], 2)),
  167. 'goods_type' => $good['goods_type'],
  168. 'default_image' => $good['default_image'],
  169. 'lowest_price' => $good['lowest_price'],
  170. ];
  171. $ids[] = $good['goods_id'];
  172. }
  173. try {
  174. if (!empty($list)) {
  175. $t = \Yii::$app->db->beginTransaction();
  176. try {
  177. QiDingDongGoods::find()->createCommand()->batchInsert(QiDingDongGoods::tableName(), array_keys($list[0]), $list)->execute();
  178. QiDingDongGoods::updateAll(['can_sale' => StatusEnum::ENABLED],
  179. ['and', ['third_goods_id' => $ids], ['status' => [StatusEnum::ENABLED, StatusEnum::DISABLED]]]); // 改成可售状态
  180. $query = QiDingDongGoods::find()
  181. ->where(['third_goods_id' => $ids])
  182. ->andWhere(['>', 'goods_id', 0]);
  183. if (!SettingService::isMasterMall($mall_id)) {
  184. $query->andWhere(['mall_id' => $mall_id]);
  185. }
  186. $gids = $query->select('goods_id')->column();
  187. Goods::updateAll(['is_on_sale' => StatusEnum::ENABLED], ['id' => $gids]); // 上架
  188. $t->commit();
  189. } catch (\Exception $e) {
  190. $t->rollBack();
  191. return $e->getMessage();
  192. }
  193. foreach ($ids as $id) {
  194. // \Yii::$app->services->rabbitMq->push(RabbitMqEnum::EXCHANGE_TASK, RabbitMqEnum::TASK_QUEUE, [
  195. // 'mall_id' => $mall_id,
  196. // 'goods_id' => $id
  197. // ], self::class, 'asyncGoodsDetail');
  198. $this->asyncGoodsDetail(['mall_id' => $mall_id, 'goods_id' => $id]);
  199. }
  200. }
  201. } catch (\Exception $e) {
  202. return $e->getMessage();
  203. }
  204. return true;
  205. }
  206. /**
  207. * 同步商品详情
  208. * @param array $data
  209. * @return bool
  210. */
  211. public function asyncGoodsDetail(array $data)
  212. {
  213. try {
  214. $resp = $this->detail($data['mall_id'], $data['goods_id']);
  215. /**
  216. * @var $goods QiDingDongGoods
  217. */
  218. $goods = QiDingDongGoods::getOne([
  219. ['mall_id' => $data['mall_id']],
  220. ['third_goods_id' => $data['goods_id']],
  221. ['status' => [StatusEnum::ENABLED, StatusEnum::DISABLED]],
  222. ]);
  223. if (empty($goods)) throw new \Exception("商品不存在{$data['goods_id']}");
  224. $goods->cate_one = $resp['goods']['cate_id_1'] ?? 0;
  225. $goods->cate_two = $resp['goods']['cate_id_2'] ?? 0;
  226. $goods->content = $resp;
  227. if (!$goods->save()) throw new \Exception($goods->getErrorMessage());
  228. } catch (\Exception $e) {
  229. \Yii::error(FormatHelper::exception($e, __METHOD__));
  230. return false;
  231. }
  232. return true;
  233. }
  234. /**
  235. * 获取商品详情
  236. * @param int $mall_id
  237. * @param int $goods_id
  238. * @return array|int|string
  239. * @throws \Exception
  240. */
  241. public function detail(int $mall_id, int $goods_id)
  242. {
  243. $contentModel = RequestHelper::contentModelInstance($mall_id);
  244. $contentModel->content->data = new GoodsDetailV2Model();
  245. $contentModel->content->data->id = $goods_id;
  246. $contentModel->content->data->device = QiDingDongEnums::REQUEST_PARAMS_DEVICE;
  247. return RequestHelper::send(new GoodsDetailV2Request(), RequestHelper::enCrypto($contentModel, $mall_id), $mall_id);
  248. }
  249. /**
  250. * 删除
  251. * @param int $mall_id
  252. * @param int $id
  253. * @return mixed|string|true|null
  254. */
  255. public function delete(int $mall_id, int $id)
  256. {
  257. $t = \Yii::$app->db->beginTransaction();
  258. try {
  259. /**
  260. * @var $goods QiDingDongGoods
  261. */
  262. $goods = QiDingDongGoods::getOne([
  263. ['mall_id' => $mall_id],
  264. ['id' => $id],
  265. ['status' => [StatusEnum::ENABLED, StatusEnum::DISABLED]],
  266. ]);
  267. if (empty($goods)) throw new \Exception('记录不存在');
  268. $goods->status = StatusEnum::DELETE;
  269. $goods->can_sale = StatusEnum::DISABLED;
  270. if (!$goods->save()) throw new \Exception($goods->getErrorMessage());
  271. // 判断
  272. $ids = [];
  273. if (SettingService::isMasterMall($mall_id)) {
  274. $ids = QiDingDongGoods::find()->where(['status' => [StatusEnum::ENABLED, StatusEnum::DISABLED]])
  275. ->andWhere(['third_goods_id' => $goods->third_goods_id])
  276. ->andWhere(['!=', 'id', $goods->id])->select('goods_id')->column();
  277. QiDingDongGoods::updateAll(['can_sale' => StatusEnum::DISABLED],
  278. ['and', ['third_goods_id' => $goods->third_goods_id], ['status' => [StatusEnum::ENABLED, StatusEnum::DISABLED]]]);
  279. } else {
  280. $ids[] = $goods->id;
  281. }
  282. // 下架商品
  283. if (!empty($ids)) Goods::updateAll(['is_on_sale' => StatusEnum::DISABLED], ['id' => $ids]);
  284. $t->commit();
  285. } catch (\Exception $e) {
  286. $t->rollBack();
  287. return $e->getMessage();
  288. }
  289. return true;
  290. }
  291. /**
  292. * @param array $params
  293. * @return string
  294. */
  295. public function write(array $params)
  296. {
  297. return $this->asyncWriter(['mall_id' => $params['mall_id'], 'id' => $params['id']]);
  298. }
  299. public function asyncWriter(array $params)
  300. {
  301. $mall_id = $params['mall_id'];
  302. $id = $params['id'];
  303. /**
  304. * @var $goods QiDingDongGoods
  305. */
  306. $goods = QiDingDongGoods::getOne([
  307. ['mall_id' => $mall_id],
  308. ['id' => $id],
  309. ['status' => [StatusEnum::ENABLED, StatusEnum::DISABLED]],
  310. ]);
  311. if (empty($goods)) return '记录不存在';
  312. $config = (new SettingService())->get($mall_id);
  313. // 这里写入商品数据
  314. try {
  315. $form = (new GoodsLogic($goods, $config))->execute();
  316. $t = \Yii::$app->db->beginTransaction();
  317. $res = Goods::setData($form);
  318. if (false === $res) throw new \Exception(Goods::getStaticError());
  319. if (!empty($goods->goods_id)) {
  320. QiDingDongGoodsAttr::deleteAll(['goods_id' => $goods->goods_id]);
  321. }
  322. if ($res->is_attr) {
  323. $attrs = GoodsAttr::lists([
  324. 'where' => [
  325. ['goods_id' => $res->id]
  326. ]
  327. ]);
  328. $f_attr = [];
  329. foreach ($form['attr'] as $item) {
  330. $f_attr[$item['goods_no']] = $item;
  331. }
  332. foreach ($attrs as $item) {
  333. QiDingDongGoodsAttr::setData([
  334. 'goods_id' => $res->id,
  335. 'goods_attr_id' => $item['id'],
  336. 'cost_price' => $item['cost_price'],
  337. 'price' => $item['price'],
  338. 'original_price'=> $item['original_price'],
  339. 'third_spec_id' => $item['goods_no'],
  340. 'mall_id' => $goods->mall_id,
  341. 'third_goods_id'=> $goods->third_goods_id,
  342. 'third_price' => $f_attr[$item['goods_no']]['third_price'],
  343. 'retail_price' => $f_attr[$item['goods_no']]['retail_price'],
  344. 'lowest_price' => $f_attr[$item['goods_no']]['lowest_price'],
  345. ]);
  346. }
  347. } else {
  348. /**
  349. * @var $attr GoodsAttr
  350. */
  351. $attr = GoodsAttr::getOne([
  352. ['goods_id' => $res->id],
  353. ], false, [], null, 'id');
  354. // 没有规格的情况下
  355. QiDingDongGoodsAttr::setData([
  356. 'goods_id' => $res->id,
  357. 'goods_attr_id' => $attr->id,
  358. 'cost_price' => $res->cost_price,
  359. 'price' => $res->price,
  360. 'original_price'=> $res->original_price,
  361. 'third_spec_id' => $res->goods_no,
  362. 'mall_id' => $goods->mall_id,
  363. 'third_goods_id'=> $goods->third_goods_id,
  364. 'third_price' => $form['third_price'],
  365. 'retail_price' => $form['retail_price'],
  366. 'lowest_price' => $form['lowest_price'],
  367. ]);
  368. }
  369. if (empty($goods->goods_id)) {
  370. $goods->goods_id = $res->id;
  371. $goods->import_at = time();
  372. }
  373. // 获取配送方式
  374. $contentModel = RequestHelper::contentModelInstance($mall_id);
  375. $contentModel->content->data = new DeliveryMethodModel();
  376. $contentModel->content->data->goods_id = $goods->third_goods_id;
  377. $contentModel->content->data->device = QiDingDongEnums::REQUEST_PARAMS_DEVICE;
  378. $sRes = RequestHelper::send(new DeliveryMethodRequest(), RequestHelper::enCrypto($contentModel, $mall_id), $mall_id);
  379. if (!is_array($sRes)) throw new \Exception($sRes);
  380. $goods->ship_method = $sRes;
  381. if (!$goods->save()) throw new \Exception($goods->getErrorMessage());
  382. $t->commit();
  383. } catch (\Exception $e) {
  384. if (!empty($t)) $t->rollBack();
  385. return $e->getMessage();
  386. }
  387. return true;
  388. }
  389. /**
  390. * @param QiDingDongEvent $event
  391. * @return void
  392. * @throws \Exception
  393. */
  394. public function eventUpdate(QiDingDongEvent &$event)
  395. {
  396. $list = QiDingDongGoods::lists([
  397. 'where' => [
  398. ['third_goods_id' => $event->content['content']['data']['goods_id']],
  399. ['status' => [StatusEnum::ENABLED, StatusEnum::DISABLED]],
  400. ],
  401. 'select'=> 'mall_id, id'
  402. ], false);
  403. if (!empty($list)) {
  404. /**
  405. * @var $goods QiDingDongGoods
  406. */
  407. foreach ($list as $goods) {
  408. if (empty($goods)) throw new \Exception('记录不存在');
  409. $event->mall_id = $goods->mall_id;
  410. \Yii::$app->services->rabbitMq->push(RabbitMqEnum::EXCHANGE_TASK, RabbitMqEnum::TASK_QUEUE, [
  411. 'mall_id' => $goods->mall_id,
  412. 'id' => $goods->id
  413. ], self::class, 'asyncWriter');
  414. }
  415. }
  416. }
  417. /**
  418. * 修改价格
  419. * @param int $mall_id
  420. * @return void
  421. * @throws \Exception
  422. */
  423. public function changePrice(int $mall_id)
  424. {
  425. // $this->asyncChangePrice(['mall_id' => $mall_id]);
  426. \Yii::$app->services->rabbitMq->push(RabbitMqEnum::EXCHANGE_TASK, RabbitMqEnum::TASK_QUEUE, [
  427. 'mall_id' => $mall_id,
  428. ], self::class, 'asyncChangePrice');
  429. }
  430. /**
  431. * 批量修改价格
  432. * @param array $params
  433. * @return void
  434. * @throws \Exception
  435. */
  436. public function asyncChangePrice(array $params)
  437. {
  438. $mall_id = $params['mall_id'];
  439. // $config = (new SettingService())->get($mall_id);
  440. // 取出所有已经绑定的商品
  441. foreach (QiDingDongGoods::find()
  442. ->where(['mall_id' => $mall_id])
  443. ->andWhere(['!=', 'goods_id', 0])
  444. ->andWhere(['status' => [StatusEnum::ENABLED, StatusEnum::DISABLED]])
  445. ->select('id, goods_id, retail_price, price, lowest_price')->each() as $each) {
  446. $data = ['mall_id' => $mall_id, 'id' => $each->goods_id, 'price' => $each->price,
  447. 'retail_price' => $each->retail_price, 'lowest_price' => $each->lowest_price];
  448. \Yii::$app->services->rabbitMq->push(RabbitMqEnum::EXCHANGE_TASK, RabbitMqEnum::TASK_QUEUE,
  449. $data, self::class, 'asyncChangeGoodsPrice');
  450. // $this->asyncChangeGoodsPrice($data);
  451. }
  452. }
  453. /**
  454. * 修改价格
  455. * @param array $params
  456. * @return bool
  457. */
  458. public function asyncChangeGoodsPrice(array $params)
  459. {
  460. $mall_id = $params['mall_id'];
  461. $config = (new SettingService())->get($mall_id);
  462. $prices = !empty($config['price']) ? Json::decode($config['price']) : [];
  463. $t = \Yii::$app->db->beginTransaction();
  464. try {
  465. // 更新商品ID
  466. $price = SettingService::getPrice($mall_id, SettingService::PRICE, $params, $prices);
  467. $cost_price = SettingService::getPrice($mall_id, SettingService::COST_PRICE, $params, $prices);
  468. $original_price = SettingService::getPrice($mall_id, SettingService::ORIGINAL_PRICE, $params, $prices);
  469. Goods::updateAll(['price' => $price, 'cost_price' => $cost_price, 'original_price' => $original_price], ['id' => $params['id']]);
  470. $attrs = QiDingDongGoodsAttr::lists([
  471. 'where' => [
  472. ['goods_id' => $params['id']]
  473. ],
  474. 'select'=> 'goods_attr_id, id, price, cost_price, original_price, lowest_price, retail_price, third_price'
  475. ]);
  476. foreach ($attrs as $attr) {
  477. $attr['price'] = $attr['third_price'];
  478. $price = SettingService::getPrice($mall_id, SettingService::PRICE, $attr, $prices);
  479. $cost_price = SettingService::getPrice($mall_id, SettingService::COST_PRICE, $attr, $prices);
  480. $original_price = SettingService::getPrice($mall_id, SettingService::ORIGINAL_PRICE, $attr, $prices);
  481. GoodsAttr::updateAll(['price' => $price, 'cost_price' => $cost_price, 'original_price' => $original_price], ['id' => $attr['goods_attr_id']]);
  482. }
  483. $t->commit();
  484. } catch (\Exception $e) {
  485. $t->rollBack();
  486. \Yii::error(FormatHelper::exception($e, __METHOD__));
  487. return false;
  488. }
  489. return true;
  490. }
  491. }