Express.php 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. <?php
  2. /**
  3. *
  4. * @copyright ©2020 点小铺
  5. * @author hanj
  6. * @link: https://dyuit.com
  7. * Created by VSCode
  8. */
  9. namespace app\common\service\delivery;
  10. use app\common\library\helper;
  11. use app\common\model\Setting as SettingModel;
  12. use app\common\model\Delivery as DeliveryModel;
  13. use app\common\enum\OrderType as OrderTypeEnum;
  14. /**
  15. * 快递配送服务类
  16. * Class Delivery
  17. * @package app\common\service
  18. */
  19. class Express
  20. {
  21. private $cityId; // 用户收货城市id
  22. private $goodsList; // 订单商品列表
  23. private $orderType; // 订单类型 (主商城、拼团)
  24. private $notInRuleGoodsId; // 不在配送范围的商品ID
  25. // 运费模板数据集
  26. private $data = [];
  27. /**
  28. * 构造方法
  29. * Express constructor.
  30. * @param $cityId
  31. * @param $goodsList
  32. * @param int $orderType
  33. * @throws \think\db\exception\DataNotFoundException
  34. * @throws \think\db\exception\ModelNotFoundException
  35. * @throws \think\exception\DbException
  36. */
  37. public function __construct($cityId, $goodsList, $orderType = OrderTypeEnum::MASTER)
  38. {
  39. // 赋值传参
  40. $this->cityId = $cityId;
  41. $this->goodsList = $goodsList;
  42. $this->orderType = $orderType;
  43. // 整合运费模板
  44. $this->initDeliveryTemplate();
  45. }
  46. /**
  47. * 验证用户收货地址是否在配送范围
  48. * @return bool
  49. */
  50. public function isIntraRegion()
  51. {
  52. if (!$this->cityId) return false;
  53. foreach ($this->data as $item) {
  54. $cityIds = [];
  55. foreach ($item['delivery']['rule'] as $ruleItem) {
  56. $cityIds = array_merge($cityIds, $ruleItem['region_data']);
  57. }
  58. if (!in_array($this->cityId, $cityIds)) {
  59. $this->notInRuleGoodsId = current($item['goodsList'])['goods_id'];
  60. return false;
  61. }
  62. }
  63. return true;
  64. }
  65. /**
  66. * 获取不在配送范围的商品名称
  67. * @return null
  68. */
  69. public function getNotInRuleGoodsName()
  70. {
  71. $item = helper::getArrayItemByColumn($this->goodsList, 'goods_id', $this->notInRuleGoodsId);
  72. return !empty($item) ? $item['goods_name'] : null;
  73. }
  74. /**
  75. * 获取订单的配送费用
  76. * @return float|string
  77. */
  78. public function getDeliveryFee()
  79. {
  80. if (empty($this->cityId) || empty($this->goodsList) || $this->notInRuleGoodsId > 0) {
  81. return helper::number2(0.00);
  82. }
  83. // 处理商品包邮
  84. $this->freeshipping();
  85. // 计算配送金额
  86. foreach ($this->data as &$item) {
  87. // 计算当前配送模板的运费
  88. $item['delivery_fee'] = $this->calcDeliveryAmount($item);
  89. }
  90. // 根据运费组合策略获取最终运费金额
  91. return helper::number2($this->getFinalFreight());
  92. }
  93. /**
  94. * 根据运费组合策略 计算最终运费
  95. * @return double
  96. */
  97. private function getFinalFreight()
  98. {
  99. // 运费合集
  100. $expressPriceArr = helper::getArrayColumn($this->data, 'delivery_fee');
  101. // 最终运费金额
  102. $expressPrice = 0.00;
  103. // 判断运费组合策略
  104. switch (SettingModel::getItem('trade')['freight_rule']) {
  105. case '10': // 策略1: 叠加
  106. $expressPrice = array_sum($expressPriceArr);
  107. break;
  108. case '20': // 策略2: 以最低运费结算
  109. $expressPrice = min($expressPriceArr);
  110. break;
  111. case '30': // 策略3: 以最高运费结算
  112. $expressPrice = max($expressPriceArr);
  113. break;
  114. }
  115. return $expressPrice;
  116. }
  117. /**
  118. * 处理商品包邮
  119. * @return bool
  120. */
  121. private function freeshipping()
  122. {
  123. // 订单商品总金额
  124. $orderTotalPrice = helper::getArrayColumnSum($this->goodsList, 'total_price');
  125. // 获取满额包邮设置
  126. $options = SettingModel::getItem('full_free');
  127. foreach ($this->data as &$item) {
  128. $item['free_goods_list'] = [];
  129. foreach ($item['goodsList'] as $goodsItem) {
  130. if (
  131. $this->orderType === OrderTypeEnum::MASTER
  132. && $options['is_open'] == true
  133. && $orderTotalPrice >= $options['money']
  134. && !in_array($goodsItem['goods_id'], $options['notin_goods'])
  135. && !in_array($this->cityId, $options['notin_region']['citys'])
  136. ) {
  137. $item['free_goods_list'][] = $goodsItem['goods_id'];
  138. }
  139. }
  140. }
  141. return true;
  142. }
  143. /**
  144. * 计算当前配送模板的运费
  145. * @param $item
  146. * @return float|mixed|string
  147. */
  148. private function calcDeliveryAmount($item)
  149. {
  150. // 获取运费模板下商品总数量or总重量
  151. if (!$totality = $this->getItemGoodsTotal($item)) {
  152. return 0.00;
  153. }
  154. // 当前收货城市配送规则
  155. $deliveryRule = $this->getCityDeliveryRule($item['delivery']);
  156. if ($totality <= $deliveryRule['first']) {
  157. return $deliveryRule['first_fee'];
  158. }
  159. // 续件or续重 数量
  160. $additional = $totality - $deliveryRule['first'];
  161. if ($additional <= $deliveryRule['additional']) {
  162. return helper::bcadd($deliveryRule['first_fee'], $deliveryRule['additional_fee']);
  163. }
  164. // 计算续重/件金额
  165. if ($deliveryRule['additional'] < 1) {
  166. // 配送规则中续件为0
  167. $additionalFee = 0.00;
  168. } else {
  169. $additionalFee = helper::bcdiv($deliveryRule['additional_fee'], $deliveryRule['additional']) * $additional;
  170. }
  171. return helper::bcadd($deliveryRule['first_fee'], $additionalFee);
  172. }
  173. /**
  174. * 获取运费模板下商品总数量or总重量
  175. * @param $item
  176. * @return int|string
  177. */
  178. private function getItemGoodsTotal($item)
  179. {
  180. $totalWeight = 0; // 总重量
  181. $totalNum = 0; // 总数量
  182. foreach ($item['goodsList'] as $goodsItem) {
  183. // 如果商品为包邮,则不计算总量中
  184. if (!in_array($goodsItem['goods_id'], $item['free_goods_list'])) {
  185. $goodsWeight = helper::bcmul($goodsItem['goods_sku']['goods_weight'], $goodsItem['total_num']);
  186. $totalWeight = helper::bcadd($totalWeight, $goodsWeight);
  187. $totalNum = helper::bcadd($totalNum, $goodsItem['total_num']);
  188. }
  189. }
  190. return $item['delivery']['method']['value'] == 10 ? $totalNum : $totalWeight;
  191. }
  192. /**
  193. * 根据城市id获取规则信息
  194. * @param
  195. * @return array|false
  196. */
  197. private function getCityDeliveryRule($delivery)
  198. {
  199. foreach ($delivery['rule'] as $item) {
  200. if (in_array($this->cityId, $item['region_data'])) {
  201. return $item;
  202. }
  203. }
  204. return false;
  205. }
  206. /**
  207. * 整合运费模板
  208. * @return bool
  209. * @throws \think\db\exception\DataNotFoundException
  210. * @throws \think\db\exception\ModelNotFoundException
  211. * @throws \think\exception\DbException
  212. */
  213. private function initDeliveryTemplate()
  214. {
  215. // 运费模板ID集
  216. $deliveryIds = helper::getArrayColumn($this->goodsList, 'delivery_id');
  217. // 运费模板列表
  218. $deliveryList = (new DeliveryModel)->getListByIds(array_values(array_unique($deliveryIds)));
  219. // 整理数据集
  220. foreach ($deliveryList as $item) {
  221. $this->data[$item['delivery_id']]['delivery'] = $item;
  222. $this->data[$item['delivery_id']]['goodsList'] = $this->getGoodsListByDeliveryId($item['delivery_id']);
  223. }
  224. return true;
  225. }
  226. /**
  227. * 根据运费模板id整理商品集
  228. * @param $deliveryId
  229. * @return array
  230. */
  231. private function getGoodsListByDeliveryId($deliveryId)
  232. {
  233. $data = [];
  234. foreach ($this->goodsList as $item) {
  235. $item['delivery_id'] == $deliveryId && $data[] = $item;
  236. }
  237. return $data;
  238. }
  239. }