StoreOrderComputedServices.php 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709
  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. namespace app\services\order;
  12. use app\services\activity\discounts\StoreDiscountsServices;
  13. use app\services\BaseServices;
  14. use app\dao\order\StoreOrderDao;
  15. use app\services\other\CityAreaServices;
  16. use app\services\pay\PayServices;
  17. use app\services\product\brand\StoreBrandServices;
  18. use app\services\product\category\StoreProductCategoryServices;
  19. use app\services\user\member\MemberCardServices;
  20. use app\services\user\UserServices;
  21. use think\exception\ValidateException;
  22. use app\services\user\UserAddressServices;
  23. use app\services\activity\coupon\StoreCouponUserServices;
  24. use app\services\product\shipping\ShippingTemplatesFreeServices;
  25. use app\services\product\shipping\ShippingTemplatesRegionServices;
  26. use app\services\product\shipping\ShippingTemplatesServices;
  27. use function Swoole\Coroutine\batch;
  28. /**
  29. * 订单计算金额
  30. * Class StoreOrderComputedServices
  31. * @package app\services\order
  32. * @mixin StoreOrderDao
  33. */
  34. class StoreOrderComputedServices extends BaseServices
  35. {
  36. /**
  37. * 支付类型
  38. * @var string[]
  39. */
  40. public $payType = ['weixin' => '微信支付', 'yue' => '余额支付', 'offline' => '线下支付', 'pc' => 'pc'];
  41. /**
  42. * 额外参数
  43. * @var array
  44. */
  45. protected $paramData = [];
  46. /**
  47. * StoreOrderComputedServices constructor.
  48. * @param StoreOrderDao $dao
  49. */
  50. public function __construct(StoreOrderDao $dao)
  51. {
  52. $this->dao = $dao;
  53. }
  54. /**
  55. * 设置额外参数
  56. * @param array $paramData
  57. * @return $this
  58. */
  59. public function setParamData(array $paramData)
  60. {
  61. $this->paramData = $paramData;
  62. return $this;
  63. }
  64. /**
  65. * 计算订单金额
  66. * @param int $uid
  67. * @param array $userInfo
  68. * @param array $cartGroup
  69. * @param int $addressId
  70. * @param string $payType
  71. * @param bool $useIntegral
  72. * @param int $couponId
  73. * @param int $shippingType
  74. * @return array
  75. */
  76. public function computedOrder(int $uid, array $userInfo = [], array $cartGroup, int $addressId, string $payType, bool $useIntegral = false, int $couponId = 0, int $shippingType = 1)
  77. {
  78. $offlinePayStatus = (int)sys_config('offline_pay_status') ?? (int)2;
  79. $systemPayType = PayServices::PAY_TYPE;
  80. if ($offlinePayStatus == 2) unset($systemPayType['offline']);
  81. if ($payType && !array_key_exists($payType, $systemPayType)) {
  82. throw new ValidateException('选择支付方式有误');
  83. }
  84. if (!$userInfo) {
  85. /** @var UserServices $userServices */
  86. $userServices = app()->make(UserServices::class);
  87. $userInfo = $userServices->getUserCacheInfo($uid);
  88. if (!$userInfo) {
  89. throw new ValidateException('用户不存在!');
  90. }
  91. }
  92. $cartInfo = $cartGroup['cartInfo'];
  93. $priceGroup = $cartGroup['priceGroup'];
  94. $deduction = $cartGroup['deduction'];
  95. $other = $cartGroup['other'];
  96. $promotions = $other['promotions'] ?? [];
  97. $payPrice = (float)$priceGroup['totalPrice'];
  98. $couponPrice = (float)$priceGroup['couponPrice'];
  99. $firstOrderPrice = (float)$priceGroup['firstOrderPrice'];
  100. $addr = $cartGroup['addr'] ?? [];
  101. $postage = $priceGroup;
  102. if (!$addr || $addr['id'] != $addressId) {
  103. /** @var UserAddressServices $addressServices */
  104. $addressServices = app()->make(UserAddressServices::class);
  105. $addr = $addressServices->getAdderssCache($addressId);
  106. //改变地址重新计算邮费
  107. $postage = [];
  108. }
  109. $combinationId = $this->paramData['combinationId'] ?? 0;
  110. $seckillId = $this->paramData['seckill_id'] ?? 0;
  111. $bargainId = $this->paramData['bargainId'] ?? 0;
  112. $newcomerId = $this->paramData['newcomerId'] ?? 0;
  113. $isActivity = $combinationId || $seckillId || $bargainId || $newcomerId || ($priceGroup['awardPrice'] > 0);
  114. $type = (int)$deduction['type'] ?? 0;
  115. $results = batch([
  116. // 'coupon' => function() use ($couponId, $uid, $cartInfo, $payPrice, $isActivity, $promotions) {
  117. // if (!$isActivity) {
  118. // try{
  119. // return $this->useCouponId($couponId, $uid, $cartInfo, $payPrice, $promotions);
  120. // }catch (\Throwable $e){
  121. // return $e->getMessage();
  122. // }
  123. // }
  124. // return [$payPrice, 0];
  125. // },
  126. 'promotions' => function () use ($cartInfo, $type) {
  127. $promotionsPrice = 0;
  128. if ($type == 8) return $promotionsPrice;
  129. foreach ($cartInfo as $key => $cart) {
  130. if (isset($cart['promotions_true_price']) && isset($cart['price_type']) && $cart['price_type'] == 'promotions') {
  131. $promotionsPrice = bcadd((string)$promotionsPrice, (string)bcmul((string)$cart['promotions_true_price'], (string)$cart['cart_num'], 2), 2);
  132. }
  133. }
  134. return $promotionsPrice;
  135. },
  136. 'postage' => function () use ($shippingType, $payType, $cartInfo, $addr, $payPrice, $postage, $other, $userInfo, $type) {
  137. if ($type == 8 || $type == 10) $shippingType = 2;
  138. return $this->computedPayPostage($shippingType, $payType, $cartInfo, $addr, $payPrice, $postage, $other, $userInfo);
  139. },
  140. ]);
  141. // if ($results['coupon'] && is_string($results['coupon'])) {
  142. // throw new ValidateException($results['coupon']);
  143. // }
  144. $promotionsDetail = [];
  145. if ($promotions) {
  146. foreach ($promotions as $key => $value) {
  147. if (isset($value['details']['sum_promotions_price']) && $value['details']['sum_promotions_price']) {
  148. $promotionsDetail[] = ['id' => $value['id'], 'name' => $value['name'], 'title' => $value['title'], 'desc' => $value['desc'], 'promotions_price' => $value['details']['sum_promotions_price'], 'promotions_type' => $value['promotions_type']];
  149. }
  150. }
  151. if ($promotionsDetail) {
  152. $typeArr = array_column($promotionsDetail, 'promotions_type');
  153. array_multisort($typeArr, SORT_ASC, $promotionsDetail);
  154. }
  155. }
  156. // [$p, $couponPrice] = $results['coupon'];
  157. [$p, $payPostage, $storePostageDiscount, $storeFreePostage, $isStoreFreePostage] = $results['postage'];
  158. if ($couponPrice < $payPrice) {//优惠券金额
  159. $payPrice = bcsub((string)$payPrice, (string)$couponPrice, 2);
  160. } else {
  161. $payPrice = 0;
  162. }
  163. if ($type == 8) {
  164. $firstOrderPrice = 0;
  165. $payPrice = 0;
  166. }
  167. if ($firstOrderPrice < $payPrice) {//首单优惠金额
  168. $payPrice = bcsub((string)$payPrice, (string)$firstOrderPrice, 2);
  169. } else {
  170. $payPrice = 0;
  171. }
  172. if (sys_config('integral_ratio_status') && !$isActivity) {
  173. //使用积分
  174. [$payPrice, $deductionPrice, $usedIntegral, $SurplusIntegral] = $this->useIntegral($useIntegral, $userInfo, $payPrice, $other);
  175. }
  176. $payPrice = (float)bcadd((string)$payPrice, (string)$payPostage, 2);
  177. foreach ($cartInfo as &$item) {
  178. $item['invalid'] = false;
  179. if ($shippingType === 2 && $item['productInfo']['store_mention']) {
  180. $item['invalid'] = true;
  181. }
  182. }
  183. $result = [
  184. 'total_price' => $priceGroup['totalPrice'],
  185. 'pay_price' => max($payPrice, 0),
  186. 'total_postage' => bcadd((string)$payPostage, (string)($storePostageDiscount ?? 0), 2),
  187. 'pay_postage' => $payPostage,
  188. 'first_order_price' => $firstOrderPrice ?? 0,
  189. 'coupon_price' => $couponPrice ?? 0,
  190. 'promotions_price' => $results['promotions'] ?? 0,
  191. 'promotions_detail' => $promotionsDetail,
  192. 'deduction_price' => $deductionPrice ?? 0,
  193. 'usedIntegral' => $usedIntegral ?? 0,
  194. 'SurplusIntegral' => $SurplusIntegral ?? 0,
  195. 'storePostageDiscount' => $storePostageDiscount ?? 0,
  196. 'isStoreFreePostage' => $isStoreFreePostage ?? false,
  197. 'storeFreePostage' => $storeFreePostage ?? 0,
  198. 'cartInfo' => $cartInfo
  199. ];
  200. $this->paramData = [];
  201. return $result;
  202. }
  203. /**
  204. * 使用优惠卷
  205. * @param int $couponId
  206. * @param int $uid
  207. * @param $cartInfo
  208. * @param $payPrice
  209. */
  210. public function useCouponId(int $couponId, int $uid, $cartInfo, $payPrice, $promotions)
  211. {
  212. //使用优惠劵
  213. $couponPrice = 0;
  214. if ($couponId && $cartInfo) {
  215. /** @var StoreCouponUserServices $couponServices */
  216. $couponServices = app()->make(StoreCouponUserServices::class);
  217. $couponInfo = $couponServices->getOne([['id', '=', $couponId], ['uid', '=', $uid], ['is_fail', '=', 0], ['status', '=', 0], ['start_time', '<=', time()], ['end_time', '>=', time()]], '*', ['issue']);
  218. if (!$couponInfo) {
  219. throw new ValidateException('选择的优惠劵无效!');
  220. }
  221. $type = $couponInfo['applicable_type'] ?? 0;
  222. $flag = false;
  223. $price = 0;
  224. $count = 0;
  225. $promotionsList = [];
  226. if ($promotions) {
  227. $promotionsList = array_combine(array_column($promotions, 'id'), $promotions);
  228. }
  229. $isOverlay = function ($cart) use ($promotionsList) {
  230. $productInfo = $cart['productInfo'] ?? [];
  231. if (!$productInfo) {
  232. return false;
  233. }
  234. //门店独立商品 不使用优惠券
  235. $isBranchProduct = isset($productInfo['type']) && isset($productInfo['pid']) && $productInfo['type'] == 1 && !$productInfo['pid'];
  236. if ($isBranchProduct) {
  237. return false;
  238. }
  239. if (isset($cart['promotions_id']) && $cart['promotions_id']) {
  240. foreach ($cart['promotions_id'] as $key => $promotions_id) {
  241. $promotions = $promotionsList[$promotions_id] ?? [];
  242. if ($promotions && $promotions['promotions_type'] != 4) {
  243. $overlay = is_string($promotions['overlay']) ? explode(',', $promotions['overlay']) : $promotions['overlay'];
  244. if (!in_array(5, $overlay)) {
  245. return false;
  246. }
  247. }
  248. }
  249. }
  250. return true;
  251. };
  252. switch ($type) {
  253. case 0:
  254. foreach ($cartInfo as $cart) {
  255. if (!$isOverlay($cart)) continue;
  256. $price = bcadd($price, bcmul((string)$cart['truePrice'], (string)$cart['cart_num'], 2), 2);
  257. $count++;
  258. }
  259. break;
  260. case 1://品类券
  261. /** @var StoreProductCategoryServices $storeCategoryServices */
  262. $storeCategoryServices = app()->make(StoreProductCategoryServices::class);
  263. $cateGorys = $storeCategoryServices->getAllById((int)$couponInfo['category_id']);
  264. if ($cateGorys) {
  265. $cateIds = array_column($cateGorys, 'id');
  266. foreach ($cartInfo as $cart) {
  267. if (!$isOverlay($cart)) continue;
  268. if (isset($cart['productInfo']['cate_id']) && array_intersect(explode(',', $cart['productInfo']['cate_id']), $cateIds)) {
  269. $price = bcadd($price, bcmul((string)$cart['truePrice'], (string)$cart['cart_num'], 2), 2);
  270. $count++;
  271. }
  272. }
  273. }
  274. break;
  275. case 2:
  276. foreach ($cartInfo as $cart) {
  277. if (!$isOverlay($cart)) continue;
  278. $product_id = isset($cart['productInfo']['pid']) && $cart['productInfo']['pid'] ? $cart['productInfo']['pid'] : ($cart['product_id'] ?? 0);
  279. if ($product_id && in_array($product_id, explode(',', $couponInfo['product_id']))) {
  280. $price = bcadd((string)$price, (string)bcmul((string)$cart['truePrice'], (string)$cart['cart_num'], 2), 2);
  281. $count++;
  282. }
  283. }
  284. case 3:
  285. /** @var StoreBrandServices $storeBrandServices */
  286. $storeBrandServices = app()->make(StoreBrandServices::class);
  287. $brands = $storeBrandServices->getAllById((int)$couponInfo['brand_id']);
  288. if ($brands) {
  289. $brandIds = array_column($brands, 'id');
  290. foreach ($cartInfo as $cart) {
  291. if (!$isOverlay($cart)) continue;
  292. if (isset($cart['productInfo']['brand_id']) && in_array($cart['productInfo']['brand_id'], $brandIds)) {
  293. $price = bcadd((string)$price, (string)bcmul((string)$cart['truePrice'], (string)$cart['cart_num'], 2), 2);
  294. $count++;
  295. }
  296. }
  297. }
  298. break;
  299. }
  300. if ($count && $couponInfo['use_min_price'] <= $price) {
  301. $flag = true;
  302. }
  303. if (!$flag) {
  304. return [$payPrice, 0];
  305. // throw new ValidateException('不满足优惠劵的使用条件!');
  306. }
  307. //满减券
  308. if ($couponInfo['coupon_type'] == 1) {
  309. $couponPrice = $couponInfo['coupon_price'];
  310. } else {
  311. if ($couponInfo['coupon_price'] <= 0) {//0折
  312. $couponPrice = $price;
  313. } else if ($couponInfo['coupon_price'] >= 100) {
  314. $couponPrice = 0;
  315. } else {
  316. $truePrice = (float)bcmul((string)$price, bcdiv((string)$couponInfo['coupon_price'], '100', 2), 2);
  317. $couponPrice = (float)bcsub((string)$price, (string)$truePrice, 2);
  318. }
  319. }
  320. if ($couponPrice < $payPrice) {
  321. $payPrice = (float)bcsub((string)$payPrice, (string)$couponPrice, 2);
  322. } else {
  323. $couponPrice = $payPrice;
  324. $payPrice = 0;
  325. }
  326. }
  327. return [$payPrice, $couponPrice];
  328. }
  329. /**
  330. * 使用积分
  331. * @param $useIntegral
  332. * @param $userInfo
  333. * @param $payPrice
  334. * @param $other
  335. * @return array
  336. */
  337. public function useIntegral(bool $useIntegral, $userInfo, string $payPrice, array $other)
  338. {
  339. $SurplusIntegral = 0;
  340. $deductionPrice = 0;
  341. $usedIntegral = 0;
  342. if ($userInfo && $useIntegral && $userInfo['integral'] > 0 && $payPrice) {
  343. $integralMaxType = sys_config('integral_max_type', 1);//积分抵用上限类型1:积分、2:订单金额比例
  344. if ($integralMaxType == 1) {//最多抵用积分
  345. $integralMaxNum = sys_config('integral_max_num', 200);
  346. if ($integralMaxNum > 0 && $userInfo['integral'] > $integralMaxNum) {
  347. $integral = $integralMaxNum;
  348. } else {
  349. $integral = $userInfo['integral'];
  350. }
  351. $deductionPrice = (float)bcmul((string)$integral, (string)$other['integralRatio'], 2);
  352. if ($deductionPrice < $payPrice) {
  353. $payPrice = bcsub((string)$payPrice, (string)$deductionPrice, 2);
  354. $usedIntegral = $integral;
  355. } else {
  356. if ($other['integralRatio']) {
  357. $deductionPrice = $payPrice;
  358. $usedIntegral = (int)ceil(bcdiv((string)$payPrice, (string)$other['integralRatio'], 2));
  359. }
  360. $payPrice = 0;
  361. }
  362. } else {//最高抵用比率
  363. $integralMaxRate = sys_config('integral_max_rate', 0);
  364. $deductionPrice = (float)bcmul((string)$userInfo['integral'], (string)$other['integralRatio'], 2);
  365. if ($integralMaxRate > 0 && $integralMaxRate <= 100) {
  366. $integralMaxPrice = (float)bcmul((string)$payPrice, (string)bcdiv((string)$integralMaxRate, '100', 2), 2);
  367. } else {
  368. $integralMaxPrice = $payPrice;
  369. }
  370. $deductionPrice = min($deductionPrice, $integralMaxPrice);
  371. $payPrice = bcsub((string)$payPrice, (string)$deductionPrice, 2);
  372. $usedIntegral = ceil(bcdiv((string)$deductionPrice, (string)$other['integralRatio'], 2));
  373. }
  374. if ($payPrice <= 0) $payPrice = 0;
  375. }
  376. $SurplusIntegral = (int)bcsub((string)$userInfo['integral'], (string)$usedIntegral, 0);
  377. return [$payPrice, $deductionPrice, $usedIntegral, $SurplusIntegral];
  378. }
  379. /**
  380. * 计算邮费
  381. * @param int $shipping_type
  382. * @param string $payType
  383. * @param array $cartInfo
  384. * @param array $addr
  385. * @param string $payPrice
  386. * @param array $postage
  387. * @param array $other
  388. * @param array $userInfo
  389. * @return array
  390. */
  391. public function computedPayPostage(int $shipping_type, string $payType, array $cartInfo, array $addr, string $payPrice, array $postage = [], array $other, $userInfo = [])
  392. {
  393. $storePostageDiscount = 0;
  394. $storeFreePostage = $postage['storeFreePostage'] ?? 0;
  395. $isStoreFreePostage = false;
  396. if (!$storeFreePostage) {
  397. $storeFreePostage = floatval(sys_config('store_free_postage')) ?: 0;//满额包邮金额
  398. }
  399. if (!$addr && !isset($addr['id']) || !$cartInfo) {
  400. $payPostage = 0;
  401. } else {
  402. //$shipping_type = 1 快递发货 $shipping_type = 2 门店自提
  403. if ($shipping_type == 2) {
  404. if (!sys_config('store_func_status', 1) || !sys_config('store_self_mention', 1)) $shipping_type = 1;
  405. }
  406. //门店自提 || (线下支付 && 线下支付包邮) 没有邮费支付
  407. if ($shipping_type === 2 || ($payType == 'offline' && ((isset($other['offlinePostage']) && $other['offlinePostage']) || sys_config('offline_postage')) == 1)) {
  408. $payPostage = 0;
  409. } else {
  410. if (!$postage || !isset($postage['storePostage']) || !isset($postage['storePostageDiscount'])) {
  411. $postage = $this->getOrderPriceGroup($cartInfo, $addr, $userInfo, $storeFreePostage);
  412. }
  413. $payPostage = $postage['storePostage'];
  414. $storePostageDiscount = $postage['storePostageDiscount'];
  415. $isStoreFreePostage = $postage['isStoreFreePostage'] ?? false;
  416. $payPrice = (float)bcadd((string)$payPrice, (string)$payPostage, 2);
  417. }
  418. }
  419. return [$payPrice, $payPostage, $storePostageDiscount, $storeFreePostage, $isStoreFreePostage];
  420. }
  421. /**
  422. * 运费计算,总金额计算
  423. * @param $cartInfo
  424. * @param $addr
  425. * @param array $userInfo
  426. * @param null $storeFreePostage
  427. * @return array
  428. * @throws \think\db\exception\DataNotFoundException
  429. * @throws \think\db\exception\DbException
  430. * @throws \think\db\exception\ModelNotFoundException
  431. */
  432. public function getOrderPriceGroup($cartInfo, $addr, $userInfo = [], $storeFreePostage = null)
  433. {
  434. $storePostage = 0;
  435. $storePostageDiscount = 0;
  436. $isStoreFreePostage = false;//是否满额包邮
  437. if (is_null($storeFreePostage)) {
  438. $storeFreePostage = floatval(sys_config('store_free_postage')) ?: 0;//满额包邮金额
  439. }
  440. $sumPrice = $this->getOrderSumPrice($cartInfo, 'sum_price');//获取订单原总金额
  441. $totalPrice = $this->getOrderSumPrice($cartInfo, 'truePrice');//获取订单svip、用户等级优惠之后总金额
  442. $costPrice = $this->getOrderSumPrice($cartInfo, 'costPrice');//获取订单成本价
  443. $awardPrice = $this->getOrderSumPrice($cartInfo, 'awardPrice');//获取订单成本价
  444. $vipPrice = $this->getOrderSumPrice($cartInfo, 'vip_truePrice');//获取订单会员优惠金额
  445. //如果满额包邮等于0
  446. $free_shipping = 0;
  447. $postageArr = [];
  448. if (isset($cartInfo[0]['productInfo']['product_type']) && in_array($cartInfo[0]['productInfo']['product_type'], [1, 2])) {
  449. $storePostage = 0;
  450. } elseif ($cartInfo && $addr) {
  451. //优惠套餐包邮判断
  452. if (isset($cartInfo[0]['type']) && $cartInfo[0]['type'] == 5 && isset($cartInfo[0]['activity_id']) && $cartInfo[0]['activity_id']) {
  453. /** @var StoreDiscountsServices $discountService */
  454. $discountService = app()->make(StoreDiscountsServices::class);
  455. $free_shipping = $discountService->value(['id' => $cartInfo[0]['activity_id']], 'free_shipping');
  456. }
  457. if ($free_shipping) {
  458. $storePostage = 0;
  459. } else if ($storeFreePostage && $sumPrice >= $storeFreePostage) {//如果总价大于等于满额包邮 邮费等于0
  460. $isStoreFreePostage = true;
  461. $storePostage = 0;
  462. } else {
  463. // 判断商品包邮和固定运费
  464. foreach ($cartInfo as &$item) {
  465. if (!isset($item['productInfo']['freight'])) continue;
  466. if ($item['productInfo']['freight'] == 1) {
  467. $item['postage_price'] = 0;
  468. } elseif ($item['productInfo']['freight'] == 2) {
  469. $item['postage_price'] = bcmul((string)$item['productInfo']['postage'], (string)$item['cart_num'], 2);
  470. $storePostage = bcadd((string)$storePostage, (string)$item['postage_price'], 2);
  471. }
  472. }
  473. //按照运费模板计算每个运费模板下商品的件数/重量/体积以及总金额 按照首重倒序排列
  474. $cityId = (int)($addr['city_id'] ?? 0);
  475. $ids = [];
  476. if ($cityId) {
  477. /** @var CityAreaServices $cityAreaServices */
  478. $cityAreaServices = app()->make(CityAreaServices::class);
  479. $ids = $cityAreaServices->getRelationCityIds($cityId);
  480. }
  481. $cityIds = array_merge([0], $ids);
  482. $tempIds[] = 1;
  483. foreach ($cartInfo as $key_c => $item_c) {
  484. if (isset($item_c['productInfo']['freight']) && $item_c['productInfo']['freight'] == 3) {
  485. $tempIds[] = $item_c['productInfo']['temp_id'];
  486. }
  487. }
  488. $tempIds = array_unique($tempIds);
  489. /** @var ShippingTemplatesServices $shippServices */
  490. $shippServices = app()->make(ShippingTemplatesServices::class);
  491. $temp = $shippServices->getShippingColumnCache(['id' => $tempIds], 'appoint,group', 'id');
  492. /** @var ShippingTemplatesRegionServices $regionServices */
  493. $regionServices = app()->make(ShippingTemplatesRegionServices::class);
  494. $regions = $regionServices->getTempRegionListCache($tempIds, $cityIds);
  495. $temp_num = [];
  496. foreach ($cartInfo as $cart) {
  497. if (isset($cart['productInfo']['freight']) && in_array($cart['productInfo']['freight'], [1, 2])) {
  498. continue;
  499. }
  500. $tempId = $cart['productInfo']['temp_id'] ?? 1;
  501. $group = isset($temp[$tempId]['group']) ? $temp[$tempId]['group'] : $temp[1]['group'];
  502. if ($group == 1) {
  503. $num = $cart['cart_num'];
  504. } elseif ($group == 2) {
  505. $num = $cart['cart_num'] * $cart['productInfo']['attrInfo']['weight'];
  506. } else {
  507. $num = $cart['cart_num'] * $cart['productInfo']['attrInfo']['volume'];
  508. }
  509. $region = isset($regions[$tempId]) ? $regions[$tempId] : ($regions[1] ?? []);
  510. if (!$region) {
  511. continue;
  512. }
  513. if (!isset($temp_num[$tempId])) {
  514. $temp_num[$tempId] = [
  515. 'number' => $num,
  516. 'group' => $group,
  517. 'price' => bcmul($cart['cart_num'], $cart['truePrice'], 2),
  518. 'first' => $region['first'],
  519. 'first_price' => $region['first_price'],
  520. 'continue' => $region['continue'],
  521. 'continue_price' => $region['continue_price'],
  522. 'temp_id' => $tempId
  523. ];
  524. } else {
  525. $temp_num[$tempId]['number'] += $num;
  526. $temp_num[$tempId]['price'] += bcmul($cart['cart_num'], $cart['truePrice'], 2);
  527. }
  528. }
  529. if ($temp_num) {
  530. /** @var ShippingTemplatesFreeServices $freeServices */
  531. $freeServices = app()->make(ShippingTemplatesFreeServices::class);
  532. $freeList = $freeServices->isFreeListCache($tempIds, $cityIds);
  533. if ($freeList) {
  534. foreach ($temp_num as $k => $v) {
  535. if (isset($temp[$v['temp_id']]['appoint']) && $temp[$v['temp_id']]['appoint'] && isset($freeList[$v['temp_id']])) {
  536. $free = $freeList[$v['temp_id']];
  537. $condition = $free['number'] <= $v['number'];
  538. if ($free['price'] <= $v['price'] && $condition) {
  539. unset($temp_num[$k]);
  540. }
  541. }
  542. }
  543. }
  544. //首件运费最大值
  545. $maxFirstPrice = $temp_num ? max(array_column($temp_num, 'first_price')) : 0;
  546. //初始运费为0
  547. $storePostage_arr = [];
  548. $i = 0;
  549. //循环运费数组
  550. foreach ($temp_num as $fk => $fv) {
  551. //找到首件运费等于最大值
  552. if ($fv['first_price'] == $maxFirstPrice) {
  553. //每次循环设置初始值
  554. $tempArr = $temp_num;
  555. $Postage = 0;
  556. //计算首件运费
  557. if ($fv['number'] <= $fv['first']) {
  558. $Postage = bcadd($Postage, $fv['first_price'], 2);
  559. } else {
  560. if ($fv['continue'] <= 0) {
  561. $Postage = $Postage;
  562. } else {
  563. $Postage = bcadd(bcadd($Postage, $fv['first_price'], 2), bcmul(ceil(bcdiv(bcsub($fv['number'], $fv['first'], 2), $fv['continue'] ?? 0, 2)), $fv['continue_price'], 4), 2);
  564. }
  565. }
  566. $postageArr[$i]['data'][$fk] = $Postage;
  567. //删除计算过的首件数据
  568. unset($tempArr[$fk]);
  569. //循环计算剩余运费
  570. foreach ($tempArr as $ck => $cv) {
  571. if ($cv['continue'] <= 0) {
  572. $Postage = $Postage;
  573. } else {
  574. $one_postage = bcmul(ceil(bcdiv($cv['number'], $cv['continue'] ?? 0, 2)), $cv['continue_price'], 2);
  575. $Postage = bcadd($Postage, $one_postage, 2);
  576. $postageArr[$i]['data'][$ck] = $one_postage;
  577. }
  578. }
  579. $postageArr[$i]['sum'] = $Postage;
  580. $storePostage_arr[] = $Postage;
  581. $i++;
  582. }
  583. }
  584. $maxStorePostage = $storePostage_arr ? max($storePostage_arr) : 0;
  585. // //获取运费计算中的最大值
  586. $storePostage = bcadd((string)$storePostage, (string)$maxStorePostage, 2);
  587. }
  588. }
  589. }
  590. //会员邮费享受折扣
  591. if ($storePostage) {
  592. $express_rule_number = 0;
  593. if (!$userInfo) {
  594. /** @var UserServices $userService */
  595. $userService = app()->make(UserServices::class);
  596. $userInfo = $userService->getUserCacheInfo($addr['uid']);
  597. }
  598. if ($userInfo && isset($userInfo['is_money_level']) && $userInfo['is_money_level'] > 0) {
  599. //看是否开启会员折扣奖励
  600. /** @var MemberCardServices $memberCardService */
  601. $memberCardService = app()->make(MemberCardServices::class);
  602. $express_rule_number = $memberCardService->isOpenMemberCardCache('express');
  603. $express_rule_number = $express_rule_number <= 0 ? 0 : $express_rule_number;
  604. }
  605. $truePostageArr = [];
  606. foreach ($postageArr as $postageitem) {
  607. if ($postageitem['sum'] == ($maxStorePostage ?? 0)) {
  608. $truePostageArr = $postageitem['data'];
  609. break;
  610. }
  611. }
  612. $cartAlready = [];
  613. foreach ($cartInfo as &$item) {
  614. if (isset($item['productInfo']['freight']) && in_array($item['productInfo']['freight'], [1, 2])) {
  615. if (isset($item['postage_price']) && $item['postage_price'] && $express_rule_number && $express_rule_number < 100) {
  616. $item['postage_price'] = bcmul($item['postage_price'], bcdiv($express_rule_number, 100, 4), 2);
  617. }
  618. continue;
  619. }
  620. $tempId = $item['productInfo']['temp_id'] ?? 0;
  621. $tempPostage = $truePostageArr[$tempId] ?? 0;
  622. $tempNumber = $temp_num[$tempId]['number'] ?? 0;
  623. if (!$tempId || !$tempPostage) continue;
  624. $group = $temp_num[$tempId]['group'];
  625. if ($group == 1) {
  626. $num = $item['cart_num'];
  627. } elseif ($group == 2) {
  628. $num = $item['cart_num'] * $item['productInfo']['attrInfo']['weight'];
  629. } else {
  630. $num = $item['cart_num'] * $item['productInfo']['attrInfo']['volume'];
  631. }
  632. if ((($cartAlready[$tempId]['number'] ?? 0) + $num) >= $tempNumber) {
  633. $price = isset($cartAlready[$tempId]['price']) ? bcsub((string)$tempPostage, (string)$cartAlready[$tempId]['price'], 6) : $tempPostage;
  634. } else {
  635. $price = bcmul((string)$tempPostage, bcdiv((string)$num, (string)$tempNumber, 6), 6);
  636. }
  637. $cartAlready[$tempId]['number'] = bcadd((string)($cartAlready[$tempId]['number'] ?? 0), (string)$num, 4);
  638. $cartAlready[$tempId]['price'] = bcadd((string)($cartAlready[$tempId]['price'] ?? 0.00), (string)$price, 4);
  639. if ($express_rule_number && $express_rule_number < 100) {
  640. $price = bcmul($price, bcdiv($express_rule_number, 100, 4), 4);
  641. }
  642. $price = sprintf("%.2f", $price);
  643. $item['postage_price'] = $price;
  644. }
  645. if ($express_rule_number && $express_rule_number < 100) {
  646. $storePostageDiscount = $storePostage;
  647. $storePostage = bcmul($storePostage, bcdiv($express_rule_number, 100, 4), 2);
  648. $storePostageDiscount = bcsub($storePostageDiscount, $storePostage, 2);
  649. } else {
  650. $storePostageDiscount = 0;
  651. $storePostage = $storePostage;
  652. }
  653. }
  654. return compact('storePostage', 'awardPrice', 'storeFreePostage', 'isStoreFreePostage', 'sumPrice', 'totalPrice', 'costPrice', 'vipPrice', 'storePostageDiscount', 'cartInfo');
  655. }
  656. /**
  657. * 获取某个字段总金额
  658. * @param $cartInfo
  659. * @param string $key
  660. * @param bool $is_unit
  661. * @return int|string
  662. */
  663. public function getOrderSumPrice($cartInfo, $key = 'truePrice', $is_unit = true)
  664. {
  665. $SumPrice = 0;
  666. foreach ($cartInfo as $cart) {
  667. if (isset($cart['cart_info'])) $cart = $cart['cart_info'];
  668. if (isset($cart['is_gift']) && $cart['is_gift']) {
  669. continue;
  670. }
  671. if ($is_unit) {
  672. $SumPrice = bcadd($SumPrice, bcmul($cart['cart_num'] ?? 1, $cart[$key] ?? 0, 2), 2);
  673. } else {
  674. $SumPrice = bcadd($SumPrice, $cart[$key] ?? 0, 2);
  675. }
  676. }
  677. return $SumPrice;
  678. }
  679. }