CartController.php 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409
  1. <?php
  2. namespace api\modules\v1\controllers\goods;
  3. use addons\Agent\common\models\AgentCommissionItemSetting;
  4. use addons\Mch\common\models\Mch;
  5. use addons\Store\common\models\StoreCart;
  6. use api\controllers\OnAuthController;
  7. use common\enums\GoodsTypeEnum;
  8. use common\enums\StatusEnum;
  9. use common\globals\GlobalInvoke;
  10. use common\events\census\EditCartEvent;
  11. use common\helpers\ApiCode;
  12. use common\models\cart\Cart;
  13. use common\models\goods\Goods;
  14. use common\models\goods\GoodsAttr;
  15. use common\models\mall\MallAddons;
  16. use common\models\user\UserLevel;
  17. use common\enums\AddonsEnum;
  18. use common\enums\CartEnum;
  19. use yii\helpers\Json;
  20. /**
  21. * Class CartController
  22. * @package api\modules\goods\controllers
  23. */
  24. class CartController extends OnAuthController
  25. {
  26. public $modelClass = '';
  27. /**
  28. *
  29. * 不用进行登录验证的方法
  30. * 例如: ['index', 'update', 'create', 'view', 'delete']
  31. * 默认全部需要验证
  32. *
  33. * @var array
  34. */
  35. protected $authOptional = ['index', 'get-cart-type-list'];
  36. /**
  37. * 添加购物车
  38. * @return mixed|void
  39. */
  40. public function actionAdd()
  41. {
  42. try {
  43. $params = \Yii::$app->request->post();
  44. $res = \Yii::$app->services->validate->validate($params, [
  45. [['goods_id', 'attr_id', 'num'], 'required'],
  46. ]);
  47. if ($res === false) return $this->error(\Yii::$app->services->validate->getErrorMessage());
  48. $goods = Goods::findOne(['id' => $params['goods_id'], 'status' => StatusEnum::ENABLED, 'is_on_sale' => 1]);
  49. if (empty($goods)) return $this->error("商品不存在");
  50. if(isset($goods->is_pay_limit)&& $goods->is_pay_limit)
  51. {
  52. return $this->error("该商品限制支付,不允许加入购物车");
  53. }
  54. // 处方药不能加入购物车
  55. if ($goods->goods_subtype == GoodsTypeEnum::GOODS_SUBTYPE_PRESCRIPTION) {
  56. return $this->error("处方药商品不允许加入购物车");
  57. }
  58. $member_buy_limit_setting = json_decode($goods->extra['member_buy_limit_setting'], true);
  59. $params['buy_limit_nums'] = $member_buy_limit_setting['nums'] ?? 0;
  60. $params['buy_limit_total_num'] = $member_buy_limit_setting['total_num'] ?? 0;
  61. // 没有规格id则查找默认规格【寻找价格最低的规格】
  62. $where = ['goods_id' => $params['goods_id'], 'status' => StatusEnum::ENABLED];
  63. !empty($params['attr_id']) && $where['id'] = $params['attr_id'];
  64. $goods_attr = GoodsAttr::find()->where($where)->orderBy('price asc')->one();
  65. if ($goods_attr->goods_id != $params['goods_id']) return $this->error("goods_id与attr_id不配对");
  66. if (empty($goods->is_attr)) {
  67. if (empty($goods->stock)) return $this->error('库存不足');
  68. } else {
  69. //多规格
  70. if (empty($goods_attr)) return $this->error("所选的规格不存在");
  71. if (empty($goods_attr->stock)) return $this->error("所选的规格库存不足");
  72. }
  73. // 公共调用
  74. $res = GlobalInvoke::exec(GlobalInvoke::CART_ADD, $this->mall_id, [
  75. 'goods' => $goods,
  76. 'mall_id' => $this->mall_id,
  77. ]);
  78. if (!empty($res) && $res['cart'] == false) {
  79. return $this->error($res['message']);
  80. }
  81. $params['mch_id'] = $goods->mch_id;
  82. $params['mall_id'] = $this->mall_id;
  83. $params['user_id'] = $this->user_id;
  84. Cart::addCart($params);
  85. $event = new EditCartEvent($this->mall_id);
  86. $data = ['num' => $params['num'], 'goods_id' => $params['goods_id'], 'mall_id' => $this->mall_id];
  87. \Yii::$app->services->events->dispatch($event, $data, $event->listeners);
  88. return $this->success('加入购物车成功', []);
  89. } catch (\Exception $e) {
  90. return $this->error($e->getMessage(), []);
  91. }
  92. }
  93. /**
  94. * 删除购物车
  95. * @return mixed|void
  96. */
  97. public function actionDel()
  98. {
  99. $params = \Yii::$app->request->post();
  100. $res = \Yii::$app->services->validate->validate($params, [
  101. [['list'], 'required'],
  102. ]);
  103. if ($res === false) return $this->error(\Yii::$app->services->validate->getErrorMessage());
  104. $params['mall_id'] = $this->mall_id;
  105. $params['user_id'] = $this->user_id;
  106. Cart::delCart($params);
  107. return $this->success('操作成功', []);
  108. }
  109. /**
  110. * 购物车列表
  111. * @return mixed|void
  112. */
  113. public function actionIndex()
  114. {
  115. $get = \Yii::$app->request->get();
  116. if (empty($this->user_id)) return $this->success('请登录');
  117. $res = \Yii::$app->services->validate->validate($get, [
  118. [['location_style'], 'safe'],
  119. ]);
  120. if ($res === false) return $this->error(\Yii::$app->services->validate->getErrorMessage());
  121. $list = Cart::getCartList($this->user_id);
  122. $attr_ids = $this->attrIdsByDesc($list);
  123. $data = [];
  124. if (!empty($list)) {
  125. $attr_id_arr = [];
  126. $num_arr = [];
  127. foreach ($list as $v) {
  128. $row = json_decode($v, true);
  129. if (is_array($row)) {
  130. $attr_id_arr[] = $row['attr_id'];
  131. $num_arr[$row['attr_id']] = $row['num'];
  132. }
  133. }
  134. $params['where'] = [['id' => $attr_id_arr]];
  135. $params['with'] = ['goods' => function ($query) {
  136. $query->select('id,goods_name,mch_id,is_on_sale,cover_pic,is_attr,goods_source,status')->with(['extra']);
  137. }];
  138. $params['select'] = 'id,sign_id,goods_id,pic_url,price,name';
  139. $list = GoodsAttr::lists($params);
  140. // 按顺序重组
  141. $list = $this->listSortByAttrIds($list, $attr_ids);
  142. $rows = [];
  143. $user = $this->user;
  144. $user_level = UserLevel::findOne(['mall_id' => $this->mall_id, 'level' => $user['level'], 'status' => StatusEnum::ENABLED]);
  145. $discount = 10;
  146. if (isset($user_level)) $discount = $user_level['discount'];
  147. $goods_deduct_arr = [];
  148. if(MallAddons::isInstall($this->mall_id,AddonsEnum::ADDONS_NAME_AGENT)){
  149. $setting = \Yii::$app->services->setting->addons->get($this->mall_id, AddonsEnum::ADDONS_NAME_AGENT, 'basic');
  150. if (!empty($setting['is_self_buy']) && !empty($setting['enable_goods_deduct'])) {
  151. $goods_deduct_arr = \common\logic\order\marketing\AgentMarketing::getDeductMoney($this->mall_id,$this->user_id,$num_arr);
  152. }
  153. }
  154. if (!empty($list)) {
  155. foreach ($list as $v) {
  156. $member_price_setting = Json::decode($v['goods']['extra']['member_price_setting']);
  157. $v['member_price'] = Goods::computeMemberPrice($v['price'],$v['sign_id'],$user['level'],$v['goods']['is_attr'],
  158. $v['goods']['extra']['is_member_price'],$member_price_setting,$discount);
  159. if (!empty($v['goods']['extra']['is_member_price']) && !empty($v['extra']['member_price_setting'])) {
  160. $member_price_setting = $v['extra']['member_price_setting'];
  161. if ($v['goods']['is_attr']) {
  162. if (!empty($member_price_setting['level' . $user['level']][$v['sign_id']])) $v['price'] = $member_price_setting['level' . $user['level']][$v['sign_id']];
  163. } else {
  164. if (!empty($member_price_setting[$user['level']])) $v['price'] = $member_price_setting[$user['level']];
  165. }
  166. }
  167. // 最低起购
  168. $member_buy_limit_setting = isset($v['goods']['extra']['member_buy_limit_setting'])
  169. ? json_decode($v['goods']['extra']['member_buy_limit_setting'], true)
  170. : [];
  171. $member_buy_limit_num = $member_buy_limit_setting['nums'] ?? 0;
  172. $member_price = $v['member_price'];
  173. // 渠道分红内购会员价特殊计算方式
  174. if (isset($goods_deduct_arr[$v['id']]) && $goods_deduct_arr[$v['id']]) {
  175. // 如果内购返回价格,并且价格不为0,则取内购价格
  176. $member_price = bcdiv($goods_deduct_arr[$v['id']], $num_arr[$v['id']], 2);
  177. } elseif (isset($goods_deduct_arr[$v['id']]) && ($goods_deduct_arr[$v['id']] == 0)) {
  178. // 或者内购返回价格等于0,则取商品价格
  179. $agent_item = AgentCommissionItemSetting::find()
  180. ->select('item_id, is_participate, is_self_buy_alone, is_self_buy, enable_goods_deduct')
  181. ->where(['item_id' => $v['goods_id'], 'status' => StatusEnum::ENABLED])
  182. ->asArray()
  183. ->one();
  184. $member_price = $v['price'];
  185. if (!empty($agent_item) && empty($agent_item['is_participate'])) {
  186. $member_price = $v['member_price'];
  187. }
  188. if (!empty($agent_item['is_self_buy_alone'])) {
  189. if (empty($agent_item['is_self_buy']) || empty($agent_item['enable_goods_deduct'])) {
  190. $member_price = $v['member_price'];
  191. }
  192. }
  193. }
  194. $rows[$v['goods']['mch_id']][] = [
  195. 'goods_id' => $v['goods_id'],
  196. 'num' => $num_arr[$v['id']],
  197. 'attr_id' => $v['id'],
  198. 'price' => $v['price'],
  199. 'member_price' => $member_price,
  200. 'cover_pic' => $v['goods']['cover_pic']??'',
  201. 'attr' => $v['name'],
  202. 'goods_name' => $v['goods']['goods_name'],
  203. 'is_on_sale' => $v['goods']['status'] < StatusEnum::ENABLED ? StatusEnum::DISABLED : $v['goods']['is_on_sale'],
  204. 'goods_source' => $v['goods']['goods_source'],
  205. 'member_buy_limit_num' => $member_buy_limit_num,
  206. ];
  207. }
  208. }
  209. $mall_name = \Yii::$app->services->setting->mall->get($this->mall_id, 'basic.name');
  210. $mall_logo = \Yii::$app->services->setting->mall->get($this->mall_id, 'basic.logo');
  211. $mch_arr = [
  212. ['id' => 0, 'mch_pic' => $mall_logo, 'mch_name' => $mall_name, 'list' => []]
  213. ];
  214. //多商户
  215. if (MallAddons::isInstall($this->mall_id, AddonsEnum::ADDONS_NAME_MCH)) {
  216. $mch_ids = [];
  217. foreach ($list as $item) {
  218. if($item['goods']['mch_id'])$mch_ids[] = $item['goods']['mch_id'];
  219. }
  220. if(!empty($mch_ids)){
  221. $mch_list = Mch::lists(['where' => [['id' => $mch_ids]], 'select' => 'id,logo_img as mch_pic,store_name as mch_name'], true);
  222. if($mch_list)$mch_arr = array_merge($mch_arr, $mch_list);
  223. }
  224. }
  225. $data = [];
  226. foreach ($mch_arr as $key => $val) {
  227. if (isset($rows[$val['id']])) {
  228. $data[]=[
  229. 'id'=>$val['id'],
  230. 'mch_name'=>$val['mch_name'],
  231. 'mch_pic'=>$val['mch_pic'],
  232. 'list'=>$rows[$val['id']],
  233. ];
  234. } else {
  235. unset($mch_arr[$key]);
  236. }
  237. }
  238. $new_data = GlobalInvoke::exec(GlobalInvoke::CART_LIST_APPEND_FILEDS, $this->mall_id, ['mall_id' => $this->mall_id, 'location_style'=> $get['location_style'] ?? 1, 'user_id' => $this->user_id, 'data' => $data]);
  239. $data = $new_data ? $new_data : $data;
  240. }
  241. return $this->success('操作成功', $data);
  242. }
  243. public function actionUpdateCart()
  244. {
  245. try {
  246. $params = \Yii::$app->request->post();
  247. $res = \Yii::$app->services->validate->validate($params, [
  248. [['goods_id', 'num'], 'required'],
  249. [['attr_id'], 'safe'],
  250. ]);
  251. if ($res === false) return $this->error(\Yii::$app->services->validate->getErrorMessage());
  252. $goods = Goods::findOne(['id' => $params['goods_id'], 'status' => StatusEnum::ENABLED, 'is_on_sale' => 1]);
  253. if (empty($goods)) return $this->error("商品不存在");
  254. $member_buy_limit_setting = json_decode($goods->extra['member_buy_limit_setting'], true);
  255. $params['buy_limit_nums'] = $member_buy_limit_setting['nums'] ?? 0;
  256. $params['buy_limit_total_num'] = $member_buy_limit_setting['total_num'] ?? 0;
  257. // 最低购买
  258. if ($params['num'] < $params['buy_limit_nums']) {
  259. return $this->error(sprintf('该商品最低购买数量为%s', $params['buy_limit_nums']));
  260. }
  261. // 没有规格id则查找默认规格【寻找价格最低的规格】
  262. $where = ['goods_id' => $params['goods_id'], 'status' => StatusEnum::ENABLED];
  263. !empty($params['attr_id']) && $where['id'] = $params['attr_id'];
  264. $goods_attr = GoodsAttr::find()->where($where)->orderBy('price asc')->one();
  265. if ($goods_attr->goods_id != $params['goods_id']) return $this->error("goods_id与attr_id不配对");
  266. if (empty($goods->is_attr)) {
  267. if (empty($goods->stock)) return $this->error('库存不足');
  268. } else {
  269. //多规格
  270. if (empty($goods_attr)) return $this->error("所选的规格不存在");
  271. if (empty($goods_attr->stock)) return $this->error("所选的规格库存不足");
  272. }
  273. // 公共调用
  274. $res = GlobalInvoke::exec(GlobalInvoke::CART_ADD, $this->mall_id, [
  275. 'goods' => $goods,
  276. 'mall_id' => $this->mall_id,
  277. ]);
  278. if (!empty($res) && $res['cart'] == false) {
  279. return $this->error($res['message']);
  280. }
  281. $params['mch_id'] = $goods->mch_id;
  282. $params['mall_id'] = $this->mall_id;
  283. $params['user_id'] = $this->user_id;
  284. Cart::updateCart($params);
  285. $event = new EditCartEvent($this->mall_id);
  286. $data = ['num' => $params['num'], 'goods_id' => $params['goods_id'], 'mall_id' => $this->mall_id];
  287. \Yii::$app->services->events->dispatch($event, $data, $event->listeners);
  288. return $this->success('更新购物车成功', []);
  289. } catch (\Exception $e) {
  290. return $this->error($e->getMessage(), []);
  291. }
  292. }
  293. /**
  294. * 获取排序后的规格ID
  295. *
  296. * @param array $list
  297. * @return array
  298. */
  299. protected function attrIdsByDesc($list)
  300. {
  301. $attr_id = [];
  302. if (empty($list)) return $attr_id;
  303. // 转义JSON字符串
  304. $list = array_filter(array_map(function ($item) {
  305. $arr = Json::decode($item);
  306. // 兼容旧程序没有microtime
  307. if (is_array($arr) && empty($arr['microtime'])) {
  308. $arr['microtime'] = 0;
  309. }
  310. return $arr;
  311. }, $list), function ($item) {
  312. return is_array($item);
  313. });
  314. // 按时间排序
  315. array_multisort(array_column($list, 'microtime'), SORT_DESC, $list);
  316. // 获取attr_id
  317. return array_column($list, 'attr_id');
  318. }
  319. /**
  320. * 通过AttrIds重新排序
  321. *
  322. * @param array $list
  323. * @param array $attr_ids
  324. * @return array
  325. */
  326. protected function listSortByAttrIds($list, $attr_ids)
  327. {
  328. $new_list = [];
  329. if (empty($list)) return $new_list;
  330. // 获取已经排序的规格ID
  331. foreach ($attr_ids as $attr_id) {
  332. foreach ($list as $item) {
  333. if ($item['id'] == $attr_id) {
  334. $new_list[] = $item;
  335. }
  336. }
  337. }
  338. return $new_list;
  339. }
  340. //获取要展示的商品购物车类型列表
  341. public function actionGetCartTypeList()
  342. {
  343. if (\Yii::$app->request->isGET) {
  344. // $get = \Yii::$app->request->get();
  345. $config = \Yii::$app->services->setting->mall->get($this->mall_id, 'cart');
  346. if (!empty($config['cart_type'])) {
  347. $config['cart_type'] = json_decode($config['cart_type'],true);
  348. } else {
  349. if (!isset($config['cart_type'])) {
  350. $config['cart_type'][] = CartEnum::MALL_CART;
  351. }
  352. }
  353. if (!empty($config['cart_name'])) {
  354. $config['cart_name'] = json_decode($config['cart_name'],true);
  355. }
  356. if (!MallAddons::isInstall($this->mall_id, AddonsEnum::ADDONS_NAME_STORE)) {
  357. foreach ($config['cart_type'] as $key => $value) {
  358. if ($value == CartEnum::STORE_MALL_CART) {
  359. unset($config['cart_type'][$key]);
  360. }
  361. }
  362. }
  363. sort($config['cart_type']); //整理顺序
  364. $cart_num = [];
  365. $cart_type = CartEnum::getMap();
  366. foreach ($config['cart_type'] as $k => $val) {
  367. switch ($val) {
  368. case 1:
  369. $list = Cart::getCartList($this->user_id);
  370. break;
  371. case 2:
  372. $list = StoreCart::getCartList($this->user_id);
  373. break;
  374. }
  375. if (!empty($config['cart_name']) && $config['cart_name'][$val]['is_show'] == 1) {
  376. $cart_type[$val] = $config['cart_name'][$val]['name'];
  377. }
  378. $ids = $this->attrIdsByDesc($list);
  379. $cart_num[$val] = count($ids);
  380. }
  381. $this->success('获取成功',['list' => $config['cart_type'], 'cart_num' => $cart_num, 'type' => $cart_type]);
  382. }
  383. }
  384. }