TemplateFreight.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358
  1. <?php
  2. /**
  3. * Created by PhpStorm.
  4. * User: Administrator
  5. * Date: 2020/8/18
  6. * Time: 10:58
  7. */
  8. namespace app\frontend\modules\dispatch\freight;
  9. use app\backend\modules\goods\models\Dispatch;
  10. use app\common\models\goods\GoodsDispatch;
  11. use app\common\modules\orderGoods\OrderGoodsCollection;
  12. use app\frontend\models\OrderGoods;
  13. use app\frontend\modules\order\models\PreOrder;
  14. use app\frontend\modules\orderGoods\models\PreOrderGoods;
  15. class TemplateFreight
  16. {
  17. protected $code = 'template';
  18. protected $name = '运费模板';
  19. /**
  20. * @var PreOrder
  21. */
  22. protected $order;
  23. /**
  24. * 金额
  25. * @var float
  26. */
  27. protected $freightAmount;
  28. /*
  29. * 排序:数值越低权重越大
  30. */
  31. protected $weight;
  32. /**
  33. * BaseFreight constructor.
  34. * @param PreOrder $order
  35. * @param $weight
  36. */
  37. public function __construct(PreOrder $order, $weight = 0)
  38. {
  39. $this->order = $order;
  40. $this->weight = $weight;
  41. }
  42. /**
  43. * 返回运费金额
  44. * @return float|mixed
  45. */
  46. public function getAmount()
  47. {
  48. if (!isset($this->freightAmount)) {
  49. $this->freightAmount = $this->_getAmount();
  50. }
  51. return $this->freightAmount;
  52. }
  53. public function needDispatch()
  54. {
  55. // 虚拟物品不需要配送
  56. if ($this->order->is_virtual) {
  57. return false;
  58. }
  59. return true;
  60. }
  61. protected function _getAmount()
  62. {
  63. //去掉重复的OrderGoods
  64. $uniqueOrderGoods = $this->order->orderGoods->unique('goods_id');
  65. $dispatch_prices = [];
  66. $dispatch_ids = $this->getDispatchIds($uniqueOrderGoods);
  67. trace_log()->freight('订单模板运费模板id', json_encode($dispatch_ids));
  68. foreach ($dispatch_ids as $dispatch_id) {
  69. $dispatch_prices[] = $this->getDispatchPrice($dispatch_id, $uniqueOrderGoods);
  70. }
  71. return max($dispatch_prices);
  72. }
  73. /**
  74. * 通过订单商品集合 获取所用到的配送模版 ID 集
  75. *
  76. * @param $orderGoodsCollection
  77. * @return array
  78. */
  79. private function getDispatchIds(OrderGoodsCollection $orderGoodsCollection)
  80. {
  81. $dispatch_ids = [];
  82. foreach ($orderGoodsCollection as $aOrderGoods) {
  83. /**
  84. * @var OrderGoods $aOrderGoods
  85. */
  86. $goodsDispatch = $aOrderGoods->goods->hasOneGoodsDispatch;
  87. if ($goodsDispatch->dispatch_type == GoodsDispatch::TEMPLATE_TYPE) {
  88. $dispatch_id = $goodsDispatch->dispatch_id;
  89. if (empty($dispatch_id)) {
  90. //$goodsDispatch->dispatch_id = $this->getDefaultDispatchId();
  91. //todo 从order类获取,这样插件订单类好复写
  92. $goodsDispatch->dispatch_id = $this->order->getPluginDefaultDispatchId();
  93. }
  94. if (!in_array($dispatch_id, $dispatch_ids)) {
  95. $dispatch_ids[] = $goodsDispatch->dispatch_id;
  96. }
  97. }
  98. }
  99. return $dispatch_ids;
  100. }
  101. private function getDefaultDispatchId()
  102. {
  103. $defaultDispatch = Dispatch::getOneByDefault();
  104. //todo 如果没有默认配送模版 如何处理
  105. trace_log()->freight('订单模板运费','不存在默认的配送模板');
  106. return $defaultDispatch->id ?: 0;
  107. }
  108. private function getDispatchPrice($dispatch_id, $orderGoods)
  109. {
  110. $dispatch_good_total = 0;
  111. $dispatch_good_weight = 0;
  112. foreach ($orderGoods as $aOrderGoods) {
  113. /**
  114. * @var OrderGoods $aOrderGoods
  115. */
  116. //商品满额、满件减免运费
  117. if ($aOrderGoods->isFreeShipping()) {
  118. trace_log()->freight('订单模板运费','商品'.$aOrderGoods->goods_id.'免运费');
  119. continue;
  120. }
  121. $dispatchModel = $aOrderGoods->goods->hasOneGoodsDispatch;
  122. //配送模版不存在
  123. if (!isset($dispatchModel)) {
  124. trace_log()->freight('订单模板运费','商品'.$aOrderGoods->goods_id.'运费模板不存在');
  125. continue;
  126. }
  127. //如果是默认配送模版
  128. if (!$dispatchModel->dispatch_id) {
  129. $dispatchModel->dispatch_id = $this->getDefaultDispatchId();
  130. }
  131. if ($dispatchModel->dispatch_type != GoodsDispatch::TEMPLATE_TYPE) {
  132. trace_log()->freight('订单模板运费','商品'.$aOrderGoods->goods_id.'配送费计算方式不是运费模板');
  133. continue;
  134. }
  135. if ($dispatchModel->dispatch_id != $dispatch_id) {
  136. trace_log()->freight('订单模板运费','商品'.$aOrderGoods->goods_id.'配送模板('.$dispatchModel->dispatch_id.')不匹配'.$dispatch_id);
  137. continue;
  138. }
  139. $dispatch_good_total += $this->getGoodsTotalInOrder($aOrderGoods);
  140. $dispatch_good_weight += $this->getGoodsTotalWeightInOrder($aOrderGoods);
  141. }
  142. $amount = $this->calculation($dispatch_id, $dispatch_good_total, $dispatch_good_weight);
  143. trace_log()->freight('订单模板运费','配送费'.$amount.'元');
  144. return $amount;
  145. }
  146. /**
  147. * 累加订单中每个 同类型不同规格商品 的总数
  148. * @param PreOrderGoods $orderGoods
  149. * @return mixed
  150. */
  151. private function getGoodsTotalInOrder(PreOrderGoods $orderGoods)
  152. {
  153. $result = $orderGoods->order->orderGoods->where('goods_id', $orderGoods->goods_id)->sum(function ($orderGoods) {
  154. return $orderGoods->total;
  155. });
  156. return $result;
  157. }
  158. /**
  159. * 累加订单中每个 同类型不同规格商品 的总重量
  160. * @param PreOrderGoods $orderGoods
  161. * @return mixed
  162. */
  163. private function getGoodsTotalWeightInOrder(PreOrderGoods $orderGoods)
  164. {
  165. $result = $orderGoods->order->orderGoods->where('goods_id', $orderGoods->goods_id)->sum(function ($orderGoods) {
  166. return $orderGoods->total * $orderGoods->getWeight();
  167. });
  168. return $result;
  169. }
  170. private function calculation($dispatch_id, $dispatch_good_total, $dispatch_good_weight)
  171. {
  172. $price = 0;
  173. if (!$dispatch_id) {
  174. trace_log()->freight('订单模板运费','配送模板id'.$dispatch_id.'不存在');
  175. return $price;
  176. }
  177. $dispatchModel = Dispatch::getOne($dispatch_id);
  178. if (!$dispatch_id) {
  179. trace_log()->freight('订单模板运费','配送模板id'.$dispatch_id.'不存在');
  180. return $price;
  181. }
  182. switch ($dispatchModel->calculate_type) {
  183. case 1:
  184. $price = $this->calculationByPiece($dispatchModel, $dispatch_good_total);
  185. break;
  186. case 0:
  187. $price = $this->calculationByWeight($dispatchModel, $dispatch_good_weight);
  188. break;
  189. }
  190. $price = $this->verify($price);
  191. return $price;
  192. }
  193. private function verify($price)
  194. {
  195. if (empty($price)) {
  196. return 0;
  197. }
  198. return $price;
  199. }
  200. private function calculationByPiece($dispatchModel, $goods_total)
  201. {
  202. if (!$goods_total) {
  203. return 0;
  204. }
  205. $piece_data = unserialize($dispatchModel->piece_data);
  206. // 存在
  207. if ($piece_data) {
  208. $dispatch = '';
  209. // 根据配送地址匹配区域数据
  210. $city_id = isset($this->order->orderAddress->city_id) ? $this->order->orderAddress->city_id : 0;
  211. if (!$city_id) {
  212. return 0;
  213. }
  214. foreach ($piece_data as $key => $piece) {
  215. $area_ids = explode(';', $piece['area_ids']);
  216. if (in_array($this->order->orderAddress->city_id, $area_ids)) {
  217. $dispatch = $piece;
  218. break;
  219. }
  220. }
  221. if ($dispatch) {
  222. // 找到匹配的数量数据
  223. if ($goods_total > $dispatch['first_piece']) {
  224. $diff = $goods_total - $dispatch['first_piece'];
  225. $another_piece = $dispatch['another_piece_price'];
  226. if ($diff > 0) {
  227. $another_piece = ceil($diff / $dispatch['another_piece']) * $dispatch['another_piece_price'];
  228. }
  229. return $dispatch['first_piece_price'] + $another_piece;
  230. } else {
  231. return $dispatch['first_piece_price'];
  232. }
  233. }
  234. }
  235. // 默认件数
  236. if ($goods_total > $dispatchModel->first_piece) {
  237. $diff = $goods_total - $dispatchModel->first_piece;
  238. $another_piece = $dispatchModel->another_piece_price;
  239. if ($diff > 0) {
  240. $another_piece = ceil($diff / $dispatchModel->another_piece) * $dispatchModel->another_piece_price;
  241. }
  242. return $dispatchModel->first_piece_price + $another_piece;
  243. } else {
  244. return $dispatchModel->first_piece_price;
  245. }
  246. }
  247. private function calculationByWeight($dispatchModel, $weight_total)
  248. {
  249. if (!$weight_total) {
  250. return 0;
  251. }
  252. $weight_data = unserialize($dispatchModel->weight_data);
  253. // 存在重量数据
  254. if ($weight_data) {
  255. $dispatch = '';
  256. // 根据配送地址匹配区域数据
  257. $city_id = isset($this->order->orderAddress->city_id) ? $this->order->orderAddress->city_id : '';
  258. if (!$city_id) {
  259. return 0;
  260. }
  261. foreach ($weight_data as $key => $weight) {
  262. //dd($weight['area_ids']);
  263. $area_ids = explode(';', $weight['area_ids']);
  264. if (in_array($city_id, $area_ids)) {
  265. $dispatch = $weight;
  266. break;
  267. }
  268. }
  269. if ($dispatch) {
  270. // 找到匹配的重量数据
  271. if ($weight_total > $dispatch['first_weight']) {
  272. // 续重: 首重价格+(重量-首重)/续重*续重价格
  273. // 20 + (500 - 400)
  274. return $dispatch['first_weight_price'] + ceil(($weight_total - $dispatch['first_weight']) / $dispatch['another_weight']) * $dispatch['another_weight_price'];
  275. } else {
  276. return $dispatch['first_weight_price'];
  277. }
  278. }
  279. }
  280. // 默认全国重量运费
  281. if ($weight_total > $dispatchModel->first_weight) {
  282. return $dispatchModel->first_weight_price + ceil(($weight_total - $dispatchModel->first_weight) / $dispatchModel->another_weight) * $dispatchModel->another_weight_price;
  283. } else {
  284. return $dispatchModel->first_weight_price;
  285. }
  286. }
  287. }