Express.php 7.9 KB

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