StoreCartServices.php 72 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
  4. // +----------------------------------------------------------------------
  5. // | Copyright (c) 2016~2020 https://www.crmeb.com All rights reserved.
  6. // +----------------------------------------------------------------------
  7. // | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
  8. // +----------------------------------------------------------------------
  9. // | Author: CRMEB Team <admin@crmeb.com>
  10. // +----------------------------------------------------------------------
  11. declare (strict_types=1);
  12. namespace app\services\order;
  13. use app\dao\order\StoreCartDao;
  14. use app\services\activity\discounts\StoreDiscountsProductsServices;
  15. use app\services\activity\bargain\StoreBargainServices;
  16. use app\services\activity\combination\StoreCombinationServices;
  17. use app\services\activity\integral\StoreIntegralServices;
  18. use app\services\activity\lottery\LuckLotteryRecordServices;
  19. use app\services\activity\newcomer\StoreNewcomerServices;
  20. use app\services\activity\promotions\StorePromotionsServices;
  21. use app\services\activity\seckill\StoreSeckillServices;
  22. use app\services\BaseServices;
  23. use app\services\other\CityAreaServices;
  24. use app\services\product\branch\StoreBranchProductServices;
  25. use app\services\product\product\StoreProductServices;
  26. use app\services\product\sku\StoreProductAttrValueServices;
  27. use app\services\product\shipping\ShippingTemplatesServices;
  28. use app\services\product\shipping\ShippingTemplatesNoDeliveryServices;
  29. use app\services\store\SystemStoreServices;
  30. use app\services\user\level\SystemUserLevelServices;
  31. use app\services\user\member\MemberCardServices;
  32. use app\services\user\UserAddressServices;
  33. use app\services\user\UserServices;
  34. use app\jobs\product\ProductLogJob;
  35. use crmeb\services\CacheService;
  36. use crmeb\traits\OptionTrait;
  37. use crmeb\traits\ServicesTrait;
  38. use think\exception\ValidateException;
  39. /**
  40. *
  41. * Class StoreCartServices
  42. * @package app\services\order
  43. * @mixin StoreCartDao
  44. */
  45. class StoreCartServices extends BaseServices
  46. {
  47. use OptionTrait, ServicesTrait;
  48. //库存字段比对
  49. const STOCK_FIELD = 'sum_stock';
  50. //购物车最大数量
  51. protected $maxCartNum = 100;
  52. /**
  53. * StoreCartServices constructor.
  54. * @param StoreCartDao $dao
  55. */
  56. public function __construct(StoreCartDao $dao)
  57. {
  58. $this->dao = $dao;
  59. }
  60. /**
  61. * 获取某个用户下的购物车数量
  62. * @param array $unique
  63. * @param int $productId
  64. * @param int $uid
  65. * @param string $userKey
  66. * @return array
  67. */
  68. public function getUserCartNums(array $unique, int $productId, int $uid, string $userKey = 'uid')
  69. {
  70. $where['is_pay'] = 0;
  71. $where['is_del'] = 0;
  72. $where['is_new'] = 0;
  73. $where['product_id'] = $productId;
  74. $where[$userKey] = $uid;
  75. return $this->dao->getUserCartNums($where, $unique);
  76. }
  77. /**
  78. * 计算首单优惠
  79. * @param int $uid
  80. * @param array $cartInfo
  81. * @param array $newcomerArr
  82. * @return array
  83. */
  84. public function computedFirstDiscount(int $uid, array $cartInfo, array $newcomerArr = [])
  85. {
  86. $first_order_price = $first_discount = $first_discount_limit = 0;
  87. if ($uid && $cartInfo) {
  88. if (!$newcomerArr) {
  89. /** @var StoreNewcomerServices $newcomerServices */
  90. $newcomerServices = app()->make(StoreNewcomerServices::class);
  91. $newcomerArr = $newcomerServices->checkUserFirstDiscount($uid);
  92. }
  93. if ($newcomerArr) {//首单优惠
  94. [$first_discount, $first_discount_limit] = $newcomerArr;
  95. /** @var StoreOrderComputedServices $orderServices */
  96. $orderServices = app()->make(StoreOrderComputedServices::class);
  97. $totalPrice = $orderServices->getOrderSumPrice($cartInfo, 'truePrice');//获取订单svip、用户等级优惠之后总金额
  98. $first_discount = bcsub('1', (string)bcdiv($first_discount, '100', 2), 2);
  99. $first_order_price = (float)bcmul((string)$totalPrice, (string)$first_discount, 2);
  100. $first_order_price = min($first_order_price, $first_discount_limit, $totalPrice);
  101. }
  102. }
  103. return [$cartInfo, $first_order_price, $first_discount, $first_discount_limit];
  104. }
  105. /**
  106. * 获取用户下的购物车列表
  107. * @param int $uid
  108. * @param $cartIds
  109. * @param bool $new
  110. * @param array $addr
  111. * @param int $shipping_type
  112. * @param int $store_id
  113. * @param int $coupon_id
  114. * @param bool $isCart
  115. * @return array
  116. * @throws \Psr\SimpleCache\InvalidArgumentException
  117. * @throws \think\db\exception\DataNotFoundException
  118. * @throws \think\db\exception\DbException
  119. * @throws \think\db\exception\ModelNotFoundException
  120. */
  121. public function getUserProductCartListV1(int $uid, $cartIds, bool $new, array $addr = [], int $shipping_type = 1, int $store_id = 0, int $coupon_id = 0, bool $isCart = false)
  122. {
  123. if ($new) {
  124. $cartIds = $cartIds && is_string($cartIds) ? explode(',', $cartIds) : (is_array($cartIds) ? $cartIds : []);
  125. $cartInfo = [];
  126. if ($cartIds) {
  127. foreach ($cartIds as $key) {
  128. $info = CacheService::redisHandler()->get($key);
  129. if ($info) {
  130. $cartInfo[] = $info;
  131. }
  132. }
  133. }
  134. } else {
  135. $cartInfo = $this->dao->getCartList(['uid' => $uid, 'status' => 1, 'id' => $cartIds], 0, 0, ['productInfo', 'attrInfo']);
  136. }
  137. if (!$cartInfo) {
  138. throw new ValidateException('获取购物车信息失败');
  139. }
  140. foreach ($cartInfo as $cart) {
  141. //检查限购
  142. if (isset($cart['type']) && $cart['type'] != 8) {
  143. $this->checkLimit($uid, $cart['product_id'] ?? 0, $cart['cart_num'] ?? 1, true, $store_id);
  144. }
  145. }
  146. [$cartInfo, $valid, $invalid] = $this->handleCartList($uid, $cartInfo, $addr, $shipping_type, $store_id);
  147. $type = array_unique(array_column($cartInfo, 'type'));
  148. $product_type = array_unique(array_column($cartInfo, 'product_type'));
  149. $activity_id = array_unique(array_column($cartInfo, 'activity_id'));
  150. $collate_code_id = array_unique(array_column($cartInfo, 'collate_code_id'));
  151. $deduction = ['product_type' => $product_type[0] ?? 0, 'type' => $type[0] ?? 0, 'activity_id' => $activity_id[0] ?? 0, 'collate_code_id' => $collate_code_id[0] ?? 0];
  152. $promotions = $giveCoupon = $giveCartList = $useCoupon = $giveProduct = [];
  153. $giveIntegral = $couponPrice = $firstOrderPrice = 0;
  154. if (!$deduction['activity_id'] && $deduction['type'] != 6) {
  155. /** @var StoreNewcomerServices $newcomerServices */
  156. $newcomerServices = app()->make(StoreNewcomerServices::class);
  157. $newcomerArr = $newcomerServices->checkUserFirstDiscount($uid);
  158. if ($newcomerArr) {//首单优惠
  159. //计算首单优惠
  160. [$valid, $firstOrderPrice, $first_discount, $first_discount_limit] = $this->computedFirstDiscount($uid, $valid, $newcomerArr);
  161. } else {
  162. /** @var StorePromotionsServices $storePromotionsServices */
  163. $storePromotionsServices = app()->make(StorePromotionsServices::class);
  164. //计算相关优惠活动
  165. [$valid, $couponPrice, $useCoupon, $promotions, $giveIntegral, $giveCoupon, $giveCartList] = $storePromotionsServices->computedPromotions($uid, $valid, $store_id, $coupon_id, $isCart);
  166. if ($giveCartList) {
  167. foreach ($giveCartList as $key => $give) {
  168. $giveProduct[] = [
  169. 'promotions_id' => $give['promotions_id'][0] ?? 0,
  170. 'product_id' => $give['product_id'] ?? 0,
  171. 'unique' => $give['product_attr_unique'] ?? '',
  172. 'cart_num' => $give['cart_num'] ?? 1,
  173. ];
  174. }
  175. }
  176. }
  177. }
  178. return compact('cartInfo', 'valid', 'invalid', 'deduction', 'couponPrice', 'useCoupon', 'promotions', 'giveCartList', 'giveIntegral', 'giveCoupon', 'giveProduct', 'firstOrderPrice');
  179. }
  180. /**
  181. * 验证库存
  182. * @param int $uid
  183. * @param int $productId
  184. * @param int $cartNum
  185. * @param int $store_id
  186. * @param string $unique
  187. * @param bool $new
  188. * @param int $type
  189. * @param int $activity_id
  190. * @param int $discount_product_id
  191. * @return array
  192. * @throws \think\db\exception\DataNotFoundException
  193. * @throws \think\db\exception\DbException
  194. * @throws \think\db\exception\ModelNotFoundException
  195. */
  196. public function checkProductStock(int $uid, int $productId, int $cartNum = 1, int $store_id = 0, string $unique = '', bool $new = false, int $type = 0, int $activity_id = 0, $discount_product_id = 0, $sum_cart_num = 0)
  197. {
  198. //验证限量
  199. $this->checkLimit($uid, $productId, $cartNum, $new, $store_id);
  200. /** @var StoreProductAttrValueServices $attrValueServices */
  201. $attrValueServices = app()->make(StoreProductAttrValueServices::class);
  202. $isSet = $this->getItem('is_set', 0);
  203. switch ($type) {
  204. case 0://普通
  205. if ($unique == '') {
  206. $unique = $attrValueServices->value(['product_id' => $productId, 'type' => 0], 'unique');
  207. }
  208. /** @var StoreProductServices $productServices */
  209. $productServices = app()->make(StoreProductServices::class);
  210. $productInfo = $productServices->isValidProduct($productId);
  211. if (!$productInfo) {
  212. throw new ValidateException('该商品已下架或删除');
  213. }
  214. if ($productInfo['is_vip_product']) {
  215. /** @var UserServices $userServices */
  216. $userServices = app()->make(UserServices::class);
  217. $is_vip = $userServices->value(['uid' => $uid], 'is_money_level');
  218. if (!$is_vip) {
  219. throw new ValidateException('该商品为付费会员专享商品');
  220. }
  221. }
  222. //预售商品
  223. if ($productInfo['is_presale_product']) {
  224. if ($productInfo['presale_start_time'] > time()) throw new ValidateException('预售活动未开始');
  225. if ($productInfo['presale_end_time'] < time()) throw new ValidateException('预售活动已结束');
  226. }
  227. $attrInfo = $attrValueServices->getOne(['unique' => $unique, 'type' => 0]);
  228. if (!$unique || !$attrInfo || $attrInfo['product_id'] != $productId) {
  229. throw new ValidateException('请选择有效的商品属性');
  230. }
  231. $nowStock = $attrInfo['stock'];//现有平台库存
  232. if ($cartNum > $nowStock) {
  233. throw new ValidateException('该商品库存不足' . $cartNum);
  234. }
  235. //直接设置购物车商品数量
  236. if ($isSet) {
  237. $stockNum = 0;
  238. } else {
  239. $stockNum = $this->dao->value(['product_id' => $productId, 'product_attr_unique' => $unique, 'uid' => $uid, 'status' => 1, 'store_id' => $store_id], 'cart_num') ?: 0;
  240. }
  241. if ($nowStock < ($cartNum + $stockNum)) {
  242. if ($store_id) {
  243. throw new ValidateException('该商品库存不足');
  244. }
  245. $surplusStock = $nowStock - $cartNum;//剩余库存
  246. if ($surplusStock < $stockNum) {
  247. $this->dao->update(['product_id' => $productId, 'product_attr_unique' => $unique, 'uid' => $uid, 'status' => 1, 'store_id' => $store_id], ['cart_num' => $surplusStock]);
  248. }
  249. }
  250. break;
  251. case 1://秒杀
  252. /** @var StoreSeckillServices $seckillService */
  253. $seckillService = app()->make(StoreSeckillServices::class);
  254. [$attrInfo, $unique, $productInfo] = $seckillService->checkSeckillStock($uid, $activity_id, $cartNum, $store_id, $unique);
  255. break;
  256. case 2://砍价
  257. /** @var StoreBargainServices $bargainService */
  258. $bargainService = app()->make(StoreBargainServices::class);
  259. [$attrInfo, $unique, $productInfo, $bargainUserInfo] = $bargainService->checkBargainStock($uid, $activity_id, $cartNum, $unique);
  260. break;
  261. case 3://拼团
  262. /** @var StoreCombinationServices $combinationService */
  263. $combinationService = app()->make(StoreCombinationServices::class);
  264. [$attrInfo, $unique, $productInfo] = $combinationService->checkCombinationStock($uid, $activity_id, $cartNum, $unique);
  265. break;
  266. case 4://积分
  267. /** @var StoreIntegralServices $storeIntegralServices */
  268. $storeIntegralServices = app()->make(StoreIntegralServices::class);
  269. [$attrInfo, $unique, $productInfo] = $storeIntegralServices->checkoutProductStock($uid, $activity_id, $cartNum, $unique);
  270. break;
  271. case 5://套餐
  272. /** @var StoreDiscountsProductsServices $discountProduct */
  273. $discountProduct = app()->make(StoreDiscountsProductsServices::class);
  274. [$attrInfo, $unique, $productInfo] = $discountProduct->checkDiscountsStock($uid, $discount_product_id, $cartNum, $unique);
  275. break;
  276. case 7://新人专享
  277. if ($cartNum > 1) {
  278. throw new ValidateException('新人专享商品限购一件');
  279. }
  280. /** @var StoreNewcomerServices $newcomerServices */
  281. $newcomerServices = app()->make(StoreNewcomerServices::class);
  282. [$attrInfo, $unique, $productInfo] = $newcomerServices->checkNewcomerStock($uid, $activity_id, $cartNum, $unique);
  283. break;
  284. case 8://抽奖
  285. if (!$activity_id) {
  286. throw new ValidateException('缺少中奖信息,请返回刷新重试');
  287. }
  288. /** @var LuckLotteryRecordServices $luckRecordServices */
  289. $luckRecordServices = app()->make(LuckLotteryRecordServices::class);
  290. $record = $luckRecordServices->get($activity_id);
  291. if (!$record) {
  292. throw new ValidateException('缺少中奖信息,请返回刷新重试');
  293. }
  294. if ($record['oid']) {
  295. throw new ValidateException('已经领取成功,不要重复领取');
  296. }
  297. if ($unique == '') {
  298. $unique = $attrValueServices->value(['product_id' => $productId, 'type' => 0], 'unique');
  299. }
  300. /** @var StoreProductServices $productServices */
  301. $productServices = app()->make(StoreProductServices::class);
  302. $productInfo = $productServices->isValidProduct($productId);
  303. if (!$productInfo) {
  304. throw new ValidateException('该商品已下架或删除');
  305. }
  306. $attrInfo = $attrValueServices->getOne(['unique' => $unique, 'type' => 0]);
  307. if (!$unique || !$attrInfo || $attrInfo['product_id'] != $productId) {
  308. throw new ValidateException('请选择有效的商品属性');
  309. }
  310. $nowStock = $attrInfo['stock'];//现有平台库存
  311. if ($cartNum > $nowStock) {
  312. throw new ValidateException('该商品库存不足' . $cartNum);
  313. }
  314. break;
  315. case 9://拼单
  316. if ($unique == '') {
  317. $unique = $attrValueServices->value(['product_id' => $productId, 'type' => 0], 'unique');
  318. }
  319. /** @var StoreProductServices $productServices */
  320. $productServices = app()->make(StoreProductServices::class);
  321. $productInfo = $productServices->isValidProduct($productId);
  322. if (!$productInfo) {
  323. throw new ValidateException('该商品已下架或删除');
  324. }
  325. $attrInfo = $attrValueServices->getOne(['unique' => $unique, 'type' => 0]);
  326. if (!$unique || !$attrInfo || $attrInfo['product_id'] != $productId) {
  327. throw new ValidateException('请选择有效的商品属性');
  328. }
  329. $nowStock = $attrInfo['stock'];//现有平台库存
  330. if (bcadd((string)$cartNum, (string)$sum_cart_num, 0) > $nowStock) {
  331. throw new ValidateException('拼单中该商品库存不足' . bcadd((string)$cartNum, (string)$sum_cart_num, 0));
  332. }
  333. break;
  334. case 10://桌码
  335. if ($unique == '') {
  336. $unique = $attrValueServices->value(['product_id' => $productId, 'type' => 0], 'unique');
  337. }
  338. /** @var StoreProductServices $productServices */
  339. $productServices = app()->make(StoreProductServices::class);
  340. $productInfo = $productServices->isValidProduct($productId);
  341. if (!$productInfo) {
  342. throw new ValidateException('该商品已下架或删除');
  343. }
  344. $attrInfo = $attrValueServices->getOne(['unique' => $unique, 'type' => 0]);
  345. if (!$unique || !$attrInfo || $attrInfo['product_id'] != $productId) {
  346. throw new ValidateException('请选择有效的商品属性');
  347. }
  348. $nowStock = $attrInfo['stock'];//现有平台库存
  349. if (bcadd((string)$cartNum, (string)$sum_cart_num, 0) > $nowStock) {
  350. throw new ValidateException('桌码中该商品库存不足' . bcadd((string)$cartNum, (string)$sum_cart_num, 0));
  351. }
  352. break;
  353. default:
  354. throw new ValidateException('请刷新后重试');
  355. break;
  356. }
  357. if (in_array($type, [1, 2, 3])) {
  358. //根商品规格库存
  359. $product_stock = $attrValueServices->value(['product_id' => $productInfo['product_id'], 'suk' => $attrInfo['suk'], 'type' => 0], 'stock');
  360. if ($product_stock < $cartNum) {
  361. throw new ValidateException('商品库存不足' . $cartNum);
  362. }
  363. if (!CacheService::checkStock($unique, (int)$cartNum, $type)) {
  364. throw new ValidateException('商品库存不足' . $cartNum . ',无法购买请选择其他商品!');
  365. }
  366. }
  367. return [$attrInfo, $unique, $bargainUserInfo['bargain_price_min'] ?? 0, $cartNum, $productInfo];
  368. }
  369. /**
  370. * 添加购物车
  371. * @param int $uid
  372. * @param int $product_id
  373. * @param int $cart_num
  374. * @param string $product_attr_unique
  375. * @param int $type
  376. * @param bool $new
  377. * @param int $activity_id
  378. * @param int $discount_product_id
  379. * @return array
  380. * @throws \think\db\exception\DataNotFoundException
  381. * @throws \think\db\exception\DbException
  382. * @throws \think\db\exception\ModelNotFoundException
  383. */
  384. public function setCart(int $uid, int $product_id, int $cart_num = 1, string $product_attr_unique = '', int $type = 0, bool $new = true, int $activity_id = 0, int $discount_product_id = 0)
  385. {
  386. if ($cart_num < 1) $cart_num = 1;
  387. //检测库存限量
  388. $store_id = $this->getItem('store_id', 0);
  389. $staff_id = $this->getItem('staff_id', 0);
  390. $tourist_uid = $this->getItem('tourist_uid', '');
  391. [$attrInfo, $product_attr_unique, $bargainPriceMin, $cart_num, $productInfo] = $this->checkProductStock(
  392. $uid,
  393. $product_id,
  394. $cart_num,
  395. $store_id,
  396. $product_attr_unique,
  397. $new,
  398. $type, $activity_id,
  399. $discount_product_id
  400. );
  401. $product_type = $productInfo['product_type'];
  402. if ($new) {
  403. /** @var StoreOrderCreateServices $storeOrderCreateService */
  404. $storeOrderCreateService = app()->make(StoreOrderCreateServices::class);
  405. $key = $storeOrderCreateService->getNewOrderId((string)$uid);
  406. //普通订单 && 商品是预售商品 订单类型改为预售订单
  407. if ($type == 0 && $productInfo['is_presale_product']) {
  408. $type = 6;
  409. }
  410. $info['id'] = $key;
  411. $info['type'] = $type;
  412. $info['store_id'] = $store_id;
  413. $info['tourist_uid'] = $tourist_uid;
  414. $info['product_type'] = $product_type;
  415. if ($type == 10 || $type == 9) {
  416. $info['collate_code_id'] = $activity_id;
  417. $activity_id = 0;
  418. }
  419. $info['activity_id'] = $activity_id;
  420. $info['discount_product_id'] = $discount_product_id;
  421. $info['product_id'] = $product_id;
  422. $info['product_attr_unique'] = $product_attr_unique;
  423. $info['cart_num'] = $cart_num;
  424. $info['productInfo'] = [];
  425. if ($productInfo) {
  426. $info['productInfo'] = is_object($productInfo) ? $productInfo->toArray() : $productInfo;
  427. }
  428. $info['attrInfo'] = $attrInfo->toArray();
  429. $info['productInfo']['attrInfo'] = $info['attrInfo'];
  430. $info['sum_price'] = $info['productInfo']['attrInfo']['price'] ?? $info['productInfo']['price'] ?? 0;
  431. //砍价
  432. if ($type == 2 && $activity_id) {
  433. $info['truePrice'] = $bargainPriceMin;
  434. $info['productInfo']['attrInfo']['price'] = $bargainPriceMin;
  435. } else {
  436. $info['truePrice'] = $info['productInfo']['attrInfo']['price'] ?? $info['productInfo']['price'] ?? 0;
  437. }
  438. //活动商品不参与会员价
  439. if ($type > 0 && $activity_id) {
  440. $info['truePrice'] = $info['productInfo']['attrInfo']['price'] ?? 0;
  441. $info['vip_truePrice'] = 0;
  442. }
  443. if ($type == 8) $info['is_luck'] = true;
  444. $info['trueStock'] = $info['productInfo']['attrInfo']['stock'] ?? 0;
  445. $info['costPrice'] = $info['productInfo']['attrInfo']['cost'] ?? 0;
  446. $info['awardPrice'] = $info['productInfo']['attrInfo']['award_price'] ?? 0;
  447. try {
  448. CacheService::redisHandler()->set($key, $info, 3600);
  449. } catch (\Throwable $e) {
  450. throw new ValidateException($e->getMessage());
  451. }
  452. return [$key, $cart_num];
  453. } else {//加入购物车记录
  454. ProductLogJob::dispatch(['cart', ['uid' => $uid, 'product_id' => $product_id, 'cart_num' => $cart_num]]);
  455. $cart = $this->dao->getOne(['type' => $type, 'uid' => $uid, 'tourist_uid' => $tourist_uid, 'product_id' => $product_id, 'product_attr_unique' => $product_attr_unique, 'is_del' => 0, 'is_new' => 0, 'is_pay' => 0, 'status' => 1, 'store_id' => $store_id, 'staff_id' => $staff_id]);
  456. if ($cart) {
  457. $cart->cart_num = $cart_num + $cart->cart_num;
  458. $cart->add_time = time();
  459. $cart->save();
  460. return [$cart->id, $cart->cart_num];
  461. } else {
  462. $add_time = time();
  463. $id = $this->dao->save(compact('uid', 'tourist_uid', 'store_id', 'staff_id', 'product_id', 'product_type', 'cart_num', 'product_attr_unique', 'type', 'activity_id', 'add_time'))->id;
  464. event('cart.add', [$uid, $tourist_uid, $store_id, $staff_id]);
  465. return [$id, $cart_num];
  466. }
  467. }
  468. }
  469. // /**拼单/桌码商品写入购物车
  470. // * @param int $uid
  471. // * @param int $product_id
  472. // * @param int $cart_num
  473. // * @param string $product_attr_unique
  474. // * @param int $type
  475. // * @param bool $new
  476. // * @param int $activity_id
  477. // * @param int $store_id
  478. // * @param int $discount_product_id
  479. // * @return mixed
  480. // * @throws \think\db\exception\DataNotFoundException
  481. // * @throws \think\db\exception\DbException
  482. // * @throws \think\db\exception\ModelNotFoundException
  483. // */
  484. // public function addCollageCart(int $uid, int $product_id, int $cart_num = 1, string $product_attr_unique = '', int $type = 0, bool $new = true, int $activity_id = 0, int $collate_code_id = 0, int $store_id = 0, int $is_settle = 0)
  485. // {
  486. // if ($cart_num < 1) $cart_num = 1;
  487. // //检测库存限量
  488. // $staff_id = $this->getItem('staff_id', 0);
  489. // $tourist_uid = $this->getItem('tourist_uid', '');
  490. // [$attrInfo, $product_attr_unique, $bargainPriceMin, $cart_num, $productInfo] = $this->checkProductStock(
  491. // $uid,
  492. // $product_id,
  493. // $cart_num,
  494. // $store_id,
  495. // $product_attr_unique,
  496. // $new,
  497. // $type,
  498. // $activity_id,
  499. // 0
  500. // );
  501. // $product_type = $productInfo['product_type'];
  502. // ProductLogJob::dispatch(['cart', ['uid' => $uid, 'product_id' => $product_id, 'cart_num' => $cart_num]]);
  503. // $cart = $this->dao->getOne(['type' => $type, 'uid' => $uid, 'product_id' => $product_id, 'product_attr_unique' => $product_attr_unique, 'activity_id' => $activity_id, 'collate_code_id' => $collate_code_id, 'is_del' => 0, 'is_new' => 0, 'is_pay' => 0, 'status' => 1, 'store_id' => $store_id]);
  504. // if ($cart) {
  505. // if (!$is_settle) {
  506. // $cart->cart_num = $cart_num + $cart->cart_num;
  507. // }
  508. // $cart->add_time = time();
  509. // $cart->save();
  510. // return $cart->id;
  511. // } else {
  512. // $add_time = time();
  513. // $id = $this->dao->save(compact('uid', 'store_id', 'staff_id', 'product_id', 'product_type', 'cart_num', 'product_attr_unique', 'type', 'activity_id', 'collate_code_id', 'add_time'))->id;
  514. // event('cart.add', [$uid, $tourist_uid, $store_id, $staff_id]);
  515. // return $id;
  516. // }
  517. // }
  518. /**
  519. * 移除购物车商品
  520. * @param int $uid
  521. * @param array $ids
  522. * @return StoreCartDao|bool
  523. */
  524. public function removeUserCart(int $uid, array $ids)
  525. {
  526. return $this->dao->removeUserCart($uid, $ids) !== false;
  527. }
  528. /**
  529. * 购物车 修改商品数量
  530. * @param $id
  531. * @param $number
  532. * @param $uid
  533. * @return bool|\crmeb\basic\BaseModel
  534. * @throws \think\db\exception\DataNotFoundException
  535. * @throws \think\db\exception\DbException
  536. * @throws \think\db\exception\ModelNotFoundException
  537. */
  538. public function changeUserCartNum($id, $number, $uid)
  539. {
  540. if (!$id || !$number) return false;
  541. $where = ['uid' => $uid, 'id' => $id];
  542. $carInfo = $this->dao->getOne($where, 'product_id,type,activity_id,product_attr_unique,cart_num');
  543. /** @var StoreProductServices $StoreProduct */
  544. $StoreProduct = app()->make(StoreProductServices::class);
  545. $stock = $StoreProduct->getProductStock($carInfo->product_id, $carInfo->product_attr_unique);
  546. if (!$stock) throw new ValidateException('暂无库存');
  547. if (!$number) throw new ValidateException('库存错误');
  548. if ($stock < $number) throw new ValidateException('库存不足' . $number);
  549. if ($carInfo->cart_num == $number) return true;
  550. $this->checkProductStock($uid, (int)$carInfo->product_id, (int)$number, 0, $carInfo->product_attr_unique, true);
  551. return $this->dao->changeUserCartNum(['uid' => $uid, 'id' => $id], (int)$number);
  552. }
  553. /**
  554. * 获取购物车列表
  555. * @param int $uid
  556. * @param int $status
  557. * @param array $cartIds
  558. * @param int $storeId
  559. * @param int $staff_id
  560. * @param int $shipping_type
  561. * @param int $touristUid
  562. * @param int $numType
  563. * @param bool $new
  564. * @param bool $isCart
  565. * @return array
  566. * @throws \think\db\exception\DataNotFoundException
  567. * @throws \think\db\exception\DbException
  568. * @throws \think\db\exception\ModelNotFoundException
  569. */
  570. public function getUserCartList(int $uid, int $status, array $cartIds = [], int $storeId = 0, int $staff_id = 0, int $shipping_type = -1, int $touristUid = 0, int $numType = 0, bool $new = false, bool $isCart = true)
  571. {
  572. // [$page, $limit] = $this->getPageValue();
  573. if ($new) {
  574. $cartIds = $cartIds && is_string($cartIds) ? explode(',', $cartIds) : (is_array($cartIds) ? $cartIds : []);
  575. $list = [];
  576. if ($cartIds) {
  577. foreach ($cartIds as $key) {
  578. $info = CacheService::redisHandler()->get($key);
  579. if ($info) {
  580. $list[] = $info;
  581. }
  582. }
  583. }
  584. } else {
  585. $where = ['uid' => $uid, 'store_id' => $storeId, 'tourist_uid' => $touristUid, 'cart_ids' => $cartIds];
  586. //有店员就证明在收银台中
  587. if ($staff_id) {
  588. $where['staff_id'] = $staff_id;
  589. }
  590. if ($status != -1) $where = array_merge($where, ['status' => $status]);
  591. $list = $this->dao->getCartList($where, 0, 0, ['productInfo', 'attrInfo']);
  592. }
  593. $count = $promotionsPrice = $coupon_price = $firstOrderPrice = 0;
  594. $cartList = $valid = $promotions = $coupon = $invalid = $type = $activity_id = [];
  595. if ($list) {
  596. [$list, $valid, $invalid] = $this->handleCartList($uid, $list, [], $shipping_type, $storeId);
  597. $activity_id = array_unique(array_column($list, 'activity_id'));
  598. $type = array_unique(array_column($list, 'type'));
  599. if (!($activity_id[0] ?? 0)) {
  600. /** @var StoreNewcomerServices $newcomerServices */
  601. $newcomerServices = app()->make(StoreNewcomerServices::class);
  602. $newcomerArr = $newcomerServices->checkUserFirstDiscount($uid);
  603. if ($newcomerArr) {
  604. //计算首单优惠
  605. [$valid, $firstOrderPrice, $first_discount, $first_discount_limit] = $this->computedFirstDiscount($uid, $valid, $newcomerArr);
  606. } else {
  607. /** @var StorePromotionsServices $storePromotionsServices */
  608. $storePromotionsServices = app()->make(StorePromotionsServices::class);
  609. //计算相关优惠活动
  610. [$valid, $coupon_price, $coupon, $promotions, $giveIntegral, $giveCoupon, $giveCartList] = $storePromotionsServices->computedPromotions($uid, $valid, $storeId, 0, $isCart);
  611. $cartList = array_merge($valid, $giveCartList);
  612. foreach ($cartList as $key => $cart) {
  613. if (isset($cart['promotions_true_price']) && isset($cart['price_type']) && $cart['price_type'] == 'promotions') {
  614. $promotionsPrice = bcadd((string)$promotionsPrice, (string)bcmul((string)$cart['promotions_true_price'], (string)$cart['cart_num'], 2), 2);
  615. }
  616. }
  617. }
  618. }
  619. if ($numType) {
  620. $count = count($valid);
  621. } else {
  622. $count = array_sum(array_column($valid, 'cart_num'));
  623. }
  624. }
  625. $deduction = ['type' => $type[0] ?? 0, 'activity_id' => $activity_id[0] ?? 0];
  626. $deduction['promotions_price'] = $promotionsPrice;
  627. $deduction['coupon_price'] = $coupon_price;
  628. $deduction['first_order_price'] = $firstOrderPrice;
  629. $user_store_id = $this->getItem('store_id', 0);
  630. $invalid_key = 'invalid_' . $user_store_id . '_' . $uid;
  631. //写入缓存
  632. if ($status == 1) {
  633. CacheService::redisHandler()->delete($invalid_key);
  634. if ($invalid) CacheService::redisHandler()->set($invalid_key, $invalid, 60);
  635. }
  636. //读取缓存
  637. if ($status == 0) {
  638. $other_invalid = CacheService::redisHandler()->get($invalid_key);
  639. if ($other_invalid) $invalid = array_merge($invalid, $other_invalid);
  640. }
  641. return ['promotions' => $promotions, 'coupon' => $coupon, 'valid' => $valid, 'invalid' => $invalid, 'deduction' => $deduction, 'count' => $count];
  642. }
  643. /**
  644. * 购物车重选
  645. * @param int $cart_id
  646. * @param int $product_id
  647. * @param string $unique
  648. */
  649. public function modifyCart(int $cart_id, int $product_id, string $unique)
  650. {
  651. /** @var StoreProductAttrValueServices $attrService */
  652. $attrService = app()->make(StoreProductAttrValueServices::class);
  653. $stock = $attrService->value(['product_id' => $product_id, 'unique' => $unique, 'type' => 0], 'stock');
  654. if ($stock > 0) {
  655. $this->dao->update($cart_id, ['product_attr_unique' => $unique, 'cart_num' => 1]);
  656. } else {
  657. throw new ValidateException('选择的规格库存不足');
  658. }
  659. }
  660. /**
  661. * 重选购物车
  662. * @param $id
  663. * @param $uid
  664. * @param $productId
  665. * @param $unique
  666. * @param $num
  667. * @param int $store_id
  668. * @throws \think\db\exception\DataNotFoundException
  669. * @throws \think\db\exception\DbException
  670. * @throws \think\db\exception\ModelNotFoundException
  671. */
  672. public function resetCart($id, $uid, $productId, $unique, $num, int $store_id = 0)
  673. {
  674. $res = $this->dao->getOne(['uid' => $uid, 'product_id' => $productId, 'product_attr_unique' => $unique, 'store_id' => $store_id]);
  675. if ($res) {
  676. /** @var StoreProductServices $StoreProduct */
  677. $StoreProduct = app()->make(StoreProductServices::class);
  678. $stock = $StoreProduct->getProductStock((int)$productId, $unique);
  679. $cart_num = $res->cart_num + $num;
  680. if ($cart_num > $stock) {
  681. $cart_num = $stock;
  682. }
  683. $res->cart_num = $cart_num;
  684. $res->save();
  685. if ($res['id'] != $id) $this->dao->delete($id);
  686. } else {
  687. $this->dao->update($id, ['product_attr_unique' => $unique, 'cart_num' => $num]);
  688. }
  689. }
  690. /**
  691. * 首页加入购物车
  692. * @param int $uid
  693. * @param int $productId
  694. * @param int $num
  695. * @param string $unique
  696. * @param int $type
  697. * @return mixed
  698. * @throws \think\db\exception\DataNotFoundException
  699. * @throws \think\db\exception\DbException
  700. * @throws \think\db\exception\ModelNotFoundException
  701. */
  702. public function setCartNum(int $uid, int $productId, int $num, string $unique, int $type)
  703. {
  704. /** @var StoreProductAttrValueServices $attrValueServices */
  705. $attrValueServices = app()->make(StoreProductAttrValueServices::class);
  706. if ($unique == '') {
  707. $unique = $attrValueServices->value(['product_id' => $productId, 'type' => 0], 'unique');
  708. }
  709. /** @var StoreProductServices $productServices */
  710. $productServices = app()->make(StoreProductServices::class);
  711. $productInfo = $productServices->isValidProduct((int)$productId);
  712. if (!$productInfo) {
  713. throw new ValidateException('该商品已下架或删除');
  714. }
  715. if (!($unique && $attrValueServices->getAttrvalueCount($productId, $unique, 0))) {
  716. throw new ValidateException('请选择有效的商品属性');
  717. }
  718. $stock = $productServices->getProductStock((int)$productId, $unique);
  719. if ($stock < $num) {
  720. throw new ValidateException('该商品库存不足' . $num);
  721. }
  722. //预售商品
  723. if ($productInfo['is_presale_product']) {
  724. if ($productInfo['presale_start_time'] > time()) throw new ValidateException('预售活动未开始');
  725. if ($productInfo['presale_end_time'] < time()) throw new ValidateException('预售活动已结束');
  726. }
  727. //检查限购
  728. if ($type != 0) $this->checkLimit($uid, $productId, $num);
  729. $cart = $this->dao->getOne(['uid' => $uid, 'product_id' => $productId, 'product_attr_unique' => $unique, 'store_id' => 0]);
  730. if ($cart) {
  731. if ($type == -1) {
  732. $cart->cart_num = $num;
  733. } elseif ($type == 0) {
  734. $cart->cart_num = $cart->cart_num - $num;
  735. } elseif ($type == 1) {
  736. if ($cart->cart_num >= $stock) {
  737. throw new ValidateException('该商品库存只有' . $stock);
  738. }
  739. $new_cart_num = $cart->cart_num + $num;
  740. if ($new_cart_num > $stock) {
  741. $new_cart_num = $stock;
  742. }
  743. $cart->cart_num = $new_cart_num;
  744. }
  745. if ($cart->cart_num === 0) {
  746. return $this->dao->delete($cart->id);
  747. } else {
  748. $cart->add_time = time();
  749. $cart->save();
  750. return $cart->id;
  751. }
  752. } else {
  753. $data = [
  754. 'uid' => $uid,
  755. 'product_id' => $productId,
  756. 'product_type' => $productInfo['product_type'],
  757. 'cart_num' => $num,
  758. 'product_attr_unique' => $unique,
  759. 'type' => 0,
  760. 'add_time' => time()
  761. ];
  762. $id = $this->dao->save($data)->id;
  763. event('cart.add', [$uid, 0, 0, 0]);
  764. return $id;
  765. }
  766. }
  767. /**
  768. * 用户购物车商品统计
  769. * @param int $uid
  770. * @param string $numType
  771. * @throws \think\db\exception\DataNotFoundException
  772. * @throws \think\db\exception\DbException
  773. * @throws \think\db\exception\ModelNotFoundException
  774. */
  775. public function getUserCartCount(int $uid, string $numType = '0', int $store_id = 0)
  776. {
  777. $count = 0;
  778. $ids = [];
  779. $cartNums = [];
  780. $sum_price = 0;
  781. $cartList = $this->dao->getUserCartList(['uid' => $uid, 'status' => 1, 'store_id' => 0], 'id,cart_num,product_id,product_attr_unique');
  782. if ($cartList) {
  783. /** @var StoreProductServices $storeProductServices */
  784. $storeProductServices = app()->make(StoreProductServices::class);
  785. $productInfos = $storeProductServices->getColumn([['id', 'in', array_column($cartList, 'product_id')]], 'id,pid,type,relation_id', 'id');
  786. /** @var StoreProductAttrValueServices $storePrdouctAttrValueServices */
  787. $storePrdouctAttrValueServices = app()->make(StoreProductAttrValueServices::class);
  788. $attrInfos = $storePrdouctAttrValueServices->getColumn([['unique', 'in', array_column($cartList, 'product_attr_unique')]], 'id,unique', 'unique');
  789. foreach ($cartList as $cart) {
  790. $productInfo = $productInfos[$cart['product_id']] ?? [];
  791. if (!$productInfo) continue;
  792. $attrInfo = $attrInfos[$cart['product_attr_unique']] ?? [];
  793. if (!$attrInfo) continue;
  794. if ($store_id) {//某门店加入购物车商品数量
  795. if (in_array($productInfo['type'], [0, 2]) || ($productInfo['type'] == 1 && $productInfo['relation_id'] == $store_id) || ($productInfo['type'] == 1 && $productInfo['pid'] > 0)) {
  796. $ids[] = $cart['id'];
  797. $cartNums[] = $cart['cart_num'];
  798. }
  799. } else {
  800. if (in_array($productInfo['type'], [0, 2]) || ($productInfo['type'] == 1 && $productInfo['pid'] > 0)) {
  801. $ids[] = $cart['id'];
  802. $cartNums[] = $cart['cart_num'];
  803. }
  804. }
  805. }
  806. if ($numType) {
  807. $count = count($ids);
  808. } else {
  809. $count = array_sum($cartNums);
  810. }
  811. }
  812. return compact('count', 'ids', 'sum_price');
  813. }
  814. /**
  815. * 处理购物车数据
  816. * @param int $uid
  817. * @param array $cartList
  818. * @param array $addr
  819. * @param int $shipping_type
  820. * @param int $store_id
  821. * @return array
  822. * @throws \think\db\exception\DataNotFoundException
  823. * @throws \think\db\exception\DbException
  824. * @throws \think\db\exception\ModelNotFoundException
  825. */
  826. public function handleCartList(int $uid, array $cartList, array $addr = [], int $shipping_type = 1, int $store_id = 0)
  827. {
  828. if (!$cartList) {
  829. return [$cartList, [], [], [], 0, [], []];
  830. }
  831. /** @var StoreProductServices $productServices */
  832. $productServices = app()->make(StoreProductServices::class);
  833. /** @var MemberCardServices $memberCardService */
  834. $memberCardService = app()->make(MemberCardServices::class);
  835. $vipStatus = $memberCardService->isOpenMemberCardCache('vip_price', false);
  836. $tempIds = [];
  837. $userInfo = [];
  838. $discount = 100;
  839. $productIds = $allStock = $attrUniquesArr = [];
  840. if ($uid) {
  841. /** @var UserServices $user */
  842. $user = app()->make(UserServices::class);
  843. $userInfo = $user->getUserCacheInfo($uid);
  844. if (!$userInfo) {
  845. throw new ValidateException('用户不存在');
  846. }
  847. $userInfo = $userInfo->toArray();
  848. //用户等级是否开启
  849. if (sys_config('member_func_status', 1) && $userInfo) {
  850. /** @var SystemUserLevelServices $systemLevel */
  851. $systemLevel = app()->make(SystemUserLevelServices::class);
  852. $discount = $systemLevel->getDiscount($uid, (int)$userInfo['level'] ?? 0);
  853. }
  854. }
  855. //不送达运费模板
  856. if ($shipping_type == 1 && $addr) {
  857. $cityId = (int)($addr['city_id'] ?? 0);
  858. if ($cityId) {
  859. /** @var CityAreaServices $cityAreaServices */
  860. $cityAreaServices = app()->make(CityAreaServices::class);
  861. $cityIds = $cityAreaServices->getRelationCityIds($cityId);
  862. foreach ($cartList as $item) {
  863. $tempIds[] = $item['productInfo']['temp_id'];
  864. }
  865. $tempIds = array_unique($tempIds);
  866. /** @var ShippingTemplatesServices $shippingService */
  867. $shippingService = app()->make(ShippingTemplatesServices::class);
  868. $tempIds = $shippingService->getColumn([['id', 'in', $tempIds], ['no_delivery', '=', 1]], 'id');
  869. if ($tempIds) {
  870. /** @var ShippingTemplatesNoDeliveryServices $noDeliveryServices */
  871. $noDeliveryServices = app()->make(ShippingTemplatesNoDeliveryServices::class);
  872. $tempIds = $noDeliveryServices->isNoDelivery($tempIds, $cityIds);
  873. }
  874. }
  875. }
  876. $latitude = $this->getItem('latitude', '');
  877. $longitude = $this->getItem('longitude', '');
  878. $user_store_id = $this->getItem('store_id', 0);
  879. $store_id = $store_id ?: $user_store_id;
  880. //平台是否开启门店自提
  881. $store_self_mention = sys_config('store_func_status', 1) && sys_config('store_self_mention');
  882. $cart_type = 0;
  883. if ($store_id) {//平台商品,在门店购买 验证门店库存
  884. /** @var StoreProductAttrValueServices $skuValueServices */
  885. $skuValueServices = app()->make(StoreProductAttrValueServices::class);
  886. /** @var StoreBranchProductServices $branchProductServics */
  887. $branchProductServics = app()->make(StoreBranchProductServices::class);
  888. foreach ($cartList as $cart) {
  889. $productInfo = $cart['productInfo'] ?? [];
  890. if (!$productInfo) continue;
  891. $product_id = 0;
  892. if (in_array($productInfo['type'], [0, 2])) {
  893. $product_id = $productInfo['id'];
  894. } else {//门店商品
  895. if ($productInfo['pid'] && $productInfo['relation_id'] != $store_id) {//平台共享商品到另一个门店购买
  896. $product_id = $productInfo['pid'];
  897. }
  898. }
  899. if (!$product_id) {//自己门店购买不用再次验证库存
  900. continue;
  901. }
  902. $productIds[] = $cart['product_id'];
  903. $suk = '';
  904. $cart_type = $cart['type'];
  905. //类型 0:普通、1:秒杀、2:砍价、3:拼团、4:积分、5:套餐、6:预售、7:新人礼、8:抽奖、9:拼单、10:桌码
  906. switch ($cart['type']) {
  907. case 0:
  908. case 6:
  909. case 8:
  910. case 9:
  911. case 10:
  912. $suk = $skuValueServices->value(['unique' => $cart['product_attr_unique'], 'product_id' => $cart['product_id'], 'type' => 0], 'suk');
  913. break;
  914. case 1:
  915. case 2:
  916. case 3:
  917. case 5:
  918. case 7:
  919. if ($cart['type'] == 5 && isset($cart['discount_product_id'])) {
  920. $product_id = $cart['discount_product_id'];
  921. } else {
  922. $product_id = $cart['activity_id'];
  923. }
  924. $suk = $skuValueServices->value(['unique' => $cart['product_attr_unique'], 'product_id' => $product_id, 'type' => $cart['type']], 'suk');
  925. break;
  926. }
  927. $branchProductInfo = $branchProductServics->isValidStoreProduct((int)$cart['product_id'], $store_id);
  928. if (!$branchProductInfo) {
  929. continue;
  930. }
  931. $attrValue = '';
  932. if ($suk) {
  933. $attrValue = $skuValueServices->get(['suk' => $suk, 'product_id' => $branchProductInfo['id'], 'type' => 0]);
  934. }
  935. if (!$attrValue) {
  936. continue;
  937. }
  938. $allStock[$attrValue['unique']] = $attrValue['stock'];
  939. $attrUniquesArr[$cart['product_attr_unique']] = $attrValue['unique'];
  940. }
  941. } else {
  942. $productIds = array_unique(array_column($cartList, 'product_id'));
  943. }
  944. $storeInfo = [];
  945. /** @var SystemStoreServices $storeServices */
  946. $storeServices = app()->make(SystemStoreServices::class);
  947. if ($user_store_id) {
  948. $storeInfo = $storeServices->getNearbyStore(['id' => $user_store_id], '', '', '', 1);
  949. } else if ($latitude && $longitude) {
  950. $storeInfo = $storeServices->getNearbyStore([], $latitude, $longitude, '', 1);
  951. }
  952. $valid = $invalid = [];
  953. foreach ($cartList as &$item) {
  954. $item['is_gift'] = 0;
  955. if (isset($item['productInfo']['delivery_type'])) {
  956. $item['productInfo']['delivery_type'] = is_string($item['productInfo']['delivery_type']) ? explode(',', $item['productInfo']['delivery_type']) : $item['productInfo']['delivery_type'];
  957. } else {
  958. $item['productInfo']['delivery_type'] = [];
  959. }
  960. $item['productInfo']['express_delivery'] = in_array(1, $item['productInfo']['delivery_type']);
  961. $item['productInfo']['store_mention'] = in_array(2, $item['productInfo']['delivery_type']);
  962. $item['productInfo']['store_delivery'] = in_array(3, $item['productInfo']['delivery_type']);
  963. if (isset($item['attrInfo']) && $item['attrInfo'] && (!isset($item['productInfo']['attrInfo']) || !$item['productInfo']['attrInfo'])) {
  964. $item['productInfo']['attrInfo'] = $item['attrInfo'] ?? [];
  965. }
  966. $item['attrStatus'] = isset($item['productInfo']['attrInfo']['stock']) && $item['productInfo']['attrInfo']['stock'];
  967. $item['productInfo']['attrInfo']['image'] = $item['productInfo']['attrInfo']['image'] ?? $item['productInfo']['image'] ?? '';
  968. $item['productInfo']['attrInfo']['suk'] = $item['productInfo']['attrInfo']['suk'] ?? '已失效';
  969. if (isset($item['productInfo']['attrInfo'])) {
  970. $item['productInfo']['attrInfo'] = get_thumb_water($item['productInfo']['attrInfo']);
  971. }
  972. $item['productInfo'] = get_thumb_water($item['productInfo']);
  973. $productInfo = $item['productInfo'];
  974. $item['vip_truePrice'] = 0;
  975. //门店独立商品
  976. $isBranchProduct = isset($productInfo['type']) && isset($productInfo['pid']) && $productInfo['type'] == 1 && !$productInfo['pid'];
  977. $product_store_id = $isBranchProduct ? $productInfo['relation_id'] : 0;
  978. if (isset($productInfo['attrInfo']['product_id']) && $item['product_attr_unique']) {
  979. $item['costPrice'] = $productInfo['attrInfo']['cost'] ?? 0;
  980. $item['awardPrice'] = $productInfo['attrInfo']['award_price'] ?? 0;
  981. $item['trueStock'] = $item['branch_stock'] = $productInfo['attrInfo']['stock'] ?? 0;
  982. $item['branch_sales'] = $productInfo['attrInfo']['sales'] ?? 0;
  983. $item['truePrice'] = $productInfo['attrInfo']['price'] ?? 0;
  984. $item['sum_price'] = $productInfo['attrInfo']['price'] ?? 0;
  985. if ((!$item['type'] || !$item['activity_id']) && !$isBranchProduct) {
  986. [$truePrice, $vip_truePrice, $type] = $productServices->setLevelPrice($productInfo['attrInfo']['price'] ?? 0, $uid, $userInfo, $vipStatus, $discount, $productInfo['attrInfo']['vip_price'] ?? 0, $productInfo['is_vip'] ?? 0, true);
  987. $item['truePrice'] = $truePrice;
  988. $item['vip_truePrice'] = $vip_truePrice;
  989. $item['price_type'] = $type;
  990. }
  991. } else {
  992. $item['costPrice'] = $item['productInfo']['cost'] ?? 0;
  993. $item['awardPrice'] = $item['productInfo']['award_price'] ?? 0;
  994. $item['trueStock'] = $item['branch_sales'] = $item['productInfo']['stock'] ?? 0;
  995. $item['branch_sales'] = $item['productInfo']['sales'] ?? 0;
  996. $item['truePrice'] = $item['productInfo']['price'] ?? 0;
  997. $item['sum_price'] = $item['productInfo']['price'] ?? 0;
  998. if ((!$item['type'] || !$item['activity_id']) && !$isBranchProduct) {
  999. [$truePrice, $vip_truePrice, $type] = $productServices->setLevelPrice($item['productInfo']['price'] ?? 0, $uid, $userInfo, $vipStatus, $discount, $item['productInfo']['vip_price'] ?? 0, $item['productInfo']['is_vip'] ?? 0, true);
  1000. $item['truePrice'] = $truePrice;
  1001. $item['vip_truePrice'] = $vip_truePrice;
  1002. $item['price_type'] = $type;
  1003. }
  1004. }
  1005. $applicable_type = $item['productInfo']['applicable_type'] ?? 1;
  1006. $applicable_store_id = [];
  1007. if (isset($item['productInfo']['applicable_store_id'])) {
  1008. $applicable_store_id = is_string($item['productInfo']['applicable_store_id']) ? explode(',', $item['productInfo']['applicable_store_id']) : $item['productInfo']['applicable_store_id'];
  1009. }
  1010. $applicableStatus = !$store_id || $applicable_type == 1 || ($applicable_type == 2 && in_array($store_id, $applicable_store_id));
  1011. if (isset($item['status']) && $item['status'] == 0) {
  1012. $item['is_valid'] = 0;
  1013. $item['invalid_desc'] = '此商品已失效';
  1014. $invalid[] = $item;
  1015. } elseif (($item['productInfo']['type'] ?? 0) == 1 && ($item['productInfo']['pid'] ?? 0) == 0 && $storeInfo && ($item['productInfo']['relation_id'] ?? 0) != $storeInfo['id'] && $cart_type != 10) {
  1016. $item['is_valid'] = 0;
  1017. $item['invalid_desc'] = '此商品超出配送/自提范围';
  1018. $invalid[] = $item;
  1019. } elseif ((isset($item['productInfo']['delivery_type']) && !$item['productInfo']['delivery_type']) || in_array($item['productInfo']['product_type'], [1, 2, 3])) {
  1020. $item['is_valid'] = 1;
  1021. $valid[] = $item;
  1022. } elseif (!$applicableStatus) {
  1023. $item['is_valid'] = 0;
  1024. $item['invalid_desc'] = '此商品超出配送/自提范围';
  1025. $invalid[] = $item;
  1026. } else {
  1027. $condition = !in_array(isset($item['productInfo']['product_id']) ? $item['productInfo']['product_id'] : $item['productInfo']['id'], $productIds) || $item['cart_num'] > ($allStock[$attrUniquesArr[$item['product_attr_unique']] ?? ''] ?? 0);
  1028. switch ($shipping_type) {
  1029. case -1://购物车列表展示
  1030. if ($isBranchProduct && $store_id && ($store_id != $product_store_id)) {
  1031. $item['is_valid'] = 0;
  1032. $item['invalid_desc'] = '此商品超出配送/自提范围';
  1033. $invalid[] = $item;
  1034. } else {
  1035. $item['is_valid'] = 1;
  1036. $valid[] = $item;
  1037. }
  1038. break;
  1039. case 1:
  1040. //不送达
  1041. if (in_array($item['productInfo']['temp_id'], $tempIds) || (isset($item['productInfo']['delivery_type']) && !in_array(1, $item['productInfo']['delivery_type']) && !in_array(3, $item['productInfo']['delivery_type']))) {
  1042. $item['is_valid'] = 0;
  1043. $item['invalid_desc'] = '此商品超出配送/自提范围';
  1044. $invalid[] = $item;
  1045. } elseif ($isBranchProduct && $store_id && ($store_id != $product_store_id || !in_array(3, $item['productInfo']['delivery_type']))) {
  1046. $item['is_valid'] = 0;
  1047. $item['invalid_desc'] = '此商品超出配送/自提范围';
  1048. $invalid[] = $item;
  1049. } elseif ((in_array($productInfo['type'], [0, 2]) || $productInfo['relation_id'] != $store_id) && $store_id && ($condition || (!in_array(2, $item['productInfo']['delivery_type']) && !in_array(3, $item['productInfo']['delivery_type'])))) {//平台商品 在门店购买 验证门店库存
  1050. $item['is_valid'] = 0;
  1051. $item['invalid_desc'] = '此商品超出配送/自提范围';
  1052. $invalid[] = $item;
  1053. } elseif (!$storeServices->checkStoreDeliveryScope($store_id, $addr, $latitude, $longitude)) {
  1054. $item['is_valid'] = 0;
  1055. $item['invalid_desc'] = '此商品超出配送/自提范围';
  1056. $invalid[] = $item;
  1057. } else {
  1058. $item['is_valid'] = 1;
  1059. $valid[] = $item;
  1060. }
  1061. break;
  1062. case 2:
  1063. //不支持到店自提
  1064. if (!$store_self_mention) {
  1065. $item['is_valid'] = 0;
  1066. $item['invalid_desc'] = '平台/门店已关闭自提';
  1067. $invalid[] = $item;
  1068. } elseif (isset($item['productInfo']['delivery_type']) && $item['productInfo']['delivery_type'] && !in_array(2, $item['productInfo']['delivery_type'])) {
  1069. $item['is_valid'] = 0;
  1070. $item['invalid_desc'] = '此商品超出配送/自提范围';
  1071. $invalid[] = $item;
  1072. } elseif ($isBranchProduct && $store_id && $store_id != $product_store_id) {
  1073. $item['is_valid'] = 0;
  1074. $item['invalid_desc'] = '此商品超出配送/自提范围';
  1075. $invalid[] = $item;
  1076. } elseif ($item['productInfo']['product_type'] == 1) {
  1077. $item['is_valid'] = 0;
  1078. $item['invalid_desc'] = '此商品超出配送/自提范围';
  1079. $invalid[] = $item;
  1080. } elseif ((in_array($productInfo['type'], [0, 2]) || $productInfo['relation_id'] != $store_id) && $store_id && $condition) {//平台、供应商商品 在门店购买 验证门店库存
  1081. $item['is_valid'] = 0;
  1082. $item['invalid_desc'] = '此商品超出配送/自提范围';
  1083. $invalid[] = $item;
  1084. } else {
  1085. $item['is_valid'] = 1;
  1086. $valid[] = $item;
  1087. }
  1088. break;
  1089. case 4:
  1090. //无库存||下架
  1091. if ($isBranchProduct && $store_id && $store_id != $product_store_id) {
  1092. $item['is_valid'] = 0;
  1093. $item['invalid_desc'] = '此商品超出配送/自提范围';
  1094. $invalid[] = $item;
  1095. } elseif (in_array($productInfo['type'], [0, 2]) && $store_id && $condition) {
  1096. $item['is_valid'] = 0;
  1097. $invalid[] = $item;
  1098. } else {
  1099. $item['is_valid'] = 1;
  1100. $valid[] = $item;
  1101. }
  1102. break;
  1103. default:
  1104. $item['is_valid'] = 1;
  1105. $valid[] = $item;
  1106. break;
  1107. }
  1108. }
  1109. unset($item['attrInfo']);
  1110. }
  1111. return [$cartList, $valid, $invalid];
  1112. }
  1113. /**
  1114. * 门店给用户加入购物车
  1115. * @param int $uid
  1116. * @param int $productId
  1117. * @param int $cartNum
  1118. * @param string $unique
  1119. * @param int $staff_id
  1120. * @return mixed
  1121. * @throws \think\db\exception\DataNotFoundException
  1122. * @throws \think\db\exception\DbException
  1123. * @throws \think\db\exception\ModelNotFoundException
  1124. */
  1125. public function addCashierCart(int $uid, int $productId, int $cartNum, string $unique, int $staff_id = 0)
  1126. {
  1127. $store_id = $this->getItem('store_id', 0);
  1128. $tourist_uid = $this->getItem('tourist_uid', '');
  1129. if (!$store_id) {
  1130. throw new ValidateException('缺少门店ID');
  1131. }
  1132. [$nowStock, $unique, $bargainPriceMin, $cart_num, $productInfo] = $this->checkProductStock($uid, $productId, $cartNum, $store_id, $unique, true);
  1133. ProductLogJob::dispatch(['cart', ['uid' => $uid, 'product_id' => $productId, 'cart_num' => $cartNum]]);
  1134. $cart = $this->dao->getOne([
  1135. 'uid' => $uid,
  1136. 'product_id' => $productId,
  1137. 'product_attr_unique' => $unique,
  1138. 'store_id' => $store_id,
  1139. 'staff_id' => $staff_id,
  1140. 'tourist_uid' => $tourist_uid,
  1141. 'is_del' => 0,
  1142. 'is_new' => 0,
  1143. 'is_pay' => 0,
  1144. 'status' => 1
  1145. ]);
  1146. if ($cart) {
  1147. if ($nowStock < ($cartNum + $cart['cart_num'])) {
  1148. $cartNum = $nowStock - $cartNum;//剩余库存
  1149. }
  1150. if ($cartNum == 0) throw new ValidateException('库存不足');
  1151. $cart->cart_num = $cartNum + $cart->cart_num;
  1152. $cart->add_time = time();
  1153. $cart->save();
  1154. return $cart->id;
  1155. } else {
  1156. $add_time = time();
  1157. $data = compact('uid', 'store_id', 'add_time', 'tourist_uid');
  1158. $data['type'] = 0;
  1159. $data['product_id'] = $productId;
  1160. $data['product_type'] = $productInfo['product_type'];
  1161. $data['cart_num'] = $cartNum;
  1162. $data['product_attr_unique'] = $unique;
  1163. $data['store_id'] = $store_id;
  1164. $data['staff_id'] = $staff_id;
  1165. $id = $this->dao->save($data)->id;
  1166. event('cart.add', [$uid, $tourist_uid, $store_id, $staff_id]);
  1167. return $id;
  1168. }
  1169. }
  1170. /**
  1171. * @param int $id
  1172. * @param int $number
  1173. * @param int $uid
  1174. * @param int $storeId
  1175. * @return bool|\crmeb\basic\BaseModel
  1176. * @throws \think\db\exception\DataNotFoundException
  1177. * @throws \think\db\exception\DbException
  1178. * @throws \think\db\exception\ModelNotFoundException
  1179. */
  1180. public function changeCashierCartNum(int $id, int $number, int $uid, int $storeId)
  1181. {
  1182. if (!$id || !$number) return false;
  1183. $where = ['uid' => $uid, 'id' => $id, 'store_id' => $storeId];
  1184. $carInfo = $this->dao->getOne($where, 'product_id,product_attr_unique,cart_num');
  1185. /** @var StoreBranchProductServices $storeProduct */
  1186. $storeProduct = app()->make(StoreBranchProductServices::class);
  1187. $stock = $storeProduct->getProductStock($carInfo->product_id, $storeId, $carInfo->product_attr_unique);
  1188. if (!$stock) throw new ValidateException('暂无库存');
  1189. if ($stock < $number) throw new ValidateException('库存不足' . $number);
  1190. if ($carInfo->cart_num == $number) return true;
  1191. $this->setItem('is_set', 1);
  1192. $this->checkProductStock($uid, (int)$carInfo->product_id, $number, $storeId, $carInfo->product_attr_unique, true);
  1193. $this->reset();
  1194. return $this->dao->changeUserCartNum(['uid' => $uid, 'id' => $id], (int)$number);
  1195. }
  1196. /**
  1197. * 购物车重选
  1198. * @param int $cart_id
  1199. * @param int $product_id
  1200. * @param string $unique
  1201. */
  1202. public function modifyCashierCart(int $storeId, int $cart_id, int $product_id, string $unique)
  1203. {
  1204. /** @var StoreProductAttrValueServices $attrService */
  1205. $attrService = app()->make(StoreProductAttrValueServices::class);
  1206. $stock = $attrService->value(['product_id' => $product_id, 'unique' => $unique, 'type' => 0], 'stock');
  1207. if ($stock > 0) {
  1208. $this->dao->update($cart_id, ['product_attr_unique' => $unique, 'cart_num' => 1]);
  1209. } else {
  1210. throw new ValidateException('选择的规格库存不足');
  1211. }
  1212. }
  1213. /**
  1214. * 批量加入购物车
  1215. * @param array $cart
  1216. * @param int $storeId
  1217. * @param int $uid
  1218. * @return array
  1219. * @throws \think\db\exception\DataNotFoundException
  1220. * @throws \think\db\exception\DbException
  1221. * @throws \think\db\exception\ModelNotFoundException
  1222. */
  1223. public function batchAddCart(array $cart, int $storeId, int $uid)
  1224. {
  1225. $this->setItem('store_id', $storeId);
  1226. $cartIds = [];
  1227. foreach ($cart as $item) {
  1228. if (!isset($item['productId'])) {
  1229. throw new ValidateException('缺少商品ID');
  1230. }
  1231. if (!isset($item['cartNum'])) {
  1232. throw new ValidateException('缺少购买商品数量');
  1233. }
  1234. if (!isset($item['uniqueId'])) {
  1235. throw new ValidateException('缺少唯一值');
  1236. }
  1237. $cartIds[] = $this->addCashierCart($uid, (int)$item['productId'], (int)$item['cartNum'], $item['uniqueId']);
  1238. }
  1239. $this->reset();
  1240. return $cartIds;
  1241. }
  1242. /**
  1243. * 组合前端购物车需要的数据结构
  1244. * @param array $cartList
  1245. * @param array $protmoions
  1246. * @return array
  1247. */
  1248. public function getReturnCartList(array $cartList, array $promotions)
  1249. {
  1250. $result = [];
  1251. if ($cartList) {
  1252. if ($promotions) $promotions = array_combine(array_column($promotions, 'id'), $promotions);
  1253. $i = 0;
  1254. foreach ($cartList as $key => $cart) {
  1255. $data = ['promotions' => [], 'pids' => [], 'cart' => []];
  1256. if ($result && isset($cart['promotions_id']) && $cart['promotions_id'] && (!isset($cart['collate_code_id']) || $cart['collate_code_id'] <= 0)) {
  1257. $isTure = false;
  1258. foreach ($result as $key => &$res) {
  1259. if (array_intersect($res['pids'], $cart['promotions_id'])) {
  1260. $res['pids'] = array_unique(array_merge($res['pids'], $cart['promotions_id'] ?? []));
  1261. $res['cart'][] = $cart;
  1262. $isTure = true;
  1263. break;
  1264. }
  1265. }
  1266. if (!$isTure) {
  1267. $data['cart'][] = $cart;
  1268. $data['pids'] = array_unique($cart['promotions_id'] ?? []);
  1269. $result[$i] = $data;
  1270. $i++;
  1271. }
  1272. } else {
  1273. $data['cart'][] = $cart;
  1274. $data['pids'] = array_unique($cart['promotions_id'] ?? []);
  1275. $result[$i] = $data;
  1276. $i++;
  1277. }
  1278. }
  1279. foreach ($result as $key => &$item) {
  1280. if ($item['pids']) {
  1281. foreach ($item['pids'] as $key => $id) {
  1282. $item['promotions'][] = $promotions[$id] ?? [];
  1283. }
  1284. }
  1285. }
  1286. }
  1287. return $result;
  1288. }
  1289. /**
  1290. * 控制购物车加入商品最大数量
  1291. * @param int $uid
  1292. * @param int $tourist_uid
  1293. * @param int $store_id
  1294. * @param int $staff_id
  1295. * @return bool
  1296. * @throws \think\db\exception\DataNotFoundException
  1297. * @throws \think\db\exception\DbException
  1298. * @throws \think\db\exception\ModelNotFoundException
  1299. */
  1300. public function controlCartNum(int $uid, int $tourist_uid = 0, int $store_id = 0, int $staff_id = 0)
  1301. {
  1302. $maxCartNum = $this->maxCartNum;
  1303. $where = [
  1304. 'is_del' => 0,
  1305. 'is_new' => 0,
  1306. 'is_pay' => 0,
  1307. 'status' => 1
  1308. ];
  1309. if ($uid) $where['uid'] = $uid;
  1310. if ($tourist_uid) $where['tourist_uid'] = $tourist_uid;
  1311. if ($store_id) $where['store_id'] = $store_id;
  1312. if ($staff_id) $where['staff_id'] = $staff_id;
  1313. try {
  1314. $count = $this->dao->count($where);
  1315. if ($count >= $maxCartNum) {//删除一个最早加入购物车商品
  1316. $one = $this->dao->search($where)->order('id asc')->find();
  1317. if ($one) {
  1318. $this->dao->delete($one['id']);
  1319. }
  1320. }
  1321. } catch (\Throwable $e) {
  1322. \think\facade\Log::error('自动控制购物车数量,删除最早加入商品失败:' . $e->getMessage());
  1323. }
  1324. return true;
  1325. }
  1326. /**
  1327. * 检测限购
  1328. * @param int $uid
  1329. * @param int $product_id
  1330. * @param int $num
  1331. * @param bool $new
  1332. * @param int $store_id
  1333. * @return bool
  1334. */
  1335. public function checkLimit(int $uid, int $product_id, int $num, bool $new = false, int $store_id = 0)
  1336. {
  1337. /** @var StoreProductServices $productServices */
  1338. $productServices = app()->make(StoreProductServices::class);
  1339. /** @var StoreOrderCartInfoServices $orderCartServices */
  1340. $orderCartServices = app()->make(StoreOrderCartInfoServices::class);
  1341. $limitInfo = $productServices->get($product_id, ['id', 'pid', 'is_limit', 'limit_type', 'limit_num']);
  1342. if (!$limitInfo) throw new ValidateException('商品不存在');
  1343. $limitInfo = $limitInfo->toArray();
  1344. if (!$limitInfo['is_limit']) return true;
  1345. $cartNum = 0;
  1346. //收银台游客限购
  1347. $tourist_uid = 0;
  1348. if (!$uid) {
  1349. $tourist_uid = $this->getItem('tourist_uid', '');
  1350. }
  1351. $pid = $limitInfo['pid'] ? $limitInfo['pid'] : $limitInfo['id'];
  1352. $product_ids = $productServices->getColumn(['pid' => $pid], 'id');
  1353. $product_ids[] = $pid;
  1354. if (!$new) {// 购物车商品数量
  1355. $cartNum = $this->dao->sum(['uid' => $uid, 'tourist_uid' => $tourist_uid, 'product_id' => $product_ids, 'store_id' => $store_id, 'status' => 1, 'is_del' => 0], 'cart_num', true);
  1356. }
  1357. if ($limitInfo['limit_type'] == 1) {
  1358. if (($num + $cartNum) > $limitInfo['limit_num']) {
  1359. throw new ValidateException('单次购买不能超过 ' . $limitInfo['limit_num'] . ' 件');
  1360. }
  1361. } else if ($limitInfo['limit_type'] == 2) {
  1362. $orderPayNum = $orderCartServices->sum(['uid' => $uid, 'product_id' => $product_ids], 'cart_num');
  1363. $orderRefundNum = $orderCartServices->sum(['uid' => $uid, 'product_id' => $product_ids], 'refund_num');
  1364. $orderNum = $cartNum + $orderPayNum - $orderRefundNum;
  1365. if (($num + $orderNum) > $limitInfo['limit_num']) {
  1366. throw new ValidateException('该商品限购 ' . $limitInfo['limit_num'] . ' 件,您已经购买 ' . $orderNum . ' 件');
  1367. }
  1368. }
  1369. return true;
  1370. }
  1371. /**
  1372. * 计算用户购物车商品(优惠活动、最优优惠券)
  1373. * @param array $user
  1374. * @param $cartId
  1375. * @param bool $new
  1376. * @param int $addressId
  1377. * @param int $shipping_type
  1378. * @param int $store_id
  1379. * @return array
  1380. * @throws \Psr\SimpleCache\InvalidArgumentException
  1381. * @throws \think\db\exception\DataNotFoundException
  1382. * @throws \think\db\exception\DbException
  1383. * @throws \think\db\exception\ModelNotFoundException
  1384. * @throws \throwable
  1385. */
  1386. public function computeUserCart(array $user, $cartId, bool $new, int $addressId, int $shipping_type = 1, int $store_id = 0)
  1387. {
  1388. $addr = $data = [];
  1389. $uid = (int)$user['uid'];
  1390. /** @var UserAddressServices $addressServices */
  1391. $addressServices = app()->make(UserAddressServices::class);
  1392. if ($addressId) {
  1393. $addr = $addressServices->getAdderssCache($addressId);
  1394. }
  1395. //没传地址id或地址已删除未找到 ||获取默认地址
  1396. if (!$addr) {
  1397. $addr = $addressServices->getUserDefaultAddressCache($uid);
  1398. }
  1399. $data['upgrade_addr'] = 0;
  1400. if ($addr) {
  1401. $addr = is_object($addr) ? $addr->toArray() : $addr;
  1402. if (isset($addr['upgrade']) && $addr['upgrade'] == 0) {
  1403. $data['upgrade_addr'] = 1;
  1404. }
  1405. } else {
  1406. $addr = [];
  1407. }
  1408. if ($store_id) {
  1409. /** @var SystemStoreServices $storeServices */
  1410. $storeServices = app()->make(SystemStoreServices::class);
  1411. $storeServices->getStoreInfo($store_id);
  1412. }
  1413. //获取购物车信息
  1414. $cartGroup = $this->getUserProductCartListV1($uid, $cartId, $new, $addr, $shipping_type, $store_id, 0, true);
  1415. $storeFreePostage = floatval(sys_config('store_free_postage')) ?: 0;//满额包邮金额
  1416. $valid = $cartGroup['valid'] ?? [];
  1417. /** @var StoreOrderComputedServices $computedServices */
  1418. $computedServices = app()->make(StoreOrderComputedServices::class);
  1419. $priceGroup = $computedServices->getOrderPriceGroup($valid, $addr, $user, $storeFreePostage);
  1420. $invalid = $cartGroup['invalid'] ?? [];
  1421. $deduction = $cartGroup['deduction'] ?? [];
  1422. $coupon = $cartGroup['useCoupon'] ?? [];
  1423. $promotions = $cartGroup['promotions'] ?? [];
  1424. $giveCartList = $cartGroup['giveCartList'] ?? [];
  1425. $couponPrice = $cartGroup['couponPrice'] ?? 0;
  1426. $firstOrderPrice = $cartGroup['firstOrderPrice'];
  1427. $cartList = array_merge($valid, $giveCartList);
  1428. $promotionsPrice = 0;
  1429. if ($cartList) {
  1430. foreach ($cartList as $key => $cart) {
  1431. if (isset($cart['promotions_true_price']) && isset($cart['price_type']) && $cart['price_type'] == 'promotions') {
  1432. $promotionsPrice = bcadd((string)$promotionsPrice, (string)bcmul((string)$cart['promotions_true_price'], (string)$cart['cart_num'], 2), 2);
  1433. }
  1434. }
  1435. }
  1436. $deduction['promotions_price'] = $promotionsPrice;
  1437. $deduction['coupon_price'] = $couponPrice;
  1438. $deduction['first_order_price'] = $firstOrderPrice;
  1439. $deduction['sum_price'] = $priceGroup['sumPrice'];
  1440. $deduction['vip_price'] = $priceGroup['vipPrice'];
  1441. $payPrice = (float)$priceGroup['totalPrice'];
  1442. if ($couponPrice < $payPrice) {//优惠券金额
  1443. $payPrice = bcsub((string)$payPrice, (string)$couponPrice, 2);
  1444. } else {
  1445. $payPrice = 0;
  1446. }
  1447. if ($firstOrderPrice < $payPrice) {//首单优惠金额
  1448. $payPrice = bcsub((string)$payPrice, (string)$firstOrderPrice, 2);
  1449. } else {
  1450. $payPrice = 0;
  1451. }
  1452. $deduction['pay_price'] = $payPrice;
  1453. return compact('promotions', 'coupon', 'deduction');
  1454. }
  1455. }