Express.php 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
  4. // +----------------------------------------------------------------------
  5. // | Copyright (c) 2017~2021 https://www.yiovo.com All rights reserved.
  6. // +----------------------------------------------------------------------
  7. // | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
  8. // +----------------------------------------------------------------------
  9. // | Author: 萤火科技 <admin@yiovo.com>
  10. // +----------------------------------------------------------------------
  11. declare (strict_types=1);
  12. namespace app\common\service\delivery;
  13. use app\common\model\DeliveryLimit as DeliveryLimitModel;
  14. use app\common\enum\Setting as SettingEnum;
  15. use app\common\library\helper;
  16. use app\common\model\store\Setting as SettingModel;
  17. use app\common\model\Delivery as DeliveryModel;
  18. use app\common\enum\OrderType as OrderTypeEnum;
  19. use app\common\service\BaseService;
  20. /**
  21. * 快递配送服务类
  22. * Class Delivery
  23. * @package app\common\service
  24. */
  25. class Express extends BaseService
  26. {
  27. private $cityId; // 用户收货城市id
  28. private $goodsList; // 订单商品列表
  29. private $notInRuleGoodsId; // 不在配送范围的商品ID
  30. // 运费模板数据集
  31. private $data = [];
  32. /**
  33. * 构造方法
  34. * Express constructor.
  35. * @param int $cityId 城市ID
  36. * @param $goodsList
  37. * @throws \think\db\exception\DataNotFoundException
  38. * @throws \think\db\exception\DbException
  39. * @throws \think\db\exception\ModelNotFoundException
  40. */
  41. public function __construct(int $cityId, $goodsList)
  42. {
  43. parent::__construct();
  44. // 赋值传参
  45. $this->cityId = $cityId;
  46. $this->goodsList = $goodsList;
  47. // 整合运费模板
  48. $this->initDeliveryTemplate();
  49. }
  50. /**
  51. * 验证用户收货地址是否在限购范围
  52. * @return bool
  53. */
  54. public function isDeliveryLimit()
  55. {
  56. if (!$this->cityId) return false;
  57. $detail = DeliveryLimitModel::detail(DeliveryLimitModel::DEFAULT_ID);
  58. if (in_array($this->cityId,$detail['region'])) {
  59. return false;
  60. }
  61. return true;
  62. }
  63. /**
  64. * 验证用户收货地址是否在配送范围
  65. * @return bool
  66. */
  67. public function isIntraRegion()
  68. {
  69. if (!$this->cityId) return false;
  70. foreach ($this->data as $item) {
  71. $cityIds = [];
  72. foreach ($item['delivery']['rule'] as $ruleItem) {
  73. $cityIds = array_merge($cityIds, $ruleItem['region']);
  74. }
  75. if (!in_array($this->cityId, $cityIds)) {
  76. $this->notInRuleGoodsId = current($item['goodsList'])['goods_id'];
  77. return false;
  78. }
  79. }
  80. return true;
  81. }
  82. /**
  83. * 获取不在配送范围的商品名称
  84. * @return null
  85. */
  86. public function getNotInRuleGoodsName()
  87. {
  88. $item = helper::getArrayItemByColumn($this->goodsList, 'goods_id', $this->notInRuleGoodsId);
  89. return !empty($item) ? $item['goods_name'] : null;
  90. }
  91. /**
  92. * 获取订单的配送费用
  93. * @return string
  94. * @throws \think\db\exception\DataNotFoundException
  95. * @throws \think\db\exception\DbException
  96. * @throws \think\db\exception\ModelNotFoundException
  97. */
  98. public function getDeliveryFee()
  99. {
  100. if (empty($this->cityId) || empty($this->goodsList) || $this->notInRuleGoodsId > 0) {
  101. return helper::number2(0.00);
  102. }
  103. // 处理商品包邮
  104. $this->freeshipping();
  105. // 计算配送金额
  106. foreach ($this->data as &$item) {
  107. // 计算当前配送模板的运费
  108. $item['delivery_fee'] = $this->calcDeliveryAmount($item);
  109. }
  110. // 根据运费组合策略获取最终运费金额
  111. return helper::number2($this->getFinalFreight());
  112. }
  113. /**
  114. * 根据运费组合策略 计算最终运费
  115. * @return float|int|mixed
  116. * @throws \think\db\exception\DataNotFoundException
  117. * @throws \think\db\exception\DbException
  118. * @throws \think\db\exception\ModelNotFoundException\
  119. */
  120. private function getFinalFreight()
  121. {
  122. // 运费合集
  123. $expressPriceArr = helper::getArrayColumn($this->data, 'delivery_fee');
  124. // 最终运费金额
  125. $expressPrice = 0.00;
  126. // 判断运费组合策略
  127. switch (SettingModel::getItem('trade')['freight_rule']) {
  128. case '10': // 策略1: 叠加
  129. $expressPrice = array_sum($expressPriceArr);
  130. break;
  131. case '20': // 策略2: 以最低运费结算
  132. $expressPrice = min($expressPriceArr);
  133. break;
  134. case '30': // 策略3: 以最高运费结算
  135. $expressPrice = max($expressPriceArr);
  136. break;
  137. }
  138. return $expressPrice;
  139. }
  140. /**
  141. * 商品满额包邮
  142. * @return bool
  143. * @throws \think\db\exception\DataNotFoundException
  144. * @throws \think\db\exception\DbException
  145. * @throws \think\db\exception\ModelNotFoundException
  146. */
  147. private function freeshipping()
  148. {
  149. // 订单商品总金额
  150. $orderTotalPrice = helper::getArrayColumnSum($this->goodsList, 'total_price');
  151. // 获取满额包邮设置
  152. $options = SettingModel::getItem(SettingEnum::FULL_FREE);
  153. foreach ($this->data as &$item) {
  154. $item['free_goods_list'] = [];
  155. foreach ($item['goodsList'] as $goodsItem) {
  156. if (
  157. $options['is_open'] == true
  158. && $orderTotalPrice >= $options['money']
  159. && !in_array($goodsItem['goods_id'], $options['excludedGoodsIds'])
  160. && !in_array($this->cityId, $options['excludedRegions']['cityIds'])
  161. ) {
  162. $item['free_goods_list'][] = $goodsItem['goods_id'];
  163. }
  164. }
  165. }
  166. return true;
  167. }
  168. /**
  169. * 计算当前配送模板的运费
  170. * @param $item
  171. * @return float|mixed|string
  172. */
  173. private function calcDeliveryAmount($item)
  174. {
  175. // 获取运费模板下商品总数量or总重量
  176. if (!$totality = $this->getItemGoodsTotal($item)) {
  177. return 0.00;
  178. }
  179. // 当前收货城市配送规则
  180. $deliveryRule = $this->getCityDeliveryRule($item['delivery']);
  181. if ($totality <= $deliveryRule['first']) {
  182. return $deliveryRule['first_fee'];
  183. }
  184. // 续件or续重 数量
  185. $additional = $totality - $deliveryRule['first'];
  186. if ($additional <= $deliveryRule['additional']) {
  187. return helper::bcadd($deliveryRule['first_fee'], $deliveryRule['additional_fee']);
  188. }
  189. // 计算续重/件金额
  190. if ($deliveryRule['additional'] < 1) {
  191. // 配送规则中续件为0
  192. $additionalFee = 0.00;
  193. } else {
  194. $num = ceil($additional/$deliveryRule['additional']);//向上取整
  195. $additionalFee = helper::bcmul($deliveryRule['additional_fee'], $num);
  196. }
  197. return helper::bcadd($deliveryRule['first_fee'], $additionalFee);
  198. }
  199. /**
  200. * 获取运费模板下商品总数量or总重量
  201. * @param $item
  202. * @return int|string
  203. */
  204. private function getItemGoodsTotal($item)
  205. {
  206. $totalWeight = 0; // 总重量
  207. $totalNum = 0; // 总数量
  208. foreach ($item['goodsList'] as $goodsItem) {
  209. // 如果商品为包邮,则不计算总量中
  210. if (!in_array($goodsItem['goods_id'], $item['free_goods_list'])) {
  211. $goodsWeight = helper::bcmul($goodsItem['skuInfo']['goods_gross_weight'], $goodsItem['total_num']);
  212. $totalWeight = helper::bcadd($totalWeight, $goodsWeight);
  213. $totalNum = helper::bcadd($totalNum, $goodsItem['total_num']);
  214. }
  215. }
  216. return $item['delivery']['method'] == 10 ? $totalNum : $totalWeight;
  217. }
  218. /**
  219. * 根据城市id获取规则信息
  220. * @param
  221. * @return array|false
  222. */
  223. private function getCityDeliveryRule($delivery)
  224. {
  225. foreach ($delivery['rule'] as $item) {
  226. if (in_array($this->cityId, $item['region'])) {
  227. return $item;
  228. }
  229. }
  230. return false;
  231. }
  232. /**
  233. * 整合运费模板
  234. * @return bool
  235. * @throws \think\db\exception\DataNotFoundException
  236. * @throws \think\db\exception\DbException
  237. * @throws \think\db\exception\ModelNotFoundException
  238. */
  239. private function initDeliveryTemplate()
  240. {
  241. // 运费模板ID集
  242. $deliveryIds = helper::getArrayColumn($this->goodsList, 'delivery_id');
  243. // 运费模板列表
  244. $deliveryList = (new DeliveryModel)->getListByIds(array_values(array_unique($deliveryIds)));
  245. // 整理数据集
  246. foreach ($deliveryList as $item) {
  247. $this->data[$item['delivery_id']]['delivery'] = $item;
  248. $this->data[$item['delivery_id']]['goodsList'] = $this->getGoodsListByDeliveryId($item['delivery_id']);
  249. }
  250. return true;
  251. }
  252. /**
  253. * 根据运费模板id整理商品集
  254. * @param $deliveryId
  255. * @return array
  256. */
  257. private function getGoodsListByDeliveryId($deliveryId)
  258. {
  259. $data = [];
  260. foreach ($this->goodsList as $item) {
  261. $item['delivery_id'] == $deliveryId && $data[] = $item;
  262. }
  263. return $data;
  264. }
  265. }